Browse Source

Merge pull request #12546 from nextcloud/bugfix/cannot-cast-previous-sort-group-state

Bugfix Check ClassCastException edge, Nullability of Bundle or Intent
Andy Scherzinger 1 năm trước cách đây
mục cha
commit
4f22097d6a

+ 89 - 0
app/src/androidTest/java/com/nextcloud/extensions/BundleExtensionTests.kt

@@ -0,0 +1,89 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Alper Ozturk
+ * Copyright (C) 2024 Alper Ozturk
+ * Copyright (C) 2024 Nextcloud GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package com.nextcloud.extensions
+
+import android.os.Bundle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.nextcloud.test.model.OtherTestData
+import com.nextcloud.test.model.TestData
+import com.nextcloud.test.model.TestDataParcelable
+import com.nextcloud.utils.extensions.getParcelableArgument
+import com.nextcloud.utils.extensions.getSerializableArgument
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@Suppress("FunctionNaming")
+@RunWith(AndroidJUnit4::class)
+class BundleExtensionTests {
+
+    private val key = "testDataKey"
+
+    @Test
+    fun test_get_serializable_argument_when_given_valid_bundle_should_return_expected_data() {
+        val bundle = Bundle()
+        val testObject = TestData("Hello")
+        bundle.putSerializable(key, testObject)
+        val retrievedObject = bundle.getSerializableArgument(key, TestData::class.java)
+        assertEquals(testObject, retrievedObject)
+    }
+
+    @Test
+    fun test_get_serializable_argument_when_given_valid_bundle_and_wrong_class_type_should_return_null() {
+        val bundle = Bundle()
+        val testObject = TestData("Hello")
+        bundle.putSerializable(key, testObject)
+        val retrievedObject = bundle.getSerializableArgument(key, Array<String>::class.java)
+        assertNull(retrievedObject)
+    }
+
+    @Test
+    fun test_get_parcelable_argument_when_given_valid_bundle_and_wrong_class_type_should_return_null() {
+        val bundle = Bundle()
+        val testObject = TestData("Hello")
+        bundle.putSerializable(key, testObject)
+        val retrievedObject = bundle.getParcelableArgument(key, OtherTestData::class.java)
+        assertNull(retrievedObject)
+    }
+
+    @Test
+    fun test_get_parcelable_argument_when_given_valid_bundle_should_return_expected_data() {
+        val bundle = Bundle()
+        val testObject = TestDataParcelable("Hello")
+        bundle.putParcelable(key, testObject)
+        val retrievedObject = bundle.getParcelableArgument(key, TestDataParcelable::class.java)
+        assertEquals(testObject, retrievedObject)
+    }
+
+    @Test
+    fun test_get_serializable_argument_when_given_null_bundle_should_return_null() {
+        val retrievedObject = (null as Bundle?).getSerializableArgument(key, TestData::class.java)
+        assertNull(retrievedObject)
+    }
+
+    @Test
+    fun test_get_parcelable_argument_when_given_null_bundle_should_return_null() {
+        val retrievedObject = (null as Bundle?).getParcelableArgument(key, TestDataParcelable::class.java)
+        assertNull(retrievedObject)
+    }
+}

+ 89 - 0
app/src/androidTest/java/com/nextcloud/extensions/IntentExtensionTests.kt

@@ -0,0 +1,89 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Alper Ozturk
+ * Copyright (C) 2024 Alper Ozturk
+ * Copyright (C) 2024 Nextcloud GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package com.nextcloud.extensions
+
+import android.content.Intent
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.nextcloud.test.model.OtherTestData
+import com.nextcloud.test.model.TestData
+import com.nextcloud.test.model.TestDataParcelable
+import com.nextcloud.utils.extensions.getParcelableArgument
+import com.nextcloud.utils.extensions.getSerializableArgument
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@Suppress("FunctionNaming")
+@RunWith(AndroidJUnit4::class)
+class IntentExtensionTests {
+
+    private val key = "testDataKey"
+
+    @Test
+    fun test_get_serializable_argument_when_given_valid_intent_should_return_expected_data() {
+        val intent = Intent()
+        val testObject = TestData("Hello")
+        intent.putExtra(key, testObject)
+        val retrievedObject = intent.getSerializableArgument(key, TestData::class.java)
+        assertEquals(testObject, retrievedObject)
+    }
+
+    @Test
+    fun test_get_serializable_argument_when_given_valid_intent_and_wrong_class_type_should_return_null() {
+        val intent = Intent()
+        val testObject = TestData("Hello")
+        intent.putExtra(key, testObject)
+        val retrievedObject = intent.getSerializableArgument(key, Array<String>::class.java)
+        assertNull(retrievedObject)
+    }
+
+    @Test
+    fun test_get_parcelable_argument_when_given_valid_intent_and_wrong_class_type_should_return_null() {
+        val intent = Intent()
+        val testObject = TestData("Hello")
+        intent.putExtra(key, testObject)
+        val retrievedObject = intent.getParcelableArgument(key, OtherTestData::class.java)
+        assertNull(retrievedObject)
+    }
+
+    @Test
+    fun test_get_parcelable_argument_when_given_valid_intent_should_return_expected_data() {
+        val intent = Intent()
+        val testObject = TestDataParcelable("Hello")
+        intent.putExtra(key, testObject)
+        val retrievedObject = intent.getParcelableArgument(key, TestDataParcelable::class.java)
+        assertEquals(testObject, retrievedObject)
+    }
+
+    @Test
+    fun test_get_serializable_argument_when_given_null_intent_should_return_null() {
+        val retrievedObject = (null as Intent?).getSerializableArgument(key, TestData::class.java)
+        assertNull(retrievedObject)
+    }
+
+    @Test
+    fun test_get_parcelable_argument_when_given_null_intent_should_return_null() {
+        val retrievedObject = (null as Intent?).getParcelableArgument(key, TestDataParcelable::class.java)
+        assertNull(retrievedObject)
+    }
+}

