Пример #1
0
        public async Task ConcurrentShortAndLongOperations()
        {
            // If a builder is running a short operation and a long operation is started,
            // that long operation will wait for the sort operation to finish
            // and will use the same builder, instead of starting a new one.

            await RemoteBuildEngineManager.RecycleAllBuilders();

            Assert.AreEqual(0, RemoteBuildEngineManager.ActiveEnginesCount);

            FilePath solFile = Util.GetSampleProject("builder-manager-tests", "builder-manager-tests.sln");

            using (var sol = (Solution)await Services.ProjectService.ReadWorkspaceItem(Util.GetMonitor(), solFile)) {
                var project1 = (Project)sol.Items.FirstOrDefault(p => p.Name == "SyncBuildProject");
                var project2 = (Project)sol.Items.FirstOrDefault(p => p.Name == "App");

                InitBuildSyncEvent(project1);

                // Start the first target. The FileSync target will pause until signaled to continue.
                // Select the ShortOperations build queue.
                var context = new TargetEvaluationContext {
                    BuilderQueue = BuilderQueue.ShortOperations
                };
                var build1 = project1.RunTarget(Util.GetMonitor(), "FileSync", sol.Configurations [0].Selector, context);

                // Wait for the build to reach the sync task
                await WaitForBuildSyncEvent(project1);

                Assert.AreEqual(1, RemoteBuildEngineManager.ActiveEnginesCount);

                // The build is now in progess. Run a new target.

                context = new TargetEvaluationContext {
                    BuilderQueue = BuilderQueue.LongOperations
                };
                var build2 = project2.RunTarget(Util.GetMonitor(), "QuickTarget", sol.Configurations [0].Selector, context);

                // Wait a bit. This should be enough to ensure the build has started.
                await Task.Delay(1000);

                // The RunTarget request should be queued, not new builder should be spawned
                Assert.AreEqual(1, RemoteBuildEngineManager.ActiveEnginesCount);

                // Continue building the first project
                SignalBuildToContinue(project1);

                // The first build should end now
                if (await Task.WhenAny(build1, Task.Delay(timeoutMs)) != build1)
                {
                    Assert.Fail("Build did not end in time");
                }

                // And now the second build should end
                if (await Task.WhenAny(build2, Task.Delay(timeoutMs)) != build2)
                {
                    Assert.Fail("Build did not end in time");
                }

                Assert.NotNull(build1.Result);
                Assert.NotNull(build2.Result);
                Assert.AreEqual(1, RemoteBuildEngineManager.ActiveEnginesCount);
            }
        }
Пример #2
0
        public async Task RecycleBuildersIsGraceful()
        {
            // RecycleBuilders can be called in the middle of a build and that should not interrupt
            // the build. Builders will be shut down when the build is finished.

            var currentDelay = RemoteBuildEngineManager.EngineDisposalDelay;

            try {
                RemoteBuildEngineManager.EngineDisposalDelay = 400;

                await RemoteBuildEngineManager.RecycleAllBuilders();

                Assert.AreEqual(0, RemoteBuildEngineManager.ActiveEnginesCount);

                FilePath solFile = Util.GetSampleProject("builder-manager-tests", "builder-manager-tests.sln");
                using (var sol = (Solution)await Services.ProjectService.ReadWorkspaceItem(Util.GetMonitor(), solFile)) {
                    var project1 = sol.Items.FirstOrDefault(p => p.Name == "SyncBuildProject");
                    var project2 = sol.Items.FirstOrDefault(p => p.Name == "App");

                    InitBuildSyncEvent(project1);

                    // Start the build
                    var build1 = project1.Build(Util.GetMonitor(), sol.Configurations [0].Selector);

                    // Wait for the build to reach the sync task
                    await WaitForBuildSyncEvent(project1);

                    Assert.AreEqual(1, RemoteBuildEngineManager.ActiveEnginesCount);
                    Assert.AreEqual(0, await RemoteBuildEngineManager.CountActiveBuildersForProject(project2.FileName));

                    // The build is now in progess. Start a new build

                    var build2 = project2.Build(Util.GetMonitor(), sol.Configurations [0].Selector);
                    if (await Task.WhenAny(build2, Task.Delay(5000)) != build2)
                    {
                        Assert.Fail("Build did not start");
                    }

                    // There should be one builder for each build

                    Assert.AreEqual(2, RemoteBuildEngineManager.ActiveEnginesCount);
                    Assert.AreEqual(2, RemoteBuildEngineManager.EnginesCount);

                    // Ask for all active builders to be shut down

                    await RemoteBuildEngineManager.RecycleAllBuilders();

                    // There is a build in progress, so there must still be one engine running

                    Assert.AreEqual(0, RemoteBuildEngineManager.ActiveEnginesCount);
                    Assert.AreEqual(1, RemoteBuildEngineManager.EnginesCount);

                    SignalBuildToContinue(project1);

                    if (await Task.WhenAny(build1, Task.Delay(5000)) != build1)
                    {
                        Assert.Fail("Build did not end in time");
                    }

                    Assert.AreEqual(0, build1.Result.ErrorCount);
                    Assert.AreEqual(0, build2.Result.ErrorCount);

                    // The builder that was running the build and was shutdown should be immediately stopped after build finishes
                    Assert.AreEqual(0, RemoteBuildEngineManager.ActiveEnginesCount);
                    Assert.AreEqual(0, RemoteBuildEngineManager.EnginesCount);
                }
            } finally {
                RemoteBuildEngineManager.EngineDisposalDelay = currentDelay;
            }
        }
