From e3c3100f21b4afdcd5180d1c1dc61d24f1e09931 Mon Sep 17 00:00:00 2001 From: Marek Safar Date: Tue, 9 May 2017 16:50:55 +0200 Subject: Test framework (#101) --- linker/Mono.Linker.Steps/MarkStep.cs | 29 +- linker/Mono.Linker.Steps/ResolveFromXmlStep.cs | 11 +- linker/Tests/Extensions/CecilExtensions.cs | 122 +++ linker/Tests/Extensions/NiceIO.cs | 891 +++++++++++++++++++++ .../BaseExpectedLinkedBehaviorAttribute.cs | 11 + .../Assertions/IgnoreTestCaseAttribute.cs | 13 + .../Assertions/KeptAssemblyAttribute.cs | 16 + .../Assertions/KeptAttribute.cs | 7 + .../Assertions/KeptAttributeAttribute.cs | 15 + .../Assertions/KeptBackingFieldAttribute.cs | 8 + .../Assertions/KeptBaseTypeAttribute.cs | 22 + .../Assertions/KeptInterfaceAttribute.cs | 16 + .../Assertions/KeptMemberAttribute.cs | 13 + .../Assertions/RemovedAssemblyAttribute.cs | 16 + .../Metadata/BaseMetadataAttribute.cs | 9 + .../Metadata/CoreLinkAttribute.cs | 13 + .../Metadata/NotATestCaseAttribute.cs | 7 + .../Metadata/ReferenceAttribute.cs | 13 + .../Metadata/SandboxDependencyAttribute.cs | 13 + .../Mono.Linker.Tests.Cases.Expectations.csproj | 64 ++ .../Advanced/DeadCodeElimination1.cs | 19 + .../Advanced/FieldThatOnlyGetsSetIsRemoved.cs | 21 + .../Attributes/AttributeOnPreservedTypeIsKept.cs | 19 + ...PreservedTypeWithTypeUsedInConstructorIsKept.cs | 26 + ...edTypeWithTypeUsedInDifferentNamespaceIsKept.cs | 32 + ...buteOnPreservedTypeWithTypeUsedInFieldIsKept.cs | 25 + ...servedTypeWithTypeUsedInPropertySetterIsKept.cs | 26 + .../AttributeOnPreservedTypeWithUsedSetter.cs | 19 + .../Attributes/AttributeOnUsedFieldIsKept.cs | 23 + .../Attributes/AttributeOnUsedMethodIsKept.cs | 26 + .../Attributes/AttributeOnUsedPropertyIsKept.cs | 26 + .../Basic/ComplexNestedClassesHasUnusedRemoved.cs | 26 + ...thodImplementedOnBaseClassDoesNotGetStripped.cs | 37 + ...MultiLevelNestedClassesAllRemovedWhenNonUsed.cs | 19 + .../Basic/NestedDelegateInvokeMethodsPreserved.cs | 24 + .../Basic/UninvokedInterfaceMemberGetsRemoved.cs | 24 + .../Basic/UnusedClassGetsRemoved.cs | 10 + .../Basic/UnusedFieldGetsRemoved.cs | 20 + .../Basic/UnusedMethodGetsRemoved.cs | 22 + .../Basic/UnusedNestedClassGetsRemoved.cs | 11 + .../Basic/UnusedPropertyGetsRemoved.cs | 20 + .../Basic/UnusedPropertySetterRemoved.cs | 17 + .../Basic/UsedPropertyIsKept.cs | 19 + .../LinkingOfCoreLibrariesRemovesUnusedTypes.cs | 15 + ...ctOverloadedMethodGetsStrippedInGenericClass.cs | 37 + ...OfGenericParametersUnusedBaseWillGetStripped.cs | 30 + ...fferentGenericArgumentNameDoesNotGetStripped.cs | 30 + ...verrideUsesADifferentNameForGenericParameter.cs | 27 + ...sADifferentNameForGenericParameterNestedCase.cs | 31 + ...ADifferentNameForGenericParameterNestedCase2.cs | 31 + ...alMethodOfSameNameWithDifferentParameterType.cs | 34 + ...adedGenericMethodInGenericClassIsNotStripped.cs | 21 + ...ricMethodInstanceInGenericClassIsNotStripped.cs | 21 + ...edGenericMethodWithNoParametersIsNotStripped.cs | 27 + ...ldsOfTypeMarkedSequentialLayoutAreNotRemoved.cs | 17 + .../DefaultConstructorOfReturnTypeIsNotRemoved.cs | 22 + .../UnusedDefaultConstructorIsRemoved.cs | 27 + ...aultConstructorOfTypePassedByRefIsNotRemoved.cs | 28 + .../UnusedFieldsOfTypesAreNotRemoved.cs | 23 + .../UnusedFieldsOfTypesPassedByRefAreNotRemoved.cs | 23 + .../UnusedFieldsOfTypesWhenHasThisAreNotRemoved.cs | 21 + .../DefaultConstructorOfReturnTypeIsNotRemoved.cs | 22 + .../PInvoke/UnusedDefaultConstructorIsRemoved.cs | 27 + ...aultConstructorOfTypePassedByRefIsNotRemoved.cs | 28 + .../PInvoke/UnusedFieldsOfTypesAreNotRemoved.cs | 23 + .../UnusedFieldsOfTypesPassedByRefAreNotRemoved.cs | 23 + .../UnusedTypeWithSequentialLayoutIsRemoved.cs | 15 + ...rveFieldsHasBackingFieldsOfPropertiesRemoved.cs | 72 ++ ...veFieldsHasBackingFieldsOfPropertiesRemoved.xml | 6 + .../LinkXml/UnusedFieldPreservedByLinkXmlIsKept.cs | 17 + .../UnusedFieldPreservedByLinkXmlIsKept.xml | 7 + .../UnusedMethodPreservedByLinkXmlIsKept.cs | 21 + .../UnusedMethodPreservedByLinkXmlIsKept.xml | 7 + .../UnusedNestedTypePreservedByLinkXmlIsKept.cs | 14 + .../UnusedNestedTypePreservedByLinkXmlIsKept.xml | 5 + .../UnusedPropertyPreservedByLinkXmlIsKept.cs | 26 + .../UnusedPropertyPreservedByLinkXmlIsKept.xml | 10 + .../LinkXml/UnusedTypePreservedByLinkXmlIsKept.cs | 14 + .../LinkXml/UnusedTypePreservedByLinkXmlIsKept.xml | 5 + ...sedTypeWithPreserveAllHasAllMembersPreserved.cs | 85 ++ ...edTypeWithPreserveAllHasAllMembersPreserved.xml | 5 + ...nusedTypeWithPreserveFieldsHasMethodsRemoved.cs | 66 ++ ...usedTypeWithPreserveFieldsHasMethodsRemoved.xml | 7 + ...nusedTypeWithPreserveMethodsHasFieldsRemoved.cs | 74 ++ ...usedTypeWithPreserveMethodsHasFieldsRemoved.xml | 5 + ...sedTypeWithPreserveNothingAndPreserveMembers.cs | 26 + ...edTypeWithPreserveNothingAndPreserveMembers.xml | 8 + ...usedTypeWithPreserveNothingHasMembersRemoved.cs | 50 ++ ...sedTypeWithPreserveNothingHasMembersRemoved.xml | 5 + .../Mono.Linker.Tests.Cases.csproj | 140 ++++ .../ReferencesAreRemovedWhenAllUsagesAreRemoved.cs | 20 + .../Statics/UnusedStaticConstructorGetsRemoved.cs | 20 + .../Statics/UnusedStaticMethodGetsRemoved.cs | 23 + ...sImplemtingInterfaceMethodsThroughBaseClass2.cs | 29 + ...sImplemtingInterfaceMethodsThroughBaseClass3.cs | 26 + ...sImplemtingInterfaceMethodsThroughBaseClass4.cs | 30 + ...sImplemtingInterfaceMethodsThroughBaseClass5.cs | 29 + ...sImplemtingInterfaceMethodsThroughBaseClass6.cs | 38 + ...HarderToDetectUnusedVirtualMethodGetsRemoved.cs | 30 + ...edThatImplementsAlreadyMarkedInterfaceMethod.cs | 35 + .../VirtualMethods/UnusedVirtualMethodRemoved.cs | 24 + .../VirtualMethods/UsedVirtualMethodNotRemoved.cs | 29 + ...alMethodGetsPerservedIfBaseMethodGetsInvoked.cs | 28 + ...ippedIfImplementingMethodGetsInvokedDirectly.cs | 27 + linker/Tests/Mono.Linker.Tests.csproj | 44 +- .../AbstractLinkingTestFixture.cs | 167 ---- .../Tests/Mono.Linker.Tests/AbstractTestFixture.cs | 118 --- linker/Tests/Mono.Linker.Tests/AssemblyInfo.cs | 42 - .../Mono.Linker.Tests/IntegrationTestFixture.cs | 123 --- .../Tests/Mono.Linker.Tests/LinkingTestFixture.cs | 67 -- .../Mono.Linker.Tests/ResolveLinkedAssemblyStep.cs | 80 -- .../Mono.Linker.Tests/XmlLinkingTestFixture.cs | 118 --- linker/Tests/TestCases/Integration/Crypto/Test.cs | 16 - linker/Tests/TestCases/Integration/Crypto/Test.exe | Bin 3072 -> 0 bytes .../TestCases/Linker/MultipleReferences/Bar.cs | 18 - .../TestCases/Linker/MultipleReferences/Baz.cs | 15 - .../TestCases/Linker/MultipleReferences/Foo.cs | 22 - .../TestCases/Linker/MultipleReferences/Program.cs | 23 - linker/Tests/TestCases/Linker/Simple/Library.cs | 39 - linker/Tests/TestCases/Linker/Simple/Program.cs | 18 - .../Tests/TestCases/Linker/VirtualCall/Library.cs | 27 - .../Tests/TestCases/Linker/VirtualCall/Library.dll | Bin 3072 -> 0 bytes linker/Tests/TestCases/Linker/VirtualCall/Makefile | 5 - .../Tests/TestCases/Linker/VirtualCall/Program.cs | 20 - .../Tests/TestCases/Linker/VirtualCall/Program.exe | Bin 3072 -> 0 bytes linker/Tests/TestCases/MarkAttribute.cs | 9 - linker/Tests/TestCases/NotLinkedAttribute.cs | 9 - linker/Tests/TestCases/TestCase.cs | 42 + linker/Tests/TestCases/TestCases.csproj | 69 -- linker/Tests/TestCases/TestDatabase.cs | 108 +++ linker/Tests/TestCases/TestSuites.cs | 76 ++ linker/Tests/TestCases/Xml/Generics/Library.cs | 61 -- linker/Tests/TestCases/Xml/Generics/desc.xml | 5 - linker/Tests/TestCases/Xml/Interface/Library.cs | 29 - linker/Tests/TestCases/Xml/Interface/desc.xml | 5 - linker/Tests/TestCases/Xml/NestedNested/Library.cs | 19 - linker/Tests/TestCases/Xml/NestedNested/desc.xml | 5 - .../Xml/PreserveFieldsRequired/Library.cs | 35 - .../TestCases/Xml/PreserveFieldsRequired/desc.xml | 7 - .../TestCases/Xml/ReferenceInAttributes/LibLib.cs | 23 - .../TestCases/Xml/ReferenceInAttributes/Library.cs | 81 -- .../TestCases/Xml/ReferenceInAttributes/desc.xml | 5 - .../Xml/ReferenceInVirtualMethod/Library.cs | 37 - .../Xml/ReferenceInVirtualMethod/desc.xml | 5 - linker/Tests/TestCases/Xml/SimpleXml/Library.cs | 48 -- linker/Tests/TestCases/Xml/SimpleXml/desc.xml | 10 - linker/Tests/TestCases/Xml/XmlPattern/Library.cs | 36 - linker/Tests/TestCases/Xml/XmlPattern/desc.xml | 7 - linker/Tests/TestCasesRunner/AssemblyChecker.cs | 323 ++++++++ .../Tests/TestCasesRunner/ExpectationsProvider.cs | 12 + linker/Tests/TestCasesRunner/LinkXmlHelpers.cs | 30 + .../Tests/TestCasesRunner/LinkedTestCaseResult.cs | 19 + .../Tests/TestCasesRunner/LinkerArgumentBuilder.cs | 42 + linker/Tests/TestCasesRunner/LinkerDriver.cs | 8 + .../TestCasesRunner/ManagedCompilationResult.cs | 15 + linker/Tests/TestCasesRunner/ObjectFactory.cs | 31 + linker/Tests/TestCasesRunner/ResultChecker.cs | 62 ++ linker/Tests/TestCasesRunner/TestCaseCollector.cs | 125 +++ linker/Tests/TestCasesRunner/TestCaseCompiler.cs | 41 + .../Tests/TestCasesRunner/TestCaseLinkerOptions.cs | 5 + .../TestCasesRunner/TestCaseMetadaProvider.cs | 75 ++ linker/Tests/TestCasesRunner/TestCaseSandbox.cs | 83 ++ linker/Tests/TestCasesRunner/TestRunner.cs | 78 ++ .../Tests/Tests/PreserveActionComparisonTests.cs | 30 + 164 files changed, 4713 insertions(+), 1445 deletions(-) create mode 100644 linker/Tests/Extensions/CecilExtensions.cs create mode 100644 linker/Tests/Extensions/NiceIO.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/BaseExpectedLinkedBehaviorAttribute.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/IgnoreTestCaseAttribute.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptAssemblyAttribute.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptAttribute.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptAttributeAttribute.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptBackingFieldAttribute.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptBaseTypeAttribute.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptInterfaceAttribute.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptMemberAttribute.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/RemovedAssemblyAttribute.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases.Expectations/Metadata/BaseMetadataAttribute.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases.Expectations/Metadata/CoreLinkAttribute.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases.Expectations/Metadata/NotATestCaseAttribute.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases.Expectations/Metadata/ReferenceAttribute.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases.Expectations/Metadata/SandboxDependencyAttribute.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases.Expectations/Mono.Linker.Tests.Cases.Expectations.csproj create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Advanced/DeadCodeElimination1.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Advanced/FieldThatOnlyGetsSetIsRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnPreservedTypeIsKept.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnPreservedTypeWithTypeUsedInConstructorIsKept.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnPreservedTypeWithTypeUsedInDifferentNamespaceIsKept.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnPreservedTypeWithTypeUsedInFieldIsKept.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnPreservedTypeWithTypeUsedInPropertySetterIsKept.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnPreservedTypeWithUsedSetter.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnUsedFieldIsKept.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnUsedMethodIsKept.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnUsedPropertyIsKept.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Basic/ComplexNestedClassesHasUnusedRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Basic/InterfaceMethodImplementedOnBaseClassDoesNotGetStripped.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Basic/MultiLevelNestedClassesAllRemovedWhenNonUsed.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Basic/NestedDelegateInvokeMethodsPreserved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Basic/UninvokedInterfaceMemberGetsRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Basic/UnusedClassGetsRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Basic/UnusedFieldGetsRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Basic/UnusedMethodGetsRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Basic/UnusedNestedClassGetsRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Basic/UnusedPropertyGetsRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Basic/UnusedPropertySetterRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Basic/UsedPropertyIsKept.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/CoreLink/LinkingOfCoreLibrariesRemovesUnusedTypes.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Generics/CorrectOverloadedMethodGetsStrippedInGenericClass.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Generics/DerivedClassWithMethodOfSameNameAsBaseButDifferentNumberOfGenericParametersUnusedBaseWillGetStripped.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Generics/GenericInstanceInterfaceMethodImplementedWithDifferentGenericArgumentNameDoesNotGetStripped.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Generics/MethodWithParameterWhichHasGenericParametersAndOverrideUsesADifferentNameForGenericParameter.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Generics/MethodWithParameterWhichHasGenericParametersAndOverrideUsesADifferentNameForGenericParameterNestedCase.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Generics/MethodWithParameterWhichHasGenericParametersAndOverrideUsesADifferentNameForGenericParameterNestedCase2.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Generics/OverrideWithAnotherVirtualMethodOfSameNameWithDifferentParameterType.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Generics/UsedOverloadedGenericMethodInGenericClassIsNotStripped.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Generics/UsedOverloadedGenericMethodInstanceInGenericClassIsNotStripped.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Generics/UsedOverloadedGenericMethodWithNoParametersIsNotStripped.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Interop/FieldsOfTypeMarkedSequentialLayoutAreNotRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Interop/InternalCalls/DefaultConstructorOfReturnTypeIsNotRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Interop/InternalCalls/UnusedDefaultConstructorIsRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Interop/InternalCalls/UnusedDefaultConstructorOfTypePassedByRefIsNotRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Interop/InternalCalls/UnusedFieldsOfTypesAreNotRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Interop/InternalCalls/UnusedFieldsOfTypesPassedByRefAreNotRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Interop/InternalCalls/UnusedFieldsOfTypesWhenHasThisAreNotRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Interop/PInvoke/DefaultConstructorOfReturnTypeIsNotRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Interop/PInvoke/UnusedDefaultConstructorIsRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Interop/PInvoke/UnusedDefaultConstructorOfTypePassedByRefIsNotRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Interop/PInvoke/UnusedFieldsOfTypesAreNotRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Interop/PInvoke/UnusedFieldsOfTypesPassedByRefAreNotRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Interop/UnusedTypeWithSequentialLayoutIsRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/LinkXml/TypeWithPreserveFieldsHasBackingFieldsOfPropertiesRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/LinkXml/TypeWithPreserveFieldsHasBackingFieldsOfPropertiesRemoved.xml create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedFieldPreservedByLinkXmlIsKept.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedFieldPreservedByLinkXmlIsKept.xml create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedMethodPreservedByLinkXmlIsKept.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedMethodPreservedByLinkXmlIsKept.xml create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedNestedTypePreservedByLinkXmlIsKept.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedNestedTypePreservedByLinkXmlIsKept.xml create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedPropertyPreservedByLinkXmlIsKept.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedPropertyPreservedByLinkXmlIsKept.xml create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypePreservedByLinkXmlIsKept.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypePreservedByLinkXmlIsKept.xml create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveAllHasAllMembersPreserved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveAllHasAllMembersPreserved.xml create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveFieldsHasMethodsRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveFieldsHasMethodsRemoved.xml create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveMethodsHasFieldsRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveMethodsHasFieldsRemoved.xml create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveNothingAndPreserveMembers.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveNothingAndPreserveMembers.xml create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveNothingHasMembersRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveNothingHasMembersRemoved.xml create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Mono.Linker.Tests.Cases.csproj create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/References/ReferencesAreRemovedWhenAllUsagesAreRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Statics/UnusedStaticConstructorGetsRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/Statics/UnusedStaticMethodGetsRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/ClassImplemtingInterfaceMethodsThroughBaseClass2.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/ClassImplemtingInterfaceMethodsThroughBaseClass3.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/ClassImplemtingInterfaceMethodsThroughBaseClass4.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/ClassImplemtingInterfaceMethodsThroughBaseClass5.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/ClassImplemtingInterfaceMethodsThroughBaseClass6.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/HarderToDetectUnusedVirtualMethodGetsRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/TypeGetsMarkedThatImplementsAlreadyMarkedInterfaceMethod.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/UnusedVirtualMethodRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/UsedVirtualMethodNotRemoved.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/VirtualMethodGetsPerservedIfBaseMethodGetsInvoked.cs create mode 100644 linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/VirtualMethodGetsStrippedIfImplementingMethodGetsInvokedDirectly.cs delete mode 100644 linker/Tests/Mono.Linker.Tests/AbstractLinkingTestFixture.cs delete mode 100644 linker/Tests/Mono.Linker.Tests/AbstractTestFixture.cs delete mode 100644 linker/Tests/Mono.Linker.Tests/AssemblyInfo.cs delete mode 100644 linker/Tests/Mono.Linker.Tests/IntegrationTestFixture.cs delete mode 100644 linker/Tests/Mono.Linker.Tests/LinkingTestFixture.cs delete mode 100644 linker/Tests/Mono.Linker.Tests/ResolveLinkedAssemblyStep.cs delete mode 100644 linker/Tests/Mono.Linker.Tests/XmlLinkingTestFixture.cs delete mode 100644 linker/Tests/TestCases/Integration/Crypto/Test.cs delete mode 100755 linker/Tests/TestCases/Integration/Crypto/Test.exe delete mode 100644 linker/Tests/TestCases/Linker/MultipleReferences/Bar.cs delete mode 100644 linker/Tests/TestCases/Linker/MultipleReferences/Baz.cs delete mode 100644 linker/Tests/TestCases/Linker/MultipleReferences/Foo.cs delete mode 100644 linker/Tests/TestCases/Linker/MultipleReferences/Program.cs delete mode 100644 linker/Tests/TestCases/Linker/Simple/Library.cs delete mode 100644 linker/Tests/TestCases/Linker/Simple/Program.cs delete mode 100644 linker/Tests/TestCases/Linker/VirtualCall/Library.cs delete mode 100644 linker/Tests/TestCases/Linker/VirtualCall/Library.dll delete mode 100644 linker/Tests/TestCases/Linker/VirtualCall/Makefile delete mode 100644 linker/Tests/TestCases/Linker/VirtualCall/Program.cs delete mode 100644 linker/Tests/TestCases/Linker/VirtualCall/Program.exe delete mode 100644 linker/Tests/TestCases/MarkAttribute.cs delete mode 100644 linker/Tests/TestCases/NotLinkedAttribute.cs create mode 100644 linker/Tests/TestCases/TestCase.cs delete mode 100644 linker/Tests/TestCases/TestCases.csproj create mode 100644 linker/Tests/TestCases/TestDatabase.cs create mode 100644 linker/Tests/TestCases/TestSuites.cs delete mode 100644 linker/Tests/TestCases/Xml/Generics/Library.cs delete mode 100644 linker/Tests/TestCases/Xml/Generics/desc.xml delete mode 100644 linker/Tests/TestCases/Xml/Interface/Library.cs delete mode 100644 linker/Tests/TestCases/Xml/Interface/desc.xml delete mode 100644 linker/Tests/TestCases/Xml/NestedNested/Library.cs delete mode 100644 linker/Tests/TestCases/Xml/NestedNested/desc.xml delete mode 100644 linker/Tests/TestCases/Xml/PreserveFieldsRequired/Library.cs delete mode 100644 linker/Tests/TestCases/Xml/PreserveFieldsRequired/desc.xml delete mode 100644 linker/Tests/TestCases/Xml/ReferenceInAttributes/LibLib.cs delete mode 100644 linker/Tests/TestCases/Xml/ReferenceInAttributes/Library.cs delete mode 100644 linker/Tests/TestCases/Xml/ReferenceInAttributes/desc.xml delete mode 100644 linker/Tests/TestCases/Xml/ReferenceInVirtualMethod/Library.cs delete mode 100644 linker/Tests/TestCases/Xml/ReferenceInVirtualMethod/desc.xml delete mode 100644 linker/Tests/TestCases/Xml/SimpleXml/Library.cs delete mode 100644 linker/Tests/TestCases/Xml/SimpleXml/desc.xml delete mode 100755 linker/Tests/TestCases/Xml/XmlPattern/Library.cs delete mode 100755 linker/Tests/TestCases/Xml/XmlPattern/desc.xml create mode 100644 linker/Tests/TestCasesRunner/AssemblyChecker.cs create mode 100644 linker/Tests/TestCasesRunner/ExpectationsProvider.cs create mode 100644 linker/Tests/TestCasesRunner/LinkXmlHelpers.cs create mode 100644 linker/Tests/TestCasesRunner/LinkedTestCaseResult.cs create mode 100644 linker/Tests/TestCasesRunner/LinkerArgumentBuilder.cs create mode 100644 linker/Tests/TestCasesRunner/LinkerDriver.cs create mode 100644 linker/Tests/TestCasesRunner/ManagedCompilationResult.cs create mode 100644 linker/Tests/TestCasesRunner/ObjectFactory.cs create mode 100644 linker/Tests/TestCasesRunner/ResultChecker.cs create mode 100644 linker/Tests/TestCasesRunner/TestCaseCollector.cs create mode 100644 linker/Tests/TestCasesRunner/TestCaseCompiler.cs create mode 100644 linker/Tests/TestCasesRunner/TestCaseLinkerOptions.cs create mode 100644 linker/Tests/TestCasesRunner/TestCaseMetadaProvider.cs create mode 100644 linker/Tests/TestCasesRunner/TestCaseSandbox.cs create mode 100644 linker/Tests/TestCasesRunner/TestRunner.cs create mode 100644 linker/Tests/Tests/PreserveActionComparisonTests.cs (limited to 'linker') diff --git a/linker/Mono.Linker.Steps/MarkStep.cs b/linker/Mono.Linker.Steps/MarkStep.cs index b0c5025c5..d92d06ad1 100644 --- a/linker/Mono.Linker.Steps/MarkStep.cs +++ b/linker/Mono.Linker.Steps/MarkStep.cs @@ -1124,7 +1124,7 @@ namespace Mono.Linker.Steps { MarkMethods (type); break; case TypePreserve.Fields: - MarkFields (type, true); + MarkFields (type, true, true); break; case TypePreserve.Methods: MarkMethods (type); @@ -1150,7 +1150,7 @@ namespace Mono.Linker.Steps { MarkMethodCollection (list); } - protected void MarkFields (TypeDefinition type, bool includeStatic) + protected void MarkFields (TypeDefinition type, bool includeStatic, bool markBackingFieldsOnlyIfPropertyMarked = false) { if (!type.HasFields) return; @@ -1158,10 +1158,35 @@ namespace Mono.Linker.Steps { foreach (FieldDefinition field in type.Fields) { if (!includeStatic && field.IsStatic) continue; + + if (markBackingFieldsOnlyIfPropertyMarked && field.Name.EndsWith (">k__BackingField")) { + // We can't reliably construct the expected property name from the backing field name for all compilers + // because csc shortens the name of the backing field in some cases + // For example: + // Field Name = .Bar>k__BackingField + // Property Name = IFoo.Bar + // + // instead we will search the properties and find the one that makes use of the current backing field + var propertyDefinition = SearchPropertiesForMatchingFieldDefinition (field); + if (propertyDefinition != null && !Annotations.IsMarked (propertyDefinition)) + continue; + } MarkField (field); } } + static PropertyDefinition SearchPropertiesForMatchingFieldDefinition (FieldDefinition field) + { + foreach (var property in field.DeclaringType.Properties) { + foreach (var ins in property.GetMethod.Body.Instructions) { + if (ins.Operand != null && ins.Operand == field) + return property; + } + } + + return null; + } + protected void MarkStaticFields(TypeDefinition type) { if (!type.HasFields) diff --git a/linker/Mono.Linker.Steps/ResolveFromXmlStep.cs b/linker/Mono.Linker.Steps/ResolveFromXmlStep.cs index 948e98649..582069911 100644 --- a/linker/Mono.Linker.Steps/ResolveFromXmlStep.cs +++ b/linker/Mono.Linker.Steps/ResolveFromXmlStep.cs @@ -223,15 +223,8 @@ namespace Mono.Linker.Steps { } } - switch (preserve) { - case TypePreserve.Nothing: - if (!nav.HasChildren) - Annotations.SetPreserve (type, TypePreserve.All); - break; - default: + if (preserve != TypePreserve.Nothing) Annotations.SetPreserve (type, preserve); - break; - } if (nav.HasChildren) { MarkSelectedFields (nav, type); @@ -261,7 +254,7 @@ namespace Mono.Linker.Steps { { string attribute = GetAttribute (nav, _preserve); if (attribute == null || attribute.Length == 0) - return TypePreserve.Nothing; + return nav.HasChildren ? TypePreserve.Nothing : TypePreserve.All; try { return (TypePreserve) Enum.Parse (typeof (TypePreserve), attribute, true); diff --git a/linker/Tests/Extensions/CecilExtensions.cs b/linker/Tests/Extensions/CecilExtensions.cs new file mode 100644 index 000000000..747abf570 --- /dev/null +++ b/linker/Tests/Extensions/CecilExtensions.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Mono.Cecil; + +namespace Mono.Linker.Tests.Extensions { + public static class CecilExtensions { + public static IEnumerable AllDefinedTypes (this AssemblyDefinition assemblyDefinition) + { + return assemblyDefinition.Modules.SelectMany (m => m.AllDefinedTypes ()); + } + + public static IEnumerable AllDefinedTypes (this ModuleDefinition moduleDefinition) + { + foreach (var typeDefinition in moduleDefinition.Types) { + yield return typeDefinition; + + foreach (var definition in typeDefinition.AllDefinedTypes ()) + yield return definition; + } + } + + public static IEnumerable AllDefinedTypes (this TypeDefinition typeDefinition) + { + foreach (var nestedType in typeDefinition.NestedTypes) { + yield return nestedType; + + foreach (var definition in nestedType.AllDefinedTypes ()) + yield return definition; + } + } + + public static IEnumerable AllMembers (this ModuleDefinition module) + { + foreach (var type in module.AllDefinedTypes ()) { + yield return type; + + foreach (var member in type.AllMembers ()) + yield return member; + } + } + + public static IEnumerable AllMembers (this TypeDefinition type) + { + foreach (var field in type.Fields) + yield return field; + + foreach (var prop in type.Properties) + yield return prop; + + foreach (var method in type.Methods) + yield return method; + + foreach (var @event in type.Events) + yield return @event; + } + + public static bool HasAttribute (this ICustomAttributeProvider provider, string name) + { + return provider.CustomAttributes.Any (ca => ca.AttributeType.Name == name); + } + + public static bool HasAttributeDerivedFrom (this ICustomAttributeProvider provider, string name) + { + return provider.CustomAttributes.Any (ca => ca.AttributeType.Resolve ().DerivesFrom (name)); + } + + public static bool DerivesFrom (this TypeDefinition type, string baseTypeName) + { + if (type.Name == baseTypeName) + return true; + + if (type.BaseType == null) + return false; + + if (type.BaseType.Name == baseTypeName) + return true; + + return type.BaseType.Resolve ().DerivesFrom (baseTypeName); + } + + public static PropertyDefinition GetPropertyDefinition (this MethodDefinition method) + { + if (!method.IsSetter && !method.IsGetter) + throw new ArgumentException (); + + var propertyName = method.Name.Substring (4); + return method.DeclaringType.Properties.First (p => p.Name == propertyName); + } + + public static string GetSignature (this MethodDefinition method) + { + var builder = new StringBuilder (); + builder.Append (method.Name); + if (method.HasGenericParameters) { + builder.Append ('<'); + + for (int i = 0; i < method.GenericParameters.Count - 1; i++) + builder.Append ($"{method.GenericParameters [i]},"); + + builder.Append ($"{method.GenericParameters [method.GenericParameters.Count - 1]}>"); + } + + builder.Append ("("); + + if (method.HasParameters) { + for (int i = 0; i < method.Parameters.Count - 1; i++) { + // TODO: modifiers + // TODO: default values + builder.Append ($"{method.Parameters [i].ParameterType},"); + } + + builder.Append (method.Parameters [method.Parameters.Count - 1].ParameterType); + } + + builder.Append (")"); + + return builder.ToString (); + } + } +} \ No newline at end of file diff --git a/linker/Tests/Extensions/NiceIO.cs b/linker/Tests/Extensions/NiceIO.cs new file mode 100644 index 000000000..2bded17f1 --- /dev/null +++ b/linker/Tests/Extensions/NiceIO.cs @@ -0,0 +1,891 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace Mono.Linker.Tests.Extensions +{ + public class NPath : IEquatable, IComparable + { + private static readonly StringComparison PathStringComparison = IsLinux() ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; + + private readonly string[] _elements; + private readonly bool _isRelative; + private readonly string _driveLetter; + + #region construction + + public NPath(string path) + { + if (path == null) + throw new ArgumentNullException(); + + path = ParseDriveLetter(path, out _driveLetter); + + if (path == "/") + { + _isRelative = false; + _elements = new string[] { }; + } + else + { + var split = path.Split('/', '\\'); + + _isRelative = _driveLetter == null && IsRelativeFromSplitString(split); + + _elements = ParseSplitStringIntoElements(split.Where(s => s.Length > 0).ToArray()); + } + } + + private NPath(string[] elements, bool isRelative, string driveLetter) + { + _elements = elements; + _isRelative = isRelative; + _driveLetter = driveLetter; + } + + private string[] ParseSplitStringIntoElements(IEnumerable inputs) + { + var stack = new List(); + + foreach (var input in inputs.Where(input => input.Length != 0)) + { + if (input == ".") + { + if ((stack.Count > 0) && (stack.Last() != ".")) + continue; + } + else if (input == "..") + { + if (HasNonDotDotLastElement(stack)) + { + stack.RemoveAt(stack.Count - 1); + continue; + } + if (!_isRelative) + throw new ArgumentException("You cannot create a path that tries to .. past the root"); + } + stack.Add(input); + } + return stack.ToArray(); + } + + private static bool HasNonDotDotLastElement(List stack) + { + return stack.Count > 0 && stack[stack.Count - 1] != ".."; + } + + private string ParseDriveLetter(string path, out string driveLetter) + { + if (path.Length >= 2 && path[1] == ':') + { + driveLetter = path[0].ToString(); + return path.Substring(2); + } + + driveLetter = null; + return path; + } + + private static bool IsRelativeFromSplitString(string[] split) + { + if (split.Length < 2) + return true; + + return split[0].Length != 0 || !split.Any(s => s.Length > 0); + } + + public NPath Combine(params string[] append) + { + return Combine(append.Select(a => new NPath(a)).ToArray()); + } + + public NPath Combine(params NPath[] append) + { + if (!append.All(p => p.IsRelative)) + throw new ArgumentException("You cannot .Combine a non-relative path"); + + return new NPath(ParseSplitStringIntoElements(_elements.Concat(append.SelectMany(p => p._elements))), _isRelative, _driveLetter); + } + + public NPath Parent + { + get + { + if (_elements.Length == 0) + throw new InvalidOperationException("Parent is called on an empty path"); + + var newElements = _elements.Take(_elements.Length - 1).ToArray(); + + return new NPath(newElements, _isRelative, _driveLetter); + } + } + + public NPath RelativeTo(NPath path) + { + if (!IsChildOf(path)) + { + if (!IsRelative && !path.IsRelative && _driveLetter != path._driveLetter) + throw new ArgumentException("Path.RelativeTo() was invoked with two paths that are on different volumes. invoked on: " + ToString() + " asked to be made relative to: " + path); + + NPath commonParent = null; + foreach (var parent in RecursiveParents) + { + commonParent = path.RecursiveParents.FirstOrDefault(otherParent => otherParent == parent); + + if (commonParent != null) + break; + } + + if (commonParent == null) + throw new ArgumentException("Path.RelativeTo() was unable to find a common parent between " + ToString() + " and " + path); + + if (IsRelative && path.IsRelative && commonParent.IsEmpty()) + throw new ArgumentException("Path.RelativeTo() was invoked with two relative paths that do not share a common parent. Invoked on: " + ToString() + " asked to be made relative to: " + path); + + var depthDiff = path.Depth - commonParent.Depth; + return new NPath(Enumerable.Repeat("..", depthDiff).Concat(_elements.Skip(commonParent.Depth)).ToArray(), true, null); + } + + return new NPath(_elements.Skip(path._elements.Length).ToArray(), true, null); + } + + public NPath ChangeExtension(string extension) + { + ThrowIfRoot(); + + var newElements = (string[])_elements.Clone(); + newElements[newElements.Length - 1] = Path.ChangeExtension(_elements[_elements.Length - 1], WithDot(extension)); + if (extension == string.Empty) + newElements[newElements.Length - 1] = newElements[newElements.Length - 1].TrimEnd('.'); + return new NPath(newElements, _isRelative, _driveLetter); + } + #endregion construction + + #region inspection + + public bool IsRelative + { + get { return _isRelative; } + } + + public string FileName + { + get + { + ThrowIfRoot(); + + return _elements.Last(); + } + } + + public string FileNameWithoutExtension + { + get { return Path.GetFileNameWithoutExtension(FileName); } + } + + public IEnumerable Elements + { + get { return _elements; } + } + + public int Depth + { + get { return _elements.Length; } + } + + public bool Exists(string append = "") + { + return Exists(new NPath(append)); + } + + public bool Exists(NPath append) + { + return FileExists(append) || DirectoryExists(append); + } + + public bool DirectoryExists(string append = "") + { + return DirectoryExists(new NPath(append)); + } + + public bool DirectoryExists(NPath append) + { + return Directory.Exists(Combine(append).ToString()); + } + + public bool FileExists(string append = "") + { + return FileExists(new NPath(append)); + } + + public bool FileExists(NPath append) + { + return File.Exists(Combine(append).ToString()); + } + + public string ExtensionWithDot + { + get + { + if (IsRoot) + throw new ArgumentException("A root directory does not have an extension"); + + var last = _elements.Last(); + var index = last.LastIndexOf("."); + if (index < 0) return String.Empty; + return last.Substring(index); + } + } + + public string InQuotes() + { + return "\"" + ToString() + "\""; + } + + public string InQuotes(SlashMode slashMode) + { + return "\"" + ToString(slashMode) + "\""; + } + + public override string ToString() + { + return ToString(SlashMode.Native); + } + + public string ToString(SlashMode slashMode) + { + // Check if it's linux root / + if (IsRoot && string.IsNullOrEmpty(_driveLetter)) + return Slash(slashMode).ToString(); + + if (_isRelative && _elements.Length == 0) + return "."; + + var sb = new StringBuilder(); + if (_driveLetter != null) + { + sb.Append(_driveLetter); + sb.Append(":"); + } + if (!_isRelative) + sb.Append(Slash(slashMode)); + var first = true; + foreach (var element in _elements) + { + if (!first) + sb.Append(Slash(slashMode)); + + sb.Append(element); + first = false; + } + return sb.ToString(); + } + + static char Slash(SlashMode slashMode) + { + switch (slashMode) + { + case SlashMode.Backward: + return '\\'; + case SlashMode.Forward: + return '/'; + default: + return Path.DirectorySeparatorChar; + } + } + + public override bool Equals(Object obj) + { + if (obj == null) + return false; + + // If parameter cannot be cast to Point return false. + var p = obj as NPath; + if ((Object)p == null) + return false; + + return Equals(p); + } + + public bool Equals(NPath p) + { + if (p._isRelative != _isRelative) + return false; + + if (!string.Equals(p._driveLetter, _driveLetter, PathStringComparison)) + return false; + + if (p._elements.Length != _elements.Length) + return false; + + for (var i = 0; i != _elements.Length; i++) + if (!string.Equals(p._elements[i], _elements[i], PathStringComparison)) + return false; + + return true; + } + + public static bool operator ==(NPath a, NPath b) + { + // If both are null, or both are same instance, return true. + if (ReferenceEquals(a, b)) + return true; + + // If one is null, but not both, return false. + if (((object)a == null) || ((object)b == null)) + return false; + + // Return true if the fields match: + return a.Equals(b); + } + + public override int GetHashCode() + { + unchecked + { + int hash = 17; + // Suitable nullity checks etc, of course :) + hash = hash * 23 + _isRelative.GetHashCode(); + foreach (var element in _elements) + hash = hash * 23 + element.GetHashCode(); + if (_driveLetter != null) + hash = hash * 23 + _driveLetter.GetHashCode(); + return hash; + } + } + + public int CompareTo(object obj) + { + if (obj == null) + return -1; + + return this.ToString().CompareTo(((NPath)obj).ToString()); + } + + public static bool operator !=(NPath a, NPath b) + { + return !(a == b); + } + + public bool HasExtension(params string[] extensions) + { + var extensionWithDotLower = ExtensionWithDot.ToLower(); + return extensions.Any(e => WithDot(e).ToLower() == extensionWithDotLower); + } + + private static string WithDot(string extension) + { + return extension.StartsWith(".") ? extension : "." + extension; + } + + private bool IsEmpty() + { + return _elements.Length == 0; + } + + public bool IsRoot + { + get { return _elements.Length == 0 && !_isRelative; } + } + + #endregion inspection + + #region directory enumeration + + public IEnumerable Files(string filter, bool recurse = false) + { + return Directory.GetFiles(ToString(), filter, recurse ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly).Select(s => new NPath(s)); + } + + public IEnumerable Files(bool recurse = false) + { + return Files("*", recurse); + } + + public IEnumerable Contents(string filter, bool recurse = false) + { + return Files(filter, recurse).Concat(Directories(filter, recurse)); + } + + public IEnumerable Contents(bool recurse = false) + { + return Contents("*", recurse); + } + + public IEnumerable Directories(string filter, bool recurse = false) + { + return Directory.GetDirectories(ToString(), filter, recurse ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly).Select(s => new NPath(s)); + } + + public IEnumerable Directories(bool recurse = false) + { + return Directories("*", recurse); + } + + #endregion + + #region filesystem writing operations + public NPath CreateFile() + { + ThrowIfRelative(); + ThrowIfRoot(); + EnsureParentDirectoryExists(); + File.WriteAllBytes(ToString(), new byte[0]); + return this; + } + + public NPath CreateFile(string file) + { + return CreateFile(new NPath(file)); + } + + public NPath CreateFile(NPath file) + { + if (!file.IsRelative) + throw new ArgumentException("You cannot call CreateFile() on an existing path with a non relative argument"); + return Combine(file).CreateFile(); + } + + public NPath CreateDirectory() + { + ThrowIfRelative(); + + if (IsRoot) + throw new NotSupportedException("CreateDirectory is not supported on a root level directory because it would be dangerous:" + ToString()); + + Directory.CreateDirectory(ToString()); + return this; + } + + public NPath CreateDirectory(string directory) + { + return CreateDirectory(new NPath(directory)); + } + + public NPath CreateDirectory(NPath directory) + { + if (!directory.IsRelative) + throw new ArgumentException("Cannot call CreateDirectory with an absolute argument"); + + return Combine(directory).CreateDirectory(); + } + + public NPath Copy(string dest) + { + return Copy(new NPath(dest)); + } + + public NPath Copy(string dest, Func fileFilter) + { + return Copy(new NPath(dest), fileFilter); + } + + public NPath Copy(NPath dest) + { + return Copy(dest, p => true); + } + + public NPath Copy(NPath dest, Func fileFilter) + { + ThrowIfRelative(); + if (dest.IsRelative) + dest = Parent.Combine(dest); + + if (dest.DirectoryExists()) + return CopyWithDeterminedDestination(dest.Combine(FileName), fileFilter); + + return CopyWithDeterminedDestination(dest, fileFilter); + } + + public NPath MakeAbsolute() + { + if (!IsRelative) + return this; + + return NPath.CurrentDirectory.Combine(this); + } + + NPath CopyWithDeterminedDestination(NPath absoluteDestination, Func fileFilter) + { + if (absoluteDestination.IsRelative) + throw new ArgumentException("absoluteDestination must be absolute"); + + if (FileExists()) + { + if (!fileFilter(absoluteDestination)) + return null; + + absoluteDestination.EnsureParentDirectoryExists(); + + File.Copy(ToString(), absoluteDestination.ToString(), true); + return absoluteDestination; + } + + if (DirectoryExists()) + { + absoluteDestination.EnsureDirectoryExists(); + foreach (var thing in Contents()) + thing.CopyWithDeterminedDestination(absoluteDestination.Combine(thing.RelativeTo(this)), fileFilter); + return absoluteDestination; + } + + throw new ArgumentException("Copy() called on path that doesnt exist: " + ToString()); + } + + public void Delete(DeleteMode deleteMode = DeleteMode.Normal) + { + ThrowIfRelative(); + + if (IsRoot) + throw new NotSupportedException("Delete is not supported on a root level directory because it would be dangerous:" + ToString()); + + if (FileExists()) + File.Delete(ToString()); + else if (DirectoryExists()) + try + { + Directory.Delete(ToString(), true); + } + catch (IOException) + { + if (deleteMode == DeleteMode.Normal) + throw; + } + else + throw new InvalidOperationException("Trying to delete a path that does not exist: " + ToString()); + } + + public void DeleteIfExists(DeleteMode deleteMode = DeleteMode.Normal) + { + ThrowIfRelative(); + + if (FileExists() || DirectoryExists()) + Delete(deleteMode); + } + + public NPath DeleteContents() + { + ThrowIfRelative(); + + if (IsRoot) + throw new NotSupportedException("DeleteContents is not supported on a root level directory because it would be dangerous:" + ToString()); + + if (FileExists()) + throw new InvalidOperationException("It is not valid to perform this operation on a file"); + + if (DirectoryExists()) + { + try + { + Files().Delete(); + Directories().Delete(); + } + catch (IOException) + { + if (Files(true).Any()) + throw; + } + + return this; + } + + return EnsureDirectoryExists(); + } + + public static NPath CreateTempDirectory(string myprefix) + { + var random = new Random(); + while (true) + { + var candidate = new NPath(Path.GetTempPath() + "/" + myprefix + "_" + random.Next()); + if (!candidate.Exists()) + return candidate.CreateDirectory(); + } + } + + public NPath Move(string dest) + { + return Move(new NPath(dest)); + } + + public NPath Move(NPath dest) + { + ThrowIfRelative(); + + if (IsRoot) + throw new NotSupportedException("Move is not supported on a root level directory because it would be dangerous:" + ToString()); + + if (dest.IsRelative) + return Move(Parent.Combine(dest)); + + if (dest.DirectoryExists()) + return Move(dest.Combine(FileName)); + + if (FileExists()) + { + dest.EnsureParentDirectoryExists(); + File.Move(ToString(), dest.ToString()); + return dest; + } + + if (DirectoryExists()) + { + Directory.Move(ToString(), dest.ToString()); + return dest; + } + + throw new ArgumentException("Move() called on a path that doesn't exist: " + ToString()); + } + + #endregion + + #region special paths + + public static NPath CurrentDirectory + { + get + { + return new NPath(Directory.GetCurrentDirectory()); + } + } + + public static NPath HomeDirectory + { + get + { + if (Path.DirectorySeparatorChar == '\\') + return new NPath(Environment.GetEnvironmentVariable("USERPROFILE")); + return new NPath(Environment.GetEnvironmentVariable("HOME")); + } + } + + public static NPath SystemTemp + { + get + { + return new NPath(Path.GetTempPath()); + } + } + + #endregion + + private void ThrowIfRelative() + { + if (_isRelative) + throw new ArgumentException("You are attempting an operation on a Path that requires an absolute path, but the path is relative"); + } + + private void ThrowIfRoot() + { + if (IsRoot) + throw new ArgumentException("You are attempting an operation that is not valid on a root level directory"); + } + + public NPath EnsureDirectoryExists(string append = "") + { + return EnsureDirectoryExists(new NPath(append)); + } + + public NPath EnsureDirectoryExists(NPath append) + { + var combined = Combine(append); + if (combined.DirectoryExists()) + return combined; + combined.EnsureParentDirectoryExists(); + combined.CreateDirectory(); + return combined; + } + + public NPath EnsureParentDirectoryExists() + { + var parent = Parent; + parent.EnsureDirectoryExists(); + return parent; + } + + public NPath FileMustExist() + { + if (!FileExists()) + throw new FileNotFoundException("File was expected to exist : " + ToString()); + + return this; + } + + public NPath DirectoryMustExist() + { + if (!DirectoryExists()) + throw new DirectoryNotFoundException("Expected directory to exist : " + ToString()); + + return this; + } + + public bool IsChildOf(string potentialBasePath) + { + return IsChildOf(new NPath(potentialBasePath)); + } + + public bool IsChildOf(NPath potentialBasePath) + { + if ((IsRelative && !potentialBasePath.IsRelative) || !IsRelative && potentialBasePath.IsRelative) + throw new ArgumentException("You can only call IsChildOf with two relative paths, or with two absolute paths"); + + // If the other path is the root directory, then anything is a child of it as long as it's not a Windows path + if (potentialBasePath.IsRoot) + { + if (_driveLetter != potentialBasePath._driveLetter) + return false; + return true; + } + + if (IsEmpty()) + return false; + + if (Equals(potentialBasePath)) + return true; + + return Parent.IsChildOf(potentialBasePath); + } + + public IEnumerable RecursiveParents + { + get + { + var candidate = this; + while (true) + { + if (candidate.IsEmpty()) + yield break; + + candidate = candidate.Parent; + yield return candidate; + } + } + } + + public NPath ParentContaining(string needle) + { + return ParentContaining(new NPath(needle)); + } + + public NPath ParentContaining(NPath needle) + { + ThrowIfRelative(); + + return RecursiveParents.FirstOrDefault(p => p.Exists(needle)); + } + + public NPath WriteAllText(string contents) + { + ThrowIfRelative(); + EnsureParentDirectoryExists(); + File.WriteAllText(ToString(), contents); + return this; + } + + public string ReadAllText() + { + ThrowIfRelative(); + return File.ReadAllText(ToString()); + } + + public NPath WriteAllLines(string[] contents) + { + ThrowIfRelative(); + EnsureParentDirectoryExists(); + File.WriteAllLines(ToString(), contents); + return this; + } + + public string[] ReadAllLines() + { + ThrowIfRelative(); + return File.ReadAllLines(ToString()); + } + + public IEnumerable CopyFiles(NPath destination, bool recurse, Func fileFilter = null) + { + destination.EnsureDirectoryExists(); + return Files(recurse).Where(fileFilter ?? AlwaysTrue).Select(file => file.Copy(destination.Combine(file.RelativeTo(this)))).ToArray(); + } + + public IEnumerable MoveFiles(NPath destination, bool recurse, Func fileFilter = null) + { + if (IsRoot) + throw new NotSupportedException("MoveFiles is not supported on this directory because it would be dangerous:" + ToString()); + + destination.EnsureDirectoryExists(); + return Files(recurse).Where(fileFilter ?? AlwaysTrue).Select(file => file.Move(destination.Combine(file.RelativeTo(this)))).ToArray(); + } + + static bool AlwaysTrue(NPath p) + { + return true; + } + + private static bool IsLinux() + { + return Directory.Exists("/proc"); + } + } + + public static class Extensions + { + public static IEnumerable Copy(this IEnumerable self, string dest) + { + return Copy(self, new NPath(dest)); + } + + public static IEnumerable Copy(this IEnumerable self, NPath dest) + { + if (dest.IsRelative) + throw new ArgumentException("When copying multiple files, the destination cannot be a relative path"); + dest.EnsureDirectoryExists(); + return self.Select(p => p.Copy(dest.Combine(p.FileName))).ToArray(); + } + + public static IEnumerable Move(this IEnumerable self, string dest) + { + return Move(self, new NPath(dest)); + } + + public static IEnumerable Move(this IEnumerable self, NPath dest) + { + if (dest.IsRelative) + throw new ArgumentException("When moving multiple files, the destination cannot be a relative path"); + dest.EnsureDirectoryExists(); + return self.Select(p => p.Move(dest.Combine(p.FileName))).ToArray(); + } + + public static IEnumerable Delete(this IEnumerable self) + { + foreach (var p in self) + p.Delete(); + return self; + } + + public static IEnumerable InQuotes(this IEnumerable self, SlashMode forward = SlashMode.Native) + { + return self.Select(p => p.InQuotes(forward)); + } + + public static NPath ToNPath(this string path) + { + return new NPath(path); + } + } + + public enum SlashMode + { + Native, + Forward, + Backward + } + + public enum DeleteMode + { + Normal, + Soft + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/BaseExpectedLinkedBehaviorAttribute.cs b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/BaseExpectedLinkedBehaviorAttribute.cs new file mode 100644 index 000000000..47f0f3415 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/BaseExpectedLinkedBehaviorAttribute.cs @@ -0,0 +1,11 @@ +using System; +using System.Diagnostics; + +namespace Mono.Linker.Tests.Cases.Expectations.Assertions { + /// + /// Base attribute for attributes that mark up the expected behavior of the linker on a member + /// + [Conditional("INCLUDE_EXPECTATIONS")] + public abstract class BaseExpectedLinkedBehaviorAttribute : Attribute { + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/IgnoreTestCaseAttribute.cs b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/IgnoreTestCaseAttribute.cs new file mode 100644 index 000000000..9d3cfacc7 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/IgnoreTestCaseAttribute.cs @@ -0,0 +1,13 @@ +using System; + +namespace Mono.Linker.Tests.Cases.Expectations.Assertions { + [AttributeUsage (AttributeTargets.Class)] + public class IgnoreTestCaseAttribute : Attribute { + public readonly string Reason; + + public IgnoreTestCaseAttribute (string reason) + { + Reason = reason; + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptAssemblyAttribute.cs b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptAssemblyAttribute.cs new file mode 100644 index 000000000..086304179 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptAssemblyAttribute.cs @@ -0,0 +1,16 @@ +using System; + +namespace Mono.Linker.Tests.Cases.Expectations.Assertions { + /// + /// Verifies that an assembly does exist in the output directory + /// + [AttributeUsage (AttributeTargets.Class | AttributeTargets.Delegate, AllowMultiple = true, Inherited = false)] + public class KeptAssemblyAttribute : KeptAttribute { + public readonly string FileName; + + public KeptAssemblyAttribute (string fileName) + { + FileName = fileName; + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptAttribute.cs b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptAttribute.cs new file mode 100644 index 000000000..837c97152 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptAttribute.cs @@ -0,0 +1,7 @@ +using System; + +namespace Mono.Linker.Tests.Cases.Expectations.Assertions { + [AttributeUsage (AttributeTargets.All, Inherited = false)] + public class KeptAttribute : BaseExpectedLinkedBehaviorAttribute { + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptAttributeAttribute.cs b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptAttributeAttribute.cs new file mode 100644 index 000000000..c183e62d2 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptAttributeAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace Mono.Linker.Tests.Cases.Expectations.Assertions +{ + [AttributeUsage (AttributeTargets.All, AllowMultiple = true, Inherited = false)] + public class KeptAttributeAttribute : KeptAttribute + { + public readonly string AttributeName; + + public KeptAttributeAttribute (string attributeName) + { + AttributeName = attributeName; + } + } +} diff --git a/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptBackingFieldAttribute.cs b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptBackingFieldAttribute.cs new file mode 100644 index 000000000..a4160490a --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptBackingFieldAttribute.cs @@ -0,0 +1,8 @@ +using System; + +namespace Mono.Linker.Tests.Cases.Expectations.Assertions +{ + [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)] + public sealed class KeptBackingFieldAttribute : KeptAttribute { + } +} diff --git a/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptBaseTypeAttribute.cs b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptBaseTypeAttribute.cs new file mode 100644 index 000000000..ff1f2fa84 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptBaseTypeAttribute.cs @@ -0,0 +1,22 @@ +using System; + +namespace Mono.Linker.Tests.Cases.Expectations.Assertions +{ + [AttributeUsage (AttributeTargets.Class | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] + public sealed class KeptBaseTypeAttribute : KeptAttribute + { + public readonly Type BaseType; + public readonly object [] GenericParameterNames; + + public KeptBaseTypeAttribute (Type baseType) + { + BaseType = baseType; + } + + public KeptBaseTypeAttribute (Type baseType, params object[] typeArguments) + { + BaseType = baseType; + GenericParameterNames = typeArguments; + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptInterfaceAttribute.cs b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptInterfaceAttribute.cs new file mode 100644 index 000000000..5014bd23a --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptInterfaceAttribute.cs @@ -0,0 +1,16 @@ +using System; +using System.Diagnostics; + +namespace Mono.Linker.Tests.Cases.Expectations.Assertions +{ + [AttributeUsage (AttributeTargets.Class, AllowMultiple = true, Inherited = false)] + public class KeptInterfaceAttribute : KeptAttribute + { + public readonly Type InterfaceType; + + public KeptInterfaceAttribute (Type interfaceType) + { + InterfaceType = interfaceType; + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptMemberAttribute.cs b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptMemberAttribute.cs new file mode 100644 index 000000000..00e55f266 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptMemberAttribute.cs @@ -0,0 +1,13 @@ +using System; + +namespace Mono.Linker.Tests.Cases.Expectations.Assertions { + [AttributeUsage (AttributeTargets.Class | AttributeTargets.Delegate, AllowMultiple = true, Inherited = false)] + public sealed class KeptMemberAttribute : KeptAttribute { + public readonly string Name; + + public KeptMemberAttribute (string name) + { + Name = name; + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/RemovedAssemblyAttribute.cs b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/RemovedAssemblyAttribute.cs new file mode 100644 index 000000000..4bb6a320a --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Assertions/RemovedAssemblyAttribute.cs @@ -0,0 +1,16 @@ +using System; + +namespace Mono.Linker.Tests.Cases.Expectations.Assertions { + /// + /// Verifies that an assembly does not exist in the output directory + /// + [AttributeUsage (AttributeTargets.Class | AttributeTargets.Delegate, AllowMultiple = true, Inherited = false)] + public class RemovedAssemblyAttribute : BaseExpectedLinkedBehaviorAttribute { + public readonly string FileName; + + public RemovedAssemblyAttribute (string fileName) + { + FileName = fileName; + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Metadata/BaseMetadataAttribute.cs b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Metadata/BaseMetadataAttribute.cs new file mode 100644 index 000000000..d98584eab --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Metadata/BaseMetadataAttribute.cs @@ -0,0 +1,9 @@ +using System; +using System.Diagnostics; + +namespace Mono.Linker.Tests.Cases.Expectations.Metadata +{ + [Conditional("INCLUDE_EXPECTATIONS")] + public abstract class BaseMetadataAttribute : Attribute { + } +} diff --git a/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Metadata/CoreLinkAttribute.cs b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Metadata/CoreLinkAttribute.cs new file mode 100644 index 000000000..7529a6255 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Metadata/CoreLinkAttribute.cs @@ -0,0 +1,13 @@ +using System; + +namespace Mono.Linker.Tests.Cases.Expectations.Metadata { + [AttributeUsage (AttributeTargets.Class)] + public class CoreLinkAttribute : BaseMetadataAttribute { + public readonly string Value; + + public CoreLinkAttribute (string value) + { + Value = value; + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Metadata/NotATestCaseAttribute.cs b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Metadata/NotATestCaseAttribute.cs new file mode 100644 index 000000000..11cd069c3 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Metadata/NotATestCaseAttribute.cs @@ -0,0 +1,7 @@ +using System; + +namespace Mono.Linker.Tests.Cases.Expectations.Metadata { + [AttributeUsage (AttributeTargets.Class)] + public class NotATestCaseAttribute : BaseMetadataAttribute { + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Metadata/ReferenceAttribute.cs b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Metadata/ReferenceAttribute.cs new file mode 100644 index 000000000..0a37a2149 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Metadata/ReferenceAttribute.cs @@ -0,0 +1,13 @@ +using System; + +namespace Mono.Linker.Tests.Cases.Expectations.Metadata { + [AttributeUsage (AttributeTargets.Class)] + public class ReferenceAttribute : BaseMetadataAttribute { + public readonly string Value; + + public ReferenceAttribute (string value) + { + Value = value; + } + } +} diff --git a/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Metadata/SandboxDependencyAttribute.cs b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Metadata/SandboxDependencyAttribute.cs new file mode 100644 index 000000000..805b1ac56 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Metadata/SandboxDependencyAttribute.cs @@ -0,0 +1,13 @@ +using System; + +namespace Mono.Linker.Tests.Cases.Expectations.Metadata { + [AttributeUsage (AttributeTargets.Class)] + public class SandboxDependencyAttribute : BaseMetadataAttribute { + public readonly string RelativePathToFile; + + public SandboxDependencyAttribute (string relativePathToFile) + { + RelativePathToFile = relativePathToFile; + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Mono.Linker.Tests.Cases.Expectations.csproj b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Mono.Linker.Tests.Cases.Expectations.csproj new file mode 100644 index 000000000..dfb15ba2a --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases.Expectations/Mono.Linker.Tests.Cases.Expectations.csproj @@ -0,0 +1,64 @@ + + + + + Debug + AnyCPU + {2C26601F-3E2F-45B9-A02F-58EE9296E19E} + Library + Properties + Mono.Linker.Tests.Cases.Expectations + Mono.Linker.Tests.Cases.Expectations + v4.0 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Advanced/DeadCodeElimination1.cs b/linker/Tests/Mono.Linker.Tests.Cases/Advanced/DeadCodeElimination1.cs new file mode 100644 index 000000000..339d223b6 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Advanced/DeadCodeElimination1.cs @@ -0,0 +1,19 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Advanced { + [IgnoreTestCase ("We cannot do this yet")] + class DeadCodeElimination1 { + public static void Main () + { + object o = null; + if (o is B) + ((B) o).Method (); + } + + class B { + public void Method () + { + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Advanced/FieldThatOnlyGetsSetIsRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/Advanced/FieldThatOnlyGetsSetIsRemoved.cs new file mode 100644 index 000000000..8c4933ad2 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Advanced/FieldThatOnlyGetsSetIsRemoved.cs @@ -0,0 +1,21 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Advanced { + [IgnoreTestCase ("We cannot do this yet")] + class FieldThatOnlyGetsSetIsRemoved { + public static void Main () + { + new B ().Method (); + } + + [KeptMember (".ctor()")] + class B { + public int _unused = 3; + + [Kept] + public void Method () + { + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnPreservedTypeIsKept.cs b/linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnPreservedTypeIsKept.cs new file mode 100644 index 000000000..887db4a00 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnPreservedTypeIsKept.cs @@ -0,0 +1,19 @@ +using System; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Attributes { + [Foo] + class AttributeOnPreservedTypeIsKept { + public static void Main () + { + } + + [KeptBaseType (typeof (System.Attribute))] + class FooAttribute : Attribute { + [Kept] + public FooAttribute () + { + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnPreservedTypeWithTypeUsedInConstructorIsKept.cs b/linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnPreservedTypeWithTypeUsedInConstructorIsKept.cs new file mode 100644 index 000000000..1e810d3fa --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnPreservedTypeWithTypeUsedInConstructorIsKept.cs @@ -0,0 +1,26 @@ +using System; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Attributes { + [Foo (typeof (A))] + class AttributeOnPreservedTypeWithTypeUsedInConstructorIsKept { + public static void Main () + { + } + + [KeptBaseType (typeof (System.Attribute))] + class FooAttribute : Attribute { + [Kept] + public FooAttribute (Type val) + { + } + } + + [Kept] + class A { + public A () + { + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnPreservedTypeWithTypeUsedInDifferentNamespaceIsKept.cs b/linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnPreservedTypeWithTypeUsedInDifferentNamespaceIsKept.cs new file mode 100644 index 000000000..ff815a73b --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnPreservedTypeWithTypeUsedInDifferentNamespaceIsKept.cs @@ -0,0 +1,32 @@ +using System; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace NamespaceForAttributeOnPreservedTypeWithTypeUsedInDifferentNamespaceIsKept { + [Kept] + class A { + public A () + { + } + } +} + +namespace Mono.Linker.Tests.Cases.Attributes { + [Foo (typeof (NamespaceForAttributeOnPreservedTypeWithTypeUsedInDifferentNamespaceIsKept.A))] + class AttributeOnPreservedTypeWithTypeUsedInDifferentNamespaceIsKept { + public static void Main () + { + } + + [KeptBaseType (typeof (System.Attribute))] + class FooAttribute : Attribute { + [Kept] + public FooAttribute (Type val) + { + } + } + + // This A is not used and should be removed + class A { + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnPreservedTypeWithTypeUsedInFieldIsKept.cs b/linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnPreservedTypeWithTypeUsedInFieldIsKept.cs new file mode 100644 index 000000000..7721da824 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnPreservedTypeWithTypeUsedInFieldIsKept.cs @@ -0,0 +1,25 @@ +using System; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Attributes { + [Foo (Val = typeof (A))] + class AttributeOnPreservedTypeWithTypeUsedInFieldIsKept { + public static void Main () + { + } + + [KeptMember(".ctor()")] + [KeptBaseType (typeof (System.Attribute))] + class FooAttribute : Attribute { + [Kept] + public Type Val; + } + + [Kept] + class A { + public A () + { + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnPreservedTypeWithTypeUsedInPropertySetterIsKept.cs b/linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnPreservedTypeWithTypeUsedInPropertySetterIsKept.cs new file mode 100644 index 000000000..fd5d66e1b --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnPreservedTypeWithTypeUsedInPropertySetterIsKept.cs @@ -0,0 +1,26 @@ +using System; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Attributes { + [Foo (Val = typeof (A))] + class AttributeOnPreservedTypeWithTypeUsedInPropertySetterIsKept { + public static void Main () + { + } + + [KeptMember (".ctor()")] + [KeptBaseType (typeof (System.Attribute))] + class FooAttribute : Attribute { + [Kept] + [KeptBackingField] + public Type Val { get; [Kept] set; } + } + + [Kept] + class A { + public A () + { + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnPreservedTypeWithUsedSetter.cs b/linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnPreservedTypeWithUsedSetter.cs new file mode 100644 index 000000000..bf443dbc8 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnPreservedTypeWithUsedSetter.cs @@ -0,0 +1,19 @@ +using System; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Attributes { + [Foo (Val = 1)] + class AttributeOnPreservedTypeWithUsedSetter { + public static void Main () + { + } + + [KeptMember (".ctor()")] + [KeptBaseType (typeof (System.Attribute))] + class FooAttribute : Attribute { + [Kept] + [KeptBackingField] + public int Val { get; [Kept] set; } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnUsedFieldIsKept.cs b/linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnUsedFieldIsKept.cs new file mode 100644 index 000000000..c94848a00 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnUsedFieldIsKept.cs @@ -0,0 +1,23 @@ +using System; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Attributes { + class AttributeOnUsedFieldIsKept { + public static void Main () + { + var val = new A ().field; + } + + [KeptMember (".ctor()")] + class A { + [Kept] + [Foo] public int field; + } + + [Kept] + [KeptMember(".ctor()")] + [KeptBaseType (typeof (System.Attribute))] + class FooAttribute : Attribute { + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnUsedMethodIsKept.cs b/linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnUsedMethodIsKept.cs new file mode 100644 index 000000000..c6e31bab6 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnUsedMethodIsKept.cs @@ -0,0 +1,26 @@ +using System; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Attributes { + class AttributeOnUsedMethodIsKept { + public static void Main () + { + new A ().Method (); + } + + [KeptMember (".ctor()")] + class A { + [Foo] + [Kept] + public void Method () + { + } + } + + [Kept] + [KeptMember (".ctor()")] + [KeptBaseType (typeof (System.Attribute))] + class FooAttribute : Attribute { + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnUsedPropertyIsKept.cs b/linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnUsedPropertyIsKept.cs new file mode 100644 index 000000000..b6edc511b --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Attributes/AttributeOnUsedPropertyIsKept.cs @@ -0,0 +1,26 @@ +using System; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Attributes { + class AttributeOnUsedPropertyIsKept { + public static void Main () + { + var val = new A ().Field; + } + + [KeptMember (".ctor()")] + [KeptMember ("get_Field()")] + class A { + [Kept] + [KeptBackingField] + [Foo] + public int Field { get; set; } + } + + [Kept] + [KeptMember (".ctor()")] + [KeptBaseType (typeof (System.Attribute))] + class FooAttribute : Attribute { + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Basic/ComplexNestedClassesHasUnusedRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/Basic/ComplexNestedClassesHasUnusedRemoved.cs new file mode 100644 index 000000000..fbdbee204 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Basic/ComplexNestedClassesHasUnusedRemoved.cs @@ -0,0 +1,26 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Basic { + class ComplexNestedClassesHasUnusedRemoved { + public static void Main () + { + new A.AB.ABD (); + } + + [Kept] + public class A { + [Kept] + public class AB { + public class ABC { + } + + [Kept] + [KeptMember(".ctor()")] + public class ABD { + public class ABDE { + } + } + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Basic/InterfaceMethodImplementedOnBaseClassDoesNotGetStripped.cs b/linker/Tests/Mono.Linker.Tests.Cases/Basic/InterfaceMethodImplementedOnBaseClassDoesNotGetStripped.cs new file mode 100644 index 000000000..fa0b762a8 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Basic/InterfaceMethodImplementedOnBaseClassDoesNotGetStripped.cs @@ -0,0 +1,37 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Basic { + class InterfaceMethodImplementedOnBaseClassDoesNotGetStripped { + public static void Main () + { + I1 i1 = new Derived (); + i1.Used (); + } + + public interface I1 { + void Unused (); + + [Kept] + void Used (); + } + + [KeptMember (".ctor()")] + public class Base { + public void Unused () + { + } + + [Kept] + public void Used () + { + } + } + + [Kept] + [KeptMember (".ctor()")] + [KeptBaseType (typeof (Base))] + [KeptInterface (typeof (I1))] + public class Derived : Base, I1 { + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Basic/MultiLevelNestedClassesAllRemovedWhenNonUsed.cs b/linker/Tests/Mono.Linker.Tests.Cases/Basic/MultiLevelNestedClassesAllRemovedWhenNonUsed.cs new file mode 100644 index 000000000..dd5f8628e --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Basic/MultiLevelNestedClassesAllRemovedWhenNonUsed.cs @@ -0,0 +1,19 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Basic { + class MultiLevelNestedClassesAllRemovedWhenNonUsed { + public static void Main () + { + } + + public class A { + public class AB { + public class ABC { + } + + public class ABD { + } + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Basic/NestedDelegateInvokeMethodsPreserved.cs b/linker/Tests/Mono.Linker.Tests.Cases/Basic/NestedDelegateInvokeMethodsPreserved.cs new file mode 100644 index 000000000..c3e231781 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Basic/NestedDelegateInvokeMethodsPreserved.cs @@ -0,0 +1,24 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Basic { + class NestedDelegateInvokeMethodsPreserved { + [Kept] + static B.Delegate @delegate; + + static void Main () + { + System.GC.KeepAlive (@delegate); + } + + [Kept] + public class B { + [Kept] + [KeptMember ("Invoke()")] + [KeptMember ("BeginInvoke(System.AsyncCallback,System.Object)")] + [KeptMember ("EndInvoke(System.IAsyncResult)")] + [KeptMember (".ctor(System.Object,System.IntPtr)")] + [KeptBaseType (typeof (System.MulticastDelegate))] + public delegate void Delegate (); + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Basic/UninvokedInterfaceMemberGetsRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/Basic/UninvokedInterfaceMemberGetsRemoved.cs new file mode 100644 index 000000000..11771f4db --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Basic/UninvokedInterfaceMemberGetsRemoved.cs @@ -0,0 +1,24 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Basic { + class UninvokedInterfaceMemberGetsRemoved { + public static void Main () + { + new B (); + } + + [Kept] + interface I { + void Method (); + } + + [Kept] + [KeptMember (".ctor()")] + [KeptInterface (typeof (I))] + class B : I { + public void Method () + { + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Basic/UnusedClassGetsRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/Basic/UnusedClassGetsRemoved.cs new file mode 100644 index 000000000..85d60e33d --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Basic/UnusedClassGetsRemoved.cs @@ -0,0 +1,10 @@ +namespace Mono.Linker.Tests.Cases.Basic { + public class UnusedClassGetsRemoved { + public static void Main () + { + } + } + + class Unused { + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Basic/UnusedFieldGetsRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/Basic/UnusedFieldGetsRemoved.cs new file mode 100644 index 000000000..6edcf5729 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Basic/UnusedFieldGetsRemoved.cs @@ -0,0 +1,20 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Basic { + class UnusedFieldGetsRemoved { + public static void Main () + { + new B ().Method (); + } + + [KeptMember (".ctor()")] + class B { + public int _unused; + + [Kept] + public void Method () + { + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Basic/UnusedMethodGetsRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/Basic/UnusedMethodGetsRemoved.cs new file mode 100644 index 000000000..eaded4de5 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Basic/UnusedMethodGetsRemoved.cs @@ -0,0 +1,22 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Basic { + class UnusedMethodGetsRemoved { + public static void Main () + { + new UnusedMethodGetsRemoved.B ().Method (); + } + + [KeptMember (".ctor()")] + class B { + public void Unused () + { + } + + [Kept] + public void Method () + { + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Basic/UnusedNestedClassGetsRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/Basic/UnusedNestedClassGetsRemoved.cs new file mode 100644 index 000000000..7ff2fc3a6 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Basic/UnusedNestedClassGetsRemoved.cs @@ -0,0 +1,11 @@ + +namespace Mono.Linker.Tests.Cases.Basic { + public class UnusedNestedClassGetsRemoved { + public static void Main () + { + } + + class Unused { + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Basic/UnusedPropertyGetsRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/Basic/UnusedPropertyGetsRemoved.cs new file mode 100644 index 000000000..63d82842e --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Basic/UnusedPropertyGetsRemoved.cs @@ -0,0 +1,20 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Basic { + class UnusedPropertyGetsRemoved { + public static void Main () + { + new UnusedPropertyGetsRemoved.B ().Method (); + } + + [KeptMember(".ctor()")] + class B { + public int Unused { get; set; } + + [Kept] + public void Method () + { + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Basic/UnusedPropertySetterRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/Basic/UnusedPropertySetterRemoved.cs new file mode 100644 index 000000000..9bf680e77 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Basic/UnusedPropertySetterRemoved.cs @@ -0,0 +1,17 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Basic { + class UnusedPropertySetterRemoved { + public static void Main () + { + var val = new UnusedPropertySetterRemoved.B ().PartiallyUsed; + } + + [KeptMember (".ctor()")] + class B { + [Kept] // FIXME: Should be removed + [KeptBackingField] + public int PartiallyUsed { [Kept] get; set; } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Basic/UsedPropertyIsKept.cs b/linker/Tests/Mono.Linker.Tests.Cases/Basic/UsedPropertyIsKept.cs new file mode 100644 index 000000000..8adc71f27 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Basic/UsedPropertyIsKept.cs @@ -0,0 +1,19 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Basic { + class UsedPropertyIsKept { + public static void Main () + { + var obj = new B (); + obj.Prop = 1; + var val = obj.Prop; + } + + [KeptMember (".ctor()")] + class B { + [Kept] + [KeptBackingField] + public int Prop { [Kept] get; [Kept] set; } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/CoreLink/LinkingOfCoreLibrariesRemovesUnusedTypes.cs b/linker/Tests/Mono.Linker.Tests.Cases/CoreLink/LinkingOfCoreLibrariesRemovesUnusedTypes.cs new file mode 100644 index 000000000..c7bb5aae4 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/CoreLink/LinkingOfCoreLibrariesRemovesUnusedTypes.cs @@ -0,0 +1,15 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.CoreLink { + [CoreLink ("link")] + [KeptAssembly ("mscorlib.dll")] + // We can't check everything that should be removed, but we should be able to check a few niche things that + // we known should be removed which will at least verify that the core library was processed + // TODO by Mike : List a few types + class LinkingOfCoreLibrariesRemovesUnusedTypes { + public static void Main () + { + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Generics/CorrectOverloadedMethodGetsStrippedInGenericClass.cs b/linker/Tests/Mono.Linker.Tests.Cases/Generics/CorrectOverloadedMethodGetsStrippedInGenericClass.cs new file mode 100644 index 000000000..362c3bb3d --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Generics/CorrectOverloadedMethodGetsStrippedInGenericClass.cs @@ -0,0 +1,37 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Generics { + class CorrectOverloadedMethodGetsStrippedInGenericClass { + public static void Main () + { + // Call overloaded method through the abstract base class + GenericClassWithTwoOverloadedAbstractMethods item = new SpecializedClassWithTwoOverloadedVirtualMethods (); + item.OverloadedMethod (5); + } + + [KeptMember (".ctor()")] + public abstract class GenericClassWithTwoOverloadedAbstractMethods { + public abstract string OverloadedMethod (T thing); // Don't call this one, it should be stripped + + [Kept] + public abstract string OverloadedMethod (int thing); // Call to this should preserve the overriden one + } + + [KeptMember (".ctor()")] + [KeptBaseType (typeof (GenericClassWithTwoOverloadedAbstractMethods))] + public class SpecializedClassWithTwoOverloadedVirtualMethods : GenericClassWithTwoOverloadedAbstractMethods { + // Don't call this one, it should be stripped + public override string OverloadedMethod (float thing) + { + return "first"; + } + + // Don't call this one, but it shouldn't be stripped because the method it overrides is invoked + [Kept] + public override string OverloadedMethod (int thing) + { + return "second"; + } + } + } +} diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Generics/DerivedClassWithMethodOfSameNameAsBaseButDifferentNumberOfGenericParametersUnusedBaseWillGetStripped.cs b/linker/Tests/Mono.Linker.Tests.Cases/Generics/DerivedClassWithMethodOfSameNameAsBaseButDifferentNumberOfGenericParametersUnusedBaseWillGetStripped.cs new file mode 100644 index 000000000..b13f8267e --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Generics/DerivedClassWithMethodOfSameNameAsBaseButDifferentNumberOfGenericParametersUnusedBaseWillGetStripped.cs @@ -0,0 +1,30 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Generics { + class DerivedClassWithMethodOfSameNameAsBaseButDifferentNumberOfGenericParametersUnusedBaseWillGetStripped { + public static void Main (string [] args) + { + MyDerived obj = new MyDerived (); + obj.Method (1); + } + + [Kept] + [KeptMember (".ctor()")] + class MyBase { + public virtual T Method (T arg1) + { + return arg1; + } + } + + [KeptMember (".ctor()")] + [KeptBaseType (typeof (MyBase))] + class MyDerived : MyBase { + [Kept] + public virtual T Method (T arg1) + { + return arg1; + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Generics/GenericInstanceInterfaceMethodImplementedWithDifferentGenericArgumentNameDoesNotGetStripped.cs b/linker/Tests/Mono.Linker.Tests.Cases/Generics/GenericInstanceInterfaceMethodImplementedWithDifferentGenericArgumentNameDoesNotGetStripped.cs new file mode 100644 index 000000000..4a912d678 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Generics/GenericInstanceInterfaceMethodImplementedWithDifferentGenericArgumentNameDoesNotGetStripped.cs @@ -0,0 +1,30 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Generics { + class GenericInstanceInterfaceMethodImplementedWithDifferentGenericArgumentNameDoesNotGetStripped { + public static void Main () + { + ISomething it = new Concrete (); + it.ShouldNotGetStripped (); + } + + [Kept] + public class GenericType { + } + + public interface ISomething { + [Kept] + GenericType ShouldNotGetStripped (); + } + + [KeptMember (".ctor()")] + [KeptInterface (typeof (ISomething))] + public class Concrete : ISomething { + [Kept] + public GenericType ShouldNotGetStripped () + { + throw new System.Exception (); + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Generics/MethodWithParameterWhichHasGenericParametersAndOverrideUsesADifferentNameForGenericParameter.cs b/linker/Tests/Mono.Linker.Tests.Cases/Generics/MethodWithParameterWhichHasGenericParametersAndOverrideUsesADifferentNameForGenericParameter.cs new file mode 100644 index 000000000..a656b1775 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Generics/MethodWithParameterWhichHasGenericParametersAndOverrideUsesADifferentNameForGenericParameter.cs @@ -0,0 +1,27 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Generics { + class MethodWithParameterWhichHasGenericParametersAndOverrideUsesADifferentNameForGenericParameter { + public static void Main (string [] args) + { + Derived tmp = new Derived (); + tmp.Method (null); + } + + [KeptMember (".ctor()")] + public abstract class Base { + [Kept] + public abstract TResult1 Method (System.Func arg); + } + + [KeptMember (".ctor()")] + [KeptBaseType (typeof (Base<>), "TResult1")] + public class Derived : Base { + [Kept] + public override TResult2 Method (System.Func arg) + { + return arg (default (TResult1)); + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Generics/MethodWithParameterWhichHasGenericParametersAndOverrideUsesADifferentNameForGenericParameterNestedCase.cs b/linker/Tests/Mono.Linker.Tests.Cases/Generics/MethodWithParameterWhichHasGenericParametersAndOverrideUsesADifferentNameForGenericParameterNestedCase.cs new file mode 100644 index 000000000..78eb14425 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Generics/MethodWithParameterWhichHasGenericParametersAndOverrideUsesADifferentNameForGenericParameterNestedCase.cs @@ -0,0 +1,31 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Generics { + class MethodWithParameterWhichHasGenericParametersAndOverrideUsesADifferentNameForGenericParameterNestedCase { + public static void Main (string [] args) + { + Derived tmp = new Derived (); + tmp.Method (null); + } + + [Kept] + public class Base { + + [KeptMember (".ctor()")] + public abstract class Nested { + [Kept] + public abstract TResult1 Method (System.Func arg); + } + } + + [KeptMember (".ctor()")] + [KeptBaseType (typeof (Base<>.Nested), "TResult1")] + public class Derived : Base.Nested { + [Kept] + public override TResult2 Method (System.Func arg) + { + return arg (default (TResult1)); + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Generics/MethodWithParameterWhichHasGenericParametersAndOverrideUsesADifferentNameForGenericParameterNestedCase2.cs b/linker/Tests/Mono.Linker.Tests.Cases/Generics/MethodWithParameterWhichHasGenericParametersAndOverrideUsesADifferentNameForGenericParameterNestedCase2.cs new file mode 100644 index 000000000..888e4278d --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Generics/MethodWithParameterWhichHasGenericParametersAndOverrideUsesADifferentNameForGenericParameterNestedCase2.cs @@ -0,0 +1,31 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Generics { + class MethodWithParameterWhichHasGenericParametersAndOverrideUsesADifferentNameForGenericParameterNestedCase2 { + public static void Main (string [] args) + { + Derived tmp = new Derived (); + tmp.Method (null); + } + + [Kept] + public class Base { + + [KeptMember (".ctor()")] + public abstract class Nested { + [Kept] + public abstract TResult1 Method (System.Func arg); + } + } + + [KeptMember (".ctor()")] + [KeptBaseType (typeof (Base<>.Nested<,,>), "TResult1", typeof (int), typeof (int), typeof (string))] + public class Derived : Base.Nested { + [Kept] + public override TResult2 Method (System.Func arg) + { + return arg (default (TResult1)); + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Generics/OverrideWithAnotherVirtualMethodOfSameNameWithDifferentParameterType.cs b/linker/Tests/Mono.Linker.Tests.Cases/Generics/OverrideWithAnotherVirtualMethodOfSameNameWithDifferentParameterType.cs new file mode 100644 index 000000000..ba81282b8 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Generics/OverrideWithAnotherVirtualMethodOfSameNameWithDifferentParameterType.cs @@ -0,0 +1,34 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Generics { + class OverrideWithAnotherVirtualMethodOfSameNameWithDifferentParameterType { + public static void Main (string [] args) + { + new Derived ().Method (1.0); + } + + [KeptMember (".ctor()")] + public class Base { + [Kept] + public virtual S Method (S arg) + { + return arg; + } + } + + [KeptMember (".ctor()")] + [KeptBaseType (typeof (Base<>), "K")] + public class Derived : Base { + [Kept] + public override K Method (K arg) + { + return arg; + } + + public virtual S Method (S arg) + { + return arg; + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Generics/UsedOverloadedGenericMethodInGenericClassIsNotStripped.cs b/linker/Tests/Mono.Linker.Tests.Cases/Generics/UsedOverloadedGenericMethodInGenericClassIsNotStripped.cs new file mode 100644 index 000000000..908e0d50d --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Generics/UsedOverloadedGenericMethodInGenericClassIsNotStripped.cs @@ -0,0 +1,21 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Generics { + public class UsedOverloadedGenericMethodInGenericClassIsNotStripped { + public static void Main () + { + B.Method (true); + } + + class B { + [Kept] + public static void Method (T value) + { + } + + public static void Method (TBase value) + { + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Generics/UsedOverloadedGenericMethodInstanceInGenericClassIsNotStripped.cs b/linker/Tests/Mono.Linker.Tests.Cases/Generics/UsedOverloadedGenericMethodInstanceInGenericClassIsNotStripped.cs new file mode 100644 index 000000000..a565d4cf5 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Generics/UsedOverloadedGenericMethodInstanceInGenericClassIsNotStripped.cs @@ -0,0 +1,21 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Generics { + public class UsedOverloadedGenericMethodInstanceInGenericClassIsNotStripped { + public static void Main () + { + B.Method (1); + } + + class B { + public static void Method (T value) + { + } + + [Kept] + public static void Method (TBase value) + { + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Generics/UsedOverloadedGenericMethodWithNoParametersIsNotStripped.cs b/linker/Tests/Mono.Linker.Tests.Cases/Generics/UsedOverloadedGenericMethodWithNoParametersIsNotStripped.cs new file mode 100644 index 000000000..52e1ff7b2 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Generics/UsedOverloadedGenericMethodWithNoParametersIsNotStripped.cs @@ -0,0 +1,27 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Generics { + public class UsedOverloadedGenericMethodWithNoParametersIsNotStripped { + public static void Main () + { + B.Call (); + } + + public class B { + public static void Method () + { + } + + [Kept] + public static void Method () + { + } + + [Kept] + public static void Call () + { + B.Method (); + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Interop/FieldsOfTypeMarkedSequentialLayoutAreNotRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/Interop/FieldsOfTypeMarkedSequentialLayoutAreNotRemoved.cs new file mode 100644 index 000000000..852a066e1 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Interop/FieldsOfTypeMarkedSequentialLayoutAreNotRemoved.cs @@ -0,0 +1,17 @@ +using System.Runtime.InteropServices; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Interop { + class FieldsOfTypeMarkedSequentialLayoutAreNotRemoved { + public static void Main () + { + new A (); + } + + [StructLayout (LayoutKind.Sequential)] + [KeptMember (".ctor()")] + class A { + [Kept] int a; + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Interop/InternalCalls/DefaultConstructorOfReturnTypeIsNotRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/Interop/InternalCalls/DefaultConstructorOfReturnTypeIsNotRemoved.cs new file mode 100644 index 000000000..ecfe75123 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Interop/InternalCalls/DefaultConstructorOfReturnTypeIsNotRemoved.cs @@ -0,0 +1,22 @@ +using System.Runtime.CompilerServices; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Interop.InternalCalls { + class DefaultConstructorOfReturnTypeIsNotRemoved { + public static void Main () + { + var a = SomeMethod (); + } + + class A { + [Kept] + public A () + { + } + } + + [Kept] + [MethodImpl (MethodImplOptions.InternalCall)] + static extern A SomeMethod (); + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Interop/InternalCalls/UnusedDefaultConstructorIsRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/Interop/InternalCalls/UnusedDefaultConstructorIsRemoved.cs new file mode 100644 index 000000000..ab21994f0 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Interop/InternalCalls/UnusedDefaultConstructorIsRemoved.cs @@ -0,0 +1,27 @@ +using System.Runtime.CompilerServices; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Interop.InternalCalls { + class UnusedDefaultConstructorIsRemoved { + public static void Main () + { + var a = new A (1); + SomeMethod (a); + } + + class A { + public A () + { + } + + [Kept] + public A (int other) + { + } + } + + [Kept] + [MethodImpl (MethodImplOptions.InternalCall)] + static extern void SomeMethod (A a); + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Interop/InternalCalls/UnusedDefaultConstructorOfTypePassedByRefIsNotRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/Interop/InternalCalls/UnusedDefaultConstructorOfTypePassedByRefIsNotRemoved.cs new file mode 100644 index 000000000..d181ecd0a --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Interop/InternalCalls/UnusedDefaultConstructorOfTypePassedByRefIsNotRemoved.cs @@ -0,0 +1,28 @@ +using System.Runtime.CompilerServices; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Interop.InternalCalls { + class UnusedDefaultConstructorOfTypePassedByRefIsNotRemoved { + public static void Main () + { + var a = new A (1); + SomeMethod (ref a); + } + + class A { + [Kept] + public A () + { + } + + [Kept] + public A (int other) + { + } + } + + [Kept] + [MethodImpl (MethodImplOptions.InternalCall)] + static extern void SomeMethod (ref A a); + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Interop/InternalCalls/UnusedFieldsOfTypesAreNotRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/Interop/InternalCalls/UnusedFieldsOfTypesAreNotRemoved.cs new file mode 100644 index 000000000..09809a60e --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Interop/InternalCalls/UnusedFieldsOfTypesAreNotRemoved.cs @@ -0,0 +1,23 @@ +using System.Runtime.CompilerServices; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Interop.InternalCalls { + class UnusedFieldsOfTypesAreNotRemoved { + public static void Main () + { + var a = new A (); + SomeMethod (a); + } + + [KeptMember (".ctor()")] + class A { + [Kept] private int field1; + + [Kept] private int field2; + } + + [Kept] + [MethodImpl (MethodImplOptions.InternalCall)] + static extern void SomeMethod (A a); + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Interop/InternalCalls/UnusedFieldsOfTypesPassedByRefAreNotRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/Interop/InternalCalls/UnusedFieldsOfTypesPassedByRefAreNotRemoved.cs new file mode 100644 index 000000000..d4f3c3779 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Interop/InternalCalls/UnusedFieldsOfTypesPassedByRefAreNotRemoved.cs @@ -0,0 +1,23 @@ +using System.Runtime.CompilerServices; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Interop.InternalCalls { + class UnusedFieldsOfTypesPassedByRefAreNotRemoved { + public static void Main () + { + var a = new A (); + SomeMethod (ref a); + } + + [KeptMember (".ctor()")] + class A { + [Kept] private int field1; + + [Kept] private int field2; + } + + [Kept] + [MethodImpl (MethodImplOptions.InternalCall)] + static extern void SomeMethod (ref A a); + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Interop/InternalCalls/UnusedFieldsOfTypesWhenHasThisAreNotRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/Interop/InternalCalls/UnusedFieldsOfTypesWhenHasThisAreNotRemoved.cs new file mode 100644 index 000000000..df77357d6 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Interop/InternalCalls/UnusedFieldsOfTypesWhenHasThisAreNotRemoved.cs @@ -0,0 +1,21 @@ +using System.Runtime.CompilerServices; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Interop.InternalCalls { + class UnusedFieldsOfTypesWhenHasThisAreNotRemoved { + public static void Main () + { + A a = new A (); + a.SomeMethod (); + } + + [KeptMember (".ctor()")] + class A { + [Kept] private int field1; + + [Kept] + [MethodImpl (MethodImplOptions.InternalCall)] + public extern void SomeMethod (); + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Interop/PInvoke/DefaultConstructorOfReturnTypeIsNotRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/Interop/PInvoke/DefaultConstructorOfReturnTypeIsNotRemoved.cs new file mode 100644 index 000000000..d714d0d95 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Interop/PInvoke/DefaultConstructorOfReturnTypeIsNotRemoved.cs @@ -0,0 +1,22 @@ +using System.Runtime.InteropServices; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Interop.PInvoke { + class DefaultConstructorOfReturnTypeIsNotRemoved { + public static void Main () + { + var a = SomeMethod (); + } + + class A { + [Kept] + public A () + { + } + } + + [Kept] + [DllImport ("Unused")] + private static extern A SomeMethod (); + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Interop/PInvoke/UnusedDefaultConstructorIsRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/Interop/PInvoke/UnusedDefaultConstructorIsRemoved.cs new file mode 100644 index 000000000..041cc9c98 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Interop/PInvoke/UnusedDefaultConstructorIsRemoved.cs @@ -0,0 +1,27 @@ +using System.Runtime.InteropServices; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Interop.PInvoke { + class UnusedDefaultConstructorIsRemoved { + public static void Main () + { + var a = new A (1); + SomeMethod (a); + } + + class A { + public A () + { + } + + [Kept] + public A (int other) + { + } + } + + [Kept] + [DllImport ("Unused")] + private static extern void SomeMethod (A a); + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Interop/PInvoke/UnusedDefaultConstructorOfTypePassedByRefIsNotRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/Interop/PInvoke/UnusedDefaultConstructorOfTypePassedByRefIsNotRemoved.cs new file mode 100644 index 000000000..db2514a4a --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Interop/PInvoke/UnusedDefaultConstructorOfTypePassedByRefIsNotRemoved.cs @@ -0,0 +1,28 @@ +using System.Runtime.InteropServices; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Interop.PInvoke { + class UnusedDefaultConstructorOfTypePassedByRefIsNotRemoved { + public static void Main () + { + var a = new A (1); + SomeMethod (ref a); + } + + class A { + [Kept] + public A () + { + } + + [Kept] + public A (int other) + { + } + } + + [Kept] + [DllImport ("Unused")] + private static extern void SomeMethod (ref A a); + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Interop/PInvoke/UnusedFieldsOfTypesAreNotRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/Interop/PInvoke/UnusedFieldsOfTypesAreNotRemoved.cs new file mode 100644 index 000000000..a34c4d6bb --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Interop/PInvoke/UnusedFieldsOfTypesAreNotRemoved.cs @@ -0,0 +1,23 @@ +using System.Runtime.InteropServices; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Interop.PInvoke { + class UnusedFieldsOfTypesAreNotRemoved { + public static void Main () + { + var a = new A (); + SomeMethod (a); + } + + [KeptMember (".ctor()")] + class A { + [Kept] private int field1; + + [Kept] private int field2; + } + + [Kept] + [DllImport ("Unused")] + private static extern void SomeMethod (A a); + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Interop/PInvoke/UnusedFieldsOfTypesPassedByRefAreNotRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/Interop/PInvoke/UnusedFieldsOfTypesPassedByRefAreNotRemoved.cs new file mode 100644 index 000000000..50ae45fe3 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Interop/PInvoke/UnusedFieldsOfTypesPassedByRefAreNotRemoved.cs @@ -0,0 +1,23 @@ +using System.Runtime.InteropServices; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Interop.PInvoke { + class UnusedFieldsOfTypesPassedByRefAreNotRemoved { + public static void Main () + { + var a = new A (); + SomeMethod (ref a); + } + + [KeptMember (".ctor()")] + class A { + [Kept] private int field1; + + [Kept] private int field2; + } + + [Kept] + [DllImport ("Unused")] + private static extern void SomeMethod (ref A a); + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Interop/UnusedTypeWithSequentialLayoutIsRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/Interop/UnusedTypeWithSequentialLayoutIsRemoved.cs new file mode 100644 index 000000000..00c47a342 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Interop/UnusedTypeWithSequentialLayoutIsRemoved.cs @@ -0,0 +1,15 @@ +using System.Runtime.InteropServices; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Interop { + class UnusedTypeWithSequentialLayoutIsRemoved { + static void Main () + { + } + + [StructLayout (LayoutKind.Sequential)] + class B { + int a; + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/TypeWithPreserveFieldsHasBackingFieldsOfPropertiesRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/TypeWithPreserveFieldsHasBackingFieldsOfPropertiesRemoved.cs new file mode 100644 index 000000000..d65701772 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/TypeWithPreserveFieldsHasBackingFieldsOfPropertiesRemoved.cs @@ -0,0 +1,72 @@ + + +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.LinkXml +{ + class TypeWithPreserveFieldsHasBackingFieldsOfPropertiesRemoved { + public static void Main () { + } + + [Kept] + [KeptInterface(typeof (IFoo))] + [KeptInterface(typeof (IFoo))] + [KeptInterface(typeof (IFoo))] + [KeptInterface(typeof (IFoo2))] + [KeptInterface(typeof (IFoo3))] + [KeptInterface(typeof (IDog))] + [KeptInterface(typeof (IFoo>))] + class Unused : IFoo, IFoo, IFoo, IFoo2, IFoo3, IDog, IFoo> { + [Kept] + public int Field1; + + [Kept] + public IFoo Field2; + + public IFoo Property1 { get; set; } + + string IDog.Name { get; set; } + + int IFoo.Bar { get; set; } + + int IFoo.Bar { get; set; } + + int IFoo.Bar { get; set; } + + int Bar2 { get; set; } + + int IFoo2.Bar2 { get; set; } + + int Bar3 { get; set; } + + int IFoo3.Bar3 { get; set; } + + int IFoo>.Bar { get; set; } + } + + [Kept] + interface IDog { + string Name { get; set; } + } + + [Kept] + interface IFoo { + + int Bar { get; set; } + } + + [Kept] + interface IFoo2 { + int Bar2 { get; set; } + } + + [Kept] + interface IFoo3 { + int Bar3 { get; set; } + } + + [Kept] + class Cat { + } + } +} diff --git a/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/TypeWithPreserveFieldsHasBackingFieldsOfPropertiesRemoved.xml b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/TypeWithPreserveFieldsHasBackingFieldsOfPropertiesRemoved.xml new file mode 100644 index 000000000..338ee7f76 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/TypeWithPreserveFieldsHasBackingFieldsOfPropertiesRemoved.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedFieldPreservedByLinkXmlIsKept.cs b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedFieldPreservedByLinkXmlIsKept.cs new file mode 100644 index 000000000..bc1b0f337 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedFieldPreservedByLinkXmlIsKept.cs @@ -0,0 +1,17 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.LinkXml { + class UnusedFieldPreservedByLinkXmlIsKept { + public static void Main () + { + } + + [Kept] + class Unused { + [Kept] + private int _preserved; + + private int _notPreserved; + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedFieldPreservedByLinkXmlIsKept.xml b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedFieldPreservedByLinkXmlIsKept.xml new file mode 100644 index 000000000..46db550dc --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedFieldPreservedByLinkXmlIsKept.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedMethodPreservedByLinkXmlIsKept.cs b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedMethodPreservedByLinkXmlIsKept.cs new file mode 100644 index 000000000..99584fd9f --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedMethodPreservedByLinkXmlIsKept.cs @@ -0,0 +1,21 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.LinkXml { + class UnusedMethodPreservedByLinkXmlIsKept { + public static void Main () + { + } + + [Kept] + class Unused { + [Kept] + private void PreservedMethod () + { + } + + private void NotPreservedMethod () + { + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedMethodPreservedByLinkXmlIsKept.xml b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedMethodPreservedByLinkXmlIsKept.xml new file mode 100644 index 000000000..231a0d203 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedMethodPreservedByLinkXmlIsKept.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedNestedTypePreservedByLinkXmlIsKept.cs b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedNestedTypePreservedByLinkXmlIsKept.cs new file mode 100644 index 000000000..5e319acfd --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedNestedTypePreservedByLinkXmlIsKept.cs @@ -0,0 +1,14 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.LinkXml { + class UnusedNestedTypePreservedByLinkXmlIsKept { + public static void Main () + { + } + + [Kept] + [KeptMember (".ctor()")] + class Unused { + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedNestedTypePreservedByLinkXmlIsKept.xml b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedNestedTypePreservedByLinkXmlIsKept.xml new file mode 100644 index 000000000..7990e8fec --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedNestedTypePreservedByLinkXmlIsKept.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedPropertyPreservedByLinkXmlIsKept.cs b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedPropertyPreservedByLinkXmlIsKept.cs new file mode 100644 index 000000000..65084580f --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedPropertyPreservedByLinkXmlIsKept.cs @@ -0,0 +1,26 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.LinkXml { + class UnusedPropertyPreservedByLinkXmlIsKept { + public static void Main () + { + } + + [Kept] + class Unused { + [Kept] + [KeptBackingField] + public int PreservedProperty1 { [Kept] get; [Kept] set; } + + [Kept] + [KeptBackingField] + public int PreservedProperty2 { [Kept] get; set; } + + [Kept] + [KeptBackingField] + public int PreservedProperty3 { get; [Kept] set; } + + public int NotPreservedProperty { get; set; } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedPropertyPreservedByLinkXmlIsKept.xml b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedPropertyPreservedByLinkXmlIsKept.xml new file mode 100644 index 000000000..1e182255d --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedPropertyPreservedByLinkXmlIsKept.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypePreservedByLinkXmlIsKept.cs b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypePreservedByLinkXmlIsKept.cs new file mode 100644 index 000000000..edaff63eb --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypePreservedByLinkXmlIsKept.cs @@ -0,0 +1,14 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.LinkXml { + class UnusedTypePreservedByLinkXmlIsKept { + public static void Main () + { + } + } + + [Kept] + [KeptMember (".ctor()")] + class UnusedTypePreservedByLinkXmlIsKeptUnusedType { + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypePreservedByLinkXmlIsKept.xml b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypePreservedByLinkXmlIsKept.xml new file mode 100644 index 000000000..ffb652e1e --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypePreservedByLinkXmlIsKept.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveAllHasAllMembersPreserved.cs b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveAllHasAllMembersPreserved.cs new file mode 100644 index 000000000..02fb956f2 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveAllHasAllMembersPreserved.cs @@ -0,0 +1,85 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.LinkXml { + class UnusedTypeWithPreserveAllHasAllMembersPreserved { + public static void Main () + { + } + + [Kept] + [KeptMember (".ctor()")] + class Unused { + [Kept] + public int Field1; + + [Kept] + private int Field2; + + [Kept] + internal int Field3; + + [Kept] + public static int Field4; + + [Kept] + private static int Field5; + + [Kept] + internal static int Field6; + + [Kept] + [KeptBackingField] + public string Property1 { [Kept] get; [Kept] set;} + + [Kept] + [KeptBackingField] + private string Property2 { [Kept] get; [Kept] set; } + + [Kept] + [KeptBackingField] + internal string Property3 { [Kept] get; [Kept] set; } + + [Kept] + [KeptBackingField] + public static string Property4 { [Kept] get; [Kept] set; } + + [Kept] + [KeptBackingField] + private static string Property5 { [Kept] get; [Kept] set; } + + [Kept] + [KeptBackingField] + internal static string Property6 { [Kept] get; [Kept] set; } + + [Kept] + public void Method1 () + { + } + + [Kept] + private void Method2 () + { + } + + [Kept] + internal void Method3 () + { + } + + [Kept] + public static void Method4 () + { + } + + [Kept] + private static void Method5 () + { + } + + [Kept] + internal static void Method6 () + { + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveAllHasAllMembersPreserved.xml b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveAllHasAllMembersPreserved.xml new file mode 100644 index 000000000..7987a7765 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveAllHasAllMembersPreserved.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveFieldsHasMethodsRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveFieldsHasMethodsRemoved.cs new file mode 100644 index 000000000..1af6f4530 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveFieldsHasMethodsRemoved.cs @@ -0,0 +1,66 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.LinkXml { + class UnusedTypeWithPreserveFieldsHasMethodsRemoved { + public static void Main () + { + } + + [Kept] + class Unused { + [Kept] + public int Field1; + + [Kept] + private int Field2; + + [Kept] + internal int Field3; + + [Kept] + public static int Field4; + + [Kept] + private static int Field5; + + [Kept] + internal static int Field6; + + public string Property1 { get; set; } + private string Property2 { get; set; } + internal string Property3 { get; set; } + public static string Property4 { get; set; } + private static string Property5 { get; set; } + internal static string Property6 { get; set; } + + [Kept] + public void PreservedMethod () + { + } + + public void Method1 () + { + } + + private void Method2 () + { + } + + internal void Method3 () + { + } + + public static void Method4 () + { + } + + private static void Method5 () + { + } + + internal static void Method6 () + { + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveFieldsHasMethodsRemoved.xml b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveFieldsHasMethodsRemoved.xml new file mode 100644 index 000000000..619a8db26 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveFieldsHasMethodsRemoved.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveMethodsHasFieldsRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveMethodsHasFieldsRemoved.cs new file mode 100644 index 000000000..8a79d2d2b --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveMethodsHasFieldsRemoved.cs @@ -0,0 +1,74 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.LinkXml { + class UnusedTypeWithPreserveMethodsHasFieldsRemoved { + public static void Main () + { + } + + [Kept] + [KeptMember (".ctor()")] + class Unused { + public int Field1; + private int Field2; + internal int Field3; + public static int Field4; + private static int Field5; + internal static int Field6; + + [Kept] + [KeptBackingField] + public string Property1 { [Kept] get; [Kept] set; } + + [Kept] + [KeptBackingField] + private string Property2 { [Kept] get; [Kept] set; } + + [Kept] + [KeptBackingField] + internal string Property3 { [Kept] get; [Kept] set; } + + [Kept] + [KeptBackingField] + public static string Property4 { [Kept] get; [Kept] set; } + + [Kept] + [KeptBackingField] + private static string Property5 { [Kept] get; [Kept] set; } + + [Kept] + [KeptBackingField] + internal static string Property6 { [Kept] get; [Kept] set; } + + [Kept] + public void Method1 () + { + } + + [Kept] + private void Method2 () + { + } + + [Kept] + internal void Method3 () + { + } + + [Kept] + public static void Method4 () + { + } + + [Kept] + private static void Method5 () + { + } + + [Kept] + internal static void Method6 () + { + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveMethodsHasFieldsRemoved.xml b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveMethodsHasFieldsRemoved.xml new file mode 100644 index 000000000..20723f8ed --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveMethodsHasFieldsRemoved.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveNothingAndPreserveMembers.cs b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveNothingAndPreserveMembers.cs new file mode 100644 index 000000000..b9d404f22 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveNothingAndPreserveMembers.cs @@ -0,0 +1,26 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.LinkXml { + class UnusedTypeWithPreserveNothingAndPreserveMembers { + public static void Main () + { + } + + [Kept] + class Unused { + [Kept] + public int Field1; + + private int Field2; + + [Kept] + public void Method1 () + { + } + + private void Method2 () + { + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveNothingAndPreserveMembers.xml b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveNothingAndPreserveMembers.xml new file mode 100644 index 000000000..d672e9e09 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveNothingAndPreserveMembers.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveNothingHasMembersRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveNothingHasMembersRemoved.cs new file mode 100644 index 000000000..d6ea29b89 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveNothingHasMembersRemoved.cs @@ -0,0 +1,50 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.LinkXml { + class UnusedTypeWithPreserveNothingHasMembersRemoved { + public static void Main () + { + } + + [Kept] + class Unused { + public int Field1; + private int Field2; + internal int Field3; + public static int Field4; + private static int Field5; + internal static int Field6; + + public string Property1 { get; set; } + private string Property2 { get; set; } + internal string Property3 { get; set; } + public static string Property4 { get; set; } + private static string Property5 { get; set; } + internal static string Property6 { get; set; } + + public void Method1 () + { + } + + private void Method2 () + { + } + + internal void Method3 () + { + } + + public static void Method4 () + { + } + + private static void Method5 () + { + } + + internal static void Method6 () + { + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveNothingHasMembersRemoved.xml b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveNothingHasMembersRemoved.xml new file mode 100644 index 000000000..f76df307a --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/LinkXml/UnusedTypeWithPreserveNothingHasMembersRemoved.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Mono.Linker.Tests.Cases.csproj b/linker/Tests/Mono.Linker.Tests.Cases/Mono.Linker.Tests.Cases.csproj new file mode 100644 index 000000000..245bc0936 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Mono.Linker.Tests.Cases.csproj @@ -0,0 +1,140 @@ + + + + + Debug + AnyCPU + {B6BEE6AA-ADA0-4E1D-9A17-FBF2936F82B5} + Library + Properties + Mono.Linker.Tests.Cases + Mono.Linker.Tests.Cases + v4.0 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE;INCLUDE_EXPECTATIONS + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE;INCLUDE_EXPECTATIONS + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {2C26601F-3E2F-45B9-A02F-58EE9296E19E} + Mono.Linker.Tests.Cases.Expectations + + + + + \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/References/ReferencesAreRemovedWhenAllUsagesAreRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/References/ReferencesAreRemovedWhenAllUsagesAreRemoved.cs new file mode 100644 index 000000000..664a0c98d --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/References/ReferencesAreRemovedWhenAllUsagesAreRemoved.cs @@ -0,0 +1,20 @@ +using System; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.References { + [CoreLink ("link")] + [Reference ("System.dll")] + [RemovedAssembly ("System.dll")] + class ReferencesAreRemovedWhenAllUsagesAreRemoved { + public static void Main () + { + } + + private static void Unused () + { + // Use something from System.dll so that we know the input assembly was compiled with the reference + var uri = new Uri ("w/e"); + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Statics/UnusedStaticConstructorGetsRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/Statics/UnusedStaticConstructorGetsRemoved.cs new file mode 100644 index 000000000..83c1d1ea8 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Statics/UnusedStaticConstructorGetsRemoved.cs @@ -0,0 +1,20 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Statics { + class UnusedStaticConstructorGetsRemoved { + public static void Main () + { + } + + static void Dead () + { + new B (); + } + + class B { + static B () + { + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/Statics/UnusedStaticMethodGetsRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/Statics/UnusedStaticMethodGetsRemoved.cs new file mode 100644 index 000000000..31c7c6ef0 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/Statics/UnusedStaticMethodGetsRemoved.cs @@ -0,0 +1,23 @@ +using System; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Statics { + public class UnusedStaticMethodGetsRemoved { + public static void Main () + { + A.UsedMethod (); + } + } + + [Kept] + class A { + [Kept] + public static void UsedMethod () + { + } + + static void UnusedMethod () + { + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/ClassImplemtingInterfaceMethodsThroughBaseClass2.cs b/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/ClassImplemtingInterfaceMethodsThroughBaseClass2.cs new file mode 100644 index 000000000..f370e9e5d --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/ClassImplemtingInterfaceMethodsThroughBaseClass2.cs @@ -0,0 +1,29 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.VirtualMethods { + class ClassImplemtingInterfaceMethodsThroughBaseClass2 { + public static void Main () + { + new B (); + IFoo i = null; + i.Foo (); + } + + interface IFoo { + [Kept] + void Foo (); + } + + [KeptMember (".ctor()")] + class B { + [Kept] // FIXME: Should be removed + public void Foo () + { + } + } + + class A : B, IFoo { + //my IFoo.Foo() is actually implemented by B which doesn't know about it. + } + } +} diff --git a/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/ClassImplemtingInterfaceMethodsThroughBaseClass3.cs b/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/ClassImplemtingInterfaceMethodsThroughBaseClass3.cs new file mode 100644 index 000000000..362407bd1 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/ClassImplemtingInterfaceMethodsThroughBaseClass3.cs @@ -0,0 +1,26 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.VirtualMethods { + class ClassImplemtingInterfaceMethodsThroughBaseClass3 { + public static void Main () + { + new B ().Foo (); + } + + interface IFoo { + void Foo (); + } + + [KeptMember (".ctor()")] + class B { + [Kept] + public void Foo () + { + } + } + + class A : B, IFoo { + //my IFoo.Foo() is actually implemented by B which doesn't know about it. + } + } +} diff --git a/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/ClassImplemtingInterfaceMethodsThroughBaseClass4.cs b/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/ClassImplemtingInterfaceMethodsThroughBaseClass4.cs new file mode 100644 index 000000000..829ab01c7 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/ClassImplemtingInterfaceMethodsThroughBaseClass4.cs @@ -0,0 +1,30 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.VirtualMethods { + class ClassImplemtingInterfaceMethodsThroughBaseClass4 { + public static void Main () + { + new A ().Foo (); + } + + [Kept] + interface IFoo { + void Foo (); + } + + [KeptMember (".ctor()")] + class B { + [Kept] + public void Foo () + { + } + } + + [KeptMember (".ctor()")] + [KeptBaseType (typeof (B))] + [KeptInterface (typeof (IFoo))] // FIXME: Why is it not removed + class A : B, IFoo { + //my IFoo.Foo() is actually implemented by B which doesn't know about it. + } + } +} diff --git a/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/ClassImplemtingInterfaceMethodsThroughBaseClass5.cs b/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/ClassImplemtingInterfaceMethodsThroughBaseClass5.cs new file mode 100644 index 000000000..10868a8a2 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/ClassImplemtingInterfaceMethodsThroughBaseClass5.cs @@ -0,0 +1,29 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.VirtualMethods { + class ClassImplemtingInterfaceMethodsThroughBaseClass5 { + public static void Main () + { + new A (); + } + + [Kept] + interface IFoo { + void Foo (); + } + + [KeptMember (".ctor()")] + class B { + public void Foo () + { + } + } + + [KeptMember (".ctor()")] + [KeptBaseType (typeof (B))] + [KeptInterface (typeof (IFoo))] + class A : B, IFoo { + //my IFoo.Foo() is actually implemented by B which doesn't know about it. + } + } +} diff --git a/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/ClassImplemtingInterfaceMethodsThroughBaseClass6.cs b/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/ClassImplemtingInterfaceMethodsThroughBaseClass6.cs new file mode 100644 index 000000000..bb295c9c2 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/ClassImplemtingInterfaceMethodsThroughBaseClass6.cs @@ -0,0 +1,38 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.VirtualMethods { + class ClassImplemtingInterfaceMethodsThroughBaseClass6 { + public static void Main () + { + B tmp = new B (); + IFoo i = new C (); + i.Foo (); + } + + interface IFoo { + [Kept] + void Foo (); + } + + [KeptMember (".ctor()")] + class B { + [Kept] // FIXME: Needs to be removed + public void Foo () + { + } + } + + class A : B, IFoo { + //my IFoo.Foo() is actually implemented by B which doesn't know about it. + } + + [KeptMember (".ctor()")] + [KeptInterface (typeof (IFoo))] + class C : IFoo { + [Kept] + public void Foo () + { + } + } + } +} diff --git a/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/HarderToDetectUnusedVirtualMethodGetsRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/HarderToDetectUnusedVirtualMethodGetsRemoved.cs new file mode 100644 index 000000000..c08e69afd --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/HarderToDetectUnusedVirtualMethodGetsRemoved.cs @@ -0,0 +1,30 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.VirtualMethods { + class HarderToDetectUnusedVirtualMethodGetsRemoved { + public static void Main () + { + new Base ().Call (); + } + + static void DeadCode () + { + new B (); + } + + [Kept] + [KeptMember (".ctor()")] + class Base { + [Kept] + public virtual void Call () + { + } + } + + class B : Base { + public override void Call () + { + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/TypeGetsMarkedThatImplementsAlreadyMarkedInterfaceMethod.cs b/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/TypeGetsMarkedThatImplementsAlreadyMarkedInterfaceMethod.cs new file mode 100644 index 000000000..880ac679e --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/TypeGetsMarkedThatImplementsAlreadyMarkedInterfaceMethod.cs @@ -0,0 +1,35 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.VirtualMethods { + class TypeGetsMarkedThatImplementsAlreadyMarkedInterfaceMethod { + public static void Main () + { + IFoo i = new A (); + i.Foo (); + } + + interface IFoo { + [Kept] + void Foo (); + } + + [KeptMember (".ctor()")] + [KeptInterface (typeof (IFoo))] + class B : IFoo { + [Kept] + public void Foo () + { + } + } + + [KeptMember (".ctor()")] + [KeptInterface (typeof (IFoo))] + class A : IFoo { + [Kept] + public void Foo () + { + new B (); /*this will cause us to mark B, but will we be smart enough to realize B.Foo implements the already marked IFoo.Foo?*/ + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/UnusedVirtualMethodRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/UnusedVirtualMethodRemoved.cs new file mode 100644 index 000000000..115ca8648 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/UnusedVirtualMethodRemoved.cs @@ -0,0 +1,24 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.VirtualMethods { + class UnusedVirtualMethodRemoved { + public static void Main () + { + new Base ().Call (); + } + + [KeptMember (".ctor()")] + class Base { + [Kept] + public virtual void Call () + { + } + } + + class B : Base { + public override void Call () + { + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/UsedVirtualMethodNotRemoved.cs b/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/UsedVirtualMethodNotRemoved.cs new file mode 100644 index 000000000..e695cca7e --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/UsedVirtualMethodNotRemoved.cs @@ -0,0 +1,29 @@ +using System; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.VirtualMethods { + public class UsedVirtualMethodNotRemoved { + public static void Main () + { + new B (); + new Base ().Call (); + } + + [KeptMember (".ctor()")] + class Base { + [Kept] + public virtual void Call () + { + } + } + + [KeptMember (".ctor()")] + [KeptBaseType (typeof (Base))] + class B : Base { + [Kept] + public override void Call () + { + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/VirtualMethodGetsPerservedIfBaseMethodGetsInvoked.cs b/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/VirtualMethodGetsPerservedIfBaseMethodGetsInvoked.cs new file mode 100644 index 000000000..372105cd3 --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/VirtualMethodGetsPerservedIfBaseMethodGetsInvoked.cs @@ -0,0 +1,28 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.VirtualMethods { + class VirtualMethodGetsPerservedIfBaseMethodGetsInvoked { + public static void Main () + { + new A (); + new B ().Foo (); + } + + [KeptMember (".ctor()")] + class B { + [Kept] + public virtual void Foo () + { + } + } + + [KeptMember (".ctor()")] + [KeptBaseType (typeof (B))] + class A : B { + [Kept] + public override void Foo () + { + } + } + } +} diff --git a/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/VirtualMethodGetsStrippedIfImplementingMethodGetsInvokedDirectly.cs b/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/VirtualMethodGetsStrippedIfImplementingMethodGetsInvokedDirectly.cs new file mode 100644 index 000000000..f1baa44eb --- /dev/null +++ b/linker/Tests/Mono.Linker.Tests.Cases/VirtualMethods/VirtualMethodGetsStrippedIfImplementingMethodGetsInvokedDirectly.cs @@ -0,0 +1,27 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.VirtualMethods { + class VirtualMethodGetsStrippedIfImplementingMethodGetsInvokedDirectly { + public static void Main () + { + new A ().Foo (); + } + + [KeptMember (".ctor()")] + class B { + [Kept] // TODO: Would be nice to be removed + public virtual void Foo () + { + } + } + + [KeptMember (".ctor()")] + [KeptBaseType (typeof (B))] + class A : B { + [Kept] + public override void Foo () + { + } + } + } +} diff --git a/linker/Tests/Mono.Linker.Tests.csproj b/linker/Tests/Mono.Linker.Tests.csproj index b3438c91b..7e1b44418 100644 --- a/linker/Tests/Mono.Linker.Tests.csproj +++ b/linker/Tests/Mono.Linker.Tests.csproj @@ -10,6 +10,7 @@ v4.6.2 + Mono.Linker true @@ -44,16 +45,10 @@ ..\..\packages\NUnit.3.6.1\lib\net45\nunit.framework.dll - - - - - - - - + + @@ -64,12 +59,39 @@ {DD28E2B1-057B-4B4D-A04D-B2EBD9E76E46} Mono.Linker - - {26D857FB-EEE3-4A5B-95BC-DAB39F880A99} - TestCases + + {B6BEE6AA-ADA0-4E1D-9A17-FBF2936F82B5} + Mono.Linker.Tests.Cases + + + {2C26601F-3E2F-45B9-A02F-58EE9296E19E} + Mono.Linker.Tests.Cases.Expectations + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/linker/Tests/Mono.Linker.Tests/AbstractLinkingTestFixture.cs b/linker/Tests/Mono.Linker.Tests/AbstractLinkingTestFixture.cs deleted file mode 100644 index d25aeab32..000000000 --- a/linker/Tests/Mono.Linker.Tests/AbstractLinkingTestFixture.cs +++ /dev/null @@ -1,167 +0,0 @@ -// -// AbstractLinkingTestFixture.cs -// -// Author: -// Jb Evain (jbevain@gmail.com) -// -// (C) 2006 Jb Evain -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System.IO; -using Mono.Cecil; -using Mono.Linker.Steps; -using NUnit.Framework; -using System.Linq; -using System.Collections.Generic; - -namespace Mono.Linker.Tests -{ - - public abstract class AbstractLinkingTestFixture : AbstractTestFixture - { - - [SetUp] - public override void SetUp () - { - base.SetUp (); - - TestCasesRoot = "Linker"; - } - - [TearDown] - public override void TearDown () - { - base.TearDown (); - - string output = GetOutputPath (); - if (Directory.Exists (output)) - Directory.Delete (output, true); - } - - protected override Pipeline GetPipeline () - { - Pipeline p = new Pipeline (); - p.AppendStep (new LoadReferencesStep ()); - p.AppendStep (new BlacklistStep ()); - p.AppendStep (new TypeMapStep ()); - p.AppendStep (new MarkStep ()); - p.AppendStep (new SweepStep ()); - p.AppendStep (new CleanStep ()); - p.AppendStep (new OutputStep ()); - return p; - } - - protected override void RunTest (string testCase) - { - base.RunTest (testCase); - - Prepare (); - } - - void Prepare () - { - Context = GetContext (); - string output = GetOutputPath (); - if (Directory.Exists (output)) - Directory.Delete (output, true); - Directory.CreateDirectory (output); - } - - protected override void Run () - { - base.Run (); - Compare (); - } - - void Compare () - { - bool compared = false; - foreach (AssemblyDefinition assembly in Context.GetAssemblies ()) { - if (Context.Annotations.GetAction (assembly) != AssemblyAction.Link) - continue; - - string fileName = GetAssemblyFileName (assembly); - - var original = AssemblyDefinition.ReadAssembly (Path.Combine (GetTestCasePath (), fileName)); - var linked = AssemblyDefinition.ReadAssembly (Path.Combine (GetOutputPath (), fileName)); - compared = CompareAssemblies (original, linked); - } - - Assert.IsTrue (compared, $"No data compared (are you missing '{TestCase}' namespace for the test case?)"); - } - - bool CompareAssemblies (AssemblyDefinition original, AssemblyDefinition linked) - { - bool compared = false; - foreach (TypeDefinition originalType in original.MainModule.Types) { - if (!originalType.FullName.StartsWith (TestCase, System.StringComparison.Ordinal)) - continue; - - compared = true; - - TypeDefinition linkedType = linked.MainModule.Types.FirstOrDefault (l => l.FullName == originalType.FullName); - if (MustBeLinked (originalType)) { - Assert.IsNull (linkedType, string.Format ("Type `{0}' was not linked", originalType)); - continue; - } - - Assert.IsNotNull (linkedType, string.Format ("Type `{0}' was linked", originalType)); - CompareTypes (originalType, linkedType); - } - - return compared; - } - - static void CompareTypes (TypeDefinition type, TypeDefinition linkedType) - { - foreach (FieldDefinition originalField in type.Fields) { - IEnumerable fd = linkedType.Fields; - FieldDefinition linkedField = fd.FirstOrDefault (l => l.Name == originalField.Name);// TODO: also get with the type! - if (MustBeLinked (originalField)) { - Assert.IsNull (linkedField, string.Format ("Field `{0}' should not have been linked", originalField)); - continue; - } - - Assert.IsNotNull (linkedField, string.Format ("Field `{0}' should have been linked", originalField)); - } - - foreach (MethodDefinition originalMethod in type.Methods) { - MethodDefinition linkedMethod = linkedType.Methods.FirstOrDefault (l => l.Name == originalMethod.Name && l.Parameters.Count == originalMethod.Parameters.Count); // TODO: lame - if (MustBeLinked (originalMethod)) { - Assert.IsNull (linkedMethod, string.Format ("Method `{0}' was not linked", originalMethod)); - continue; - } - - Assert.IsNotNull (linkedMethod, string.Format ("Method `{0}' was linked", originalMethod)); - } - } - - static bool MustBeLinked (ICustomAttributeProvider provider) - { - foreach (CustomAttribute ca in provider.CustomAttributes) - if (ca.Constructor.DeclaringType.Name == "AssertLinkedAttribute") - return true; - - return false; - } - } -} diff --git a/linker/Tests/Mono.Linker.Tests/AbstractTestFixture.cs b/linker/Tests/Mono.Linker.Tests/AbstractTestFixture.cs deleted file mode 100644 index 1ea047d73..000000000 --- a/linker/Tests/Mono.Linker.Tests/AbstractTestFixture.cs +++ /dev/null @@ -1,118 +0,0 @@ -// -// AbstractTestFixture.cs -// -// Author: -// Jb Evain (jbevain@gmail.com) -// -// (C) 2006 Jb Evain -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System.IO; - -using Mono.Cecil; - -using NUnit.Framework; - -namespace Mono.Linker.Tests -{ - - public abstract class AbstractTestFixture - { - - string _testCasesRoot; - string _testCase; - LinkContext _context; - Pipeline _pipeline; - - protected LinkContext Context { - get { return _context; } - set { _context = value; } - } - - protected Pipeline Pipeline { - get { return _pipeline; } - set { _pipeline = value; } - } - - public string TestCasesRoot { - get { return _testCasesRoot; } - set { _testCasesRoot = value; } - } - - public string TestCase { - get { return _testCase; } - set { _testCase = value; } - } - - [SetUp] - public virtual void SetUp () - { - _pipeline = GetPipeline (); - } - - [TearDown] - public virtual void TearDown () - { - } - - protected virtual Pipeline GetPipeline () - { - return new Pipeline (); - } - - protected virtual LinkContext GetContext () - { - LinkContext context = new LinkContext (_pipeline); - context.OutputDirectory = GetOutputPath (); - context.CoreAction = AssemblyAction.Copy; - return context; - } - - protected string GetOutputPath () - { - return Path.Combine ( - Path.GetTempPath (), - _testCase); - } - - protected string GetTestCasePath () - { - return Path.GetDirectoryName (GetType ().Assembly.Location); - } - - protected virtual void RunTest (string testCase) - { - _testCase = testCase; - _context = GetContext (); - } - - protected virtual void Run () - { - _pipeline.Process (_context); - } - - protected static string GetAssemblyFileName (AssemblyDefinition asm) - { - return asm.Name.Name + (asm.MainModule.Kind == ModuleKind.Dll ? ".dll" : ".exe"); - } - } -} diff --git a/linker/Tests/Mono.Linker.Tests/AssemblyInfo.cs b/linker/Tests/Mono.Linker.Tests/AssemblyInfo.cs deleted file mode 100644 index 0c169fdd6..000000000 --- a/linker/Tests/Mono.Linker.Tests/AssemblyInfo.cs +++ /dev/null @@ -1,42 +0,0 @@ -// -// AssemblyInfo.cs -// -// Author: -// Jb Evain (jbevain@gmail.com) -// -// (C) 2006 Jb Evain -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle ("Mono.Linker.Tests")] -[assembly: AssemblyDescription ("NUnit tests for Mono's CIL Linker")] -[assembly: AssemblyConfiguration ("")] -[assembly: AssemblyProduct ("")] -[assembly: AssemblyCopyright ("(C) 2006, Jb Evain")] -[assembly: AssemblyCulture ("")] - -[assembly: CLSCompliant (false)] -[assembly: ComVisible (false)] - diff --git a/linker/Tests/Mono.Linker.Tests/IntegrationTestFixture.cs b/linker/Tests/Mono.Linker.Tests/IntegrationTestFixture.cs deleted file mode 100644 index f455f6559..000000000 --- a/linker/Tests/Mono.Linker.Tests/IntegrationTestFixture.cs +++ /dev/null @@ -1,123 +0,0 @@ -// -// IntegrationTestFixture.cs -// -// Author: -// Jb Evain (jbevain@novell.com) -// -// (C) 2007 Novell, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.Diagnostics; -using System.IO; -using Mono.Linker.Steps; -using NUnit.Framework; - -namespace Mono.Linker.Tests { - - [TestFixture] - public class IntegrationTestFixture : AbstractTestFixture { - - [SetUp] - public override void SetUp () - { - base.SetUp (); - - TestCasesRoot = "Integration"; - } - - [Test] - public void TestHelloWorld () - { - RunTest ("HelloWorld"); - } - - [Test] - public void TestCrypto () - { - RunTest ("Crypto"); - } - - protected override LinkContext GetContext() - { - LinkContext context = base.GetContext (); - context.CoreAction = AssemblyAction.Link; - return context; - } - - protected override Pipeline GetPipeline () - { - Pipeline p = new Pipeline (); - p.AppendStep (new LoadReferencesStep ()); - p.AppendStep (new BlacklistStep ()); - p.AppendStep (new TypeMapStep ()); - p.AppendStep (new MarkStep ()); - p.AppendStep (new SweepStep ()); - p.AppendStep (new CleanStep ()); - p.AppendStep (new OutputStep ()); - return p; - } - - protected override void RunTest (string testCase) - { - if (!OnMono ()) - Assert.Ignore ("Integration tests are only runnable on Mono"); - - base.RunTest (testCase); - string test = Path.Combine (GetTestCasePath (), "Test.exe"); - - string original = Execute (GetTestCasePath (), "Test.exe"); - - Pipeline.PrependStep ( - new ResolveFromAssemblyStep ( - test)); - - Run (); - - string linked = Execute (Context.OutputDirectory, "Test.exe"); - - Assert.AreEqual (original, linked); - } - - static bool OnMono () - { - return Type.GetType ("System.MonoType") != null; - } - - static string Execute (string directory, string file) - { - Process p = new Process (); - p.StartInfo.EnvironmentVariables ["MONO_PATH"] = directory; - p.StartInfo.CreateNoWindow = true; - p.StartInfo.WorkingDirectory = directory; - p.StartInfo.FileName = "mono"; - p.StartInfo.Arguments = file; - p.StartInfo.RedirectStandardOutput = true; - p.StartInfo.UseShellExecute = false; - - p.Start (); - p.WaitForExit (); - - return p.StandardOutput.ReadToEnd (); - } - } -} diff --git a/linker/Tests/Mono.Linker.Tests/LinkingTestFixture.cs b/linker/Tests/Mono.Linker.Tests/LinkingTestFixture.cs deleted file mode 100644 index 6201f4885..000000000 --- a/linker/Tests/Mono.Linker.Tests/LinkingTestFixture.cs +++ /dev/null @@ -1,67 +0,0 @@ -// -// AssemblyLinkingTestFixture.cs -// -// Author: -// Jb Evain (jbevain@gmail.com) -// -// (C) 2006 Jb Evain -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System.IO; -using NUnit.Framework; - -namespace Mono.Linker.Tests { - - [TestFixture] - public class LinkingTestFixture : AbstractLinkingTestFixture { - - [Test] - public void TestSimple () - { - RunTest ("Simple"); - } - - [Test] - public void TestVirtualCall () - { - RunTest ("VirtualCall"); - } - - [Test] - public void TestMultipleReferences () - { - RunTest ("MultipleReferences"); - } - - protected override void RunTest (string testCase) - { - var fullTestCaseName = "TestCases.Linker." + testCase; - base.RunTest (fullTestCaseName); - - var step = new ResolveLinkedAssemblyStep (fullTestCaseName, Path.Combine (GetTestCasePath (), "TestCases.dll")); - - Pipeline.PrependStep (step); - - Run (); - } - } -} diff --git a/linker/Tests/Mono.Linker.Tests/ResolveLinkedAssemblyStep.cs b/linker/Tests/Mono.Linker.Tests/ResolveLinkedAssemblyStep.cs deleted file mode 100644 index 7610288a2..000000000 --- a/linker/Tests/Mono.Linker.Tests/ResolveLinkedAssemblyStep.cs +++ /dev/null @@ -1,80 +0,0 @@ -using Mono.Linker.Steps; -using Mono.Cecil; -using System.Linq; -using Mono.Collections.Generic; - -namespace Mono.Linker.Tests -{ - class ResolveLinkedAssemblyStep : ResolveFromAssemblyStep - { - string testCase; - - public ResolveLinkedAssemblyStep (string testCase, string assembly) - : base (assembly) - { - this.testCase = testCase; - } - - protected override void ProcessLibrary (AssemblyDefinition assembly) - { - SetAction (Context, assembly, AssemblyAction.Link); - - Annotations.Push (assembly); - - foreach (TypeDefinition type in assembly.MainModule.Types) - MarkType (type); - - Annotations.Pop (); - } - - static bool MarkMember (ICustomAttributeProvider member) - { - if (!member.HasCustomAttributes) - return false; - - return member.CustomAttributes.Any (l => l.AttributeType.FullName == "TestCases.MarkAttribute"); - } - - void MarkType (TypeDefinition type) - { - if (type.Namespace != testCase) - return; - - if (MarkMember (type)) - Annotations.Mark (type); - - Annotations.Push (type); - - if (type.HasFields) - MarkFields (type.Fields); - if (type.HasMethods) - MarkMethods (type.Methods); - if (type.HasNestedTypes) - foreach (var nested in type.NestedTypes) - MarkType (nested); - - Annotations.Pop (); - } - - void MarkFields (Collection fields) - { - foreach (var field in fields) { - if (MarkMember (field)) { - Annotations.Mark (field.DeclaringType); - Annotations.Mark (field); - } - } - } - - void MarkMethods (Collection methods) - { - foreach (MethodDefinition method in methods) { - if (MarkMember(method)) { - Annotations.Mark (method.DeclaringType); - Annotations.Mark (method); - Annotations.SetAction (method, MethodAction.Parse); - } - } - } - } -} diff --git a/linker/Tests/Mono.Linker.Tests/XmlLinkingTestFixture.cs b/linker/Tests/Mono.Linker.Tests/XmlLinkingTestFixture.cs deleted file mode 100644 index c87b05f3b..000000000 --- a/linker/Tests/Mono.Linker.Tests/XmlLinkingTestFixture.cs +++ /dev/null @@ -1,118 +0,0 @@ -// -// XmlLinkingTestFixture.cs -// -// Author: -// Jb Evain (jbevain@gmail.com) -// -// (C) 2006 Jb Evain -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.IO; -using System.Xml.XPath; - -using Mono.Linker.Steps; -using NUnit.Framework; - -namespace Mono.Linker.Tests { - - [TestFixture] - public class XmlLinkingTestFixture : AbstractLinkingTestFixture { - - [Test] - public void TestSimpleXml () - { - RunTest ("SimpleXml"); - } - - [Test] - public void TestInterface () - { - RunTest ("Interface"); - } - - [Test] - public void TestReferenceInVirtualMethod () - { - RunTest ("ReferenceInVirtualMethod"); - } - - [Test] - public void TestGenerics () - { - RunTest ("Generics"); - } - - [Test] - public void TestNestedNested () - { - RunTest ("NestedNested"); - } - - [Test] - public void TestPreserveFieldsRequired () - { - RunTest ("PreserveFieldsRequired"); - } - - [Test] - public void TestReferenceInAttributes () - { - RunTest ("ReferenceInAttributes"); - } - - [Test] - public void TestXmlPattern () - { - RunTest ("XmlPattern"); - } - - protected override void RunTest (string testCase) - { - var fullTestCaseName = "TestCases.Xml." + testCase; - string resourceName = fullTestCaseName + ".desc.xml"; - - var ms = new MemoryStream (); - var assembly = typeof (TestCases.AssertLinkedAttribute).Assembly; - using (Stream stream = assembly.GetManifestResourceStream (resourceName)) { - Assert.IsNotNull (stream, "Missing embedded desc.xml"); - using (StreamReader reader = new StreamReader (stream)) { - stream.CopyTo (ms); - } - } - - ms.Seek (0, SeekOrigin.Begin); - - base.RunTest (fullTestCaseName); - - Pipeline.PrependStep (new ResolveFromXmlStep (new XPathDocument (ms))); - - string cd = Environment.CurrentDirectory; - Environment.CurrentDirectory = GetTestCasePath (); - try { - Run (); - } finally { - Environment.CurrentDirectory = cd; - } - } - } -} diff --git a/linker/Tests/TestCases/Integration/Crypto/Test.cs b/linker/Tests/TestCases/Integration/Crypto/Test.cs deleted file mode 100644 index 8c38f9297..000000000 --- a/linker/Tests/TestCases/Integration/Crypto/Test.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Security.Cryptography; -using System.Text; - -class Test { - - static void Main () - { - byte [] foo = Encoding.UTF8.GetBytes ("foobared"); - - HashAlgorithm ha = MD5.Create (); - byte [] hash = ha.ComputeHash (foo); - - Console.WriteLine (Encoding.UTF8.GetString (hash)); - } -} diff --git a/linker/Tests/TestCases/Integration/Crypto/Test.exe b/linker/Tests/TestCases/Integration/Crypto/Test.exe deleted file mode 100755 index 2decc4983..000000000 Binary files a/linker/Tests/TestCases/Integration/Crypto/Test.exe and /dev/null differ diff --git a/linker/Tests/TestCases/Linker/MultipleReferences/Bar.cs b/linker/Tests/TestCases/Linker/MultipleReferences/Bar.cs deleted file mode 100644 index 371d7e109..000000000 --- a/linker/Tests/TestCases/Linker/MultipleReferences/Bar.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; - -namespace TestCases.Linker.MultipleReferences -{ - public class Bar - { - string bang = "bang !"; - - public Bar () - { - } - - public void Bang () - { - Console.WriteLine (bang); - } - } -} diff --git a/linker/Tests/TestCases/Linker/MultipleReferences/Baz.cs b/linker/Tests/TestCases/Linker/MultipleReferences/Baz.cs deleted file mode 100644 index 8dc3ad8b5..000000000 --- a/linker/Tests/TestCases/Linker/MultipleReferences/Baz.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace TestCases.Linker.MultipleReferences -{ - public class Baz - { - public void Chain (Foo f) - { - f.b.Bang (); - } - - [AssertLinked] - public void Lurman () - { - } - } -} diff --git a/linker/Tests/TestCases/Linker/MultipleReferences/Foo.cs b/linker/Tests/TestCases/Linker/MultipleReferences/Foo.cs deleted file mode 100644 index 78eaddd45..000000000 --- a/linker/Tests/TestCases/Linker/MultipleReferences/Foo.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace TestCases.Linker.MultipleReferences -{ - public class Foo - { - public Bar b; - - public Foo (Bar b) - { - this.b = b; - } - - public void UseBar () - { - b.Bang (); - } - - [AssertLinked] - public void Blam () - { - } - } -} diff --git a/linker/Tests/TestCases/Linker/MultipleReferences/Program.cs b/linker/Tests/TestCases/Linker/MultipleReferences/Program.cs deleted file mode 100644 index 2b4be8d27..000000000 --- a/linker/Tests/TestCases/Linker/MultipleReferences/Program.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace TestCases.Linker.MultipleReferences -{ - public class Program - { - [Mark] - public static int Main () - { - Program p = new Program (); - p.Run (); - - return 0; - } - - void Run () - { - Foo f = new Foo (new Bar ()); - f.UseBar (); - - Baz b = new Baz (); - b.Chain (f); - } - } -} \ No newline at end of file diff --git a/linker/Tests/TestCases/Linker/Simple/Library.cs b/linker/Tests/TestCases/Linker/Simple/Library.cs deleted file mode 100644 index 69f171375..000000000 --- a/linker/Tests/TestCases/Linker/Simple/Library.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; - -namespace TestCases.Linker.Simple -{ - public class Library - { - private int _pouet; - [AssertLinked] private int _hey; - - public Library () - { - _pouet = 1; - } - - [AssertLinked] - public Library (int pouet) - { - _pouet = pouet; - } - - public int Hello () - { - Console.WriteLine ("Hello"); - return _pouet; - } - - [AssertLinked] - public void Hey (int hey) - { - _hey = hey; - Console.WriteLine (_hey); - } - } - - [AssertLinked] - public class Toy - { - } -} \ No newline at end of file diff --git a/linker/Tests/TestCases/Linker/Simple/Program.cs b/linker/Tests/TestCases/Linker/Simple/Program.cs deleted file mode 100644 index 9e5c67e58..000000000 --- a/linker/Tests/TestCases/Linker/Simple/Program.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace TestCases.Linker.Simple -{ - public class Program - { - [Mark] - public static int Test () - { - Program p = new Program (); - return p.Run (); - } - - int Run () - { - Library lib = new Library (); - return lib.Hello (); - } - } -} \ No newline at end of file diff --git a/linker/Tests/TestCases/Linker/VirtualCall/Library.cs b/linker/Tests/TestCases/Linker/VirtualCall/Library.cs deleted file mode 100644 index c2dae1f81..000000000 --- a/linker/Tests/TestCases/Linker/VirtualCall/Library.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace TestCases.Linker.VirtualCall -{ - public class Library - { - public Library () - { - } - - public int Shebang () - { - return Bang (); - } - - protected virtual int Bang () - { - return 1; - } - } - - public class PowerFulLibrary : Library - { - protected override int Bang () - { - return 0; - } - } -} \ No newline at end of file diff --git a/linker/Tests/TestCases/Linker/VirtualCall/Library.dll b/linker/Tests/TestCases/Linker/VirtualCall/Library.dll deleted file mode 100644 index 0f2ff3f93..000000000 Binary files a/linker/Tests/TestCases/Linker/VirtualCall/Library.dll and /dev/null differ diff --git a/linker/Tests/TestCases/Linker/VirtualCall/Makefile b/linker/Tests/TestCases/Linker/VirtualCall/Makefile deleted file mode 100644 index 356b52945..000000000 --- a/linker/Tests/TestCases/Linker/VirtualCall/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -include ../../config.make - -all: - MCS /t:library Library.cs - MCS /r:Library.dll Program.cs diff --git a/linker/Tests/TestCases/Linker/VirtualCall/Program.cs b/linker/Tests/TestCases/Linker/VirtualCall/Program.cs deleted file mode 100644 index 49c041698..000000000 --- a/linker/Tests/TestCases/Linker/VirtualCall/Program.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; - -namespace TestCases.Linker.VirtualCall -{ - public class Program - { - [Mark] - public static int Test () - { - Program p = new Program (); - return p.Run (); - } - - int Run () - { - Library lib = new PowerFulLibrary (); - return lib.Shebang (); - } - } -} \ No newline at end of file diff --git a/linker/Tests/TestCases/Linker/VirtualCall/Program.exe b/linker/Tests/TestCases/Linker/VirtualCall/Program.exe deleted file mode 100644 index f5a9bec00..000000000 Binary files a/linker/Tests/TestCases/Linker/VirtualCall/Program.exe and /dev/null differ diff --git a/linker/Tests/TestCases/MarkAttribute.cs b/linker/Tests/TestCases/MarkAttribute.cs deleted file mode 100644 index 7f73bc147..000000000 --- a/linker/Tests/TestCases/MarkAttribute.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace TestCases -{ - [AttributeUsage (AttributeTargets.Method | AttributeTargets.Field)] - public class MarkAttribute : Attribute - { - } -} \ No newline at end of file diff --git a/linker/Tests/TestCases/NotLinkedAttribute.cs b/linker/Tests/TestCases/NotLinkedAttribute.cs deleted file mode 100644 index 82bd1ea68..000000000 --- a/linker/Tests/TestCases/NotLinkedAttribute.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace TestCases -{ - [AttributeUsage (AttributeTargets.All)] - public class AssertLinkedAttribute : Attribute - { - } -} \ No newline at end of file diff --git a/linker/Tests/TestCases/TestCase.cs b/linker/Tests/TestCases/TestCase.cs new file mode 100644 index 000000000..cb73b6685 --- /dev/null +++ b/linker/Tests/TestCases/TestCase.cs @@ -0,0 +1,42 @@ +using System; +using Mono.Linker.Tests.Extensions; + +namespace Mono.Linker.Tests.TestCases { + public class TestCase { + public TestCase (NPath sourceFile, NPath rootCasesDirectory, NPath originalTestCaseAssemblyPath) + { + SourceFile = sourceFile; + OriginalTestCaseAssemblyPath = originalTestCaseAssemblyPath; + Name = sourceFile.FileNameWithoutExtension; + DisplayName = $"{sourceFile.RelativeTo (rootCasesDirectory).Parent.ToString (SlashMode.Forward).Replace ('/', '.')}.{sourceFile.FileNameWithoutExtension}"; + + // A little hacky, but good enough for name. No reason why namespace & type names + // should not follow the directory structure + ReconstructedFullTypeName = $"{sourceFile.Parent.RelativeTo (rootCasesDirectory.Parent).ToString (SlashMode.Forward).Replace ('/', '.')}.{sourceFile.FileNameWithoutExtension}"; + } + + public string Name { get; } + + public string DisplayName { get; } + + public NPath SourceFile { get; } + + public NPath OriginalTestCaseAssemblyPath { get; } + + public string ReconstructedFullTypeName { get; } + + public bool HasLinkXmlFile { + get { return SourceFile.ChangeExtension ("xml").FileExists (); } + } + + public NPath LinkXmlFile { + get + { + if (!HasLinkXmlFile) + throw new InvalidOperationException ("This test case does not have a link xml file"); + + return SourceFile.ChangeExtension ("xml"); + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/TestCases/TestCases.csproj b/linker/Tests/TestCases/TestCases.csproj deleted file mode 100644 index 451fe7952..000000000 --- a/linker/Tests/TestCases/TestCases.csproj +++ /dev/null @@ -1,69 +0,0 @@ - - - - Debug - AnyCPU - {26D857FB-EEE3-4A5B-95BC-DAB39F880A99} - Library - TestCases - TestCases - v4.6.2 - - - true - full - false - bin\Debug - DEBUG; - prompt - 4 - - - true - bin\Release - prompt - 4 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/linker/Tests/TestCases/TestDatabase.cs b/linker/Tests/TestCases/TestDatabase.cs new file mode 100644 index 000000000..2575b8d46 --- /dev/null +++ b/linker/Tests/TestCases/TestDatabase.cs @@ -0,0 +1,108 @@ +using System.Linq; +using System.Collections.Generic; +using NUnit.Framework; +using System.Runtime.CompilerServices; +using System.IO; +using Mono.Linker.Tests.TestCasesRunner; + +namespace Mono.Linker.Tests.TestCases +{ + static class TestDatabase + { + public static IEnumerable XmlTests() + { + return NUnitCasesByPrefix("LinkXml."); + } + + public static IEnumerable BasicTests() + { + return NUnitCasesByPrefix("Basic."); + } + + public static IEnumerable VirtualMethodsTests() + { + return NUnitCasesByPrefix("VirtualMethods."); + } + + public static IEnumerable AttributeTests() + { + return NUnitCasesByPrefix("Attributes."); + } + + public static IEnumerable GenericsTests() + { + return NUnitCasesByPrefix("Generics."); + } + + public static IEnumerable CoreLinkTests() + { + return NUnitCasesByPrefix("CoreLink."); + } + + public static IEnumerable StaticsTests() + { + return NUnitCasesByPrefix("Statics."); + } + + public static IEnumerable InteropTests() + { + return NUnitCasesByPrefix("Interop."); + } + + public static IEnumerable ReferencesTests() + { + return NUnitCasesByPrefix("References."); + } + + public static IEnumerable OtherTests() + { + var allGroupedTestNames = new HashSet( + XmlTests() + .Concat(BasicTests()) + .Concat(XmlTests()) + .Concat(VirtualMethodsTests()) + .Concat(AttributeTests()) + .Concat(GenericsTests()) + .Concat(CoreLinkTests()) + .Concat(StaticsTests()) + .Concat(InteropTests()) + .Concat(ReferencesTests ()) + .Select(c => ((TestCase)c.Arguments[0]).ReconstructedFullTypeName)); + + return AllCases().Where(c => !allGroupedTestNames.Contains(c.ReconstructedFullTypeName)).Select(c => CreateNUnitTestCase(c, c.DisplayName)); + } + + static IEnumerable AllCases() + { + string rootSourceDirectory; + string testCaseAssemblyPath; + GetDirectoryPaths(out rootSourceDirectory, out testCaseAssemblyPath); + return new TestCaseCollector(rootSourceDirectory, testCaseAssemblyPath) + .Collect() + .OrderBy(c => c.DisplayName) + .ToArray(); + } + + static IEnumerable NUnitCasesByPrefix(string testNamePrefix) + { + return AllCases() + .Where(c => c.DisplayName.StartsWith(testNamePrefix)) + .Select(c => CreateNUnitTestCase(c, c.DisplayName.Substring(testNamePrefix.Length))) + .OrderBy(c => c.TestName); + } + + static TestCaseData CreateNUnitTestCase(TestCase testCase, string displayName) + { + var data = new TestCaseData(testCase); + data.SetName(displayName); + return data; + } + + static void GetDirectoryPaths(out string rootSourceDirectory, out string testCaseAssemblyPath, [CallerFilePath] string thisFile = null) + { + var thisDirectory = Path.GetDirectoryName(thisFile); + rootSourceDirectory = Path.GetFullPath(Path.Combine(thisDirectory, "..", "Mono.Linker.Tests.Cases")); + testCaseAssemblyPath = Path.GetFullPath(Path.Combine(rootSourceDirectory, "bin", "Debug", "Mono.Linker.Tests.Cases.dll")); + } + } +} diff --git a/linker/Tests/TestCases/TestSuites.cs b/linker/Tests/TestCases/TestSuites.cs new file mode 100644 index 000000000..ca5a0eb7e --- /dev/null +++ b/linker/Tests/TestCases/TestSuites.cs @@ -0,0 +1,76 @@ +using Mono.Linker.Tests.TestCasesRunner; +using NUnit.Framework; + +namespace Mono.Linker.Tests.TestCases +{ + [TestFixture] + public class All + { + [TestCaseSource (typeof (TestDatabase), nameof (TestDatabase.BasicTests))] + public void BasicTests (TestCase testCase) + { + Run (testCase); + } + + [TestCaseSource (typeof (TestDatabase), nameof (TestDatabase.VirtualMethodsTests))] + public void VirtualMethodTests (TestCase testCase) + { + Run (testCase); + } + + [TestCaseSource (typeof (TestDatabase), nameof (TestDatabase.XmlTests))] + public void XmlTests (TestCase testCase) + { + Run (testCase); + } + + [TestCaseSource (typeof (TestDatabase), nameof (TestDatabase.AttributeTests))] + public void AttributesTests (TestCase testCase) + { + Run (testCase); + } + + [TestCaseSource (typeof (TestDatabase), nameof (TestDatabase.GenericsTests))] + public void GenericsTests (TestCase testCase) + { + Run (testCase); + } + + [TestCaseSource (typeof (TestDatabase), nameof (TestDatabase.StaticsTests))] + public void StaticsTests (TestCase testCase) + { + Run (testCase); + } + + [TestCaseSource (typeof (TestDatabase), nameof (TestDatabase.CoreLinkTests))] + public void CoreLinkTests (TestCase testCase) + { + Run (testCase); + } + + [TestCaseSource (typeof (TestDatabase), nameof (TestDatabase.InteropTests))] + public void InteropTests (TestCase testCase) + { + Run (testCase); + } + + [TestCaseSource(typeof(TestDatabase), nameof(TestDatabase.ReferencesTests))] + public void ReferencesTests(TestCase testCase) + { + Run(testCase); + } + + [TestCaseSource (typeof (TestDatabase), nameof (TestDatabase.OtherTests))] + public void OtherTests (TestCase testCase) + { + Run (testCase); + } + + protected virtual void Run (TestCase testCase) + { + var runner = new TestRunner (new ObjectFactory ()); + var linkedResult = runner.Run (testCase); + new ResultChecker ().Check (linkedResult); + } + } +} diff --git a/linker/Tests/TestCases/Xml/Generics/Library.cs b/linker/Tests/TestCases/Xml/Generics/Library.cs deleted file mode 100644 index 9505cb2f9..000000000 --- a/linker/Tests/TestCases/Xml/Generics/Library.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; - -namespace TestCases.Xml.Generics -{ - public class Foo - { - void Bar () - { - int i = 42; - string s = "hey"; - - Baz bi = new Baz (i); - bi.Gazonk (); - bi.Bat (i, s); - - Baz bs = new Baz (s); - bs.Gazonk (); - bs.Bat (s, i); - bs.BiroBiro (); - - bs.Blam (); - } - } - - public class Baz - { - T _t; - - public Baz (T t) - { - _t = t; - } - - public void Gazonk () - { - Console.WriteLine (_t); - } - - public void Bat (T t, M m) - { - Console.WriteLine ("{0}{1}", t, m); - } - - public void Blam () - { - } - - public T [] BiroBiro () - { - return new T [0]; - } - } - - class Bang - { - [AssertLinked] - public Bang () - { - } - } -} \ No newline at end of file diff --git a/linker/Tests/TestCases/Xml/Generics/desc.xml b/linker/Tests/TestCases/Xml/Generics/desc.xml deleted file mode 100644 index d84422937..000000000 --- a/linker/Tests/TestCases/Xml/Generics/desc.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/linker/Tests/TestCases/Xml/Interface/Library.cs b/linker/Tests/TestCases/Xml/Interface/Library.cs deleted file mode 100644 index 4bbedd4ba..000000000 --- a/linker/Tests/TestCases/Xml/Interface/Library.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace TestCases.Xml.Interface -{ - public class Foo : IFoo - { - public void Gazonk () - { - } - } - - public interface IFoo : IBar - { - } - - public interface IBar - { - [AssertLinked] - void Gazonk (); - } - - [AssertLinked] - public class Baz : IBaz - { - } - - [AssertLinked] - public interface IBaz - { - } -} diff --git a/linker/Tests/TestCases/Xml/Interface/desc.xml b/linker/Tests/TestCases/Xml/Interface/desc.xml deleted file mode 100644 index 2cba1a23d..000000000 --- a/linker/Tests/TestCases/Xml/Interface/desc.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/linker/Tests/TestCases/Xml/NestedNested/Library.cs b/linker/Tests/TestCases/Xml/NestedNested/Library.cs deleted file mode 100644 index 5e024ab6c..000000000 --- a/linker/Tests/TestCases/Xml/NestedNested/Library.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace TestCases.Xml.NestedNested -{ - public class Foo - { - } - - [AssertLinked] - public class Bar - { - [AssertLinked] - public class Baz - { - [AssertLinked] - public class Gazonk - { - } - } - } -} \ No newline at end of file diff --git a/linker/Tests/TestCases/Xml/NestedNested/desc.xml b/linker/Tests/TestCases/Xml/NestedNested/desc.xml deleted file mode 100644 index 28a21adde..000000000 --- a/linker/Tests/TestCases/Xml/NestedNested/desc.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/linker/Tests/TestCases/Xml/PreserveFieldsRequired/Library.cs b/linker/Tests/TestCases/Xml/PreserveFieldsRequired/Library.cs deleted file mode 100644 index edf9e340c..000000000 --- a/linker/Tests/TestCases/Xml/PreserveFieldsRequired/Library.cs +++ /dev/null @@ -1,35 +0,0 @@ -namespace TestCases.Xml.PreserveFieldsRequired -{ - public class Foo - { - public Foo () - { - new NotRequiredButUsedNotPreserved (); - new NotRequiredButUsedAndFieldsPreserved (); - } - } - - public class NotRequiredButUsedNotPreserved - { - - [AssertLinked] public int foo; - [AssertLinked] public int bar; - } - - public class NotRequiredButUsedAndFieldsPreserved - { - public int foo; - public int bar; - - [AssertLinked] - public int FooBar () - { - return foo + bar; - } - } - - [AssertLinked] - public class NotRequiredAndNotUsed - { - } -} diff --git a/linker/Tests/TestCases/Xml/PreserveFieldsRequired/desc.xml b/linker/Tests/TestCases/Xml/PreserveFieldsRequired/desc.xml deleted file mode 100644 index e7f7d5c53..000000000 --- a/linker/Tests/TestCases/Xml/PreserveFieldsRequired/desc.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/linker/Tests/TestCases/Xml/ReferenceInAttributes/LibLib.cs b/linker/Tests/TestCases/Xml/ReferenceInAttributes/LibLib.cs deleted file mode 100644 index c42523e6e..000000000 --- a/linker/Tests/TestCases/Xml/ReferenceInAttributes/LibLib.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; - -namespace TestCases.Xml.ReferenceInAttributes -{ - public class LibLibAttribute : Attribute - { - public Type LibLibType { - [AssertLinked] - get { return null; } - set { } - } - } - - public class BilBil - { - - [AssertLinked] - public BilBil () - { - } - } -} - diff --git a/linker/Tests/TestCases/Xml/ReferenceInAttributes/Library.cs b/linker/Tests/TestCases/Xml/ReferenceInAttributes/Library.cs deleted file mode 100644 index 4d2c4ba28..000000000 --- a/linker/Tests/TestCases/Xml/ReferenceInAttributes/Library.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; - -namespace TestCases.Xml.ReferenceInAttributes -{ - public class BarAttribute : Attribute - { - public BarAttribute () - { - } - - public BarAttribute (Type type) - { - } - - public Type FieldType; - - public Type PropertyType { - [AssertLinked] - get { return null; } - set { } - } - } - - [Bar (typeof (Guy_A))] - public class Foo - { - - [Bar (FieldType = typeof (Guy_B))] - public Foo a; - - [Bar (PropertyType = typeof (Guy_C))] - public Foo b; - - [LibLib (LibLibType = typeof (BilBil))] - public Foo c; - - [LibLib (LibLibType = typeof (Guy_D))] - public Foo d; - } - - public class Guy_A - { - - [AssertLinked] - public Guy_A () - { - } - } - - public class Guy_B - { - - [AssertLinked] - public Guy_B () - { - } - } - - public class Guy_C - { - - [AssertLinked] - public Guy_C () - { - } - } - - public class Guy_D - { - - [AssertLinked] - public Guy_D () - { - } - } - - [AssertLinked] - public class Baz - { - } -} diff --git a/linker/Tests/TestCases/Xml/ReferenceInAttributes/desc.xml b/linker/Tests/TestCases/Xml/ReferenceInAttributes/desc.xml deleted file mode 100644 index 927ba58e6..000000000 --- a/linker/Tests/TestCases/Xml/ReferenceInAttributes/desc.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/linker/Tests/TestCases/Xml/ReferenceInVirtualMethod/Library.cs b/linker/Tests/TestCases/Xml/ReferenceInVirtualMethod/Library.cs deleted file mode 100644 index 8c0ee9c98..000000000 --- a/linker/Tests/TestCases/Xml/ReferenceInVirtualMethod/Library.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; - -namespace TestCases.Xml.ReferenceInVirtualMethod -{ - public class Foo - { - public void Do () - { - Bar b = new Baz (); - b.Bang (); - } - } - - public class Bar - { - public virtual void Bang () - { - } - } - - public class Baz : Bar - { - private string _hey; - - public string Hey { - [AssertLinked] - get { return _hey; } - [AssertLinked] - set { _hey = value; } - } - - public override void Bang () - { - Console.WriteLine (_hey); - } - } -} diff --git a/linker/Tests/TestCases/Xml/ReferenceInVirtualMethod/desc.xml b/linker/Tests/TestCases/Xml/ReferenceInVirtualMethod/desc.xml deleted file mode 100644 index 1372a44b9..000000000 --- a/linker/Tests/TestCases/Xml/ReferenceInVirtualMethod/desc.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/linker/Tests/TestCases/Xml/SimpleXml/Library.cs b/linker/Tests/TestCases/Xml/SimpleXml/Library.cs deleted file mode 100644 index f36710f8a..000000000 --- a/linker/Tests/TestCases/Xml/SimpleXml/Library.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; - -namespace TestCases.Xml.SimpleXml -{ - public class Foo - { - - int _baz; - [AssertLinked] int _shebang; - - public Foo () - { - _baz = 42; - } - - public int Baz () - { - return _baz; - } - - [AssertLinked] - public int Shebang (int bang) - { - return _shebang = bang * 2; - } - } - - public class Bar - { - - int _truc; - - public Bar () - { - _truc = 12; - } - - public int Truc () - { - return _truc; - } - } - - [AssertLinked] - public class Gazonk - { - } -} diff --git a/linker/Tests/TestCases/Xml/SimpleXml/desc.xml b/linker/Tests/TestCases/Xml/SimpleXml/desc.xml deleted file mode 100644 index 552a4a326..000000000 --- a/linker/Tests/TestCases/Xml/SimpleXml/desc.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/linker/Tests/TestCases/Xml/XmlPattern/Library.cs b/linker/Tests/TestCases/Xml/XmlPattern/Library.cs deleted file mode 100755 index eb40b473f..000000000 --- a/linker/Tests/TestCases/Xml/XmlPattern/Library.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace TestCases.Xml.XmlPattern -{ - [AssertLinked] - public class BarBar - { - } - - public class FooBaz - { - - public FooBaz () - { - } - - [AssertLinked] - public void BarBaz () - { - } - } - - public class TrucBaz - { - - public TrucBaz () - { - } - } - - public class BazBaz - { - - public BazBaz () - { - } - } -} \ No newline at end of file diff --git a/linker/Tests/TestCases/Xml/XmlPattern/desc.xml b/linker/Tests/TestCases/Xml/XmlPattern/desc.xml deleted file mode 100755 index 19ca58298..000000000 --- a/linker/Tests/TestCases/Xml/XmlPattern/desc.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/linker/Tests/TestCasesRunner/AssemblyChecker.cs b/linker/Tests/TestCasesRunner/AssemblyChecker.cs new file mode 100644 index 000000000..e9ed80abc --- /dev/null +++ b/linker/Tests/TestCasesRunner/AssemblyChecker.cs @@ -0,0 +1,323 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Mono.Cecil; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Extensions; +using NUnit.Framework; + +namespace Mono.Linker.Tests.TestCasesRunner { + class AssemblyChecker { + readonly AssemblyDefinition originalAssembly, linkedAssembly; + + HashSet linkedMembers; + HashSet verifiedBackingFields = new HashSet (); + + public AssemblyChecker (AssemblyDefinition original, AssemblyDefinition linked) + { + this.originalAssembly = original; + this.linkedAssembly = linked; + } + + public void Verify () + { + // TODO: Implement fully, probably via custom Kept attribute + Assert.IsFalse (linkedAssembly.MainModule.HasExportedTypes); + + VerifyCustomAttributes (linkedAssembly, originalAssembly); + + linkedMembers = new HashSet (linkedAssembly.MainModule.AllMembers ().Select (s => { + return s.FullName; + }), StringComparer.Ordinal); + + var membersToAssert = originalAssembly.MainModule.Types; + foreach (var originalMember in membersToAssert) { + var td = originalMember as TypeDefinition; + if (td != null) { + if (td.Name == "") { + linkedMembers.Remove (td.Name); + continue; + } + + TypeDefinition linkedType = linkedAssembly.MainModule.GetType (originalMember.FullName); + VerifyTypeDefinition (td, linkedType); + linkedMembers.Remove (td.FullName); + + continue; + } + + throw new NotImplementedException ($"Don't know how to check member of type {originalMember.GetType ()}"); + } + + Assert.IsEmpty (linkedMembers, "Linked output includes unexpected member"); + } + + protected virtual void VerifyTypeDefinition (TypeDefinition original, TypeDefinition linked) + { + ModuleDefinition linkedModule = linked?.Module; + + // + // Little bit complex check to allow easier test writting to match + // - It has [Kept] attribute or any variation of it + // - It contains Main method + // - It contains at least one member which has [Kept] attribute (not recursive) + // + bool expectedKept = + original.HasAttributeDerivedFrom (nameof (KeptAttribute)) || + (linked != null && linkedModule.Assembly.EntryPoint.DeclaringType == linked) || + original.AllMembers ().Any (l => l.HasAttribute (nameof (KeptAttribute))); + + if (!expectedKept) { + if (linked != null) + Assert.Fail ($"Type `{original}' should have been removed"); + + return; + } + + if (linked == null) + Assert.Fail ($"Type `{original}' should have been kept"); + + if (!original.IsInterface) + VerifyBaseType (original, linked); + + VerifyInterfaces (original, linked); + + VerifyGenericParameters (original, linked); + VerifyCustomAttributes (original, linked); + + foreach (var td in original.NestedTypes) { + VerifyTypeDefinition (td, linked?.NestedTypes.FirstOrDefault (l => td.FullName == l.FullName)); + linkedMembers.Remove (td.FullName); + } + + // Need to check properties before fields so that the KeptBackingFieldAttribute is handled correctly + foreach (var p in original.Properties) { + VerifyProperty (p, linked?.Properties.FirstOrDefault (l => p.Name == l.Name), linked); + linkedMembers.Remove (p.FullName); + } + + foreach (var f in original.Fields) { + if (verifiedBackingFields.Contains (f.FullName)) + continue; + VerifyField (f, linked?.Fields.FirstOrDefault (l => f.Name == l.Name)); + linkedMembers.Remove (f.FullName); + } + + foreach (var m in original.Methods) { + VerifyMethod (m, linked?.Methods.FirstOrDefault (l => m.GetSignature () == l.GetSignature ())); + linkedMembers.Remove (m.FullName); + } + + foreach (var e in original.Events) { + VerifyEvent (e, linked?.Events.FirstOrDefault (l => e.Name == l.Name)); + linkedMembers.Remove (e.FullName); + } + } + + void VerifyBaseType (TypeDefinition src, TypeDefinition linked) + { + string expectedBaseName; + var expectedBaseGenericAttr = src.CustomAttributes.FirstOrDefault (w => w.AttributeType.Name == nameof (KeptBaseTypeAttribute) && w.ConstructorArguments.Count > 1); + if (expectedBaseGenericAttr != null) { + StringBuilder builder = new StringBuilder (); + builder.Append (expectedBaseGenericAttr.ConstructorArguments [0].Value); + builder.Append ("<"); + bool separator = false; + foreach (var caa in (CustomAttributeArgument[])expectedBaseGenericAttr.ConstructorArguments [1].Value) { + if (separator) + builder.Append (","); + else + separator = true; + + var arg = (CustomAttributeArgument)caa.Value; + builder.Append (arg.Value); + } + + builder.Append (">"); + expectedBaseName = builder.ToString (); + } else { + expectedBaseName = GetCustomAttributeCtorValues (src, nameof (KeptBaseTypeAttribute)).FirstOrDefault ()?.ToString () ?? "System.Object"; + } + Assert.AreEqual (expectedBaseName, linked.BaseType?.FullName); + } + + void VerifyInterfaces (TypeDefinition src, TypeDefinition linked) + { + var expectedInterfaces = new HashSet (GetCustomAttributeCtorValues (src, nameof (KeptInterfaceAttribute)).Select (val => val.ToString ())); + if (expectedInterfaces.Count == 0) { + Assert.IsFalse (linked.HasInterfaces, $"Type `{src}' has unexpected interfaces"); + } else { + foreach (var iface in linked.Interfaces) { + Assert.IsTrue (expectedInterfaces.Remove (iface.InterfaceType.FullName), $"Type `{src}' interface `{iface.InterfaceType.FullName}' should have been removed"); + } + + Assert.IsEmpty (expectedInterfaces); + } + } + + void VerifyField (FieldDefinition src, FieldDefinition linked) + { + bool expectedKept = ShouldBeKept (src); + + if (!expectedKept) { + if (linked != null) + Assert.Fail ($"Field `{src}' should have been removed"); + + return; + } + + VerifyFieldKept (src, linked); + } + + void VerifyFieldKept (FieldDefinition src, FieldDefinition linked) + { + if (linked == null) + Assert.Fail ($"Field `{src}' should have been kept"); + + Assert.AreEqual (src?.Attributes, linked?.Attributes, $"Field `{src}' attributes"); + Assert.AreEqual (src?.Constant, linked?.Constant, $"Field `{src}' value"); + + VerifyCustomAttributes (src, linked); + } + + void VerifyProperty (PropertyDefinition src, PropertyDefinition linked, TypeDefinition linkedType) + { + VerifyBackingField (src, linkedType); + + bool expectedKept = ShouldBeKept (src); + + if (!expectedKept) { + if (linked != null) + Assert.Fail ($"Property `{src}' should have been removed"); + + return; + } + + if (linked == null) + Assert.Fail ($"Property `{src}' should have been kept"); + + Assert.AreEqual (src?.Attributes, linked?.Attributes, $"Property `{src}' attributes"); + Assert.AreEqual (src?.Constant, linked?.Constant, $"Property `{src}' value"); + + VerifyCustomAttributes (src, linked); + } + + void VerifyEvent (EventDefinition src, EventDefinition linked) + { + bool expectedKept = ShouldBeKept (src); + + if (!expectedKept) { + if (linked != null) + Assert.Fail ($"Event `{src}' should have been removed"); + + return; + } + + if (linked == null) + Assert.Fail ($"Event `{src}' should have been kept"); + + Assert.AreEqual (src?.Attributes, linked?.Attributes, $"Event `{src}' attributes"); + + VerifyCustomAttributes (src, linked); + } + + void VerifyMethod (MethodDefinition src, MethodDefinition linked) + { + var srcSignature = src.GetSignature (); + bool expectedKept = ShouldBeKept (src, srcSignature) || (linked != null && linked.DeclaringType.Module.EntryPoint == linked); + + if (!expectedKept) { + if (linked != null) + Assert.Fail ($"Method `{src.FullName}' should have been removed"); + + return; + } + + if (linked == null) + Assert.Fail ($"Method `{src.FullName}' should have been kept"); + + Assert.AreEqual (src?.Attributes, linked?.Attributes, $"Method `{src}' attributes"); + + VerifyGenericParameters (src, linked); + VerifyCustomAttributes (src, linked); + } + + void VerifyBackingField (PropertyDefinition src, TypeDefinition linkedType) + { + var keptBackingFieldAttribute = src.CustomAttributes.FirstOrDefault (attr => attr.AttributeType.Name == nameof (KeptBackingFieldAttribute)); + if (keptBackingFieldAttribute == null) + return; + + var backingFieldName = $"<{src.Name}>k__BackingField"; + var srcField = src.DeclaringType.Fields.FirstOrDefault (f => f.Name == backingFieldName); + + if (srcField == null) { + // Can add more here if necessary + backingFieldName = backingFieldName.Replace ("System.Int32", "int"); + backingFieldName = backingFieldName.Replace ("System.String", "string"); + backingFieldName = backingFieldName.Replace ("System.Char", "char"); + + srcField = src.DeclaringType.Fields.FirstOrDefault (f => f.Name == backingFieldName); + } + + if (srcField == null) + Assert.Fail ($"Property `{src}', could not locate the expected backing field {backingFieldName}"); + + VerifyFieldKept (srcField, linkedType?.Fields.FirstOrDefault (l => srcField.Name == l.Name)); + verifiedBackingFields.Add (srcField.FullName); + linkedMembers.Remove (srcField.FullName); + } + + static void VerifyCustomAttributes (ICustomAttributeProvider src, ICustomAttributeProvider linked) + { + var expectedAttrs = new List (GetCustomAttributeCtorValues (src, nameof (KeptAttributeAttribute))); + var linkedAttrs = new List (FilterLinkedAttributes (linked)); + + // FIXME: Linker unused attributes removal is not working + // Assert.That (linkedAttrs, Is.EquivalentTo (expectedAttrs), $"Custom attributes on `{src}' are not matching"); + } + + static IEnumerable FilterLinkedAttributes (ICustomAttributeProvider linked) + { + foreach (var attr in linked.CustomAttributes) { + switch (attr.AttributeType.FullName) { + case "System.Runtime.CompilerServices.RuntimeCompatibilityAttribute": + continue; + } + + yield return attr.AttributeType.FullName; + } + } + + static void VerifyGenericParameters (IGenericParameterProvider src, IGenericParameterProvider linked) + { + Assert.AreEqual (src.HasGenericParameters, linked.HasGenericParameters); + if (src.HasGenericParameters) { + for (int i = 0; i < src.GenericParameters.Count; ++i) { + // TODO: Verify constraints + VerifyCustomAttributes (src.GenericParameters [i], linked.GenericParameters [i]); + } + } + } + + static bool ShouldBeKept (T member, string signature = null) where T : MemberReference, ICustomAttributeProvider + { + if (member.HasAttribute (nameof (KeptAttribute))) + return true; + + ICustomAttributeProvider cap = (ICustomAttributeProvider)member.DeclaringType; + if (cap == null) + return false; + + return GetCustomAttributeCtorValues (cap, nameof (KeptMemberAttribute)).Any (a => a == (signature ?? member.Name)); + } + + static IEnumerable GetCustomAttributeCtorValues (ICustomAttributeProvider provider, string attributeName) where T : class + { + return provider.CustomAttributes. + Where (w => w.AttributeType.Name == attributeName && w.Constructor.Parameters.Count == 1). + Select (l => l.ConstructorArguments [0].Value as T); + } + } +} diff --git a/linker/Tests/TestCasesRunner/ExpectationsProvider.cs b/linker/Tests/TestCasesRunner/ExpectationsProvider.cs new file mode 100644 index 000000000..79280a35a --- /dev/null +++ b/linker/Tests/TestCasesRunner/ExpectationsProvider.cs @@ -0,0 +1,12 @@ +using Mono.Cecil; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.TestCasesRunner { + public static class ExpectationsProvider { + + public static bool IsAssemblyAssertion (CustomAttribute attr) + { + return attr.AttributeType.Name == nameof (KeptAssemblyAttribute) || attr.AttributeType.Name == nameof (RemovedAssemblyAttribute); + } + } +} diff --git a/linker/Tests/TestCasesRunner/LinkXmlHelpers.cs b/linker/Tests/TestCasesRunner/LinkXmlHelpers.cs new file mode 100644 index 000000000..c7e70cf71 --- /dev/null +++ b/linker/Tests/TestCasesRunner/LinkXmlHelpers.cs @@ -0,0 +1,30 @@ +using System.Text; +using Mono.Cecil; +using Mono.Linker.Tests.Extensions; + +namespace Mono.Linker.Tests.TestCasesRunner { + public static class LinkXmlHelpers { + public static void WriteXmlFileToPreserveEntryPoint (NPath targetProgram, NPath xmlFile) + { + using (var assembly = AssemblyDefinition.ReadAssembly (targetProgram.ToString ())) { + var method = assembly.EntryPoint; + + var sb = new StringBuilder (); + sb.AppendLine (""); + + sb.AppendLine (" "); + + if (method != null) { + sb.AppendLine (" "); + sb.AppendLine (" "); + sb.AppendLine (" "); + } + + sb.AppendLine (" "); + + sb.AppendLine (""); + xmlFile.WriteAllText (sb.ToString ()); + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/TestCasesRunner/LinkedTestCaseResult.cs b/linker/Tests/TestCasesRunner/LinkedTestCaseResult.cs new file mode 100644 index 000000000..2d05f6d09 --- /dev/null +++ b/linker/Tests/TestCasesRunner/LinkedTestCaseResult.cs @@ -0,0 +1,19 @@ +using Mono.Linker.Tests.Extensions; +using Mono.Linker.Tests.TestCases; + +namespace Mono.Linker.Tests.TestCasesRunner { + public class LinkedTestCaseResult { + public readonly TestCase TestCase; + public readonly NPath InputAssemblyPath; + public readonly NPath OutputAssemblyPath; + public readonly NPath ExpectationsAssemblyPath; + + public LinkedTestCaseResult (TestCase testCase, NPath inputAssemblyPath, NPath outputAssemblyPath, NPath expectationsAssemblyPath) + { + TestCase = testCase; + InputAssemblyPath = inputAssemblyPath; + OutputAssemblyPath = outputAssemblyPath; + ExpectationsAssemblyPath = expectationsAssemblyPath; + } + } +} \ No newline at end of file diff --git a/linker/Tests/TestCasesRunner/LinkerArgumentBuilder.cs b/linker/Tests/TestCasesRunner/LinkerArgumentBuilder.cs new file mode 100644 index 000000000..49114eb84 --- /dev/null +++ b/linker/Tests/TestCasesRunner/LinkerArgumentBuilder.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using Mono.Linker.Tests.Extensions; + +namespace Mono.Linker.Tests.TestCasesRunner { + public class LinkerArgumentBuilder { + private readonly List _arguments = new List (); + + public virtual void AddSearchDirectory (NPath directory) + { + Append ("-d"); + Append (directory.ToString ()); + } + + public virtual void AddOutputDirectory (NPath directory) + { + Append ("-o"); + Append (directory.ToString ()); + } + + public virtual void AddLinkXmlFile (NPath path) + { + Append ("-x"); + Append (path.ToString ()); + } + + public virtual void AddCoreLink (string value) + { + Append ("-c"); + Append (value); + } + + public string [] ToArgs () + { + return _arguments.ToArray (); + } + + protected void Append (string arg) + { + _arguments.Add (arg); + } + } +} \ No newline at end of file diff --git a/linker/Tests/TestCasesRunner/LinkerDriver.cs b/linker/Tests/TestCasesRunner/LinkerDriver.cs new file mode 100644 index 000000000..dc2469ca3 --- /dev/null +++ b/linker/Tests/TestCasesRunner/LinkerDriver.cs @@ -0,0 +1,8 @@ +namespace Mono.Linker.Tests.TestCasesRunner { + public class LinkerDriver { + public virtual void Link (string [] args) + { + Driver.Main (args); + } + } +} \ No newline at end of file diff --git a/linker/Tests/TestCasesRunner/ManagedCompilationResult.cs b/linker/Tests/TestCasesRunner/ManagedCompilationResult.cs new file mode 100644 index 000000000..ccce993f9 --- /dev/null +++ b/linker/Tests/TestCasesRunner/ManagedCompilationResult.cs @@ -0,0 +1,15 @@ +using Mono.Linker.Tests.Extensions; + +namespace Mono.Linker.Tests.TestCasesRunner { + public class ManagedCompilationResult { + public ManagedCompilationResult (NPath inputAssemblyPath, NPath expectationsAssemblyPath) + { + InputAssemblyPath = inputAssemblyPath; + ExpectationsAssemblyPath = expectationsAssemblyPath; + } + + public NPath InputAssemblyPath { get; } + + public NPath ExpectationsAssemblyPath { get; } + } +} \ No newline at end of file diff --git a/linker/Tests/TestCasesRunner/ObjectFactory.cs b/linker/Tests/TestCasesRunner/ObjectFactory.cs new file mode 100644 index 000000000..d756df6f9 --- /dev/null +++ b/linker/Tests/TestCasesRunner/ObjectFactory.cs @@ -0,0 +1,31 @@ +using Mono.Cecil; +using Mono.Linker.Tests.TestCases; + +namespace Mono.Linker.Tests.TestCasesRunner { + public class ObjectFactory { + public virtual TestCaseSandbox CreateSandbox (TestCase testCase) + { + return new TestCaseSandbox (testCase); + } + + public virtual TestCaseCompiler CreateCompiler () + { + return new TestCaseCompiler (); + } + + public virtual LinkerDriver CreateLinker () + { + return new LinkerDriver (); + } + + public virtual TestCaseMetadaProvider CreateMetadataProvider (TestCase testCase, AssemblyDefinition fullTestCaseAssemblyDefinition) + { + return new TestCaseMetadaProvider (testCase, fullTestCaseAssemblyDefinition); + } + + public virtual LinkerArgumentBuilder CreateLinkerArgumentBuilder () + { + return new LinkerArgumentBuilder (); + } + } +} \ No newline at end of file diff --git a/linker/Tests/TestCasesRunner/ResultChecker.cs b/linker/Tests/TestCasesRunner/ResultChecker.cs new file mode 100644 index 000000000..df66381fd --- /dev/null +++ b/linker/Tests/TestCasesRunner/ResultChecker.cs @@ -0,0 +1,62 @@ +using System; +using System.Linq; +using Mono.Cecil; +using Mono.Linker.Tests.Extensions; +using NUnit.Framework; + +namespace Mono.Linker.Tests.TestCasesRunner { + public class ResultChecker { + + public virtual void Check (LinkedTestCaseResult linkResult) + { + Assert.IsTrue (linkResult.OutputAssemblyPath.FileExists (), $"The linked output assembly was not found. Expected at {linkResult.OutputAssemblyPath}"); + + using (var original = ReadAssembly (linkResult.ExpectationsAssemblyPath)) { + PerformOutputAssemblyChecks (original.Definition, linkResult.OutputAssemblyPath.Parent); + + using (var linked = ReadAssembly (linkResult.OutputAssemblyPath)) { + var checker = new AssemblyChecker (original.Definition, linked.Definition); + checker.Verify (); + } + } + } + + static AssemblyContainer ReadAssembly (NPath assemblyPath) + { + var readerParams = new ReaderParameters (); + var resolver = new AssemblyResolver (); + readerParams.AssemblyResolver = resolver; + resolver.AddSearchDirectory (assemblyPath.Parent.ToString ()); + return new AssemblyContainer (AssemblyDefinition.ReadAssembly (assemblyPath.ToString (), readerParams), resolver); + } + + void PerformOutputAssemblyChecks (AssemblyDefinition original, NPath outputDirectory) + { + var assembliesToCheck = original.MainModule.Types.SelectMany (t => t.CustomAttributes).Where (attr => ExpectationsProvider.IsAssemblyAssertion(attr)); + + foreach (var assemblyAttr in assembliesToCheck) { + var name = (string) assemblyAttr.ConstructorArguments.First ().Value; + var expectedPath = outputDirectory.Combine (name); + Assert.IsTrue (expectedPath.FileExists (), $"Expected the assembly {name} to exist in {outputDirectory}, but it did not"); + } + } + + struct AssemblyContainer : IDisposable + { + public readonly AssemblyResolver Resolver; + public readonly AssemblyDefinition Definition; + + public AssemblyContainer (AssemblyDefinition definition, AssemblyResolver resolver) + { + Definition = definition; + Resolver = resolver; + } + + public void Dispose () + { + Resolver?.Dispose (); + Definition?.Dispose (); + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/TestCasesRunner/TestCaseCollector.cs b/linker/Tests/TestCasesRunner/TestCaseCollector.cs new file mode 100644 index 000000000..c84ff3534 --- /dev/null +++ b/linker/Tests/TestCasesRunner/TestCaseCollector.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Mono.Cecil; +using Mono.Linker.Tests.TestCases; +using Mono.Linker.Tests.Extensions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.TestCasesRunner { + public class TestCaseCollector { + private readonly NPath _rootDirectory; + private readonly NPath _testCaseAssemblyPath; + + public TestCaseCollector (string rootDirectory, string testCaseAssemblyPath) + : this (rootDirectory.ToNPath (), testCaseAssemblyPath.ToNPath ()) + { + } + + public TestCaseCollector (NPath rootDirectory, NPath testCaseAssemblyPath) + { + _rootDirectory = rootDirectory; + _testCaseAssemblyPath = testCaseAssemblyPath; + } + + public IEnumerable Collect () + { + return Collect (AllSourceFiles ()); + } + + public TestCase Collect (NPath sourceFile) + { + return Collect (new [] { sourceFile }).First (); + } + + public IEnumerable Collect (IEnumerable sourceFiles) + { + _rootDirectory.DirectoryMustExist (); + _testCaseAssemblyPath.FileMustExist (); + + using (var caseAssemblyDefinition = AssemblyDefinition.ReadAssembly (_testCaseAssemblyPath.ToString ())) { + foreach (var file in sourceFiles) { + TestCase testCase; + if (CreateCase (caseAssemblyDefinition, file, out testCase)) + yield return testCase; + } + } + } + + public IEnumerable AllSourceFiles () + { + _rootDirectory.DirectoryMustExist (); + + foreach (var file in _rootDirectory.Files ("*.cs")) { + yield return file; + } + + foreach (var subDir in _rootDirectory.Directories ()) { + if (subDir.FileName == "bin" || subDir.FileName == "obj" || subDir.FileName == "Properties") + continue; + + foreach (var file in subDir.Files ("*.cs", true)) { + yield return file; + } + } + } + + private bool CreateCase (AssemblyDefinition caseAssemblyDefinition, NPath sourceFile, out TestCase testCase) + { + var potentialCase = new TestCase (sourceFile, _rootDirectory, _testCaseAssemblyPath); + + var typeDefinition = FindTypeDefinition (caseAssemblyDefinition, potentialCase); + + if (typeDefinition == null) + throw new InvalidOperationException ($"Could not find the matching type for test case {sourceFile}. Ensure the file name and class name match"); + + if (typeDefinition.HasAttribute (nameof (NotATestCaseAttribute))) { + testCase = null; + return false; + } + + // Verify the class as a static main method + var mainMethod = typeDefinition.Methods.FirstOrDefault (m => m.Name == "Main"); + + if (mainMethod == null) + throw new InvalidOperationException ($"{typeDefinition} in {sourceFile} is missing a Main() method"); + + if (!mainMethod.IsStatic) + throw new InvalidOperationException ($"The Main() method for {typeDefinition} in {sourceFile} should be static"); + + testCase = potentialCase; + return true; + } + + private static TypeDefinition FindTypeDefinition (AssemblyDefinition caseAssemblyDefinition, TestCase testCase) + { + var typeDefinition = caseAssemblyDefinition.MainModule.GetType (testCase.ReconstructedFullTypeName); + + // For all of the Test Cases, the full type name we constructed from the directory structure will be correct and we can successfully find + // the type from GetType. + if (typeDefinition != null) + return typeDefinition; + + // However, some of types are supporting types rather than test cases. and may not follow the standardized naming scheme of the test cases + // We still need to be able to locate these type defs so that we can parse some of the metadata on them. + // One example, Unity run's into this with it's tests that require a type UnityEngine.MonoBehaviours to exist. This tpe is defined in it's own + // file and it cannot follow our standardized naming directory & namespace naming scheme since the namespace must be UnityEngine + foreach (var type in caseAssemblyDefinition.MainModule.Types) { + // Let's assume we should never have to search for a test case that has no namespace. If we don't find the type from GetType, then o well, that's not a test case. + if (string.IsNullOrEmpty (type.Namespace)) + continue; + + if (type.Name == testCase.Name) { + // This isn't foolproof, but let's do a little extra vetting to make sure the type we found corresponds to the source file we are + // processing. + if (!testCase.SourceFile.ReadAllText ().Contains ($"namespace {type.Namespace}")) + continue; + + return type; + } + } + + return null; + } + } +} \ No newline at end of file diff --git a/linker/Tests/TestCasesRunner/TestCaseCompiler.cs b/linker/Tests/TestCasesRunner/TestCaseCompiler.cs new file mode 100644 index 000000000..ce203b478 --- /dev/null +++ b/linker/Tests/TestCasesRunner/TestCaseCompiler.cs @@ -0,0 +1,41 @@ +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Mono.Linker.Tests.Extensions; + +namespace Mono.Linker.Tests.TestCasesRunner { + public class TestCaseCompiler { + public virtual NPath CompileTestIn (NPath outputDirectory, IEnumerable sourceFiles, IEnumerable references, IEnumerable defines) + { + var compilerOptions = CreateCompilerOptions (outputDirectory, references, defines); + var provider = CodeDomProvider.CreateProvider ("C#"); + var result = provider.CompileAssemblyFromFile (compilerOptions, sourceFiles.ToArray ()); + if (!result.Errors.HasErrors) + return compilerOptions.OutputAssembly.ToNPath (); + + var errors = new StringBuilder (); + foreach (var error in result.Errors) + errors.AppendLine (error.ToString ()); + throw new Exception ("Compilation errors: " + errors); + } + + protected virtual CompilerParameters CreateCompilerOptions (NPath outputDirectory, IEnumerable references, IEnumerable defines) + { + var outputPath = outputDirectory.Combine ("test.exe"); + + var compilerParameters = new CompilerParameters + { + OutputAssembly = outputPath.ToString (), + GenerateExecutable = true + }; + + compilerParameters.CompilerOptions = defines?.Aggregate (string.Empty, (buff, arg) => $"{buff} /define:{arg}"); + + compilerParameters.ReferencedAssemblies.AddRange (references.ToArray ()); + + return compilerParameters; + } + } +} \ No newline at end of file diff --git a/linker/Tests/TestCasesRunner/TestCaseLinkerOptions.cs b/linker/Tests/TestCasesRunner/TestCaseLinkerOptions.cs new file mode 100644 index 000000000..49e0992e1 --- /dev/null +++ b/linker/Tests/TestCasesRunner/TestCaseLinkerOptions.cs @@ -0,0 +1,5 @@ +namespace Mono.Linker.Tests.TestCasesRunner { + public class TestCaseLinkerOptions { + public string CoreLink; + } +} \ No newline at end of file diff --git a/linker/Tests/TestCasesRunner/TestCaseMetadaProvider.cs b/linker/Tests/TestCasesRunner/TestCaseMetadaProvider.cs new file mode 100644 index 000000000..d4bfc1f19 --- /dev/null +++ b/linker/Tests/TestCasesRunner/TestCaseMetadaProvider.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Mono.Cecil; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; +using Mono.Linker.Tests.Extensions; +using Mono.Linker.Tests.TestCases; + +namespace Mono.Linker.Tests.TestCasesRunner { + public class TestCaseMetadaProvider { + protected readonly TestCase _testCase; + protected readonly AssemblyDefinition _fullTestCaseAssemblyDefinition; + protected readonly TypeDefinition _testCaseTypeDefinition; + + public TestCaseMetadaProvider (TestCase testCase, AssemblyDefinition fullTestCaseAssemblyDefinition) + { + _testCase = testCase; + _fullTestCaseAssemblyDefinition = fullTestCaseAssemblyDefinition; + // The test case types are never nested so we don't need to worry about that + _testCaseTypeDefinition = fullTestCaseAssemblyDefinition.MainModule.GetType (_testCase.ReconstructedFullTypeName); + + if (_testCaseTypeDefinition == null) + throw new InvalidOperationException ($"Could not find the type definition for {_testCase.Name} in {_testCase.SourceFile}"); + } + + public virtual TestCaseLinkerOptions GetLinkerOptions () + { + // This will end up becoming more complicated as we get into more complex test cases that require additional + // data + var value = "skip"; + var coreLinkAttribute = _testCaseTypeDefinition.CustomAttributes.FirstOrDefault (attr => attr.AttributeType.Name == nameof (CoreLinkAttribute)); + if (coreLinkAttribute != null) + value = (string) coreLinkAttribute.ConstructorArguments.First ().Value; + return new TestCaseLinkerOptions {CoreLink = value}; + } + + public virtual IEnumerable GetReferencedAssemblies () + { + yield return "mscorlib.dll"; + + foreach (var referenceAttr in _testCaseTypeDefinition.CustomAttributes.Where (attr => attr.AttributeType.Name == nameof (ReferenceAttribute))) { + yield return (string) referenceAttr.ConstructorArguments.First ().Value; + } + } + + public virtual IEnumerable GetExtraLinkerSearchDirectories () + { + yield break; + } + + public bool IsIgnored (out string reason) + { + var ignoreAttribute = _testCaseTypeDefinition.CustomAttributes.FirstOrDefault (attr => attr.AttributeType.Name == nameof (IgnoreTestCaseAttribute)); + if (ignoreAttribute != null) { + reason = (string)ignoreAttribute.ConstructorArguments.First ().Value; + return true; + } + + reason = null; + return false; + } + + public virtual IEnumerable AdditionalFilesToSandbox () + { + foreach (var attr in _testCaseTypeDefinition.CustomAttributes) { + if (attr.AttributeType.Name != nameof (SandboxDependencyAttribute)) + continue; + + var relativeDepPath = ((string) attr.ConstructorArguments.First ().Value).ToNPath (); + yield return _testCase.SourceFile.Parent.Combine (relativeDepPath); + } + } + } +} \ No newline at end of file diff --git a/linker/Tests/TestCasesRunner/TestCaseSandbox.cs b/linker/Tests/TestCasesRunner/TestCaseSandbox.cs new file mode 100644 index 000000000..7eb29f4b7 --- /dev/null +++ b/linker/Tests/TestCasesRunner/TestCaseSandbox.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Extensions; +using Mono.Linker.Tests.TestCases; + +namespace Mono.Linker.Tests.TestCasesRunner { + public class TestCaseSandbox { + protected readonly TestCase _testCase; + protected readonly NPath _directory; + + public TestCaseSandbox (TestCase testCase) + : this (testCase, NPath.SystemTemp) + { + } + + public TestCaseSandbox (TestCase testCase, NPath rootTemporaryDirectory) + : this (testCase, rootTemporaryDirectory, string.Empty) + { + } + + public TestCaseSandbox (TestCase testCase, string rootTemporaryDirectory, string namePrefix) + : this (testCase, rootTemporaryDirectory.ToNPath (), namePrefix) + { + } + + public TestCaseSandbox (TestCase testCase, NPath rootTemporaryDirectory, string namePrefix) + { + _testCase = testCase; + var name = string.IsNullOrEmpty (namePrefix) ? "linker_tests" : $"{namePrefix}_linker_tests"; + _directory = rootTemporaryDirectory.Combine (name); + + _directory.DeleteContents (); + + InputDirectory = _directory.Combine ("input").EnsureDirectoryExists (); + OutputDirectory = _directory.Combine ("output").EnsureDirectoryExists (); + ExpectationsDirectory = _directory.Combine ("expectations").EnsureDirectoryExists (); + } + + public NPath InputDirectory { get; } + + public NPath OutputDirectory { get; } + + public NPath ExpectationsDirectory { get; } + + public IEnumerable SourceFiles { + get { return _directory.Files ("*.cs"); } + } + + public IEnumerable InputDirectoryReferences { + get { return InputDirectory.Files ("*.dll"); } + } + + public IEnumerable ExpectationsDirectoryReferences { + get { return ExpectationsDirectory.Files ("*.dll"); } + } + + public IEnumerable LinkXmlFiles { + get { return InputDirectory.Files ("*.xml"); } + } + + public virtual void Populate (TestCaseMetadaProvider metadataProvider) + { + _testCase.SourceFile.Copy (_directory); + + if (_testCase.HasLinkXmlFile) + _testCase.LinkXmlFile.Copy (InputDirectory); + + GetExpectationsAssemblyPath ().Copy (InputDirectory); + + foreach (var dep in metadataProvider.AdditionalFilesToSandbox ()) { + dep.FileMustExist ().Copy (_directory); + } + + InputDirectoryReferences.Copy (ExpectationsDirectory); + } + + private static NPath GetExpectationsAssemblyPath () + { + return new Uri (typeof (KeptAttribute).Assembly.CodeBase).LocalPath.ToNPath (); + } + } +} \ No newline at end of file diff --git a/linker/Tests/TestCasesRunner/TestRunner.cs b/linker/Tests/TestCasesRunner/TestRunner.cs new file mode 100644 index 000000000..0763ca142 --- /dev/null +++ b/linker/Tests/TestCasesRunner/TestRunner.cs @@ -0,0 +1,78 @@ +using System.Linq; +using Mono.Cecil; +using Mono.Linker.Tests.TestCases; +using NUnit.Framework; + +namespace Mono.Linker.Tests.TestCasesRunner { + public class TestRunner { + private readonly ObjectFactory _factory; + + public TestRunner (ObjectFactory factory) + { + _factory = factory; + } + + public LinkedTestCaseResult Run (TestCase testCase) + { + using (var fullTestCaseAssemblyDefinition = AssemblyDefinition.ReadAssembly (testCase.OriginalTestCaseAssemblyPath.ToString ())) { + var metadataProvider = _factory.CreateMetadataProvider (testCase, fullTestCaseAssemblyDefinition); + + string ignoreReason; + if (metadataProvider.IsIgnored (out ignoreReason)) + Assert.Ignore (ignoreReason); + + var sandbox = Sandbox (testCase, metadataProvider); + var compilationResult = Compile (sandbox, metadataProvider); + PrepForLink (sandbox, compilationResult); + return Link (testCase, sandbox, compilationResult, metadataProvider); + } + } + + private TestCaseSandbox Sandbox (TestCase testCase, TestCaseMetadaProvider metadataProvider) + { + var sandbox = _factory.CreateSandbox (testCase); + sandbox.Populate (metadataProvider); + return sandbox; + } + + private ManagedCompilationResult Compile (TestCaseSandbox sandbox, TestCaseMetadaProvider metadataProvider) + { + var compiler = _factory.CreateCompiler (); + var sourceFiles = sandbox.SourceFiles.Select(s => s.ToString()).ToArray(); + + var references = metadataProvider.GetReferencedAssemblies ().Concat (sandbox.InputDirectoryReferences.Select (r => r.ToString ())).ToArray (); + var inputAssemblyPath = compiler.CompileTestIn (sandbox.InputDirectory, sourceFiles, references, null); + + references = metadataProvider.GetReferencedAssemblies ().Concat (sandbox.ExpectationsDirectoryReferences.Select (r => r.ToString ())).ToArray (); + var expectationsAssemblyPath = compiler.CompileTestIn (sandbox.ExpectationsDirectory, sourceFiles, references, new [] { "INCLUDE_EXPECTATIONS" }); + return new ManagedCompilationResult (inputAssemblyPath, expectationsAssemblyPath); + } + + private void PrepForLink (TestCaseSandbox sandbox, ManagedCompilationResult compilationResult) + { + var entryPointLinkXml = sandbox.InputDirectory.Combine ("entrypoint.xml"); + LinkXmlHelpers.WriteXmlFileToPreserveEntryPoint (compilationResult.InputAssemblyPath, entryPointLinkXml); + } + + private LinkedTestCaseResult Link (TestCase testCase, TestCaseSandbox sandbox, ManagedCompilationResult compilationResult, TestCaseMetadaProvider metadataProvider) + { + var linker = _factory.CreateLinker (); + var builder = _factory.CreateLinkerArgumentBuilder (); + var caseDefinedOptions = metadataProvider.GetLinkerOptions (); + + builder.AddOutputDirectory (sandbox.OutputDirectory); + foreach (var linkXmlFile in sandbox.LinkXmlFiles) + builder.AddLinkXmlFile (linkXmlFile); + + builder.AddSearchDirectory (sandbox.InputDirectory); + foreach (var extraSearchDir in metadataProvider.GetExtraLinkerSearchDirectories ()) + builder.AddSearchDirectory (extraSearchDir); + + builder.AddCoreLink (caseDefinedOptions.CoreLink); + + linker.Link (builder.ToArgs ()); + + return new LinkedTestCaseResult (testCase, compilationResult.InputAssemblyPath, sandbox.OutputDirectory.Combine (compilationResult.InputAssemblyPath.FileName), compilationResult.ExpectationsAssemblyPath); + } + } +} \ No newline at end of file diff --git a/linker/Tests/Tests/PreserveActionComparisonTests.cs b/linker/Tests/Tests/PreserveActionComparisonTests.cs new file mode 100644 index 000000000..21c881468 --- /dev/null +++ b/linker/Tests/Tests/PreserveActionComparisonTests.cs @@ -0,0 +1,30 @@ +using NUnit.Framework; + +namespace Mono.Linker.Tests +{ + [TestFixture] + public class PreserveActionComparisonTests + { + [TestCase (TypePreserve.All, TypePreserve.All, TypePreserve.All)] + [TestCase (TypePreserve.All, TypePreserve.Methods, TypePreserve.All)] + [TestCase (TypePreserve.All, TypePreserve.Fields, TypePreserve.All)] + [TestCase (TypePreserve.All, TypePreserve.Nothing, TypePreserve.All)] + [TestCase (TypePreserve.Methods, TypePreserve.All, TypePreserve.All)] + [TestCase (TypePreserve.Methods, TypePreserve.Methods, TypePreserve.Methods)] + [TestCase (TypePreserve.Methods, TypePreserve.Fields, TypePreserve.All)] + [TestCase (TypePreserve.Methods, TypePreserve.Nothing, TypePreserve.Methods)] + [TestCase (TypePreserve.Fields, TypePreserve.All, TypePreserve.All)] + [TestCase (TypePreserve.Fields, TypePreserve.Methods, TypePreserve.All)] + [TestCase (TypePreserve.Fields, TypePreserve.Fields, TypePreserve.Fields)] + [TestCase (TypePreserve.Fields, TypePreserve.Nothing, TypePreserve.Fields)] + [TestCase (TypePreserve.Nothing, TypePreserve.All, TypePreserve.All)] + [TestCase (TypePreserve.Nothing, TypePreserve.Methods, TypePreserve.Methods)] + [TestCase (TypePreserve.Nothing, TypePreserve.Fields, TypePreserve.Fields)] + [TestCase (TypePreserve.Nothing, TypePreserve.Nothing, TypePreserve.Nothing)] + public void VerifyBehaviorOfChoosePreserveActionWhichPreservesTheMost (TypePreserve left, TypePreserve right, TypePreserve expected) + { + Assert.That (expected, Is.EqualTo (AnnotationStore.ChoosePreserveActionWhichPreservesTheMost (left, right))); + Assert.That (expected, Is.EqualTo (AnnotationStore.ChoosePreserveActionWhichPreservesTheMost (right, left))); + } + } +} -- cgit v1.2.3