/// <summary> /// Main “driver” method of all the tests. Contains common housekeeping code. All tests must call it. /// </summary> /// <remarks> /// <para> /// The <paramref name="runner"/> parameter is an action which is passed the following arguments, in /// the order of declaration: /// </para> /// <para> /// <list type="bullet"> /// <item> /// <term>TestProjectInfo testInfo</term> /// <description>Information about the location of all the project files and directories</description> /// </item> /// /// <item> /// <term>bool many</term> /// <description>whether the test is building more than 20 layouts to test parallel builds</description> /// </item> /// /// <item> /// <term>bool dtb</term> /// <description>whether the test is running a design time build (DTB)</description> /// </item> /// /// <item> /// <term>LocalBuilder builder</term> /// <description>the object that runs the actual build session</description> /// </item> /// </list> /// </para> /// </remarks> /// <param name="testName">Name of the test being run. Must be unique</param> /// <param name="many">Generate code in parallel if <c>true</c>, serially otherwise</param> /// <param name="dtb">Test design-time build if <c>true</c>, regular build otherwise</param> /// <param name="runner">Action consituting the main body of the test. Passed parameters are described above in the remarks section.</param> void RunTest(string testName, bool many, bool dtb, Action <TestProjectInfo, bool, bool, LocalBuilder> runner) { string temporaryProjectDir = PrepareProject(testName); LocalBuilder builder = GetBuilder($"{ProjectName}.{testName}"); builder.BuildingInsideVisualStudio = dtb; var testInfo = new TestProjectInfo(ProjectName, testName, temporaryProjectDir, TestOutputDir); try { runner(testInfo, many, dtb, builder); if (many) { Assert.That(WasParsedInParallel(testInfo), Is.True, "Should have been parsed in parallel"); Assert.That(WasGeneratedInParallel(testInfo), Is.True, "Should have been generated in parallel"); } else { Assert.That(WasParsedInParallel(testInfo), Is.False, "Should have been parsed in serial manner"); Assert.That(WasGeneratedInParallel(testInfo), Is.False, "Should have been generated in serial manner"); } } catch { CopyLogs(testInfo, false); throw; } }
void SuccessfulBuild_RunTest(TestProjectInfo testInfo, bool many, bool dtb, LocalBuilder builder) { string[] parameters = GetBuildProperties(many, dtb); bool success = builder.Build(testInfo.SlnPath, GetBuildTarget(dtb), parameters); CopyLogs(testInfo, true); Assert.That(success, Is.True, "Build should have succeeded"); CopyGeneratedFiles(testInfo); foreach (SourceFile src in generated_sources) { foreach (SourceFileMember member in src) { string generatedFile = Path.Combine(testInfo.GeneratedPath, src.Path); if (member.IsMethod) { Assert.That(SourceHasMethod(generatedFile, member.Visibility, member.Type, member.Name, member.Arguments), Is.True, $"Method {member.Name} must exist in {generatedFile}"); } else { Assert.That(SourceHasProperty(generatedFile, member.Visibility, member.Type, member.Name, member.IsExpressionBody), Is.True, $"Property {member.Name} must exist in {generatedFile}"); } } } if (dtb) { return; // DTB doesn't produce binaries } foreach (string binaryName in produced_binaries) { AssertExists(testInfo.TestName, Path.Combine(testInfo.BinPath, binaryName)); } }
/// <summary> /// Main “driver” method of all the tests. Contains common housekeeping code. All tests must call it. /// </summary> /// <remarks> /// <para> /// The <paramref name="runner"/> parameter is an action which is passed the following arguments, in /// the order of declaration: /// </para> /// <para> /// <list type="bullet"> /// <item> /// <term>TestProjectInfo testInfo</term> /// <description>Information about the location of all the project files and directories</description> /// </item> /// /// <item> /// <term>bool many</term> /// <description>whether the test is building more than 20 layouts to test parallel builds</description> /// </item> /// /// <item> /// <term>bool dtb</term> /// <description>whether the test is running a design time build (DTB)</description> /// </item> /// /// <item> /// <term>LocalBuilder builder</term> /// <description>the object that runs the actual build session</description> /// </item> /// </list> /// </para> /// </remarks> /// <param name="testName">Name of the test being run. Must be unique</param> /// <param name="many">Generate code in parallel if <c>true</c>, serially otherwise</param> /// <param name="dtb">Test design-time build if <c>true</c>, regular build otherwise</param> /// <param name="runner">Action consituting the main body of the test. Passed parameters are described above in the remarks section.</param> void RunTest(string testName, bool many, bool dtb, Action <TestProjectInfo, bool, bool, LocalBuilder> runner) { string temporaryProjectDir = PrepareProject(testName); LocalBuilder builder = GetBuilder($"{ProjectName}.{testName}"); builder.BuildingInsideVisualStudio = dtb; var testInfo = new TestProjectInfo(ProjectName, testName, temporaryProjectDir, TestOutputDir); try { runner(testInfo, many, dtb, builder); if (many) { Assert.That(WasParsedInParallel(testInfo), Is.True, "Should have been parsed in parallel"); Assert.That(WasGeneratedInParallel(testInfo), Is.True, "Should have been generated in parallel"); } else { Assert.That(WasParsedInParallel(testInfo), Is.False, "Should have been parsed in serial manner"); Assert.That(WasGeneratedInParallel(testInfo), Is.False, "Should have been generated in serial manner"); } } catch { CopyLogs(testInfo, false); foreach (var file in Directory.GetFiles(testInfo.OutputDirectory, "*.log", SearchOption.AllDirectories)) { TestContext.AddTestAttachment(file); } throw; } // Clean up successful tests FileSystemUtils.SetDirectoryWriteable(testInfo.OutputDirectory); Directory.Delete(testInfo.OutputDirectory, recursive: true); }
void CopyLogs(TestProjectInfo testInfo, bool assert) { AssertExistsAndCopy(testInfo, GetTestLogName(testInfo), testInfo.OutputDirectory, assert); foreach (string log in log_files) { AssertExistsAndCopy(testInfo, log, testInfo.OutputDirectory, assert); } }
void CopyGeneratedFiles(TestProjectInfo testInfo) { string destDir = Path.Combine(testInfo.OutputDirectory, "generated"); foreach (SourceFile src in generated_sources) { AssertExistsAndCopy(testInfo, Path.Combine(testInfo.GeneratedPath, src.Path), destDir); } }
void FailedBuild_ConflictingRelativeLayout_RunTest(TestProjectInfo testInfo, bool many, bool dtb, LocalBuilder builder) { string[] parameters = GetBuildProperties(many, dtb, "NOT_CONFLICTING_RELATIVELAYOUT"); bool success = builder.Build(testInfo.SlnPath, GetBuildTarget(dtb), parameters); CopyLogs(testInfo, true); Assert.That(success, Is.False, "Build should have failed"); string logPath = GetTestLogPath(testInfo); bool haveError = HaveCompilerError_CS0266(logPath, "OnboardingActivityPartial.cs", 43, "Android.Views.View", "Android.Widget.RelativeLayout"); AssertHaveCompilerError(haveError, "OnboardingActivityPartial.cs", 43); haveError = HaveCompilerError_CS0266(logPath, "OnboardingActivity.cs", 38, "Android.Views.View", "Android.Widget.RelativeLayout"); AssertHaveCompilerError(haveError, "OnboardingActivity.cs", 38); }
void FailedBuild_ConflictingFragment_RunTest(TestProjectInfo testInfo, bool many, bool dtb, LocalBuilder builder) { string[] parameters = GetBuildProperties(many, dtb, "NOT_CONFLICTING_FRAGMENT"); bool success = builder.Build(testInfo.SlnPath, GetBuildTarget(dtb), parameters); CopyLogs(testInfo, true); Assert.That(success, Is.False, "Build should have failed"); string logPath = GetTestLogPath(testInfo); bool haveError = HaveCompilerError_CS0266(logPath, "MainActivity.cs", 26, "Android.App.Fragment", "CommonSampleLibrary.LogFragment"); AssertHaveCompilerError(haveError, "MainActivity.cs", 26); haveError = HaveCompilerError_CS0266(logPath, "AnotherMainActivity.cs", 23, "Android.App.Fragment", "CommonSampleLibrary.LogFragment"); AssertHaveCompilerError(haveError, "AnotherMainActivity.cs", 23); }
void AssertExistsAndCopy(TestProjectInfo testInfo, string relativeFilePath, string destDir, bool nunitAssert = true) { string source = Path.Combine(testInfo.RootDirectory, relativeFilePath); if (nunitAssert) { AssertExists(testInfo.TestName, source); } else if (!File.Exists(source)) { return; } if (!Directory.Exists(destDir)) { Directory.CreateDirectory(destDir); } string destination = Path.Combine(destDir, Path.GetFileName(relativeFilePath)); File.Copy(source, destination, true); }
string GetTestLogPath(TestProjectInfo testInfo) { return(Path.Combine(testInfo.RootDirectory, GetTestLogName(testInfo))); }
string GetTestLogName(TestProjectInfo testInfo) { return($"{testInfo.ProjectName}.{testInfo.TestName}.log"); }
bool WasGeneratedInParallel(TestProjectInfo testInfo) { var regex = new Regex($"^\\s*Generating binding code in parallel.*$", RegexOptions.Compiled); return(FileMatches(regex, GetTestLogPath(testInfo))); }
bool WasParsedInParallel(TestProjectInfo testInfo) { var regex = new Regex($"^\\s*Parsing layouts in parallel.*$", RegexOptions.Compiled); return(FileMatches(regex, GetTestLogPath(testInfo))); }