Пример #3
0
        public async Task ExtraBuilderAutoShutdown()
        {
            // When an extra builder is created, it should be shut down when it is
            // not used anymore, after a delay

            var currentDelay = RemoteBuildEngineManager.EngineDisposalDelay;

            try {
                RemoteBuildEngineManager.EngineDisposalDelay = 400;

                await RemoteBuildEngineManager.RecycleAllBuilders();

                Assert.AreEqual(0, RemoteBuildEngineManager.ActiveEnginesCount);

                FilePath solFile = Util.GetSampleProject("builder-manager-tests", "builder-manager-tests.sln");
                using (var sol = (Solution)await Services.ProjectService.ReadWorkspaceItem(Util.GetMonitor(), solFile)) {
                    var project1 = sol.Items.FirstOrDefault(p => p.Name == "SyncBuildProject");
                    var project2 = sol.Items.FirstOrDefault(p => p.Name == "App");

                    InitBuildSyncEvent(project1);

                    // Start the build
                    var build1 = project1.Build(Util.GetMonitor(), sol.Configurations [0].Selector);

                    // Wait for the build to reach the sync task
                    await WaitForBuildSyncEvent(project1);

                    Assert.AreEqual(1, RemoteBuildEngineManager.ActiveEnginesCount);
                    Assert.AreEqual(0, await RemoteBuildEngineManager.CountActiveBuildersForProject(project2.FileName));

                    // The build is now in progess. Start a new build

                    var build2 = project2.Build(Util.GetMonitor(), sol.Configurations [0].Selector);
                    if (await Task.WhenAny(build2, Task.Delay(5000)) != build2)
                    {
                        Assert.Fail("Build did not start");
                    }

                    Assert.AreEqual(2, RemoteBuildEngineManager.ActiveEnginesCount);
                    Assert.AreEqual(1, await RemoteBuildEngineManager.CountActiveBuildersForProject(project2.FileName));

                    // Build engine disposal delay is set to 400ms, so it should be gone after waiting the following wait.

                    await Task.Delay(500);

                    // The second builder should now be gone

                    Assert.AreEqual(1, RemoteBuildEngineManager.ActiveEnginesCount);
                    Assert.AreEqual(1, RemoteBuildEngineManager.EnginesCount);
                    Assert.AreEqual(0, await RemoteBuildEngineManager.CountActiveBuildersForProject(project2.FileName));

                    SignalBuildToContinue(project1);

                    if (await Task.WhenAny(build1, Task.Delay(5000)) != build1)
                    {
                        Assert.Fail("Build did not end in time");
                    }
                }
            } finally {
                RemoteBuildEngineManager.EngineDisposalDelay = currentDelay;
            }
        }
