/// <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);
        }