Implements a logger for use by a project builder during testing.
Inheritance: Logger
        public void BuildTest()
        {
            /*
             * Find the initializer assembly
             */
            string instrumentationAssemblyLocation = typeof(InjectModuleInitializer).Assembly.Location;

            Assert.IsTrue(File.Exists(instrumentationAssemblyLocation), "Could not find Initializer assembly at '{0}'.", instrumentationAssemblyLocation);

            /*
             * Find test app project
             */
            const string projectFileName = TestAppProjectName + ".csproj";
            string       directory       = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

            Assert.IsNotNull(directory, "Could not find executing assembly directory - location was null.");
            Assert.IsTrue(Directory.Exists(directory), "Could not find executing assembly directory.");

            string root        = Path.GetPathRoot(directory);
            string projectFile = null;

            while (directory != root)
            {
                string[] subDirs = Directory.GetDirectories(directory, TestAppProjectDirectoryName, SearchOption.AllDirectories);

                if (subDirs.Length > 0)
                {
                    // Only one match allowed
                    Assert.AreEqual(1, subDirs.Length, "Found '{1}' sub directories matching '{2}'.{0}{3}",
                                    Environment.NewLine,
                                    subDirs.Length,
                                    TestAppProjectName,
                                    String.Join(Environment.NewLine, subDirs));

                    // Look for project file.
                    string pd = subDirs.Single();
                    Assert.IsNotNull(pd, "Null sub directory!");

                    string[] files = Directory.GetFiles(pd, projectFileName, SearchOption.AllDirectories);
                    if (files.Length > 0)
                    {
                        // Only one match allowed
                        Assert.AreEqual(1, files.Length, "Found '{1}' project files matching '{2}'.{0}{3}",
                                        Environment.NewLine,
                                        files.Length,
                                        projectFileName,
                                        String.Join(Environment.NewLine, files));

                        // Found our file
                        directory   = pd;
                        projectFile = files.Single();
                        break;
                    }
                }

                // Go up a directory
                directory = Path.GetDirectoryName(directory);
            }

            // Check we have the project
            Assert.AreNotEqual(root, directory, "Could not find the '{0}' project - reached root '{1}'.",
                               projectFileName, root);
            Assert.IsNotNull(projectFile, "Could not find the '{0}' project.", projectFileName);

            /*
             * Create a copy of the instrumentation library - this stops the problems that occur with locking.
             */
            string assemblyExtension = Path.GetExtension(instrumentationAssemblyLocation);
            string tempInstrumentationAssemblyLocation = Path.ChangeExtension(directory + Path.DirectorySeparatorChar + Guid.NewGuid(),
                                                                              assemblyExtension);

            Assert.IsNotNull(tempInstrumentationAssemblyLocation, "Temporary location for instrumentation assembly is null!");
            Assert.IsFalse(File.Exists(tempInstrumentationAssemblyLocation), "Temporary location for instrumentation assembly already exists.");
            File.Copy(instrumentationAssemblyLocation, tempInstrumentationAssemblyLocation);

            try
            {
                // Load the project
                Project             project = new Project(projectFile);
                UnitTestBuildLogger logger  = new UnitTestBuildLogger();

                // Find solution directory
                string solutionDirectory = directory;
                while (solutionDirectory != root)
                {
                    if (Directory.GetFiles(solutionDirectory, "*.sln", SearchOption.TopDirectoryOnly).Any())
                    {
                        break;
                    }

                    // Go up a directory
                    solutionDirectory = Path.GetDirectoryName(solutionDirectory);
                }
                if (!solutionDirectory.EndsWith("\\"))
                {
                    solutionDirectory += "\\";
                }

                Assert.AreNotEqual(root, solutionDirectory,
                                   "Could not find solution directory for '{0}' project - reached root '{1}'",
                                   projectFile, root);

                // Variable to hold the newly created assembly.
                string instrumentedAssemblyLocation;

                // Create a project collection
                using (ProjectCollection projectCollection = new ProjectCollection(ToolsetDefinitionLocations.Registry | ToolsetDefinitionLocations.ConfigurationFile))
                {
                    // Set up environment - explicitly setting path to tool.
                    BuildRequestData requestData = new BuildRequestData(project.FullPath,
                                                                        new Dictionary <string, string>
                    {
                        { "SolutionDir", solutionDirectory },
                        {
                            "InjectModuleInitializerTool",
                            tempInstrumentationAssemblyLocation
                        }
                    },
                                                                        project.ToolsVersion,
                                                                        new[] { "Rebuild" },
                                                                        null);

                    // Set parameters.
                    BuildParameters parameters = new BuildParameters(projectCollection)
                    {
                        Loggers = new[] { logger },
                        ToolsetDefinitionLocations = projectCollection.ToolsetLocations,
                        NodeExeLocation            = Assembly.GetExecutingAssembly().Location
                    };

                    // Get build manager and reset any caches.
                    BuildManager buildManager = BuildManager.DefaultBuildManager;
                    buildManager.ResetCaches();

                    // Build
                    BuildResult result = buildManager.Build(parameters, requestData);

                    // Check it built correctly.
                    Assert.AreEqual(BuildResultCode.Success, result.OverallResult, "Project failed to build.{0}{1}",
                                    Environment.NewLine, logger.Errors);
                    Assert.IsTrue(String.IsNullOrWhiteSpace(logger.Errors), "The build reported errors.{0}{1}",
                                  Environment.NewLine, logger.Errors);

                    // Try to get output path from build results.
                    string       targetPath = null;
                    TargetResult targetResult;
                    if (result.ResultsByTarget.TryGetValue("Build", out targetResult) && (targetResult.Items.Any()))
                    {
                        targetPath = targetResult.Items.First().ItemSpec;
                    }

                    instrumentedAssemblyLocation = targetPath ?? project.GetPropertyValue("TargetPath");

                    // Check instrumented assembly.
                    Assert.IsNotNull(instrumentedAssemblyLocation, "No instrumented assembly found after build.");
                    Assert.IsTrue(File.Exists(instrumentedAssemblyLocation),
                                  "Could not find instrumented assembly '{0}'.", instrumentedAssemblyLocation);

                    // Check for success message in build output.
                    string successMessage = string.Format(AddedMessage, instrumentedAssemblyLocation);
                    Assert.IsTrue(logger.Output.Contains(successMessage), "The build output did not contain '{0}'.",
                                  successMessage);
                }

                DateTime beforeLoad = DateTime.Now;

                // Load instrumented assembly to test.
                Assembly builtAssembly = Assembly.LoadFile(instrumentedAssemblyLocation);
                Assert.IsNotNull(builtAssembly, "Failed to load instrumented assembly '{0}'.");

                Type initializerType =
                    builtAssembly.GetTypes().FirstOrDefault(
                        t => t.Name.Equals("ModuleInitializer", StringComparison.InvariantCultureIgnoreCase));

                Assert.IsNotNull(initializerType, "Could not find initializer type in instrumented assembly '{0}'.", instrumentedAssemblyLocation);

                // Get InitializerHit value.
                FieldInfo initializerHitField = initializerType.GetField("InitializerHit",
                                                                         BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
                Assert.IsNotNull(initializerHitField, "Could not find InitializerHit field on type '{0}' in instrumented assembly '{1}'.",
                                 initializerType.FullName, instrumentedAssemblyLocation);
                DateTime initializerHit = (DateTime)initializerHitField.GetValue(null);

                DateTime afterFirstAccess = DateTime.Now;

                // Check initializer was hit by the time the first access occurred.
                Assert.AreNotEqual(default(DateTime), initializerHit, "The initializer was not hit.");
                Assert.IsTrue(initializerHit > beforeLoad, "The initializer was hit at '{0:mm:ss.fffffff}' before the assembly was loaded at '{1:mm:ss.fffffff}'!", initializerHit, beforeLoad);
                Assert.IsTrue(initializerHit < afterFirstAccess, "The initializer was hit at '{0:mm:ss.fffffff}' after the assembly was finished loading at '{1:mm:ss.fffffff}'!", initializerHit, afterFirstAccess);

                // Get StaticConstructorHit value.
                FieldInfo staticConstructorHitField = initializerType.GetField("StaticConstructorHit",
                                                                               BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
                Assert.IsNotNull(staticConstructorHitField, "Could not find StaticConstructorHit field on type '{0}' in instrumented assembly '{1}'.",
                                 initializerType.FullName, instrumentedAssemblyLocation);
                DateTime staticConstructorHit = (DateTime)staticConstructorHitField.GetValue(null);

                // Check the static constructor was hit before the initializer method itself was hit.
                Assert.AreNotEqual(default(DateTime), staticConstructorHit, "The initializer static constructor was not hit.");
                Assert.IsTrue(staticConstructorHit > beforeLoad, "The initializer static constructor was hit at '{0:mm:ss.fffffff}' before the assembly was loaded at '{1:mm:ss.fffffff}'!", staticConstructorHit, beforeLoad);
                Assert.IsTrue(staticConstructorHit < initializerHit, "The initializer static constructor was hit at '{0:mm:ss.fffffff}' after the initializer method was hit at '{1:mm:ss.fffffff}'!", staticConstructorHit, initializerHit);
                Assert.IsTrue(staticConstructorHit < afterFirstAccess, "The initializer static constructor was hit at '{0:mm:ss.fffffff}' after the assembly was finished loading at '{1:mm:ss.fffffff}'!", staticConstructorHit, afterFirstAccess);
            }
            finally
            {
                // Lock for all assemblies in directory.
                string pattern = Path.ChangeExtension("*", assemblyExtension);
                foreach (string tempAssembly in Directory.GetFiles(directory, pattern))
                {
                    if (String.IsNullOrWhiteSpace(tempAssembly))
                    {
                        continue;
                    }

                    // If the assembly has a guid name we can delete it.
                    string guidStr = Path.GetFileNameWithoutExtension(tempAssembly);
                    Guid   guid;
                    if (!Guid.TryParse(guidStr, out guid))
                    {
                        continue;
                    }

                    try
                    {
                        // Try to delete immediately
                        File.Delete(tempAssembly);
                    }
                    catch (UnauthorizedAccessException)
                    {
                        // Delete file on next reboot as locked.
                        MoveFileEx(tempAssembly, null, MoveFileFlags.MOVEFILE_DELAY_UNTIL_REBOOT);
                    }
                }
            }
        }
        public void BuildTest()
        {
            /*
             * Find the initializer assembly
             */
            string instrumentationAssemblyLocation = typeof(InjectModuleInitializer).Assembly.Location;
            Assert.IsTrue(File.Exists(instrumentationAssemblyLocation), "Could not find Initializer assembly at '{0}'.", instrumentationAssemblyLocation);

            /*
             * Find test app project
             */
            const string projectFileName = TestAppProjectName + ".csproj";
            string directory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            Assert.IsNotNull(directory, "Could not find executing assembly directory - location was null.");
            Assert.IsTrue(Directory.Exists(directory), "Could not find executing assembly directory.");

            string root = Path.GetPathRoot(directory);
            string projectFile = null;

            while (directory != root)
            {
                string[] subDirs = Directory.GetDirectories(directory, TestAppProjectDirectoryName, SearchOption.AllDirectories);

                if (subDirs.Length > 0)
                {
                    // Only one match allowed
                    Assert.AreEqual(1, subDirs.Length, "Found '{1}' sub directories matching '{2}'.{0}{3}",
                                    Environment.NewLine,
                                    subDirs.Length,
                                    TestAppProjectName,
                                    String.Join(Environment.NewLine, subDirs));

                    // Look for project file.
                    string pd = subDirs.Single();
                    Assert.IsNotNull(pd, "Null sub directory!");

                    string[] files = Directory.GetFiles(pd, projectFileName, SearchOption.AllDirectories);
                    if (files.Length > 0)
                    {
                        // Only one match allowed
                        Assert.AreEqual(1, files.Length, "Found '{1}' project files matching '{2}'.{0}{3}",
                                        Environment.NewLine,
                                        files.Length,
                                        projectFileName,
                                        String.Join(Environment.NewLine, files));

                        // Found our file
                        directory = pd;
                        projectFile = files.Single();
                        break;
                    }
                }

                // Go up a directory
                directory = Path.GetDirectoryName(directory);
            }

            // Check we have the project
            Assert.AreNotEqual(root, directory, "Could not find the '{0}' project - reached root '{1}'.",
                               projectFileName, root);
            Assert.IsNotNull(projectFile, "Could not find the '{0}' project.", projectFileName);

            /*
             * Create a copy of the instrumentation library - this stops the problems that occur with locking.
             */
            string assemblyExtension = Path.GetExtension(instrumentationAssemblyLocation);
            string tempInstrumentationAssemblyLocation = Path.ChangeExtension(directory + Path.DirectorySeparatorChar + Guid.NewGuid(),
                                                       assemblyExtension);
            Assert.IsNotNull(tempInstrumentationAssemblyLocation, "Temporary location for instrumentation assembly is null!");
            Assert.IsFalse(File.Exists(tempInstrumentationAssemblyLocation), "Temporary location for instrumentation assembly already exists.");
            File.Copy(instrumentationAssemblyLocation, tempInstrumentationAssemblyLocation);

            try
            {
                // Load the project
                Project project = new Project(projectFile);
                UnitTestBuildLogger logger = new UnitTestBuildLogger();

                // Find solution directory
                string solutionDirectory = directory;
                while (solutionDirectory != root)
                {
                    if (Directory.GetFiles(solutionDirectory, "*.sln", SearchOption.TopDirectoryOnly).Any())
                        break;

                    // Go up a directory
                    solutionDirectory = Path.GetDirectoryName(solutionDirectory);
                }
                if (!solutionDirectory.EndsWith("\\")) solutionDirectory += "\\";

                Assert.AreNotEqual(root, solutionDirectory,
                                   "Could not find solution directory for '{0}' project - reached root '{1}'",
                                   projectFile, root);

                // Variable to hold the newly created assembly.
                string instrumentedAssemblyLocation;
                
                // Create a project collection
                using (ProjectCollection projectCollection = new ProjectCollection(ToolsetDefinitionLocations.Registry | ToolsetDefinitionLocations.ConfigurationFile))
                {
                    // Set up environment - explicitly setting path to tool.
                    BuildRequestData requestData = new BuildRequestData(project.FullPath,
                                                                        new Dictionary<string, string>
                                                                            {
                                                                                {"SolutionDir", solutionDirectory},
                                                                                {
                                                                                    "InjectModuleInitializerTool",
                                                                                    tempInstrumentationAssemblyLocation
                                                                                    }
                                                                            },
                                                                        project.ToolsVersion,
                                                                        new[] {"Rebuild"},
                                                                        null);

                    // Set parameters.
                    BuildParameters parameters = new BuildParameters(projectCollection)
                                                     {
                                                         Loggers = new[] {logger},
                                                         ToolsetDefinitionLocations = projectCollection.ToolsetLocations,
                                                         NodeExeLocation = Assembly.GetExecutingAssembly().Location
                                                     };

                    // Get build manager and reset any caches.
                    BuildManager buildManager = BuildManager.DefaultBuildManager;
                    buildManager.ResetCaches();

                    // Build
                    BuildResult result = buildManager.Build(parameters, requestData);

                    // Check it built correctly.
                    Assert.AreEqual(BuildResultCode.Success, result.OverallResult, "Project failed to build.{0}{1}",
                                    Environment.NewLine, logger.Errors);
                    Assert.IsTrue(String.IsNullOrWhiteSpace(logger.Errors), "The build reported errors.{0}{1}",
                                  Environment.NewLine, logger.Errors);

                    // Try to get output path from build results.
                    string targetPath = null;
                    TargetResult targetResult;
                    if (result.ResultsByTarget.TryGetValue("Build", out targetResult) && (targetResult.Items.Any()))
                        targetPath = targetResult.Items.First().ItemSpec;

                    instrumentedAssemblyLocation = targetPath ?? project.GetPropertyValue("TargetPath");

                    // Check instrumented assembly.
                    Assert.IsNotNull(instrumentedAssemblyLocation, "No instrumented assembly found after build.");
                    Assert.IsTrue(File.Exists(instrumentedAssemblyLocation),
                                  "Could not find instrumented assembly '{0}'.", instrumentedAssemblyLocation);

                    // Check for success message in build output.
                    string successMessage = string.Format(AddedMessage, instrumentedAssemblyLocation);
                    Assert.IsTrue(logger.Output.Contains(successMessage), "The build output did not contain '{0}'.",
                                  successMessage);
                }

                DateTime beforeLoad = DateTime.Now;

                // Load instrumented assembly to test.
                Assembly builtAssembly = Assembly.LoadFile(instrumentedAssemblyLocation);
                Assert.IsNotNull(builtAssembly, "Failed to load instrumented assembly '{0}'.");
                
                Type initializerType =
                    builtAssembly.GetTypes().FirstOrDefault(
                        t => t.Name.Equals("ModuleInitializer", StringComparison.InvariantCultureIgnoreCase));
                
                Assert.IsNotNull(initializerType, "Could not find initializer type in instrumented assembly '{0}'.", instrumentedAssemblyLocation);
                
                // Get InitializerHit value.
                FieldInfo initializerHitField = initializerType.GetField("InitializerHit",
                                          BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
                Assert.IsNotNull(initializerHitField, "Could not find InitializerHit field on type '{0}' in instrumented assembly '{1}'.",
                                 initializerType.FullName, instrumentedAssemblyLocation);
                DateTime initializerHit = (DateTime) initializerHitField.GetValue(null);

                DateTime afterFirstAccess = DateTime.Now;
                
                // Check initializer was hit by the time the first access occurred.
                Assert.AreNotEqual(default(DateTime), initializerHit, "The initializer was not hit.");
                Assert.IsTrue(initializerHit > beforeLoad, "The initializer was hit at '{0:mm:ss.fffffff}' before the assembly was loaded at '{1:mm:ss.fffffff}'!", initializerHit, beforeLoad);
                Assert.IsTrue(initializerHit < afterFirstAccess, "The initializer was hit at '{0:mm:ss.fffffff}' after the assembly was finished loading at '{1:mm:ss.fffffff}'!", initializerHit, afterFirstAccess);

                // Get StaticConstructorHit value.
                FieldInfo staticConstructorHitField = initializerType.GetField("StaticConstructorHit",
                                          BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
                Assert.IsNotNull(staticConstructorHitField, "Could not find StaticConstructorHit field on type '{0}' in instrumented assembly '{1}'.",
                                 initializerType.FullName, instrumentedAssemblyLocation);
                DateTime staticConstructorHit = (DateTime)staticConstructorHitField.GetValue(null);

                // Check the static constructor was hit before the initializer method itself was hit.
                Assert.AreNotEqual(default(DateTime), staticConstructorHit, "The initializer static constructor was not hit.");
                Assert.IsTrue(staticConstructorHit > beforeLoad, "The initializer static constructor was hit at '{0:mm:ss.fffffff}' before the assembly was loaded at '{1:mm:ss.fffffff}'!", staticConstructorHit, beforeLoad);
                Assert.IsTrue(staticConstructorHit < initializerHit, "The initializer static constructor was hit at '{0:mm:ss.fffffff}' after the initializer method was hit at '{1:mm:ss.fffffff}'!", staticConstructorHit, initializerHit);
                Assert.IsTrue(staticConstructorHit < afterFirstAccess, "The initializer static constructor was hit at '{0:mm:ss.fffffff}' after the assembly was finished loading at '{1:mm:ss.fffffff}'!", staticConstructorHit, afterFirstAccess);
            }
            finally
            {
                // Lock for all assemblies in directory.
                string pattern = Path.ChangeExtension("*", assemblyExtension);
                foreach (string tempAssembly in Directory.GetFiles(directory, pattern))
                {
                    if (String.IsNullOrWhiteSpace(tempAssembly))
                        continue;

                    // If the assembly has a guid name we can delete it.
                    string guidStr = Path.GetFileNameWithoutExtension(tempAssembly);
                    Guid guid;
                    if (!Guid.TryParse(guidStr, out guid)) continue;

                    try
                    {
                        // Try to delete immediately
                        File.Delete(tempAssembly);
                    }
                    catch (UnauthorizedAccessException)
                    {
                        // Delete file on next reboot as locked.
                        MoveFileEx(tempAssembly, null, MoveFileFlags.MOVEFILE_DELAY_UNTIL_REBOOT);
                    }
                }
            }
        }