+ 54 - 0
app/src/androidTest/java/com/nextcloud/test/model/TestModels.kt

@@ -0,0 +1,54 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Alper Ozturk
+ * Copyright (C) 2024 Alper Ozturk
+ * Copyright (C) 2024 Nextcloud GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package com.nextcloud.test.model
+
+import android.os.Parcel
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+import java.io.Serializable
+
+@Parcelize
+class OtherTestData : Parcelable
+
+data class TestData(val message: String) : Serializable
+
+data class TestDataParcelable(val message: String) : Parcelable {
+    constructor(parcel: Parcel) : this(parcel.readString() ?: "")
+
+    override fun writeToParcel(parcel: Parcel, flags: Int) {
+        parcel.writeString(message)
+    }
+
+    override fun describeContents(): Int {
+        return 0
+    }
+
+    companion object CREATOR : Parcelable.Creator<TestDataParcelable> {
+        override fun createFromParcel(parcel: Parcel): TestDataParcelable {
+            return TestDataParcelable(parcel)
+        }
+
+        override fun newArray(size: Int): Array<TestDataParcelable?> {
+            return arrayOfNulls(size)
+        }
+    }
+}

+ 39 - 12
app/src/main/java/com/nextcloud/utils/extensions/BundleExtensions.kt

@@ -24,22 +24,49 @@ package com.nextcloud.utils.extensions
 import android.os.Build
 import android.os.Bundle
 import android.os.Parcelable
+import com.owncloud.android.lib.common.utils.Log_OC
 import java.io.Serializable
 
-fun <T : Serializable?> Bundle.getSerializableArgument(key: String, type: Class<T>): T? {
-    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
-        this.getSerializable(key, type)
-    } else {
-        @Suppress("UNCHECKED_CAST", "DEPRECATION")
-        this.getSerializable(key) as T
+@Suppress("TopLevelPropertyNaming")
+private const val tag = "BundleExtension"
+
+fun <T : Serializable?> Bundle?.getSerializableArgument(key: String, type: Class<T>): T? {
+    if (this == null) {
+        return null
+    }
+
+    return try {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+            this.getSerializable(key, type)
+        } else {
+            @Suppress("UNCHECKED_CAST", "DEPRECATION")
+            if (type.isInstance(this.getSerializable(key))) {
+                this.getSerializable(key) as T
+            } else {
+                null
+            }
+        }
+    } catch (e: ClassCastException) {
+        Log_OC.e(tag, e.localizedMessage)
+        null
     }
 }
 
-fun <T : Parcelable?> Bundle.getParcelableArgument(key: String, type: Class<T>): T? {
-    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
-        this.getParcelable(key, type)
-    } else {
-        @Suppress("DEPRECATION")
-        this.getParcelable(key)
+fun <T : Parcelable?> Bundle?.getParcelableArgument(key: String, type: Class<T>): T? {
+    if (this == null) {
+        return null
+    }
+
+    return try {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+            this.getParcelable(key, type)
+        } else {
+            @Suppress("DEPRECATION")
+            this.getParcelable(key)
+        }
+    } catch (e: ClassCastException) {
+        Log_OC.e(tag, e.localizedMessage)
+        e.printStackTrace()
+        null
     }
 }

+ 38 - 12
app/src/main/java/com/nextcloud/utils/extensions/IntentExtensions.kt

@@ -24,22 +24,48 @@ package com.nextcloud.utils.extensions
 import android.content.Intent
 import android.os.Build
 import android.os.Parcelable
+import com.owncloud.android.lib.common.utils.Log_OC
 import java.io.Serializable
 
-fun <T : Serializable?> Intent.getSerializableArgument(key: String, type: Class<T>): T? {
-    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
-        this.getSerializableExtra(key, type)
-    } else {
-        @Suppress("UNCHECKED_CAST", "DEPRECATION")
-        this.getSerializableExtra(key) as T
+@Suppress("TopLevelPropertyNaming")
+private const val tag = "IntentExtension"
+
+fun <T : Serializable?> Intent?.getSerializableArgument(key: String, type: Class<T>): T? {
+    if (this == null) {
+        return null
+    }
+
+    return try {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+            this.getSerializableExtra(key, type)
+        } else {
+            @Suppress("UNCHECKED_CAST", "DEPRECATION")
+            if (type.isInstance(this.getSerializableExtra(key))) {
+                this.getSerializableExtra(key) as T
+            } else {
+                null
+            }
+        }
+    } catch (e: ClassCastException) {
+        Log_OC.e(tag, e.localizedMessage)
+        null
     }
 }
 
-fun <T : Parcelable?> Intent.getParcelableArgument(key: String, type: Class<T>): T? {
-    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
-        this.getParcelableExtra(key, type)
-    } else {
-        @Suppress("DEPRECATION")
-        this.getParcelableExtra(key)
+fun <T : Parcelable?> Intent?.getParcelableArgument(key: String, type: Class<T>): T? {
+    if (this == null) {
+        return null
+    }
+
+    return try {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+            this.getParcelableExtra(key, type)
+        } else {
+            @Suppress("DEPRECATION")
+            this.getParcelableExtra(key)
+        }
+    } catch (e: ClassCastException) {
+        Log_OC.e(tag, e.localizedMessage)
+        null
     }
 }