internal static OrleansTaskScheduler InitializeSchedulerForTesting( ISchedulingContext context, ILoggerFactory loggerFactory) { var services = new ServiceCollection(); services.AddOptions(); services.AddLogging(); services.AddSingleton <ExecutorService>(); services.AddSingleton <SchedulerStatisticsGroup>(); services.AddSingleton <StageAnalysisStatisticsGroup>(); services.AddSingleton(loggerFactory); services.Configure <SchedulingOptions>(options => { options.MaxActiveThreads = 4; options.DelayWarningThreshold = TimeSpan.FromMilliseconds(100); options.ActivationSchedulingQuantum = TimeSpan.FromMilliseconds(100); options.TurnWarningLengthThreshold = TimeSpan.FromMilliseconds(100); options.StoppedActivationWarningInterval = TimeSpan.FromMilliseconds(200); }); var serviceProvider = services.BuildServiceProvider(); var scheduler = ActivatorUtilities.CreateInstance <OrleansTaskScheduler>(serviceProvider); scheduler.Start(); WorkItemGroup ignore = scheduler.RegisterWorkContext(context); return(scheduler); }
public async Task Can_delete_resource_with_OneToOne_relationship_from_dependent_side() { // Arrange RgbColor existingColor = _fakers.RgbColor.Generate(); existingColor.Group = _fakers.WorkItemGroup.Generate(); await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.RgbColors.Add(existingColor); await dbContext.SaveChangesAsync(); }); string route = "/rgbColors/" + existingColor.StringId; // Act (HttpResponseMessage httpResponse, string responseDocument) = await _testContext.ExecuteDeleteAsync <string>(route); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.NoContent); responseDocument.Should().BeEmpty(); await _testContext.RunOnDatabaseAsync(async dbContext => { RgbColor colorsInDatabase = await dbContext.RgbColors.FirstWithIdOrDefaultAsync(existingColor.Id); colorsInDatabase.Should().BeNull(); WorkItemGroup groupInDatabase = await dbContext.Groups.FirstWithIdAsync(existingColor.Group.Id); groupInDatabase.Color.Should().BeNull(); }); }
internal static WorkItemGroup CreateWorkItemGroupForTesting( IGrainContext context, ILoggerFactory loggerFactory) { var services = new ServiceCollection(); services.AddOptions(); services.AddLogging(); services.AddSingleton <SchedulerStatisticsGroup>(); services.AddSingleton <StageAnalysisStatisticsGroup>(); services.AddSingleton(loggerFactory); services.Configure <SchedulingOptions>(options => { options.DelayWarningThreshold = TimeSpan.FromMilliseconds(100); options.ActivationSchedulingQuantum = TimeSpan.FromMilliseconds(100); options.TurnWarningLengthThreshold = TimeSpan.FromMilliseconds(100); options.StoppedActivationWarningInterval = TimeSpan.FromMilliseconds(200); }); var s = services.BuildServiceProvider(); var result = new WorkItemGroup( context, s.GetRequiredService <ILogger <WorkItemGroup> >(), s.GetRequiredService <ILogger <ActivationTaskScheduler> >(), s.GetRequiredService <SchedulerStatisticsGroup>(), s.GetRequiredService <IOptions <StatisticsOptions> >(), s.GetRequiredService <IOptions <SchedulingOptions> >()); return(result); }
public async Task Can_create_OneToOne_relationship_from_principal_side() { // Arrange WorkItemGroup existingGroup = _fakers.WorkItemGroup.Generate(); existingGroup.Color = _fakers.RgbColor.Generate(); await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.Groups.Add(existingGroup); await dbContext.SaveChangesAsync(); }); var requestBody = new { data = new { type = "workItemGroups", relationships = new { color = new { data = new { type = "rgbColors", id = existingGroup.Color.StringId } } } } }; const string route = "/workItemGroups"; // Act (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAsync <Document>(route, requestBody); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.Created); responseDocument.SingleData.Should().NotBeNull(); responseDocument.SingleData.Attributes.Should().NotBeEmpty(); responseDocument.SingleData.Relationships.Should().NotBeEmpty(); string newGroupId = responseDocument.SingleData.Id; newGroupId.Should().NotBeNullOrEmpty(); await _testContext.RunOnDatabaseAsync(async dbContext => { List <WorkItemGroup> groupsInDatabase = await dbContext.Groups.Include(group => group.Color).ToListAsync(); WorkItemGroup newGroupInDatabase = groupsInDatabase.Single(group => group.StringId == newGroupId); newGroupInDatabase.Color.Should().NotBeNull(); newGroupInDatabase.Color.Id.Should().Be(existingGroup.Color.Id); WorkItemGroup existingGroupInDatabase = groupsInDatabase.Single(group => group.Id == existingGroup.Id); existingGroupInDatabase.Color.Should().BeNull(); }); }
public void SiloStatusChangeNotification(SiloAddress updatedSilo, SiloStatus status) { WorkItemGroup.QueueAction(() => { Utils.SafeExecute(() => this.OnSiloStatusChange(updatedSilo, status), this.logger); }); }
public async Task Can_clear_OneToOne_relationship() { // Arrange WorkItemGroup existingGroup = _fakers.WorkItemGroup.Generate(); existingGroup.Color = _fakers.RgbColor.Generate(); await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.Groups.AddRange(existingGroup); await dbContext.SaveChangesAsync(); }); var requestBody = new { data = (object)null }; string route = $"/workItemGroups/{existingGroup.StringId}/relationships/color"; // Act (HttpResponseMessage httpResponse, string responseDocument) = await _testContext.ExecutePatchAsync <string>(route, requestBody); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.NoContent); responseDocument.Should().BeEmpty(); await _testContext.RunOnDatabaseAsync(async dbContext => { WorkItemGroup groupInDatabase = await dbContext.Groups.Include(group => group.Color).FirstWithIdOrDefaultAsync(existingGroup.Id); groupInDatabase.Color.Should().BeNull(); }); }
public async Task Can_create_OneToOne_relationship_from_principal_side() { // Arrange WorkItemGroup existingGroup = _fakers.WorkItemGroup.Generate(); existingGroup.Color = _fakers.RgbColor.Generate(); RgbColor existingColor = _fakers.RgbColor.Generate(); await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.AddRange(existingGroup, existingColor); await dbContext.SaveChangesAsync(); }); var requestBody = new { data = new { type = "workItemGroups", id = existingGroup.StringId, relationships = new { color = new { data = new { type = "rgbColors", id = existingColor.StringId } } } } }; string route = "/workItemGroups/" + existingGroup.StringId; // Act (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePatchAsync <Document>(route, requestBody); httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); responseDocument.SingleData.Should().NotBeNull(); await _testContext.RunOnDatabaseAsync(async dbContext => { List <RgbColor> colorsInDatabase = await dbContext.RgbColors.Include(rgbColor => rgbColor.Group).ToListAsync(); RgbColor colorInDatabase1 = colorsInDatabase.Single(color => color.Id == existingGroup.Color.Id); colorInDatabase1.Group.Should().BeNull(); RgbColor colorInDatabase2 = colorsInDatabase.Single(color => color.Id == existingColor.Id); colorInDatabase2.Group.Should().NotBeNull(); colorInDatabase2.Group.Id.Should().Be(existingGroup.Id); }); }
public async Task Sched_Stopped_WorkItemGroup() { var context = new UnitTestSchedulingContext(); var scheduler = this.orleansTaskScheduler = TestInternalHelper.InitializeSchedulerForTesting(context, this.loggerFactory); WorkItemGroup workItemGroup = this.orleansTaskScheduler.GetWorkItemGroup(context); void CheckScheduler(object state) { Assert.IsType <string>(state); Assert.Equal("some state", state as string); Assert.IsType <ActivationTaskScheduler>(TaskScheduler.Current); } Task <Task> ScheduleTask() => Task.Factory.StartNew( state => { CheckScheduler(state); return(Task.Factory.StartNew( async s => { CheckScheduler(s); await Task.Delay(50); CheckScheduler(s); }, state).Unwrap()); }, "some state", CancellationToken.None, TaskCreationOptions.DenyChildAttach, workItemGroup.TaskScheduler); // Check that the WorkItemGroup is functioning. await await ScheduleTask(); workItemGroup.Stop(); var taskAfterStopped = ScheduleTask(); var resultTask = await Task.WhenAny(taskAfterStopped, Task.Delay(TimeSpan.FromSeconds(10))); Assert.Same(taskAfterStopped, resultTask); await await taskAfterStopped; // Wait for the WorkItemGroup to upgrade the warning to an error and try again. // This delay is based upon SchedulingOptions.StoppedActivationWarningInterval. await Task.Delay(TimeSpan.FromMilliseconds(300)); taskAfterStopped = ScheduleTask(); resultTask = await Task.WhenAny(taskAfterStopped, Task.Delay(TimeSpan.FromSeconds(10))); Assert.Same(taskAfterStopped, resultTask); await await taskAfterStopped; }
internal static OrleansTaskScheduler InitializeSchedulerForTesting(ISchedulingContext context, ICorePerformanceMetrics performanceMetrics, ILoggerFactory loggerFactory) { StatisticsCollector.StatisticsCollectionLevel = StatisticsLevel.Info; SchedulerStatisticsGroup.Init(loggerFactory); var scheduler = OrleansTaskScheduler.CreateTestInstance(4, performanceMetrics, loggerFactory); scheduler.Start(); WorkItemGroup ignore = scheduler.RegisterWorkContext(context); return(scheduler); }
public async Task Can_replace_OneToOne_relationship_from_principal_side() { // Arrange List <WorkItemGroup> existingGroups = _fakers.WorkItemGroup.Generate(2); existingGroups[0].Color = _fakers.RgbColor.Generate(); existingGroups[1].Color = _fakers.RgbColor.Generate(); await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.Groups.AddRange(existingGroups); await dbContext.SaveChangesAsync(); }); var requestBody = new { data = new { type = "rgbColors", id = existingGroups[0].Color.StringId } }; string route = $"/workItemGroups/{existingGroups[1].StringId}/relationships/color"; // Act (HttpResponseMessage httpResponse, string responseDocument) = await _testContext.ExecutePatchAsync <string>(route, requestBody); httpResponse.Should().HaveStatusCode(HttpStatusCode.NoContent); responseDocument.Should().BeEmpty(); await _testContext.RunOnDatabaseAsync(async dbContext => { List <WorkItemGroup> groupsInDatabase = await dbContext.Groups.Include(group => group.Color).ToListAsync(); WorkItemGroup groupInDatabase1 = groupsInDatabase.Single(group => group.Id == existingGroups[0].Id); groupInDatabase1.Color.Should().BeNull(); WorkItemGroup groupInDatabase2 = groupsInDatabase.Single(group => group.Id == existingGroups[1].Id); groupInDatabase2.Color.Should().NotBeNull(); groupInDatabase2.Color.Id.Should().Be(existingGroups[0].Color.Id); List <RgbColor> colorsInDatabase = await dbContext.RgbColors.Include(color => color.Group).ToListAsync(); RgbColor colorInDatabase1 = colorsInDatabase.Single(color => color.Id == existingGroups[0].Color.Id); colorInDatabase1.Group.Should().NotBeNull(); colorInDatabase1.Group.Id.Should().Be(existingGroups[1].Id); RgbColor colorInDatabase2 = colorsInDatabase.SingleOrDefault(color => color.Id == existingGroups[1].Color.Id); colorInDatabase1.Should().NotBeNull(); colorInDatabase2 !.Group.Should().BeNull(); }); }
internal static OrleansTaskScheduler InitializeSchedulerForTesting(ISchedulingContext context) { StatisticsCollector.StatisticsCollectionLevel = StatisticsLevel.Info; SchedulerStatisticsGroup.Init(); var scheduler = OrleansTaskScheduler.CreateTestInstance(4); scheduler.Start(); WorkItemGroup ignore = scheduler.RegisterWorkContext(context); return(scheduler); }
public async Task Can_partially_update_resource_with_guid_ID() { // Arrange WorkItemGroup existingGroup = _fakers.WorkItemGroup.Generate(); string newName = _fakers.WorkItemGroup.Generate().Name; await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.Groups.Add(existingGroup); await dbContext.SaveChangesAsync(); }); var requestBody = new { data = new { type = "workItemGroups", id = existingGroup.StringId, attributes = new { name = newName } } }; string route = "/workItemGroups/" + existingGroup.StringId; // Act (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePatchAsync <Document>(route, requestBody); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); responseDocument.SingleData.Should().NotBeNull(); responseDocument.SingleData.Type.Should().Be("workItemGroups"); responseDocument.SingleData.Id.Should().Be(existingGroup.StringId); responseDocument.SingleData.Attributes["name"].Should().Be(newName); responseDocument.SingleData.Attributes["isPublic"].Should().Be(existingGroup.IsPublic); responseDocument.SingleData.Relationships.Should().NotBeEmpty(); await _testContext.RunOnDatabaseAsync(async dbContext => { WorkItemGroup groupInDatabase = await dbContext.Groups.FirstWithIdAsync(existingGroup.Id); groupInDatabase.Name.Should().Be(newName); groupInDatabase.IsPublic.Should().Be(existingGroup.IsPublic); }); PropertyInfo property = typeof(WorkItemGroup).GetProperty(nameof(Identifiable.Id)); property.Should().NotBeNull().And.Subject.PropertyType.Should().Be(typeof(Guid)); }
/// <summary> /// Send a ping to a remote silo via an intermediary silo. This is intended to be called from a <see cref="SiloHealthMonitor"/> /// in order to initiate the call from the <see cref="MembershipSystemTarget"/>'s context /// </summary> /// <param name="intermediary">The intermediary which will directly probe the target.</param> /// <param name="target">The target which will be probed.</param> /// <param name="probeTimeout">The timeout for the eventual direct probe request.</param> /// <param name="probeNumber">The probe number, for diagnostic purposes.</param> /// <returns>The result of pinging the remote silo.</returns> public Task <IndirectProbeResponse> ProbeRemoteSiloIndirectly(SiloAddress intermediary, SiloAddress target, TimeSpan probeTimeout, int probeNumber) { Task <IndirectProbeResponse> ProbeIndirectly() { var remoteOracle = this.grainFactory.GetSystemTarget <IMembershipService>(Constants.MembershipServiceType, intermediary); return(remoteOracle.ProbeIndirectly(target, probeTimeout, probeNumber)); } var workItem = new AsyncClosureWorkItem <IndirectProbeResponse>(ProbeIndirectly, this); WorkItemGroup.QueueWorkItem(workItem); return(workItem.Task); }
public async Task Can_create_resource_with_client_generated_guid_ID_having_side_effects_with_fieldset() { // Arrange WorkItemGroup newGroup = _fakers.WorkItemGroup.Generate(); newGroup.Id = Guid.NewGuid(); var requestBody = new { data = new { type = "workItemGroups", id = newGroup.StringId, attributes = new { name = newGroup.Name } } }; const string route = "/workItemGroups?fields[workItemGroups]=name"; // Act (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAsync <Document>(route, requestBody); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.Created); responseDocument.SingleData.Should().NotBeNull(); responseDocument.SingleData.Type.Should().Be("workItemGroups"); responseDocument.SingleData.Id.Should().Be(newGroup.StringId); responseDocument.SingleData.Attributes.Should().HaveCount(1); responseDocument.SingleData.Attributes["name"].Should().Be(newGroup.Name); responseDocument.SingleData.Relationships.Should().BeNull(); await _testContext.RunOnDatabaseAsync(async dbContext => { WorkItemGroup groupInDatabase = await dbContext.Groups.FirstWithIdAsync(newGroup.Id); groupInDatabase.Name.Should().Be(newGroup.Name); }); PropertyInfo property = typeof(WorkItemGroup).GetProperty(nameof(Identifiable.Id)); property.Should().NotBeNull().And.Subject.PropertyType.Should().Be(typeof(Guid)); }
private async Task <bool> MoveWorkItemGroup(WorkItemGroup group, string processId, string witRefName, string pageId, string sectionId, string oldSectionId, string oldPageId = null) { var client = GetHttpClient( $"{new UriBuilder(Options.Organisation)}/_apis/work/processes" + $"/{processId}/workItemTypes/{witRefName}/layout/pages/{pageId}/sections/{sectionId}/groups/{group.Id}?" + $"removeFromSectionId={oldSectionId}" + (!string.IsNullOrEmpty(oldPageId) ? $"&removeFromPageId={oldPageId}" : ""), "api-version=6.1-preview.1"); DefaultContractResolver contractResolver = new DefaultContractResolver { NamingStrategy = new CamelCaseNamingStrategy() }; var jsonSettings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, ContractResolver = contractResolver, }; string body = JsonConvert.SerializeObject(group.ToJson(), jsonSettings); var content = new StringContent(body, Encoding.UTF8, "application/json"); var result = await client.PutAsync(client.BaseAddress, content); var responseContent = await result.Content.ReadAsStringAsync(); if (result.StatusCode != HttpStatusCode.OK && result.StatusCode != HttpStatusCode.Created) { Log.LogError("Error moving {DefinitionType}: {processId}::{witRefName}::{pageId}::{sectionId}::{groupLabel}. Please migrate it manually. \r\nUrl: PUT {Url}\r\n{ErrorText}", typeof(WorkItemGroup).Name, processId, witRefName, pageId, sectionId, group.Label, result.RequestMessage.RequestUri.ToString(), responseContent); return(false); } else { var targetObject = JsonConvert.DeserializeObject <WorkItemGroup>(responseContent); group.Id = targetObject.Id; return(true); } }
public void Sched_Task_Turn_Execution_Order() { // A unit test that checks that any turn is indeed run till completion before any other turn? // For example, you have a long running main turn and in the middle it spawns a lot of short CWs (on Done promise) and StartNew. // You test that no CW/StartNew runs until the main turn is fully done. And run in stress. UnitTestSchedulingContext context = new UnitTestSchedulingContext(); OrleansTaskScheduler masterScheduler = this.orleansTaskScheduler = TestInternalHelper.InitializeSchedulerForTesting(context, this.performanceMetrics, this.loggerFactory); WorkItemGroup workItemGroup = this.orleansTaskScheduler.GetWorkItemGroup(context); ActivationTaskScheduler activationScheduler = workItemGroup.TaskRunner; this.mainDone = false; this.stageNum1 = this.stageNum2 = 0; var result1 = new TaskCompletionSource <bool>(); var result2 = new TaskCompletionSource <bool>(); Task wrapper = null; Task finalTask1 = null; Task finalPromise2 = null; masterScheduler.QueueWorkItem(new ClosureWorkItem(() => { Log(1, "Outer ClosureWorkItem " + Task.CurrentId + " starting"); Assert.Equal(activationScheduler, TaskScheduler.Current); // "TaskScheduler.Current #0" Log(2, "Starting wrapper Task"); wrapper = Task.Factory.StartNew(() => { Log(3, "Inside wrapper Task Id=" + Task.CurrentId); Assert.Equal(activationScheduler, TaskScheduler.Current); // "TaskScheduler.Current #1" // Execution chain #1 Log(4, "Wrapper Task Id=" + Task.CurrentId + " creating Task chain"); Task task1 = Task.Factory.StartNew(() => { Log(5, "#11 Inside sub-Task Id=" + Task.CurrentId); Assert.Equal(activationScheduler, TaskScheduler.Current); // "TaskScheduler.Current #11" SubProcess1(11); }); Task task2 = task1.ContinueWith((Task task) => { Log(6, "#12 Inside continuation Task Id=" + Task.CurrentId); Assert.Equal(activationScheduler, TaskScheduler.Current); // "TaskScheduler.Current #12" if (task.IsFaulted) { throw task.Exception.Flatten(); } SubProcess1(12); }); Task task3 = task2.ContinueWith(task => { Log(7, "#13 Inside continuation Task Id=" + Task.CurrentId); Assert.Equal(activationScheduler, TaskScheduler.Current); // "TaskScheduler.Current #13" if (task.IsFaulted) { throw task.Exception.Flatten(); } SubProcess1(13); }); finalTask1 = task3.ContinueWith(task => { Log(8, "#14 Inside final continuation Task Id=" + Task.CurrentId); Assert.Equal(activationScheduler, TaskScheduler.Current); // "TaskScheduler.Current #14" if (task.IsFaulted) { throw task.Exception.Flatten(); } SubProcess1(14); result1.SetResult(true); }); // Execution chain #2 Log(9, "Wrapper Task " + Task.CurrentId + " creating AC chain"); Task promise2 = Task.Factory.StartNew(() => { Log(10, "#21 Inside sub-Task Id=" + Task.CurrentId); Assert.Equal(activationScheduler, TaskScheduler.Current); // "TaskScheduler.Current #21" SubProcess2(21); }); finalPromise2 = promise2.ContinueWith((_) => { Log(11, "#22 Inside final continuation Task Id=" + Task.CurrentId); Assert.Equal(activationScheduler, TaskScheduler.Current); // "TaskScheduler.Current #22" SubProcess2(22); result2.SetResult(true); }); finalPromise2.Ignore(); Log(12, "Wrapper Task Id=" + Task.CurrentId + " sleeping #2"); Thread.Sleep(TimeSpan.FromSeconds(1)); Log(13, "Wrapper Task Id=" + Task.CurrentId + " finished"); }); Log(14, "Outer ClosureWorkItem Task Id=" + Task.CurrentId + " sleeping"); Thread.Sleep(TimeSpan.FromSeconds(1)); Log(15, "Outer ClosureWorkItem Task Id=" + Task.CurrentId + " awake"); Log(16, "Finished Outer ClosureWorkItem Task Id=" + wrapper.Id); this.mainDone = true; }), context); Log(17, "Waiting for ClosureWorkItem to spawn wrapper Task"); for (int i = 0; i < 5 * WaitFactor; i++) { if (wrapper != null) { break; } Thread.Sleep(TimeSpan.FromSeconds(1).Multiply(WaitFactor)); } Assert.NotNull(wrapper); // Wrapper Task was not created Log(18, "Waiting for wrapper Task Id=" + wrapper.Id + " to complete"); bool finished = wrapper.Wait(TimeSpan.FromSeconds(4 * WaitFactor)); Log(19, "Done waiting for wrapper Task Id=" + wrapper.Id + " Finished=" + finished); if (!finished) { throw new TimeoutException(); } Assert.False(wrapper.IsFaulted, "Wrapper Task faulted: " + wrapper.Exception); Assert.True(wrapper.IsCompleted, "Wrapper Task should be completed"); Log(20, "Waiting for TaskWorkItem to complete"); for (int i = 0; i < 15 * WaitFactor; i++) { if (this.mainDone) { break; } Thread.Sleep(1000 * WaitFactor); } Log(21, "Done waiting for TaskWorkItem to complete MainDone=" + this.mainDone); Assert.True(this.mainDone, "Main Task should be completed"); Assert.NotNull(finalTask1); // Task chain #1 not created Assert.NotNull(finalPromise2); // Task chain #2 not created Log(22, "Waiting for final task #1 to complete"); bool ok = finalTask1.Wait(TimeSpan.FromSeconds(4 * WaitFactor)); Log(23, "Done waiting for final task #1 complete Ok=" + ok); if (!ok) { throw new TimeoutException(); } Assert.False(finalTask1.IsFaulted, "Final Task faulted: " + finalTask1.Exception); Assert.True(finalTask1.IsCompleted, "Final Task completed"); Assert.True(result1.Task.Result, "Timeout-1"); Log(24, "Waiting for final promise #2 to complete"); finalPromise2.Wait(TimeSpan.FromSeconds(4 * WaitFactor)); Log(25, "Done waiting for final promise #2"); Assert.False(finalPromise2.IsFaulted, "Final Task faulted: " + finalPromise2.Exception); Assert.True(finalPromise2.IsCompleted, "Final Task completed"); Assert.True(result2.Task.Result, "Timeout-2"); Assert.NotEqual(0, this.stageNum1); // "Work items did not get executed-1" Assert.Equal(14, this.stageNum1); // "Work items executed out of order-1" Assert.NotEqual(0, this.stageNum2); // "Work items did not get executed-2" Assert.Equal(22, this.stageNum2); // "Work items executed out of order-2" }
/// <summary> /// Move a work item group from one Layout->Section to another Layout->Section /// </summary> /// <param name="group"></param> /// <param name="processId"></param> /// <param name="witRefName"></param> /// <param name="pageId"></param> /// <param name="sectionId"></param> /// <param name="oldSectionId"></param> /// <returns></returns> public async Task <bool> MoveWorkItemGroupWithinPage(WorkItemGroup group, string processId, string witRefName, string pageId, string sectionId, string oldSectionId) { return(await MoveWorkItemGroup(group, processId, witRefName, pageId, sectionId, oldSectionId)); }
private async Task SyncWorkItemType(WorkItemTypeModel sourceWit, string processId) { var targetWit = TargetModel.WorkItemTypes.ContainsKey(sourceWit.WorkItemType.Id) ? TargetModel.WorkItemTypes[sourceWit.WorkItemType.Id] : new(); targetWit.WorkItemType = await Target.SyncDefinition(sourceWit.WorkItemType, targetWit.WorkItemType, processId); foreach (var state in sourceWit.States.Where(x => x.CustomizationType == "custom")) { if (state.StateCategory == "Completed") { Log.LogWarning("Cannot modify [Completed] category on work item state [{0}] on wit type [{1}].", state.Name, sourceWit.WorkItemType.ReferenceName); } else { await SyncDefinitionType <WorkItemState>( TargetModel.WorkItemStates, state, TargetModel.WorkItemStates.Values.FirstOrDefault(x => x.Name == state.Name), processId, targetWit.WorkItemType.ReferenceName); } } foreach (var field in sourceWit.Fields) { var existingField = TargetModel.WorkItemFields.Values.FirstOrDefault(x => x.ReferenceName == field.ReferenceName); //if (existingField == null || (existingField != null && field.Customization != "system")) // I don't think you can modify //{ await SyncDefinitionType <WorkItemTypeField>( TargetModel.WorkItemFields, field, existingField, processId, targetWit.WorkItemType.ReferenceName); //} } foreach (var rule in sourceWit.Rules) { await SyncDefinitionType <WorkItemRule>( TargetModel.WorkItemRules, rule, TargetModel.WorkItemRules.Values.FirstOrDefault(x => x.Name == rule.Name), processId, targetWit.WorkItemType.ReferenceName); } foreach (var behavior in sourceWit.Behaviors) { await SyncDefinitionType <WorkItemTypeBehavior>( TargetModel.WorkItemTypeBehaviors, behavior, targetWit.Behaviors.FirstOrDefault(x => x.Id == behavior.Id), processId, targetWit.WorkItemType.ReferenceName); } #region Sync Pages ... // Making sure the pages themselves are in sync is not too hard.. let's do them first foreach (var page in sourceWit.Layout.Pages) { var targetPage = targetWit.Layout.Pages .FirstOrDefault(x => x.Label.Equals(page.Label, StringComparison.OrdinalIgnoreCase)); await SyncDefinitionType <WorkItemPage>( TargetModel.WorkItemPages, page, targetPage, processId, targetWit.WorkItemType.ReferenceName); } #endregion #region Sync Sections and Groups ... // Now that we know all the target pages are present we can iterate over what resides on pages.. // A page's constituent parts is a little more difficult because you need to support an existing // group or control that has moved pages, sections or groups foreach (var sourcePage in sourceWit.Layout.Pages) { var targetPage = targetWit.Layout.Pages .FirstOrDefault(x => x.Label.Equals(sourcePage.Label, StringComparison.OrdinalIgnoreCase)); foreach (var sourceSection in sourcePage.Sections) { foreach (var sourceGroup in sourceSection.Groups) { var sourceGroupKey = $"{sourceWit.WorkItemType.ReferenceName}::{sourcePage.Label}::{sourceSection.Id}::{sourceGroup.Label}"; // first let's see if the target has any inherited group for this source group .. // It will have a group.inherits != null/"" if it is inherited.. you can edit "system" groups if (!sourceGroup.Inherited) // It's a custom group { // look for the group on the flattened set of groups.. remember flat groups are keyed on $"{wit.ReferenceName}::{page.Label}::{section.Id}::{group.Label}" var existingGroup = TargetModel.WorkItemGroups.Select(x => new { x.Key, x.Value }) .FirstOrDefault(x => x.Key.StartsWith($"{targetWit.WorkItemType.ReferenceName}::") && x.Value.Label.Equals(sourceGroup.Label, StringComparison.OrdinalIgnoreCase)); if (existingGroup != null) { WorkItemGroup finalTargetGroup = null; WorkItemPage finalTargetPage = null; // here we know the group exists.. we need to check if its on the same page if (sourceGroupKey.Equals(existingGroup.Key, StringComparison.OrdinalIgnoreCase)) { // It's on the same page/section.. no need to move Log.LogInformation("Target group [{0}:{1}] located on same page/section. Skipping group location sync..", targetPage.Label, existingGroup.Value.Label); finalTargetPage = targetPage; finalTargetGroup = existingGroup.Value; } else { // It's on a different page or section.. we need to move it .. but how can we tell // if it moved to a different page or just a different section? We split and compare var sourceSplit = sourceGroupKey.Split("::".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); var existingSplit = existingGroup.Key.Split("::".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); var existingPage = targetWit.Layout.Pages.FirstOrDefault(p => p.Label.Equals(existingSplit[1])); if (sourceSplit[1].Equals(existingSplit[1], StringComparison.OrdinalIgnoreCase)) { // Its on the same page.. it must be section change.. lets move it to the new section var tempTargetGroup = existingGroup.Value.CloneAsNew(); tempTargetGroup.Id = existingGroup.Value.Id; if (await Target.MoveWorkItemGroupWithinPage( tempTargetGroup, processId, sourceWit.WorkItemType.ReferenceName, targetPage.Id, sourceSplit[2], existingSplit[2])) { Log.LogInformation("Target group [{0}] located on same page but different section. Moved from [{1}] to [{2}] ..", sourceGroup.Label, sourceSplit[2], existingSplit[2]); } else { Log.LogError("Target group [{0}] located on same page but different section. Unable to move from [{1}] to [{2}] ..", sourceGroup.Label, sourceSplit[2], existingSplit[2]); } finalTargetPage = existingPage; finalTargetGroup = tempTargetGroup; } else { // Its on a different page .. lets move pages var tempTargetGroup = existingGroup.Value.CloneAsNew(); tempTargetGroup.Id = existingGroup.Value.Id; if (await Target.MoveWorkItemGroupToNewPage( tempTargetGroup, processId, targetWit.WorkItemType.ReferenceName, targetPage.Id, sourceSplit[2], existingPage.Id, existingSplit[2])) { Log.LogInformation("Target group located on different page. Moved from [{0}:{1}] to [{2}:{3}] ..", sourceSplit[1], sourceSplit[2], existingSplit[1], existingSplit[2]); } else { Log.LogError("Target group located on different page. Unable to move from [{0}:{1}] to [{2}:{3}]!", existingSplit[1], existingSplit[2], targetPage.Label, sourceSplit[2]); } finalTargetPage = existingPage; finalTargetGroup = tempTargetGroup; } } // TODO Finish this! // Iterate through the source controls and make sure the target has all the controls from source foreach (var sourceControl in sourceGroup.Controls) { if (sourceControl.ControlType == "HtmlFieldControl") { Log.LogWarning("Skipped HTML control sync [{0}] as it should have already been migrated as part of the group sync.", sourceControl.Label); } else { // Let's see if we can't find the control already present in the "final target" var targetControl = finalTargetGroup.Controls.FirstOrDefault(ctl => ctl.Id.Equals(sourceControl.Id, StringComparison.OrdinalIgnoreCase)); if (targetControl == null) { // Let's see if its in another group perhaps.. if so we might want to move it.. that would imply that the group it was in is no longer WorkItemGroup oldGroup = null; foreach (var tempSection in finalTargetPage.Sections) { oldGroup = tempSection.Groups.FirstOrDefault(g => g.Controls.Any(c => c.Id.Equals(sourceControl.Id, StringComparison.OrdinalIgnoreCase))); if (oldGroup != null) { break; } } if (oldGroup == null) // It must be a new control { if (await Target.AddWorkItemControlToGroup(sourceControl.CloneAsNew(), processId, sourceWit.WorkItemType.ReferenceName, finalTargetGroup.Id, sourceControl.Id)) { Log.LogInformation("Attached control [{0}] to group [{1}].", sourceControl.Label, finalTargetGroup.Label); } else { Log.LogError("Failed to attach control [{0}] to new group [{1}]!", sourceControl.Label, finalTargetGroup.Label); } } else { // It must be control movement between groups if (await Target.MoveWorkItemControlToOtherGroup(sourceControl.CloneAsNew(), processId, sourceWit.WorkItemType.ReferenceName, finalTargetGroup.Id, sourceControl.Id, oldGroup.Id)) { Log.LogInformation("Moved control [{0}] from [{1}] to existing group [{2}].", sourceControl.Id, oldGroup.Label, finalTargetGroup.Label); } else { Log.LogError("Failed to move control [{0}] from [{1}] to existing group [{2}].", sourceControl.Id, oldGroup.Label, finalTargetGroup.Label); } } } else { { Log.LogInformation("Target already contains control [{0}] in proper group [{1}].", sourceControl.Label, finalTargetGroup.Label); } } } } } else { // Target doesn't have the group at all WorkItemGroup newGroup = await SyncDefinitionType <WorkItemGroup>(TargetModel.WorkItemGroups, sourceGroup, null, processId, sourceWit.WorkItemType.ReferenceName, targetPage.Id, sourceSection.Id); // Add all the controls foreach (var sourceControl in sourceGroup.Controls.Where(c => !c.ControlType.Equals("HtmlFieldControl", StringComparison.OrdinalIgnoreCase))) { if (await Target.AddWorkItemControlToGroup(sourceControl.CloneAsNew(), processId, sourceWit.WorkItemType.ReferenceName, newGroup.Id, sourceControl.Id)) { Log.LogInformation("Attached control [{0}] to new group [{1}].", sourceControl.Label, newGroup.Label); } else { Log.LogError("Failed to attach control [{0}] to new group [{1}]!", sourceControl.Label, newGroup.Label); } } } } } } } #endregion #region Sync Controls ... // Let's get a fresh layout from the target, now that we know pages and groups are aligned. await LoadLayout(TargetModel, targetWit, Target, processId); // At this point all Pages, Sections and Groups should be aligned.. lets sync the controls foreach (var sourcePage in sourceWit.Layout.Pages) { foreach (var sourceSection in sourcePage.Sections) { foreach (var sourceGroup in sourceSection.Groups) { foreach (var sourceControl in sourceGroup.Controls) { } } } } #endregion Log.LogInformation($"Completed sync of work item type [{Source.Options.Name}::{sourceWit.WorkItemType.Name}] in [{Target.Options.Name}::{targetWit.WorkItemType.Name}]."); }