/// <summary> /// Resolve for Gradle using a template .gradle file. /// </summary> /// <param name="gradleTemplate">Gradle template to use.</param> /// <param name="expectedAssetsDir">Directory that contains the assets expected from the /// resolution step.</param> /// <param name="testCase">Object executing this method.</param> /// <param name="testCaseComplete">Called with the test result.</param> /// <param name="otherExpectedFiles">Set of additional files that are expected in the /// project.</param> /// <param name="deleteGradleTemplate">Whether to delete the gradle template before /// testCaseComplete is called.</param> /// <param name="filesToIgnore">Set of files to relative to the generatedAssetsDir.</param> private static void ResolveWithGradleTemplate( string gradleTemplate, string expectedAssetsDir, IntegrationTester.TestCase testCase, Action <IntegrationTester.TestCaseResult> testCaseComplete, IEnumerable <string> otherExpectedFiles = null, bool deleteGradleTemplate = true, ICollection <string> filesToIgnore = null) { var cleanUpFiles = new List <string>(); if (deleteGradleTemplate) { cleanUpFiles.Add(GRADLE_TEMPLATE_ENABLED); } if (otherExpectedFiles != null) { cleanUpFiles.AddRange(otherExpectedFiles); } Action cleanUpTestCase = () => { GooglePlayServices.SettingsDialog.PatchMainTemplateGradle = false; foreach (var filename in cleanUpFiles) { if (File.Exists(filename)) { File.Delete(filename); } } }; try { GooglePlayServices.SettingsDialog.PatchMainTemplateGradle = true; File.Copy(gradleTemplate, GRADLE_TEMPLATE_ENABLED); Resolve("Gradle", false, expectedAssetsDir, null, filesToIgnore, testCase, (IntegrationTester.TestCaseResult testCaseResult) => { if (otherExpectedFiles != null) { foreach (var expectedFile in otherExpectedFiles) { if (!File.Exists(expectedFile)) { testCaseResult.ErrorMessages.Add(String.Format("{0} not found", expectedFile)); } } } cleanUpTestCase(); testCaseComplete(testCaseResult); }, synchronous: true); } catch (Exception ex) { var testCaseResult = new IntegrationTester.TestCaseResult(testCase); testCaseResult.ErrorMessages.Add(ex.ToString()); cleanUpTestCase(); testCaseComplete(testCaseResult); } }
/// <summary> // Log a test case result to the journal so that it isn't executed again if the app // domain is reloaded. /// </summary> private static bool WriteTestCaseResult(TestCaseResult testCaseResult) { var existingTestCaseResults = ReadTestCaseResults(); existingTestCaseResults.Add(testCaseResult); try { Directory.CreateDirectory(Path.GetDirectoryName(TestCaseResultsFilename)); using (var writer = new XmlTextWriter(new StreamWriter(TestCaseResultsFilename)) { Formatting = Formatting.Indented }) { writer.WriteStartElement("TestCaseResults"); foreach (var result in existingTestCaseResults) { writer.WriteStartElement("TestCaseResult"); if (!String.IsNullOrEmpty(result.TestCaseName)) { writer.WriteStartElement("TestCaseName"); writer.WriteValue(result.TestCaseName); writer.WriteEndElement(); } writer.WriteStartElement("Skipped"); writer.WriteValue(result.Skipped); writer.WriteEndElement(); if (result.ErrorMessages.Count > 0) { writer.WriteStartElement("ErrorMessages"); foreach (var errorMessage in result.ErrorMessages) { writer.WriteStartElement("ErrorMessage"); writer.WriteValue(errorMessage); writer.WriteEndElement(); } writer.WriteEndElement(); } writer.WriteEndElement(); } writer.WriteEndElement(); writer.Flush(); writer.Close(); } } catch (Exception e) { UnityEngine.Debug.LogWarning( String.Format("Failed while writing {0} ({1}), test execution will restart " + "if the app domain is reloaded.", TestCaseResultsFilename, e)); return(false); } return(true); }
/// <summary> /// Validate Android libraries and repos are setup correctly. /// </summary> /// <param name="testCaseResult">TestCaseResult instance to add errors to if this method /// fails. </param> private static void ValidateDependencies(IntegrationTester.TestCaseResult testCaseResult) { // Validate set dependencies are present. CompareKeyValuePairLists( new List <KeyValuePair <string, string> >() { new KeyValuePair <string, string>( "com.android.support:support-annotations:26.1.0", "Assets/ExternalDependencyManager/Editor/TestDependencies.xml:4"), new KeyValuePair <string, string>( "com.google.firebase:firebase-app-unity:5.1.1", "Assets/ExternalDependencyManager/Editor/TestDependencies.xml:10"), new KeyValuePair <string, string>( "com.google.firebase:firebase-common:16.0.0", "Google.AndroidResolverIntegrationTests.SetupDependencies"), new KeyValuePair <string, string>( "org.test.psr:classifier:1.0.1:foo@aar", "Assets/ExternalDependencyManager/Editor/TestDependencies.xml:12"), }, PlayServicesResolver.GetPackageSpecs(), "Package Specs", testCaseResult); // Validate configured repos are present. CompareKeyValuePairLists( new List <KeyValuePair <string, string> >() { new KeyValuePair <string, string>( "file:///my/nonexistant/test/repo", "Assets/ExternalDependencyManager/Editor/TestDependencies.xml:17"), new KeyValuePair <string, string>( "file:///" + Path.GetFullPath("project_relative_path/repo").Replace("\\", "/"), "Assets/ExternalDependencyManager/Editor/TestDependencies.xml:17"), new KeyValuePair <string, string>( "file:///" + Path.GetFullPath( "Assets/Firebase/m2repository").Replace("\\", "/"), "Assets/ExternalDependencyManager/Editor/TestDependencies.xml:10") }, PlayServicesResolver.GetRepos(), "Repos", testCaseResult); }
/// <summary> /// Compare two ordered lists. /// </summary> /// <param name="expectedList">Expected list.</param> /// <param name="testList">List to compare with expectedList.</param> /// <param name="listDescription">Human readable description of both lists.</param> /// <param name="testCaseResult">TestCaseResult instance to add errors to if lists do not /// match.</param> private static void CompareKeyValuePairLists( IList <KeyValuePair <string, string> > expectedList, IList <KeyValuePair <string, string> > testList, string listDescription, IntegrationTester.TestCaseResult testCaseResult) { if (expectedList.Count != testList.Count) { testCaseResult.ErrorMessages.Add(String.Format( "Returned list of {0} is an unexpected size {1} vs {2}", listDescription, testList.Count, expectedList.Count)); return; } for (int i = 0; i < expectedList.Count; ++i) { var expected = expectedList[i]; var test = testList[i]; if (expected.Key != test.Key || expected.Value != test.Value) { testCaseResult.ErrorMessages.Add(String.Format( "Element {0} of list {1} ({2} {3}) mismatches the expected value ({4} {5})", i, listDescription, test.Key, test.Value, expected.Key, expected.Value)); } } }
public static void ConfigureTestCases() { // Set of files to ignore (relative to the Assets/Plugins/Android directory) in all tests // that do not use the Gradle template. var nonGradleTemplateFilesToIgnore = new HashSet <string>() { Path.GetFileName(GRADLE_TEMPLATE_DISABLED), Path.GetFileName(GRADLE_TEMPLATE_LIBRARY_DISABLED) }; UnityEngine.Debug.Log("Setting up test cases for execution."); IntegrationTester.Runner.ScheduleTestCases(new [] { // This *must* be the first test case as other test cases depend upon it. new IntegrationTester.TestCase { Name = "ValidateAndroidTargetSelected", Method = ValidateAndroidTargetSelected, }, new IntegrationTester.TestCase { Name = "SetupDependencies", Method = (testCase, testCaseComplete) => { ClearAllDependencies(); SetupDependencies(); var testCaseResult = new IntegrationTester.TestCaseResult(testCase); ValidateDependencies(testCaseResult); testCaseComplete(testCaseResult); } }, new IntegrationTester.TestCase { Name = "ResolveForGradleBuildSystemWithTemplate", Method = (testCase, testCaseComplete) => { ClearAllDependencies(); SetupDependencies(); ResolveWithGradleTemplate( GRADLE_TEMPLATE_DISABLED, "ExpectedArtifacts/NoExport/GradleTemplate", testCase, testCaseComplete, otherExpectedFiles: new [] { "Assets/Firebase/m2repository/com/google/firebase/" + "firebase-app-unity/5.1.1/firebase-app-unity-5.1.1.aar" }, filesToIgnore: new HashSet <string> { Path.GetFileName(GRADLE_TEMPLATE_LIBRARY_DISABLED) }); } }, new IntegrationTester.TestCase { Name = "ResolverForGradleBuildSystemWithTemplateUsingJetifier", Method = (testCase, testCaseComplete) => { ClearAllDependencies(); SetupDependencies(); GooglePlayServices.SettingsDialog.UseJetifier = true; ResolveWithGradleTemplate( GRADLE_TEMPLATE_DISABLED, "ExpectedArtifacts/NoExport/GradleTemplateJetifier", testCase, testCaseComplete, otherExpectedFiles: new [] { "Assets/Firebase/m2repository/com/google/firebase/" + "firebase-app-unity/5.1.1/firebase-app-unity-5.1.1.aar" }, filesToIgnore: new HashSet <string> { Path.GetFileName(GRADLE_TEMPLATE_LIBRARY_DISABLED) }); } }, new IntegrationTester.TestCase { Name = "ResolveForGradleBuildSystemLibraryWithTemplate", Method = (testCase, testCaseComplete) => { ClearAllDependencies(); SetupDependencies(); ResolveWithGradleTemplate( GRADLE_TEMPLATE_LIBRARY_DISABLED, "ExpectedArtifacts/NoExport/GradleTemplateLibrary", testCase, testCaseComplete, otherExpectedFiles: new [] { "Assets/Firebase/m2repository/com/google/firebase/" + "firebase-app-unity/5.1.1/firebase-app-unity-5.1.1.aar" }, filesToIgnore: new HashSet <string> { Path.GetFileName(GRADLE_TEMPLATE_DISABLED) }); } }, new IntegrationTester.TestCase { Name = "ResolveForGradleBuildSystemWithTemplateEmpty", Method = (testCase, testCaseComplete) => { string enabledDependencies = "Assets/ExternalDependencyManager/Editor/TestDependencies.xml"; string disabledDependencies = "Assets/ExternalDependencyManager/Editor/TestDependenciesDISABLED.xml"; Action enableDependencies = () => { UnityEditor.AssetDatabase.MoveAsset(disabledDependencies, enabledDependencies); }; try { // Disable all XML dependencies. var error = UnityEditor.AssetDatabase.MoveAsset(enabledDependencies, disabledDependencies); if (!String.IsNullOrEmpty(error)) { testCaseComplete(new IntegrationTester.TestCaseResult(testCase) { ErrorMessages = new List <string>() { error } }); return; } ClearAllDependencies(); ResolveWithGradleTemplate( GRADLE_TEMPLATE_DISABLED, "ExpectedArtifacts/NoExport/GradleTemplateEmpty", testCase, (testCaseResult) => { enableDependencies(); testCaseComplete(testCaseResult); }, filesToIgnore: new HashSet <string> { Path.GetFileName(GRADLE_TEMPLATE_LIBRARY_DISABLED) }); } finally { enableDependencies(); } } }, new IntegrationTester.TestCase { Name = "ResolveForGradleBuildSystem", Method = (testCase, testCaseComplete) => { ClearAllDependencies(); SetupDependencies(); Resolve("Gradle", false, "ExpectedArtifacts/NoExport/Gradle", null, nonGradleTemplateFilesToIgnore, testCase, testCaseComplete); } }, new IntegrationTester.TestCase { Name = "ResolveForGradleBuildSystemSync", Method = (testCase, testCaseComplete) => { ClearAllDependencies(); SetupDependencies(); Resolve("Gradle", false, "ExpectedArtifacts/NoExport/Gradle", null, nonGradleTemplateFilesToIgnore, testCase, testCaseComplete, synchronous: true); } }, new IntegrationTester.TestCase { Name = "ResolveForInternalBuildSystem", Method = (testCase, testCaseComplete) => { ClearAllDependencies(); SetupDependencies(); Resolve("Internal", false, AarsWithNativeLibrariesSupported ? "ExpectedArtifacts/NoExport/InternalNativeAars" : "ExpectedArtifacts/NoExport/InternalNativeAarsExploded", null, nonGradleTemplateFilesToIgnore, testCase, testCaseComplete); } }, new IntegrationTester.TestCase { Name = "ResolveForInternalBuildSystemUsingJetifier", Method = (testCase, testCaseComplete) => { ClearAllDependencies(); SetupDependencies(); GooglePlayServices.SettingsDialog.UseJetifier = true; Resolve("Internal", false, AarsWithNativeLibrariesSupported ? "ExpectedArtifacts/NoExport/InternalNativeAarsJetifier" : "ExpectedArtifacts/NoExport/InternalNativeAarsExplodedJetifier", null, nonGradleTemplateFilesToIgnore, testCase, testCaseComplete); } }, new IntegrationTester.TestCase { Name = "ResolveForGradleBuildSystemAndExport", Method = (testCase, testCaseComplete) => { ClearAllDependencies(); SetupDependencies(); Resolve("Gradle", true, "ExpectedArtifacts/Export/Gradle", null, nonGradleTemplateFilesToIgnore, testCase, testCaseComplete); } }, new IntegrationTester.TestCase { Name = "ResolveAddedDependencies", Method = (testCase, testCaseComplete) => { ClearAllDependencies(); SetupDependencies(); UpdateAdditionalDependenciesFile(true); Resolve("Gradle", true, "ExpectedArtifacts/Export/GradleAddedDeps", null, nonGradleTemplateFilesToIgnore, testCase, testCaseComplete); } }, new IntegrationTester.TestCase { Name = "ResolveRemovedDependencies", Method = (testCase, testCaseComplete) => { ClearAllDependencies(); SetupDependencies(); // Add the additional dependencies file then immediately remove it. UpdateAdditionalDependenciesFile(true); UpdateAdditionalDependenciesFile(false); Resolve("Gradle", true, "ExpectedArtifacts/Export/Gradle", null, nonGradleTemplateFilesToIgnore, testCase, testCaseComplete); } }, new IntegrationTester.TestCase { Name = "DeleteResolvedLibraries", Method = (testCase, testCaseComplete) => { ClearAllDependencies(); SetupDependencies(); Resolve("Gradle", true, "ExpectedArtifacts/Export/Gradle", null, nonGradleTemplateFilesToIgnore, testCase, (testCaseResult) => { PlayServicesResolver.DeleteResolvedLibrariesSync(); var unexpectedFilesMessage = new List <string>(); var resolvedFiles = ListFiles("Assets/Plugins/Android", nonGradleTemplateFilesToIgnore); if (resolvedFiles.Count > 0) { unexpectedFilesMessage.Add("Libraries not deleted!"); foreach (var filename in resolvedFiles.Values) { unexpectedFilesMessage.Add(filename); } } testCaseResult.ErrorMessages.AddRange(unexpectedFilesMessage); testCaseComplete(testCaseResult); }, synchronous: true); } }, new IntegrationTester.TestCase { Name = "ResolveForGradleBuildSystemWithTemplateDeleteLibraries", Method = (testCase, testCaseComplete) => { ClearAllDependencies(); SetupDependencies(); var filesToIgnore = new HashSet <string> { Path.GetFileName(GRADLE_TEMPLATE_LIBRARY_DISABLED) }; ResolveWithGradleTemplate( GRADLE_TEMPLATE_DISABLED, "ExpectedArtifacts/NoExport/GradleTemplate", testCase, (testCaseResult) => { PlayServicesResolver.DeleteResolvedLibrariesSync(); testCaseResult.ErrorMessages.AddRange(CompareDirectoryContents( "ExpectedArtifacts/NoExport/GradleTemplateEmpty", "Assets/Plugins/Android", filesToIgnore)); if (File.Exists(GRADLE_TEMPLATE_ENABLED)) { File.Delete(GRADLE_TEMPLATE_ENABLED); } testCaseComplete(testCaseResult); }, deleteGradleTemplate: false, filesToIgnore: filesToIgnore); } }, }); // Test resolution with Android ABI filtering. if (IntegrationTester.Runner.UnityVersion >= 2018.0f) { IntegrationTester.Runner.ScheduleTestCase( new IntegrationTester.TestCase { Name = "ResolverForGradleBuildSystemUsingAbisArmeabiv7aAndArm64", Method = (testCase, testCaseComplete) => { ClearAllDependencies(); Resolve("Gradle", false, "ExpectedArtifacts/NoExport/GradleArmeabiv7aArm64", "armeabi-v7a, arm64-v8a", nonGradleTemplateFilesToIgnore, testCase, testCaseComplete); } }); } else if (IntegrationTester.Runner.UnityVersion >= 5.0f) { IntegrationTester.Runner.ScheduleTestCase( new IntegrationTester.TestCase { Name = "ResolverForGradleBuildSystemUsingAbisArmeabiv7a", Method = (testCase, testCaseComplete) => { ClearAllDependencies(); Resolve("Gradle", false, "ExpectedArtifacts/NoExport/GradleArmeabiv7a", "armeabi-v7a", nonGradleTemplateFilesToIgnore, testCase, testCaseComplete); } }); } }
/// <summary> /// Log a test case result with error details. /// </summary> /// <param name="testCaseResult">Result to log.</param> public static void LogTestCaseResult(TestCaseResult testCaseResult) { testCaseResults.Add(testCaseResult); UnityEngine.Debug.Log(testCaseResult.FormatString(true)); WriteTestCaseResult(testCaseResult); }
/// <summary> /// Read test case results from the journal. /// </summary> /// <returns>List of TestCaseResults.</returns> private static List <TestCaseResult> ReadTestCaseResults() { var readTestCaseResults = new List <TestCaseResult>(); if (!File.Exists(TestCaseResultsFilename)) { return(readTestCaseResults); } bool successful = XmlUtilities.ParseXmlTextFileElements( TestCaseResultsFilename, new Logger(), (XmlTextReader reader, string elementName, bool isStart, string parentElementName, List <string> elementNameStack) => { TestCaseResult currentTestCaseResult = null; int testCaseResultsCount = readTestCaseResults.Count; if (testCaseResultsCount > 0) { currentTestCaseResult = readTestCaseResults[testCaseResultsCount - 1]; } if (elementName == "TestCaseResults" && parentElementName == "") { if (isStart) { readTestCaseResults.Clear(); } return(true); } else if (elementName == "TestCaseResult" && parentElementName == "TestCaseResults") { if (isStart) { readTestCaseResults.Add(new TestCaseResult(new TestCase())); } return(true); } else if (elementName == "TestCaseName" && parentElementName == "TestCaseResult") { if (isStart && reader.Read() && reader.NodeType == XmlNodeType.Text) { currentTestCaseResult.TestCaseName = reader.ReadContentAsString(); } return(true); } else if (elementName == "Skipped" && parentElementName == "TestCaseResult") { if (isStart && reader.Read() && reader.NodeType == XmlNodeType.Text) { currentTestCaseResult.Skipped = reader.ReadContentAsBoolean(); } return(true); } else if (elementName == "ErrorMessages" && parentElementName == "TestCaseResult") { return(true); } else if (elementName == "ErrorMessage" && parentElementName == "ErrorMessages") { if (isStart && reader.Read() && reader.NodeType == XmlNodeType.Text) { currentTestCaseResult.ErrorMessages.Add(reader.ReadContentAsString()); } return(true); } return(false); }); if (!successful) { UnityEngine.Debug.LogWarning( String.Format("Failed while reading {0}, test execution will restart if the " + "app domain is reloaded.", TestCaseResultsFilename)); } return(readTestCaseResults); }