diff options
author | Aaron Vaage <vaage@google.com> | 2022-07-20 12:04:38 -0700 |
---|---|---|
committer | Aaron Vaage <vaage@google.com> | 2022-08-10 14:48:47 +0000 |
commit | debbd4706b60cfe5380a7c54475ba057b5ae4655 (patch) | |
tree | c58735c4bd13b8e7074cfc53fe697de1ca553479 | |
parent | 81668564896d9ccb63fceb8cc6014108e568c2bb (diff) | |
download | idea-debbd4706b60cfe5380a7c54475ba057b5ae4655.tar.gz |
Added tests for TraceSignatureConverter
Added tests for TraceSignatureConverter. This did require making some
changes to TraceSignatureConverter in order to make it easier to test
(changing a method signature). Since this code works with PSI types,
which are hard to initialize, the tests do rely heavily on mocks.
Change-Id: Iee2aeac51dcf110f58738c6f489f2d1a4e95d83b
5 files changed, 147 insertions, 19 deletions
diff --git a/codenavigation/BUILD b/codenavigation/BUILD index 74bd2dbad80..9c83dbf8f4f 100644 --- a/codenavigation/BUILD +++ b/codenavigation/BUILD @@ -13,5 +13,6 @@ iml_module( "//tools/adt/idea/.idea/libraries:truth[test]", "//tools/adt/idea/native-symbolizer[module]", "//tools/adt/idea/android:intellij.android.core[module]", + "//tools/adt/idea/.idea/libraries:mockito[test]", ], ) diff --git a/codenavigation/codenavigation.iml b/codenavigation/codenavigation.iml index 2044b7850a4..21d293ed98c 100644 --- a/codenavigation/codenavigation.iml +++ b/codenavigation/codenavigation.iml @@ -12,5 +12,6 @@ <orderEntry type="library" scope="TEST" name="truth" level="project" /> <orderEntry type="module" module-name="native-symbolizer" /> <orderEntry type="module" module-name="intellij.android.core" /> + <orderEntry type="library" scope="TEST" name="mockito" level="project" /> </component> </module>
\ No newline at end of file diff --git a/codenavigation/src/com/android/tools/idea/codenavigation/PsiNavSource.kt b/codenavigation/src/com/android/tools/idea/codenavigation/PsiNavSource.kt index d846b775529..5e3835021ff 100644 --- a/codenavigation/src/com/android/tools/idea/codenavigation/PsiNavSource.kt +++ b/codenavigation/src/com/android/tools/idea/codenavigation/PsiNavSource.kt @@ -21,6 +21,7 @@ import com.intellij.pom.Navigatable import com.intellij.psi.PsiClass import com.intellij.psi.PsiManager import com.intellij.psi.PsiMethod +import com.intellij.psi.PsiSubstitutor import com.intellij.psi.util.ClassUtil /** @@ -85,7 +86,12 @@ class PsiNavSource(private val project: Project): NavSource { return null } - val methods = psiClass.findMethodsByName(location.methodName, true) - return methods.firstOrNull{ location.signature == TraceSignatureConverter.getTraceSignature(it) } + return psiClass.findMethodsByName(location.methodName, true).firstOrNull{ + val signature = TraceSignatureConverter.getTraceSignature( + it.returnType, + it.getSignature(PsiSubstitutor.EMPTY).parameterTypes) + + return@firstOrNull location.signature == signature + } } }
\ No newline at end of file diff --git a/codenavigation/src/com/android/tools/idea/codenavigation/TraceSignatureConverter.java b/codenavigation/src/com/android/tools/idea/codenavigation/TraceSignatureConverter.java index 586be96180e..e606c6332a1 100644 --- a/codenavigation/src/com/android/tools/idea/codenavigation/TraceSignatureConverter.java +++ b/codenavigation/src/com/android/tools/idea/codenavigation/TraceSignatureConverter.java @@ -18,16 +18,15 @@ package com.android.tools.idea.codenavigation; import com.google.common.collect.ImmutableMap; import com.intellij.psi.PsiArrayType; import com.intellij.psi.PsiClassType; -import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiPrimitiveType; -import com.intellij.psi.PsiSubstitutor; import com.intellij.psi.PsiType; import com.intellij.psi.util.TypeConversionUtil; import java.util.Map; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.VisibleForTesting; -final class TraceSignatureConverter { +public final class TraceSignatureConverter { /** * Mapping from java primitive type encoding to PsiPrimitiveType * More about java primitive type encoding: https://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#getName() @@ -54,15 +53,18 @@ final class TraceSignatureConverter { * void => "V" */ @Nullable - private static String convertToString(@NotNull PsiType psiType) { - if (psiType instanceof PsiArrayType) { - return '[' + convertToString(((PsiArrayType)psiType).getComponentType()); + @VisibleForTesting + public static String convertToString(@NotNull PsiType psiType) { + PsiType sanitizedType = TypeConversionUtil.erasure(psiType); + + if (sanitizedType instanceof PsiArrayType) { + return '[' + convertToString(((PsiArrayType)sanitizedType).getComponentType()); } - else if (psiType instanceof PsiPrimitiveType) { - return String.valueOf(PRIMITIVE_TYPES.get(psiType)); + else if (sanitizedType instanceof PsiPrimitiveType) { + return String.valueOf(PRIMITIVE_TYPES.get(sanitizedType)); } - else if (psiType instanceof PsiClassType) { - return "L" + psiType.getCanonicalText().replace('.', '/') + ";"; + else if (sanitizedType instanceof PsiClassType) { + return "L" + sanitizedType.getCanonicalText().replace('.', '/') + ";"; } return null; } @@ -75,18 +77,15 @@ final class TraceSignatureConverter { * returns (Ljava/util/List;Ljava/util/ArrayList;Z[[Ljava/lang/Integer;)I */ @NotNull - public static String getTraceSignature(@NotNull PsiMethod method) { + public static String getTraceSignature(@Nullable PsiType returnType, @NotNull PsiType[] parameterTypes) { StringBuilder signature = new StringBuilder("("); - for (PsiType type : method.getSignature(PsiSubstitutor.EMPTY).getParameterTypes()) { - String converted = convertToString(TypeConversionUtil.erasure(type)); - signature.append(converted); + for (PsiType type : parameterTypes) { + signature.append(convertToString(type)); } signature.append(")"); - PsiType returnType = method.getReturnType(); if (returnType != null) { - String converted = convertToString(TypeConversionUtil.erasure(returnType)); - signature.append(converted); + signature.append(convertToString(returnType)); } return signature.toString(); } diff --git a/codenavigation/testSrc/TraceSignatureConverterTest.kt b/codenavigation/testSrc/TraceSignatureConverterTest.kt new file mode 100644 index 00000000000..607bff4cb04 --- /dev/null +++ b/codenavigation/testSrc/TraceSignatureConverterTest.kt @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.android.tools.idea.codenavigation.TraceSignatureConverter +import com.google.common.truth.Truth.assertThat +import com.intellij.psi.PsiArrayType +import com.intellij.psi.PsiClassType +import com.intellij.psi.PsiType +import com.intellij.psi.PsiTypeVisitor +import org.junit.Test +import org.mockito.Mockito.any +import org.mockito.Mockito.mock +import org.mockito.Mockito.`when` + +class TraceSignatureConverterTest { + // Testing with PSI types is challenging since they are not trivial to create. To work around + // this we mock the PsiClassType which allows us to test our logic. The hardest part of mocking + // them comes from the "accept visitor" functionality which is used to resolve generics. This is + // why we needed to mock "accept", "resolve", and "rawTypes". + companion object { + fun createPsiTypeFor(className: String): PsiType { + val type = mock(PsiClassType::class.java) + `when`(type.canonicalText).thenReturn(className) + `when`(type.accept(any<PsiTypeVisitor<*>>())).thenCallRealMethod() + `when`(type.resolve()).thenReturn(null) + `when`(type.rawType()).thenReturn(type) // We are our own raw type. + + return type + } + + fun createPsiTypeForGeneric(className: String, rawName: String): PsiType { + val rawType = mock(PsiClassType::class.java) + `when`(rawType.canonicalText).thenReturn(rawName) + `when`(rawType.accept(any<PsiTypeVisitor<*>>())).thenCallRealMethod() + `when`(rawType.resolve()).thenReturn(null) + `when`(rawType.rawType()).thenReturn(rawType) // We are our own raw type. + + val genericType = mock(PsiClassType::class.java) + `when`(genericType.canonicalText).thenReturn(className) + `when`(genericType.accept(any<PsiTypeVisitor<*>>())).thenCallRealMethod() + `when`(genericType.resolve()).thenReturn(null) + `when`(genericType.rawType()).thenReturn(rawType) + + return genericType + } + } + + @Test + fun convertsPrimitiveToString() { + val str = TraceSignatureConverter.convertToString(PsiType.BYTE) + assertThat(str).isEqualTo("B") + } + + @Test + fun convertsArrayToString() { + val type = createPsiTypeFor("java.lang.String") + val array = PsiArrayType(PsiArrayType(PsiArrayType(type))) + + val str = TraceSignatureConverter.convertToString(array) + assertThat(str).isEqualTo("[[[Ljava/lang/String;") + } + + @Test + fun convertClassToString() { + val type = createPsiTypeFor("java.util.ArrayList") + + val str = TraceSignatureConverter.convertToString(type) + assertThat(str).isEqualTo("Ljava/util/ArrayList;") + } + + @Test + fun convertGenericClassToString() { + val type = createPsiTypeForGeneric("java.util.ArrayList<T>", "java.util.ArrayList") + + val str = TraceSignatureConverter.convertToString(type) + assertThat(str).isEqualTo("Ljava/util/ArrayList;") + } + + @Test + fun convertsVoidToString() { + val str = TraceSignatureConverter.convertToString(PsiType.VOID) + assertThat(str).isEqualTo("V") + } + + @Test + fun convertsEmptyMethodToString() { + val str = TraceSignatureConverter.getTraceSignature(PsiType.INT, emptyArray()) + assertThat(str).isEqualTo("()I") + } + + @Test + fun convertsMethodToString() { + val str = TraceSignatureConverter.getTraceSignature(PsiType.INT, arrayOf( + createPsiTypeForGeneric("java.util.List<String>", "java.util.List"), + createPsiTypeForGeneric("java.util.ArrayList<T>", "java.util.ArrayList"), + PsiType.BOOLEAN, + PsiArrayType(PsiArrayType(createPsiTypeFor("java.lang.Integer"))) + )) + + assertThat(str).isEqualTo("(Ljava/util/List;Ljava/util/ArrayList;Z[[Ljava/lang/Integer;)I") + } + + @Test + fun convertsMethodWithNoReturnValueToString() { + val str = TraceSignatureConverter.getTraceSignature(null, arrayOf(PsiType.BOOLEAN)) + assertThat(str).isEqualTo("(Z)") + } +}
\ No newline at end of file |