From 566023bb763629a0f271b76bd9adf209e0f57773 Mon Sep 17 00:00:00 2001 From: Sebastian Zumbrunn Date: Wed, 27 Nov 2024 11:16:58 +0100 Subject: [PATCH] update tests fix tests to not rely on removed python versions --- .../python/checks/PytzUsageCheckTest.java | 10 ---- .../java/org/sonar/python/types/TypeShed.java | 3 + .../python/semantic/ClassSymbolImplTest.java | 11 +++- .../semantic/SymbolTableBuilderTest.java | 5 +- .../semantic/v2/TypeInferenceV2Test.java | 59 ++++++++++--------- .../TypeShedDescriptorsProviderTest.java | 20 +++---- .../org/sonar/python/types/TypeShedTest.java | 21 +++---- .../src/test/resources/semantic/symbols2.py | 4 +- .../sonar/plugins/python/IPynbSensorTest.java | 4 +- .../plugins/python/PythonSensorTest.java | 4 +- 10 files changed, 67 insertions(+), 74 deletions(-) diff --git a/python-checks/src/test/java/org/sonar/python/checks/PytzUsageCheckTest.java b/python-checks/src/test/java/org/sonar/python/checks/PytzUsageCheckTest.java index 2ad6200ca9..27baeb027b 100644 --- a/python-checks/src/test/java/org/sonar/python/checks/PytzUsageCheckTest.java +++ b/python-checks/src/test/java/org/sonar/python/checks/PytzUsageCheckTest.java @@ -22,17 +22,7 @@ import org.sonar.plugins.python.api.PythonVersionUtils; import org.sonar.python.checks.utils.PythonCheckVerifier; -import static org.assertj.core.api.Assertions.assertThat; - class PytzUsageCheckTest { - @Test - void test_38() { - ProjectPythonVersion.setCurrentVersions(EnumSet.of(PythonVersionUtils.Version.V_38)); - var issues = PythonCheckVerifier.issues("src/test/resources/checks/pytzUsage.py", new PytzUsageCheck()); - assertThat(issues) - .isEmpty(); - } - @Test void test_39_310_311_312() { ProjectPythonVersion diff --git a/python-frontend/src/main/java/org/sonar/python/types/TypeShed.java b/python-frontend/src/main/java/org/sonar/python/types/TypeShed.java index 99d9a7dc29..930da907d6 100644 --- a/python-frontend/src/main/java/org/sonar/python/types/TypeShed.java +++ b/python-frontend/src/main/java/org/sonar/python/types/TypeShed.java @@ -231,6 +231,9 @@ public static boolean isValidForProjectPythonVersion(List validForPython if (validForPythonVersions.isEmpty()) { return true; } + if(supportedPythonVersions == null) { + throw new IllegalStateException("supportedPythonVersion is uninitialized. Call builtinSymbols() first"); + } HashSet intersection = new HashSet<>(validForPythonVersions); intersection.retainAll(supportedPythonVersions); return !intersection.isEmpty(); diff --git a/python-frontend/src/test/java/org/sonar/python/semantic/ClassSymbolImplTest.java b/python-frontend/src/test/java/org/sonar/python/semantic/ClassSymbolImplTest.java index 536b7862f2..6f384e90c1 100644 --- a/python-frontend/src/test/java/org/sonar/python/semantic/ClassSymbolImplTest.java +++ b/python-frontend/src/test/java/org/sonar/python/semantic/ClassSymbolImplTest.java @@ -19,6 +19,7 @@ import com.google.protobuf.TextFormat; import java.util.Collections; import java.util.HashSet; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.sonar.plugins.python.api.symbols.AmbiguousSymbol; import org.sonar.plugins.python.api.symbols.ClassSymbol; @@ -36,6 +37,10 @@ import static org.sonar.python.PythonTestUtils.parse; class ClassSymbolImplTest { + @BeforeEach + void setup() { + TypeShed.resetBuiltinSymbols(); + } @Test void hasUnresolvedTypeHierarchy() { @@ -315,12 +320,12 @@ void overloaded_methods() throws TextFormat.ParseException { "methods {\n" + " name: \"foo\"\n" + " fully_qualified_name: \"mod.A.foo\"\n" + - " valid_for: \"36\"\n" + + " valid_for: \"39\"\n" + "}\n" + "overloaded_methods {\n" + " name: \"foo\"\n" + " fullname: \"mod.A.foo\"\n" + - " valid_for: \"39\"\n" + + " valid_for: \"310\"\n" + " definitions {\n" + " name: \"foo\"\n" + " fully_qualified_name: \"mod.A.foo\"\n" + @@ -334,7 +339,7 @@ void overloaded_methods() throws TextFormat.ParseException { ClassSymbolImpl classSymbol = new ClassSymbolImpl(classSymbol(protobuf), "mod"); Symbol foo = classSymbol.resolveMember("foo").get(); assertThat(foo.is(Symbol.Kind.AMBIGUOUS)).isTrue(); - assertThat(((SymbolImpl) foo).validForPythonVersions()).containsExactlyInAnyOrder("36", "39"); + assertThat(((SymbolImpl) foo).validForPythonVersions()).containsExactlyInAnyOrder("39", "310"); } private static SymbolsProtos.ClassSymbol classSymbol(String protobuf) throws TextFormat.ParseException { diff --git a/python-frontend/src/test/java/org/sonar/python/semantic/SymbolTableBuilderTest.java b/python-frontend/src/test/java/org/sonar/python/semantic/SymbolTableBuilderTest.java index e6cdb8a221..799df71478 100644 --- a/python-frontend/src/test/java/org/sonar/python/semantic/SymbolTableBuilderTest.java +++ b/python-frontend/src/test/java/org/sonar/python/semantic/SymbolTableBuilderTest.java @@ -273,8 +273,8 @@ void importing_stdlib() { FunctionDef functionDef = functionTreesByName.get("importing_stdlib"); Map symbolByName = getSymbolByName(functionDef); - assertThat(symbolByName.keySet()).containsOnly("math"); - assertThat(symbolByName.get("math").usages()).extracting(Usage::kind).containsExactly(Usage.Kind.IMPORT, Usage.Kind.OTHER); + assertThat(symbolByName.keySet()).containsOnly("os"); + assertThat(symbolByName.get("os").usages()).extracting(Usage::kind).containsExactly(Usage.Kind.IMPORT, Usage.Kind.OTHER); CallExpression callExpression = (CallExpression) ((ExpressionStatement) functionDef.body().statements().get(1)).expressions().get(0); Symbol qualifiedExpressionSymbol = callExpression.calleeSymbol(); @@ -282,7 +282,6 @@ void importing_stdlib() { assertThat(qualifiedExpressionSymbol.kind()).isEqualTo(Symbol.Kind.AMBIGUOUS); Symbol symbol = ((AmbiguousSymbolImpl) qualifiedExpressionSymbol).alternatives().iterator().next(); assertThat(symbol.kind()).isEqualTo(Symbol.Kind.FUNCTION); - assertThat(((FunctionSymbolImpl)symbol).declaredReturnType().canOnlyBe("float")).isTrue(); } @Test diff --git a/python-frontend/src/test/java/org/sonar/python/semantic/v2/TypeInferenceV2Test.java b/python-frontend/src/test/java/org/sonar/python/semantic/v2/TypeInferenceV2Test.java index 1a0a8dc6c3..06ce2f8e31 100644 --- a/python-frontend/src/test/java/org/sonar/python/semantic/v2/TypeInferenceV2Test.java +++ b/python-frontend/src/test/java/org/sonar/python/semantic/v2/TypeInferenceV2Test.java @@ -2646,48 +2646,49 @@ void unary_expression_of_variables_with_try_except(String code, PythonType expec @Test void imported_ambiguous_symbol() { FileInput fileInput = inferTypes(""" - from math import acos, atan - acos - atan - """); - UnionType acosType = (UnionType) ((ExpressionStatement) fileInput.statements().statements().get(1)).expressions().get(0).typeV2(); - assertThat(acosType.candidates()).allMatch(p -> p instanceof FunctionType); - assertThat(acosType.candidates()).extracting(PythonType::name).containsExactly("acos", "acos"); - assertThat(acosType.candidates()).map(FunctionType.class::cast).extracting(FunctionType::returnType).extracting(PythonType::unwrappedType).containsExactly(FLOAT_TYPE, - FLOAT_TYPE); - - UnionType atanType = (UnionType) ((ExpressionStatement) fileInput.statements().statements().get(2)).expressions().get(0).typeV2(); - assertThat(atanType.candidates()).allMatch(p -> p instanceof FunctionType); - assertThat(atanType.candidates()).extracting(PythonType::name).containsExactly("atan", "atan"); - assertThat(atanType.candidates()).map(FunctionType.class::cast).extracting(FunctionType::returnType).extracting(PythonType::unwrappedType).containsExactly(FLOAT_TYPE, - FLOAT_TYPE); + from os.path import realpath + realpath + """); + UnionType realpathType = (UnionType) ((ExpressionStatement) fileInput.statements().statements().get(1)).expressions().get(0).typeV2(); + assertThat(realpathType.candidates()).allMatch(p -> p instanceof FunctionType); + assertThat(realpathType.candidates()).extracting(PythonType::name).containsExactly("realpath", "realpath", "realpath", "realpath"); + assertThat(realpathType.candidates()) + .map(FunctionType.class::cast) + .extracting(FunctionType::returnType) + .extracting(PythonType::unwrappedType) + .containsExactly(PythonType.UNKNOWN, PythonType.UNKNOWN, PythonType.UNKNOWN, PythonType.UNKNOWN); } @Test void imported_ambiguous_symbol_try_except() { FileInput fileInput = inferTypes(""" try: - from math import acos - acos + from os.path import realpath + realpath except: ... - acos + realpath """); Expression acosExpr1 = TreeUtils.firstChild(fileInput.statements().statements().get(0), ExpressionStatement.class::isInstance) .map(ExpressionStatement.class::cast) .map(ExpressionStatement::expressions) .map(expressions -> expressions.get(0)) - .map(Expression.class::cast) .get(); UnionType acosType1 = (UnionType) acosExpr1.typeV2(); assertThat(acosType1.candidates()).allMatch(p -> p instanceof FunctionType); - assertThat(acosType1.candidates()).map(FunctionType.class::cast).extracting(FunctionType::returnType).extracting(PythonType::unwrappedType).containsExactly(FLOAT_TYPE, - FLOAT_TYPE); + assertThat(acosType1.candidates()) + .map(FunctionType.class::cast) + .extracting(FunctionType::returnType) + .extracting(PythonType::unwrappedType) + .containsExactly(PythonType.UNKNOWN, PythonType.UNKNOWN, PythonType.UNKNOWN, PythonType.UNKNOWN); UnionType acosType2 = (UnionType) ((ExpressionStatement) fileInput.statements().statements().get(1)).expressions().get(0).typeV2(); assertThat(acosType2.candidates()).allMatch(p -> p instanceof FunctionType); - assertThat(acosType2.candidates()).map(FunctionType.class::cast).extracting(FunctionType::returnType).extracting(PythonType::unwrappedType).containsExactly(FLOAT_TYPE, - FLOAT_TYPE); + assertThat(acosType2.candidates()) + .map(FunctionType.class::cast) + .extracting(FunctionType::returnType) + .extracting(PythonType::unwrappedType) + .containsExactly(PythonType.UNKNOWN, PythonType.UNKNOWN, PythonType.UNKNOWN, PythonType.UNKNOWN); } @Test @@ -2971,18 +2972,18 @@ def lock(): void basic_imported_symbols() { FileInput fileInput = inferTypes( """ - import fcntl, math + import fcntl, os.path fcntl - math + os.path """ ); PythonType fnctlModule = ((ExpressionStatement) fileInput.statements().statements().get(1)).expressions().get(0).typeV2(); assertThat(fnctlModule).isInstanceOf(ModuleType.class); assertThat(fnctlModule.name()).isEqualTo("fcntl"); - PythonType mathModule = ((ExpressionStatement) fileInput.statements().statements().get(2)).expressions().get(0).typeV2(); - assertThat(mathModule).isInstanceOf(ModuleType.class); - assertThat(mathModule.name()).isEqualTo("math"); - assertThat(((UnionType) mathModule.resolveMember("acos").get()).candidates()).allMatch(FunctionType.class::isInstance); + PythonType osPathModule = ((ExpressionStatement) fileInput.statements().statements().get(2)).expressions().get(0).typeV2(); + assertThat(osPathModule).isInstanceOf(ModuleType.class); + assertThat(osPathModule.name()).isEqualTo("path"); + assertThat(((UnionType) osPathModule.resolveMember("realpath").get()).candidates()).allMatch(FunctionType.class::isInstance); } // TODO SONARPY-2176 ProjectLevelSymbolTable#getType should be able to resolve types when there is a conflict between a member and a subpackage diff --git a/python-frontend/src/test/java/org/sonar/python/semantic/v2/typeshed/TypeShedDescriptorsProviderTest.java b/python-frontend/src/test/java/org/sonar/python/semantic/v2/typeshed/TypeShedDescriptorsProviderTest.java index 48ed526868..9d4bb46676 100644 --- a/python-frontend/src/test/java/org/sonar/python/semantic/v2/typeshed/TypeShedDescriptorsProviderTest.java +++ b/python-frontend/src/test/java/org/sonar/python/semantic/v2/typeshed/TypeShedDescriptorsProviderTest.java @@ -68,9 +68,9 @@ void builtin312DescriptorsTest() { void builtinFloatDisambiguation() { var provider = typeshedDescriptorsProvider(); var builtinDescriptors = provider.builtinDescriptors(); - var floatDescriptor = builtinDescriptors.get("float"); + var floatDescriptor = builtinDescriptors.get("int"); assertThat(floatDescriptor.kind()).isEqualTo(Descriptor.Kind.CLASS); - var newMember = ((ClassDescriptor) floatDescriptor).members().stream().filter(m -> m.name().equals("__new__")).findFirst().get(); + var newMember = ((ClassDescriptor) floatDescriptor).members().stream().filter(m -> m.name().equals("to_bytes")).findFirst().get(); assertThat(newMember.kind()).isEqualTo(Descriptor.Kind.AMBIGUOUS); assertThat(((AmbiguousDescriptor) newMember).alternatives()).hasSize(2); } @@ -79,9 +79,9 @@ void builtinFloatDisambiguation() { void builtinFloatNoDisambiguation() { var provider = new TypeShedDescriptorsProvider(Set.of(), Set.of(PythonVersionUtils.Version.V_312)); var builtinDescriptors = provider.builtinDescriptors(); - var floatDescriptor = builtinDescriptors.get("float"); + var floatDescriptor = builtinDescriptors.get("int"); assertThat(floatDescriptor.kind()).isEqualTo(Descriptor.Kind.CLASS); - var newMember = ((ClassDescriptor) floatDescriptor).members().stream().filter(m -> m.name().equals("__new__")).findFirst().get(); + var newMember = ((ClassDescriptor) floatDescriptor).members().stream().filter(m -> m.name().equals("to_bytes")).findFirst().get(); assertThat(newMember.kind()).isEqualTo(Descriptor.Kind.FUNCTION); } @@ -111,13 +111,13 @@ void cacheTest() { @Test void stdlibDescriptors() { var provider = typeshedDescriptorsProvider(); - var mathDescriptors = provider.descriptorsForModule("math"); - var descriptor = mathDescriptors.get("acos"); + var osPathDescriptor = provider.descriptorsForModule("os.path"); + var descriptor = osPathDescriptor.get("realpath"); assertThat(descriptor.kind()).isEqualTo(Descriptor.Kind.AMBIGUOUS); - var acosDescriptor = ((AmbiguousDescriptor) descriptor).alternatives().iterator().next(); - assertThat(acosDescriptor.kind()).isEqualTo(Descriptor.Kind.FUNCTION); - assertThat(((FunctionDescriptor) acosDescriptor).parameters()).hasSize(1); - assertThat(((FunctionDescriptor) acosDescriptor).annotatedReturnTypeName()).isEqualTo("float"); + var realPathDescriptor = ((AmbiguousDescriptor) descriptor).alternatives().iterator().next(); + assertThat(realPathDescriptor.kind()).isEqualTo(Descriptor.Kind.FUNCTION); + assertThat(((FunctionDescriptor) realPathDescriptor).parameters()).hasSizeBetween(1, 2); + assertThat(((FunctionDescriptor) realPathDescriptor).annotatedReturnTypeName()).isNull(); var threadingSymbols = provider.descriptorsForModule("threading"); assertThat(threadingSymbols.get("Thread").kind()).isEqualTo(Descriptor.Kind.CLASS); diff --git a/python-frontend/src/test/java/org/sonar/python/types/TypeShedTest.java b/python-frontend/src/test/java/org/sonar/python/types/TypeShedTest.java index 59f35826e4..36c3436d1c 100644 --- a/python-frontend/src/test/java/org/sonar/python/types/TypeShedTest.java +++ b/python-frontend/src/test/java/org/sonar/python/types/TypeShedTest.java @@ -83,12 +83,6 @@ void str() { assertThat(strClass.resolveMember("removeprefix")).isNotEmpty(); assertThat(strClass.resolveMember("removesuffix")).isNotEmpty(); - setPythonVersions(PythonVersionUtils.fromString("3.8")); - strClass = TypeShed.typeShedClass("str"); - assertThat(strClass.superClasses()).extracting(Symbol::kind, Symbol::name).containsExactlyInAnyOrder(tuple(Kind.CLASS, "Sequence")); - assertThat(strClass.resolveMember("removeprefix")).isEmpty(); - assertThat(strClass.resolveMember("removesuffix")).isEmpty(); - setPythonVersions(PythonVersionUtils.allVersions()); } @@ -120,14 +114,15 @@ void typing_module() { @Test void stdlib_symbols() { - Map mathSymbols = symbolsForModule("math"); - Symbol symbol = mathSymbols.get("acos"); + Map mathSymbols = symbolsForModule("os.path"); + Symbol symbol = mathSymbols.get("realpath"); assertThat(symbol.kind()).isEqualTo(Symbol.Kind.AMBIGUOUS); Symbol acosSymbol = ((AmbiguousSymbolImpl) symbol).alternatives().iterator().next(); assertThat(acosSymbol.kind()).isEqualTo(Kind.FUNCTION); - assertThat(((FunctionSymbolImpl) acosSymbol).parameters()).hasSize(1); - assertThat(((FunctionSymbolImpl) acosSymbol).declaredReturnType().canOnlyBe("float")).isTrue(); - assertThat(TypeShed.symbolWithFQN("math", "math.acos")).isSameAs(symbol); + FunctionSymbolImpl acosFunctionsymbol = (FunctionSymbolImpl) acosSymbol; + assertThat(acosFunctionsymbol.parameters()).hasSizeBetween(1, 2); + assertThat(acosFunctionsymbol.declaredReturnType()).isInstanceOf(AnyType.class); + assertThat(TypeShed.symbolWithFQN("os.path", "os.path.realpath")).isSameAs(symbol); assertThat(mathSymbols.values()).allMatch(s -> s.usages().isEmpty()); Map threadingSymbols = symbolsForModule("threading"); @@ -450,7 +445,7 @@ void function_symbols_from_protobuf() throws TextFormat.ParseException { @Test void pythonVersions() { Symbol range = TypeShed.builtinSymbols().get("range"); - assertThat(((SymbolImpl) range).validForPythonVersions()).containsExactlyInAnyOrder( "36", "37", "38", "39", "310", "311"); + assertThat(((SymbolImpl) range).validForPythonVersions()).containsExactlyInAnyOrder( "39", "310", "311", "312", "313"); assertThat(range.kind()).isEqualTo(Kind.CLASS); // python 2 @@ -462,7 +457,7 @@ void pythonVersions() { // python 3 setPythonVersions(PythonVersionUtils.fromString("3.8")); range = TypeShed.builtinSymbols().get("range"); - assertThat(((SymbolImpl) range).validForPythonVersions()).containsExactlyInAnyOrder("36", "37", "38", "39", "310", "311"); + assertThat(((SymbolImpl) range).validForPythonVersions()).containsExactlyInAnyOrder("39", "310", "311", "312", "313"); assertThat(range.kind()).isEqualTo(Kind.CLASS); setPythonVersions(PythonVersionUtils.fromString("3.10")); diff --git a/python-frontend/src/test/resources/semantic/symbols2.py b/python-frontend/src/test/resources/semantic/symbols2.py index fd6879c2bb..603b57bdd2 100644 --- a/python-frontend/src/test/resources/semantic/symbols2.py +++ b/python-frontend/src/test/resources/semantic/symbols2.py @@ -186,8 +186,8 @@ def assignment_expression_in_dict_comprehension(): something(last) def importing_stdlib(): - import math - math.acos(0) + import os.path + os.path.realpath("") def importing_submodule(): diff --git a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/IPynbSensorTest.java b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/IPynbSensorTest.java index cbb5624ef5..3bf9a58bc6 100644 --- a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/IPynbSensorTest.java +++ b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/IPynbSensorTest.java @@ -159,12 +159,12 @@ void test_python_version_parameter() { PythonInputFile inputFile = inputFile(FILE_1); activeRules = new ActiveRulesBuilder().build(); - context.setSettings(new MapSettings().setProperty("sonar.python.version", "3.8")); + context.setSettings(new MapSettings().setProperty("sonar.python.version", "3.13")); PythonIndexer pythonIndexer = pythonIndexer(List.of(inputFile)); sensor(pythonIndexer).execute(context); - assertThat(ProjectPythonVersion.currentVersions()).containsExactly(PythonVersionUtils.Version.V_38); + assertThat(ProjectPythonVersion.currentVersions()).containsExactly(PythonVersionUtils.Version.V_313); } private IPynbSensor notebookSensor() { diff --git a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/PythonSensorTest.java b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/PythonSensorTest.java index e95e5f88f4..8704388e16 100644 --- a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/PythonSensorTest.java +++ b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/PythonSensorTest.java @@ -680,9 +680,9 @@ void test_python_version_parameter_no_warning() { activeRules = new ActiveRulesBuilder().build(); - context.setSettings(new MapSettings().setProperty("sonar.python.version", "3.8")); + context.setSettings(new MapSettings().setProperty("sonar.python.version", "3.13")); sensor().execute(context); - assertThat(ProjectPythonVersion.currentVersions()).containsExactly(PythonVersionUtils.Version.V_38); + assertThat(ProjectPythonVersion.currentVersions()).containsExactly(PythonVersionUtils.Version.V_313); assertThat(logTester.logs(Level.WARN)).doesNotContain(PythonSensor.UNSET_VERSION_WARNING); verify(analysisWarning, times(0)).addUnique(PythonSensor.UNSET_VERSION_WARNING); }