public void Read()
        {
            const string product = "product";
            const string productVersion = "2.0";
            const string testPurpose = "client";

            const string operatingSystemName = "Os";
            const string operatingSystemServicePack = "SP300";
            const string operatingSystemCulture = "en-US";
            const string operatingSystemPointerSize = "32";

            const string applicationName = "application";
            const string applicationVersion = "1.0";

            const string environmentName = "environment_a";
            const string notificationPath = @"c:\a\b\c\d";

            // Note that the order of the elements is purposefully out of document order to
            // test the ordering of the test steps
            var files = new Dictionary<string, string>();
            var console = BuildConsoleExecuteTestStepFunctions(3, environmentName, files);
            var msi = BuildMsiDeployTestStepFunctions(1, environmentName, files);
            var script = BuildScriptExecuteTestStepFunctions(2, environmentName, files);
            var xcopy = BuildXCopyDeployTestStepFunctions(0, environmentName, files);

            // Load the config file string
            var text = EmbeddedResourceExtracter.LoadEmbeddedTextFile(
               Assembly.GetExecutingAssembly(),
               "Sherlock.Console.Sherlock.Configuration.v12.xml");
            string configText;
            {
                var builder = new StringBuilder(text);
                builder.Replace("${NAME_OF_PRODUCT_UNDER_TEST}$", product);
                builder.Replace("${VERSION_OF_PRODUCT_UNDER_TEST}$", productVersion);
                builder.Replace("${PURPOSE_OF_TEST}$", testPurpose);

                builder.Replace("${OPERATING_SYSTEM_NAME}$", operatingSystemName);
                builder.Replace("${OPERATING_SYSTEM_SERVICE_PACK}$", operatingSystemServicePack);
                builder.Replace("${OPERATING_SYSTEM_CULTURE}$", operatingSystemCulture);
                builder.Replace("${OPERATING_SYSTEM_POINTER_SIZE}$", operatingSystemPointerSize);

                builder.Replace("${APPLICATION_NAME}$", applicationName);
                builder.Replace("${APPLICATION_VERSION}$", applicationVersion);

                console.Item1(builder);
                msi.Item1(builder);
                script.Item1(builder);
                xcopy.Item1(builder);

                builder.Replace("${NOTIFICATION_DIRECTORY_FULL_PATH_GOES_HERE}$", notificationPath);

                configText = builder.ToString();
            }

            var doc = XDocument.Parse(configText, LoadOptions.None);

            var file = new MockFile(files);
            var directory = new MockDirectory(xcopy.Item4);
            var fileSystem = new Mock<IFileSystem>();
            {
                fileSystem.Setup(f => f.File)
                    .Returns(file);
                fileSystem.Setup(f => f.Directory)
                    .Returns(directory);
            }

            StoreFileDataForEnvironment storage =
                (environment, step, name, data) =>
                {
                    switch (step)
                    {
                        case 0:
                            xcopy.Item2(environment, step, name, data);
                            break;
                        case 1:
                            msi.Item2(environment, step, name, data);
                            break;
                        case 2:
                            script.Item2(environment, step, name, data);
                            break;
                        case 3:
                            console.Item2(environment, step, name, data);
                            break;
                        default:
                            Assert.Fail();
                            break;
                    }
                };

            var reader = new ConfigurationReaderVersion12(fileSystem.Object, storage);
            var config = reader.Read(doc);
            Assert.IsNotNull(config);

            Assert.AreEqual(product, config.Test.ProductUnderTest);
            Assert.AreEqual(productVersion, config.Test.VersionOfProductUnderTest);
            Assert.AreEqual(testPurpose, config.Test.Description);

            Assert.AreEqual(1, config.Test.Environments.Count());

            var testSteps = config.Test.TestSteps.ToList();
            console.Item3(testSteps[0] as ConsoleExecuteTestStepDescription);
            msi.Item3(testSteps[1] as MsiInstallTestStepDescription);
            script.Item3(testSteps[2] as ScriptExecuteTestStepDescription);
            xcopy.Item3(testSteps[3] as XCopyTestStepDescription);

            Assert.AreEqual(notificationPath, config.Test.ReportPath);
        }
        public void SendReports()
        {
            var context = new Mock<IContextAware>();
            {
                context.Setup(c => c.IsSynchronized)
                    .Returns(true);
            }

            var command = new Mock<ICommand>();
            {
                command.Setup(c => c.Execute(It.IsAny<object>()))
                    .Verifiable();
            }

            var file = new FileInfo(Assembly.GetExecutingAssembly().LocalFilePath());

            var counter = 0;
            var collector = new Mock<ICollectFeedbackReports>();
            {
                collector.Setup(c => c.LocateFeedbackReports())
                    .Returns(
                        () =>
                        {
                            counter++;
                            var result = new List<FileInfo>();
                            if (counter <= 1)
                            {
                                result.Add(file);
                            }

                            return result;
                        });
            }

            var mockFile = new MockFile(file.FullName, "a");
            var fileSystem = new Mock<IFileSystem>();
            {
                fileSystem.Setup(f => f.File)
                    .Returns(mockFile);
            }

            var model = new ErrorReportsModel(context.Object, command.Object, collector.Object, fileSystem.Object);
            while (!model.HasErrorReports)
            {
                Thread.Yield();
            }

            var wasRaised = false;
            model.PropertyChanged += (s, e) =>
            {
                wasRaised = true;
            };

            model.SendReports(
                new[]
                    {
                        new FeedbackFileModel(context.Object, file.FullName, file.CreationTime),
                    });

            command.Verify(c => c.Execute(It.IsAny<object>()), Times.Once());

            while (!wasRaised)
            {
                Thread.Yield();
            }

            // For some reason in release mode this doesn't quite work, except if we slow things down a bit
            // so ....
            Thread.Sleep(50);
            Assert.IsFalse(model.HasErrorReports);
        }