Пример #4
0
        public async Task AtLeastOneBuilderPersolution()
        {
            // There should always be at least one builder running per solution,
            // until the solution is explicitly disposed

            var currentDelay = RemoteBuildEngineManager.EngineDisposalDelay;

            try {
                RemoteBuildEngineManager.EngineDisposalDelay = 400;

                await RemoteBuildEngineManager.RecycleAllBuilders();

                Assert.AreEqual(0, RemoteBuildEngineManager.ActiveEnginesCount);

                FilePath solFile = Util.GetSampleProject("builder-manager-tests", "builder-manager-tests.sln");
                using (var sol = (Solution)await Services.ProjectService.ReadWorkspaceItem(Util.GetMonitor(), solFile)) {
                    var project1 = sol.Items.FirstOrDefault(p => p.Name == "SyncBuildProject");
                    var project2 = sol.Items.FirstOrDefault(p => p.Name == "App");

                    InitBuildSyncEvent(project1);

                    // Start the build
                    var build1 = project1.Build(Util.GetMonitor(), sol.Configurations [0].Selector);

                    // Wait for the build to reach the sync task
                    await WaitForBuildSyncEvent(project1);

                    Assert.AreEqual(1, RemoteBuildEngineManager.ActiveEnginesCount);
                    Assert.AreEqual(0, await RemoteBuildEngineManager.CountActiveBuildersForProject(project2.FileName));

                    // The build is now in progess. Start a new build

                    var build2 = project2.Build(Util.GetMonitor(), sol.Configurations [0].Selector);

                    if (await Task.WhenAny(build2, Task.Delay(5000)) != build2)
                    {
                        Assert.Fail("Build did not start");
                    }

                    Assert.AreEqual(2, RemoteBuildEngineManager.ActiveEnginesCount);
                    Assert.AreEqual(2, RemoteBuildEngineManager.EnginesCount);
                    Assert.AreEqual(1, await RemoteBuildEngineManager.CountActiveBuildersForProject(project1.FileName));
                    Assert.AreEqual(1, await RemoteBuildEngineManager.CountActiveBuildersForProject(project2.FileName));

                    SignalBuildToContinue(project1);

                    if (await Task.WhenAny(build1, Task.Delay(5000)) != build1)
                    {
                        Assert.Fail("Build did not end in time");
                    }

                    // Build engine disposal delay is set to 400ms, so unused
                    // builders should go away after a 500ms wait.

                    await Task.Delay(500);

                    // There should be at least one builder left

                    Assert.AreEqual(1, RemoteBuildEngineManager.ActiveEnginesCount);
                    Assert.AreEqual(1, RemoteBuildEngineManager.EnginesCount);
                }
            } finally {
                RemoteBuildEngineManager.EngineDisposalDelay = currentDelay;
            }

            // All builders should be gone now
            Assert.AreEqual(0, RemoteBuildEngineManager.ActiveEnginesCount);
            Assert.AreEqual(0, RemoteBuildEngineManager.EnginesCount);
        }
Пример #5
0
        public async Task ReloadProject_ProjectDisposedWhilstTargetRunning_AnotherTargetRun()
        {
            // When a long operation is running and a new one is requested, a new builder is created

            await RemoteBuildEngineManager.RecycleAllBuilders();

            Assert.AreEqual(0, RemoteBuildEngineManager.ActiveEnginesCount);

            FilePath solFile = Util.GetSampleProject("builder-manager-tests", "builder-manager-tests.sln");

            using (var sol = (Solution)await Services.ProjectService.ReadWorkspaceItem(Util.GetMonitor(), solFile)) {
                var project1 = (Project)sol.Items.FirstOrDefault(p => p.Name == "SyncBuildProject");

                InitBuildSyncEvent(project1);

                // Start the build. Use RunTargetInternal to avoid the BindTask which will cancel the build on project dispose.
                var build1 = project1.RunTargetInternal(Util.GetMonitor(), "Build", sol.Configurations [0].Selector);

                // Wait for the build to reach the sync task
                await WaitForBuildSyncEvent(project1);

                // The build is now in progess. Simulate reloading the project.
                using (var project2 = (Project)await sol.RootFolder.ReloadItem(Util.GetMonitor(), project1)) {
                    var builderTask = project2.GetProjectBuilder(CancellationToken.None, null, allowBusy: true);

                    // Allow second build to finish and dispose its project builder. Have to do this here otherwise
                    // GetProjectBuilder will hang waiting for a connection response back after it creates a new
                    // project builder.
                    SignalBuildToContinue(project1);
                    if (await Task.WhenAny(build1, Task.Delay(timeoutMs)) != build1)
                    {
                        Assert.Fail("Build did not end in time");
                    }

                    Assert.AreEqual(0, build1.Result.BuildResult.ErrorCount);

                    using (var builder = await builderTask) {
                        // Sanity check. We should only have one project builder and one build engine.
                        Assert.AreEqual(1, RemoteBuildEngineManager.ActiveEnginesCount);
                        Assert.AreEqual(1, RemoteBuildEngineManager.EnginesCount);
                        Assert.AreEqual(1, await RemoteBuildEngineManager.CountActiveBuildersForProject(project2.FileName));

                        var configs = project2.GetConfigurations(sol.Configurations [0].Selector, false);

                        var build2 = await Task.Run(() => {
                            // Previously this would throw a NullReferenceException since the builder has been disposed
                            // and the engine is null.
                            return(builder.Run(
                                       configs,
                                       new StringWriter(),
                                       new MSBuildLogger(),
                                       MSBuildVerbosity.Quiet,
                                       new [] { "ResolveAssemblyReferences" },
                                       new string [0],
                                       new string [0],
                                       new System.Collections.Generic.Dictionary <string, string> (),
                                       CancellationToken.None));
                        });

                        Assert.AreEqual(0, build2.Errors.Length);
                    }
                }
            }
        }