From b47cce49cd05c4d22d54f36c3cc82571d35f9a60 Mon Sep 17 00:00:00 2001 From: Neil Green Date: Sat, 20 Oct 2018 08:13:25 +0100 Subject: [PATCH 1/2] handle methods from java.lang.Object Object.toString() Object.equals(Object other) Object.hashCode() --- .../main/java/com/tinder/scarlet/Scarlet.kt | 27 +++++++-- .../java/com/tinder/scarlet/ScarletTest.kt | 59 +++++++++++++++++++ 2 files changed, 82 insertions(+), 4 deletions(-) diff --git a/scarlet/src/main/java/com/tinder/scarlet/Scarlet.kt b/scarlet/src/main/java/com/tinder/scarlet/Scarlet.kt index 1801e277..a88b1459 100644 --- a/scarlet/src/main/java/com/tinder/scarlet/Scarlet.kt +++ b/scarlet/src/main/java/com/tinder/scarlet/Scarlet.kt @@ -20,6 +20,7 @@ import com.tinder.scarlet.retry.ExponentialBackoffStrategy import com.tinder.scarlet.streamadapter.builtin.BuiltInStreamAdapterFactory import io.reactivex.schedulers.Schedulers import java.lang.reflect.InvocationHandler +import java.lang.reflect.Method import java.lang.reflect.Proxy /** @@ -117,13 +118,31 @@ class Scarlet internal constructor( private fun createInvocationHandler(serviceInterface: Class<*>, serviceInstance: Service): InvocationHandler = InvocationHandler { proxy, method, nullableArgs -> val args = nullableArgs ?: arrayOf() - if (runtimePlatform.isDefaultMethod(method)) { - runtimePlatform.invokeDefaultMethod(method, serviceInterface, proxy, args) - } else { - serviceInstance.execute(method, args) + when { + runtimePlatform.isDefaultMethod(method) -> runtimePlatform.invokeDefaultMethod(method, serviceInterface, proxy, args) + isJavaObjectMethod(method) -> handleJavaObjectMethod(method, serviceInstance, serviceInterface, proxy, args) + else -> serviceInstance.execute(method, args) } } + private fun isJavaObjectMethod(method: Method) = method.declaringClass == Object::class.java + + private fun handleJavaObjectMethod(method: Method, serviceInstance: Service, serviceInterface: Class<*>, proxy: Any, args: Array): Any { + return when { + isEquals(method) -> proxy === args[0] + isToString(method) -> "Scarlet service implementation for ${serviceInterface.name}" + isHashCode(method) -> serviceInstance.hashCode() + else -> throw IllegalStateException("Cannot execute $method") + } + } + + private fun isHashCode(method: Method) = method.name == "hashCode" && method.parameterTypes.isEmpty() + + private fun isToString(method: Method) = method.name == "toString" && method.parameterTypes.isEmpty() + + private fun isEquals(method: Method) = + method.name == "equals" && arrayOf(Object::class.java).contentEquals(method.parameterTypes) + /** * Build a new [Scarlet] instance. * diff --git a/scarlet/src/test/java/com/tinder/scarlet/ScarletTest.kt b/scarlet/src/test/java/com/tinder/scarlet/ScarletTest.kt index a98ae049..c0130f39 100644 --- a/scarlet/src/test/java/com/tinder/scarlet/ScarletTest.kt +++ b/scarlet/src/test/java/com/tinder/scarlet/ScarletTest.kt @@ -12,6 +12,7 @@ import com.tinder.scarlet.internal.Service import com.tinder.scarlet.internal.utils.RuntimePlatform import com.tinder.scarlet.ws.Receive import com.tinder.scarlet.ws.Send +import org.assertj.core.api.Assertions.assertThat import org.junit.Test import org.junit.runner.RunWith import org.mockito.junit.MockitoJUnitRunner @@ -73,6 +74,64 @@ internal class ScarletTest { then(service).should().execute(ExampleService::class.java.getDeclaredMethod("receive"), emptyArray()) } + @Test + fun create_hashCode_shouldEqualServiceInstanceHashCode() { + // Given + val service = mock() + given(serviceFactory.create(ExampleService::class.java)).willReturn(service) + val exampleService = scarlet.create() + + // When + val hashCode = exampleService.hashCode() + + // Then + assertThat(hashCode).isEqualTo(service.hashCode()) + } + + @Test + fun create_toString_shouldProduceCorrectValue() { + // Given + val service = mock() + given(serviceFactory.create(ExampleService::class.java)).willReturn(service) + val exampleService = scarlet.create() + + // When + val toString = exampleService.toString() + + // Then + assertThat(toString) + .isEqualTo("Scarlet service implementation for com.tinder.scarlet.ScarletTest\$Companion\$ExampleService") + } + + @Test + fun create_equals_shouldEqualSelf() { + // Given + val service = mock() + given(serviceFactory.create(ExampleService::class.java)).willReturn(service) + val exampleService = scarlet.create() + + // When + val equalsSelf = exampleService.equals(exampleService) + + // Then + assert(equalsSelf) { "equals must be reflexive" } + } + + @Test + fun create_equals_shouldNotEqualOther() { + // Given + val service = mock() + given(serviceFactory.create(ExampleService::class.java)).willReturn(service) + val exampleService = scarlet.create() + val otherExampleService = scarlet.create() + + // When + val equalsOther = exampleService.equals(otherExampleService) + + // Then + assert(!equalsOther) { "should not equal other instance" } + } + @Suppress("UNUSED") companion object { interface ExampleService { From fb9088a4416131da2a40018af7646db7c901c97c Mon Sep 17 00:00:00 2001 From: Neil Green Date: Sat, 20 Oct 2018 21:19:08 +0100 Subject: [PATCH 2/2] use assertj to assert equality --- scarlet/src/test/java/com/tinder/scarlet/ScarletTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scarlet/src/test/java/com/tinder/scarlet/ScarletTest.kt b/scarlet/src/test/java/com/tinder/scarlet/ScarletTest.kt index c0130f39..a8040407 100644 --- a/scarlet/src/test/java/com/tinder/scarlet/ScarletTest.kt +++ b/scarlet/src/test/java/com/tinder/scarlet/ScarletTest.kt @@ -114,7 +114,7 @@ internal class ScarletTest { val equalsSelf = exampleService.equals(exampleService) // Then - assert(equalsSelf) { "equals must be reflexive" } + assertThat(equalsSelf).describedAs("equals must be reflexive").isTrue() } @Test @@ -129,7 +129,7 @@ internal class ScarletTest { val equalsOther = exampleService.equals(otherExampleService) // Then - assert(!equalsOther) { "should not equal other instance" } + assertThat(equalsOther).describedAs("should not equal other instance").isFalse() } @Suppress("UNUSED")