/// <summary> /// Initializes a new instance of the <see cref="DistributionSuggestion"/> class. /// </summary> /// <param name="plan">The suggested plan.</param> /// <exception cref="ArgumentNullException"> /// Thrown when <paramref name="plan"/> is <see langword="null" />. /// </exception> public DistributionSuggestion(DistributionPlan plan) { { Enforce.Argument(() => plan); } Rating = plan.Proposal.Rate(); Plan = plan; }
/// <summary> /// Processes the dataset request and returns a collection of distribution plans. /// </summary> /// <param name="activationRequest"> /// The request that describes the characteristics of the dataset that /// should be loaded. /// </param> /// <param name="token">The cancellation token that can be used to terminate the proposal.</param> /// <returns> /// The collection containing all the distribution plans. /// </returns> public IEnumerable <DistributionPlan> ProposeDistributionFor(DatasetActivationRequest activationRequest, CancellationToken token) { using (m_Diagnostics.Profiler.Measure(BaseConstants.TimingGroup, "Generating local proposal")) { var proposal = m_LocalDistributor.ProposeForLocalMachine(activationRequest.ExpectedLoadPerMachine); var plan = new DistributionPlan( ImplementPlan, activationRequest.DatasetToActivate, new NetworkIdentifier(proposal.Endpoint.OriginatesOnMachine()), proposal); return(new[] { plan }); } }
private static DistributionPlan CreateNewDistributionPlan( DatasetActivationProposal proposal, IDatasetOfflineInformation offlineInfo, SystemDiagnostics systemDiagnostics) { var plan = new DistributionPlan( (p, t, r) => Task<DatasetOnlineInformation>.Factory.StartNew( () => new DatasetOnlineInformation( new DatasetId(), new EndpointId("id"), new NetworkIdentifier("machine"), new Mock<ISendCommandsToRemoteEndpoints>().Object, new Mock<INotifyOfRemoteEndpointEvents>().Object, systemDiagnostics), t), offlineInfo, NetworkIdentifier.ForLocalMachine(), proposal); return plan; }
public void BuildWithDistributorAndStorage() { ITimeline timeline = new Timeline(BuildStorage); var systemDiagnostics = new SystemDiagnostics((p, s) => { }, null); var offline = new Mock<IDatasetOfflineInformation>(); var builder = new ProjectBuilder(); var plan = new DistributionPlan( (p, t, r) => new Task<DatasetOnlineInformation>( () => new DatasetOnlineInformation( new DatasetId(), new EndpointId("id"), new NetworkIdentifier("machine"), new Mock<ISendCommandsToRemoteEndpoints>().Object, new Mock<INotifyOfRemoteEndpointEvents>().Object, systemDiagnostics), t), offline.Object, new NetworkIdentifier("mymachine"), new DatasetActivationProposal()); Func<DatasetActivationRequest, CancellationToken, IEnumerable<DistributionPlan>> distributor = (r, c) => new List<DistributionPlan> { plan }; var proxyLayer = new Mock<IProxyCompositionLayer>(); var project = builder.Define() .WithTimeline(timeline) .WithDatasetDistributor(distributor) .FromStorage(new Mock<IPersistenceInformation>().Object) .WithDataStorageBuilder( d => new DatasetStorageProxy( d, new GroupSelector( new Mock<IConnectGroups>().Object, proxyLayer.Object), proxyLayer.Object)) .WithNotifications(new Mock<ICollectNotifications>().Object) .WithDiagnostics(systemDiagnostics) .Build(); Assert.IsNotNull(project); }
private static DistributionPlan CreateNewDistributionPlan( DatasetActivationProposal proposal, IDatasetOfflineInformation offlineInfo, SystemDiagnostics systemDiagnostics) { var plan = new DistributionPlan( (p, t, r) => Task <DatasetOnlineInformation> .Factory.StartNew( () => new DatasetOnlineInformation( new DatasetId(), new EndpointId("id"), new NetworkIdentifier("machine"), new Mock <ISendCommandsToRemoteEndpoints>().Object, new Mock <INotifyOfRemoteEndpointEvents>().Object, systemDiagnostics), t, TaskCreationOptions.None, new CurrentThreadTaskScheduler()), offlineInfo, NetworkIdentifier.ForLocalMachine(), proposal); return(plan); }
/// <summary> /// Takes the set of distribution plans and loads the given datasets onto the specified machines. /// </summary> /// <param name="planToImplement">The distribution plan that should be implemented.</param> /// <param name="token">The token used to indicate cancellation of the task.</param> /// <param name="progressReporter">The action that handles the reporting of progress.</param> /// <returns> /// A set of objects which allow act as proxies for the loaded datasets. /// </returns> public Task <DatasetOnlineInformation> ImplementPlan( DistributionPlan planToImplement, CancellationToken token, Action <int, string, bool> progressReporter) { Func <DatasetOnlineInformation> result = () => { IDatasetActivationCommands activationCommands; lock (m_Lock) { if (!m_ActivatorCommands.ContainsKey(planToImplement.Proposal.Endpoint)) { throw new EndpointNotContactableException(planToImplement.Proposal.Endpoint); } activationCommands = m_ActivatorCommands[planToImplement.Proposal.Endpoint]; } // We shouldn't have to load the TCP channel at this point because that channel would have // been loaded when the loaders broadcast their message indicating that they exist. var info = m_CommunicationLayer.LocalConnectionFor(ChannelType.TcpIP); var endpointTask = activationCommands.Activate(info.Item1, ChannelType.TcpIP, info.Item2, planToImplement.DistributionFor.Id); endpointTask.Wait(); var endpoint = endpointTask.Result; var resetEvent = new AutoResetEvent(false); var commandAvailabilityNotifier = Observable.FromEventPattern <CommandSetAvailabilityEventArgs>( h => m_CommandHub.OnEndpointSignedIn += h, h => m_CommandHub.OnEndpointSignedIn -= h) .Where(args => args.EventArgs.Endpoint.Equals(endpoint)) .Take(1) .Subscribe( args => { resetEvent.Set(); }); using (commandAvailabilityNotifier) { if (!m_CommandHub.HasCommandsFor(endpoint) || !m_NotificationHub.HasNotificationsFor(endpoint)) { resetEvent.WaitOne(); } } EventHandler <ProgressEventArgs> progressHandler = (s, e) => progressReporter(e.Progress, e.Description, e.HasErrors); var notifications = m_NotificationHub.NotificationsFor <IDatasetApplicationNotifications>(endpoint); notifications.OnProgress += progressHandler; try { // Store the file var file = planToImplement.DistributionFor.StoredAt.AsFile(); var uploadToken = m_Uploads.Register(file.FullName); // The commands have been registered, so now load the dataset Debug.Assert( m_CommandHub.HasCommandFor(endpoint, typeof(IDatasetApplicationCommands)), "No application commands registered."); var applicationCommands = m_CommandHub.CommandsFor <IDatasetApplicationCommands>(endpoint); var task = applicationCommands.Load( EndpointIdExtensions.CreateEndpointIdForCurrentProcess(), uploadToken); task.Wait(); return(m_DatasetInformationBuilder( planToImplement.DistributionFor.Id, endpoint, planToImplement.MachineToDistributeTo)); } finally { notifications.OnProgress -= progressHandler; } }; return(Task <DatasetOnlineInformation> .Factory.StartNew( result, token, TaskCreationOptions.LongRunning, m_Scheduler)); }
public void BuildWithoutTimeline() { var systemDiagnostics = new SystemDiagnostics((p, s) => { }, null); var builder = new ProjectBuilder(); var offline = new Mock<IDatasetOfflineInformation>(); var plan = new DistributionPlan( (p, t, r) => new Task<DatasetOnlineInformation>( () => new DatasetOnlineInformation( new DatasetId(), new EndpointId("id"), new NetworkIdentifier("machine"), new Mock<ISendCommandsToRemoteEndpoints>().Object, new Mock<INotifyOfRemoteEndpointEvents>().Object, systemDiagnostics), t), offline.Object, new NetworkIdentifier("mymachine"), new DatasetActivationProposal()); Func<DatasetActivationRequest, CancellationToken, IEnumerable<DistributionPlan>> distributor = (r, c) => new List<DistributionPlan> { plan }; Assert.Throws<CannotCreateProjectWithoutTimelineException>(() => builder.WithDatasetDistributor(distributor).Build()); }
/// <summary> /// Processes the dataset request and returns a collection of distribution plans. /// </summary> /// <param name="activationRequest"> /// The request that describes the characteristics of the dataset that /// should be loaded. /// </param> /// <param name="token">The cancellation token that can be used to terminate the proposal.</param> /// <returns> /// The collection containing all the distribution plans. /// </returns> public IEnumerable<DistributionPlan> ProposeDistributionFor(DatasetActivationRequest activationRequest, CancellationToken token) { using (m_Diagnostics.Profiler.Measure(BaseConstants.TimingGroup, "Generating local proposal")) { var proposal = m_LocalDistributor.ProposeForLocalMachine(activationRequest.ExpectedLoadPerMachine); var plan = new DistributionPlan( ImplementPlan, activationRequest.DatasetToActivate, new NetworkIdentifier(proposal.Endpoint.OriginatesOnMachine()), proposal); return new[] { plan }; } }
/// <summary> /// Takes the set of distribution plans and loads the given datasets onto the specified machines. /// </summary> /// <param name="planToImplement">The distribution plan that should be implemented.</param> /// <param name="token">The token used to indicate cancellation of the task.</param> /// <param name="progressReporter">The action that handles the reporting of progress.</param> /// <returns> /// A set of objects which allow act as proxies for the loaded datasets. /// </returns> public Task<DatasetOnlineInformation> ImplementPlan( DistributionPlan planToImplement, CancellationToken token, Action<int, string, bool> progressReporter) { Func<DatasetOnlineInformation> result = () => { m_Diagnostics.Log(LevelToLog.Info, BaseConstants.LogPrefix, "Activating dataset"); var info = m_CommunicationLayer.LocalConnectionFor(ChannelType.NamedPipe); EndpointId endpoint; try { endpoint = m_Loader.ActivateDataset(info.Item1, ChannelType.NamedPipe, info.Item2); } catch (Exception e) { m_Diagnostics.Log( LevelToLog.Error, BaseConstants.LogPrefix, string.Format( CultureInfo.InvariantCulture, "Failed to activate the dataset. Error was: {0}", e)); throw; } var resetEvent = new AutoResetEvent(false); var commandAvailabilityNotifier = Observable.FromEventPattern<CommandSetAvailabilityEventArgs>( h => m_CommandHub.OnEndpointSignedIn += h, h => m_CommandHub.OnEndpointSignedIn -= h) .Where(args => args.EventArgs.Endpoint.Equals(endpoint)) .Take(1); var notificationAvailabilityNotifier = Observable.FromEventPattern<NotificationSetAvailabilityEventArgs>( h => m_NotificationHub.OnEndpointSignedIn += h, h => m_NotificationHub.OnEndpointSignedIn -= h) .Where(args => args.EventArgs.Endpoint.Equals(endpoint)) .Take(1); var availability = commandAvailabilityNotifier.Zip( notificationAvailabilityNotifier, (a, b) => { return true; }) .Subscribe( args => { resetEvent.Set(); }); using (availability) { if (!m_CommandHub.HasCommandsFor(endpoint) || !m_NotificationHub.HasNotificationsFor(endpoint)) { m_Diagnostics.Log(LevelToLog.Trace, BaseConstants.LogPrefix, "Waiting for dataset to connect."); resetEvent.WaitOne(); } } m_Diagnostics.Log(LevelToLog.Trace, "Received commands and notifications from dataset."); IDatasetApplicationNotifications notifications; try { notifications = m_NotificationHub.NotificationsFor<IDatasetApplicationNotifications>(endpoint); } catch (Exception e) { m_Diagnostics.Log( LevelToLog.Error, BaseConstants.LogPrefix, string.Format( CultureInfo.InvariantCulture, "Failed to get the notifications. Error was: {0}", e)); throw; } EventHandler<ProgressEventArgs> progressHandler = (s, e) => progressReporter(e.Progress, e.Description, e.HasErrors); notifications.OnProgress += progressHandler; try { // Store the file var file = planToImplement.DistributionFor.StoredAt.AsFile(); var uploadToken = m_Uploads.Register(file.FullName); // The commands have been registered, so now load the dataset Debug.Assert( m_CommandHub.HasCommandFor(endpoint, typeof(IDatasetApplicationCommands)), "No application commands registered."); var commands = m_CommandHub.CommandsFor<IDatasetApplicationCommands>(endpoint); var task = commands.Load( EndpointIdExtensions.CreateEndpointIdForCurrentProcess(), uploadToken); task.Wait(); // Now the dataset loading is complete return m_DatasetInformationBuilder( planToImplement.DistributionFor.Id, endpoint, planToImplement.MachineToDistributeTo); } finally { notifications.OnProgress -= progressHandler; } }; return Task<DatasetOnlineInformation>.Factory.StartNew( result, token, TaskCreationOptions.LongRunning, m_Scheduler); }
/// <summary> /// Takes the set of distribution plans and loads the given datasets onto the specified machines. /// </summary> /// <param name="planToImplement">The distribution plan that should be implemented.</param> /// <param name="token">The token used to indicate cancellation of the task.</param> /// <param name="progressReporter">The action that handles the reporting of progress.</param> /// <returns> /// A set of objects which allow act as proxies for the loaded datasets. /// </returns> public Task<DatasetOnlineInformation> ImplementPlan( DistributionPlan planToImplement, CancellationToken token, Action<int, string, bool> progressReporter) { Func<DatasetOnlineInformation> result = () => { IDatasetActivationCommands activationCommands; lock (m_Lock) { if (!m_ActivatorCommands.ContainsKey(planToImplement.Proposal.Endpoint)) { throw new EndpointNotContactableException(planToImplement.Proposal.Endpoint); } activationCommands = m_ActivatorCommands[planToImplement.Proposal.Endpoint]; } // We shouldn't have to load the TCP channel at this point because that channel would have // been loaded when the loaders broadcast their message indicating that they exist. var info = m_CommunicationLayer.LocalConnectionFor(ChannelType.TcpIP); var endpointTask = activationCommands.Activate(info.Item1, ChannelType.TcpIP, info.Item2, planToImplement.DistributionFor.Id); endpointTask.Wait(); var endpoint = endpointTask.Result; var resetEvent = new AutoResetEvent(false); var commandAvailabilityNotifier = Observable.FromEventPattern<CommandSetAvailabilityEventArgs>( h => m_CommandHub.OnEndpointSignedIn += h, h => m_CommandHub.OnEndpointSignedIn -= h) .Where(args => args.EventArgs.Endpoint.Equals(endpoint)) .Take(1) .Subscribe( args => { resetEvent.Set(); }); using (commandAvailabilityNotifier) { if (!m_CommandHub.HasCommandsFor(endpoint) || !m_NotificationHub.HasNotificationsFor(endpoint)) { resetEvent.WaitOne(); } } EventHandler<ProgressEventArgs> progressHandler = (s, e) => progressReporter(e.Progress, e.Description, e.HasErrors); var notifications = m_NotificationHub.NotificationsFor<IDatasetApplicationNotifications>(endpoint); notifications.OnProgress += progressHandler; try { // Store the file var file = planToImplement.DistributionFor.StoredAt.AsFile(); var uploadToken = m_Uploads.Register(file.FullName); // The commands have been registered, so now load the dataset Debug.Assert( m_CommandHub.HasCommandFor(endpoint, typeof(IDatasetApplicationCommands)), "No application commands registered."); var applicationCommands = m_CommandHub.CommandsFor<IDatasetApplicationCommands>(endpoint); var task = applicationCommands.Load( EndpointIdExtensions.CreateEndpointIdForCurrentProcess(), uploadToken); task.Wait(); return m_DatasetInformationBuilder( planToImplement.DistributionFor.Id, endpoint, planToImplement.MachineToDistributeTo); } finally { notifications.OnProgress -= progressHandler; } }; return Task<DatasetOnlineInformation>.Factory.StartNew( result, token, TaskCreationOptions.LongRunning, m_Scheduler); }
/// <summary> /// Takes the set of distribution plans and loads the given datasets onto the specified machines. /// </summary> /// <param name="planToImplement">The distribution plan that should be implemented.</param> /// <param name="token">The token used to indicate cancellation of the task.</param> /// <param name="progressReporter">The action that handles the reporting of progress.</param> /// <returns> /// A set of objects which allow act as proxies for the loaded datasets. /// </returns> public Task <DatasetOnlineInformation> ImplementPlan( DistributionPlan planToImplement, CancellationToken token, Action <int, string, bool> progressReporter) { Func <DatasetOnlineInformation> result = () => { m_Diagnostics.Log(LevelToLog.Info, BaseConstants.LogPrefix, "Activating dataset"); var info = m_CommunicationLayer.LocalConnectionFor(ChannelType.NamedPipe); EndpointId endpoint; try { endpoint = m_Loader.ActivateDataset(info.Item1, ChannelType.NamedPipe, info.Item2); } catch (Exception e) { m_Diagnostics.Log( LevelToLog.Error, BaseConstants.LogPrefix, string.Format( CultureInfo.InvariantCulture, "Failed to activate the dataset. Error was: {0}", e)); throw; } var resetEvent = new AutoResetEvent(false); var commandAvailabilityNotifier = Observable.FromEventPattern <CommandSetAvailabilityEventArgs>( h => m_CommandHub.OnEndpointSignedIn += h, h => m_CommandHub.OnEndpointSignedIn -= h) .Where(args => args.EventArgs.Endpoint.Equals(endpoint)) .Take(1); var notificationAvailabilityNotifier = Observable.FromEventPattern <NotificationSetAvailabilityEventArgs>( h => m_NotificationHub.OnEndpointSignedIn += h, h => m_NotificationHub.OnEndpointSignedIn -= h) .Where(args => args.EventArgs.Endpoint.Equals(endpoint)) .Take(1); var availability = commandAvailabilityNotifier.Zip( notificationAvailabilityNotifier, (a, b) => { return(true); }) .Subscribe( args => { resetEvent.Set(); }); using (availability) { if (!m_CommandHub.HasCommandsFor(endpoint) || !m_NotificationHub.HasNotificationsFor(endpoint)) { m_Diagnostics.Log(LevelToLog.Trace, BaseConstants.LogPrefix, "Waiting for dataset to connect."); resetEvent.WaitOne(); } } m_Diagnostics.Log(LevelToLog.Trace, "Received commands and notifications from dataset."); IDatasetApplicationNotifications notifications; try { notifications = m_NotificationHub.NotificationsFor <IDatasetApplicationNotifications>(endpoint); } catch (Exception e) { m_Diagnostics.Log( LevelToLog.Error, BaseConstants.LogPrefix, string.Format( CultureInfo.InvariantCulture, "Failed to get the notifications. Error was: {0}", e)); throw; } EventHandler <ProgressEventArgs> progressHandler = (s, e) => progressReporter(e.Progress, e.Description, e.HasErrors); notifications.OnProgress += progressHandler; try { // Store the file var file = planToImplement.DistributionFor.StoredAt.AsFile(); var uploadToken = m_Uploads.Register(file.FullName); // The commands have been registered, so now load the dataset Debug.Assert( m_CommandHub.HasCommandFor(endpoint, typeof(IDatasetApplicationCommands)), "No application commands registered."); var commands = m_CommandHub.CommandsFor <IDatasetApplicationCommands>(endpoint); var task = commands.Load( EndpointIdExtensions.CreateEndpointIdForCurrentProcess(), uploadToken); task.Wait(); // Now the dataset loading is complete return(m_DatasetInformationBuilder( planToImplement.DistributionFor.Id, endpoint, planToImplement.MachineToDistributeTo)); } finally { notifications.OnProgress -= progressHandler; } }; return(Task <DatasetOnlineInformation> .Factory.StartNew( result, token, TaskCreationOptions.LongRunning, m_Scheduler)); }
public void ExportAfterClosing() { ITimeline timeline = null; timeline = new Timeline(t => BuildStorage(timeline, t)); var systemDiagnostics = new SystemDiagnostics((p, s) => { }, null); var offline = new Mock<IDatasetOfflineInformation>(); var plan = new DistributionPlan( (p, t, r) => Task<DatasetOnlineInformation>.Factory.StartNew( () => new DatasetOnlineInformation( new DatasetId(), new EndpointId("id"), new NetworkIdentifier("machine"), new Mock<ISendCommandsToRemoteEndpoints>().Object, new Mock<INotifyOfRemoteEndpointEvents>().Object, systemDiagnostics), t, TaskCreationOptions.None, new CurrentThreadTaskScheduler()), offline.Object, new NetworkIdentifier("mymachine"), new DatasetActivationProposal()); Func<DatasetActivationRequest, CancellationToken, IEnumerable<DistributionPlan>> distributor = (r, c) => new List<DistributionPlan> { plan }; var proxyLayer = new Mock<IProxyCompositionLayer>(); var project = new Project( timeline, distributor, d => new DatasetStorageProxy( d, new GroupSelector( new Mock<IConnectGroups>().Object, proxyLayer.Object), proxyLayer.Object), new Mock<ICollectNotifications>().Object, systemDiagnostics); var dataset = project.BaseDataset(); project.Close(); Assert.Throws<CannotUseProjectAfterClosingItException>( () => project.Export(dataset.Id, false, new Mock<IPersistenceInformation>().Object)); }
public void Summary() { ITimeline timeline = null; timeline = new Timeline(t => BuildStorage(timeline, t)); var systemDiagnostics = new SystemDiagnostics((p, s) => { }, null); var offline = new Mock<IDatasetOfflineInformation>(); var plan = new DistributionPlan( (p, t, r) => Task<DatasetOnlineInformation>.Factory.StartNew( () => new DatasetOnlineInformation( new DatasetId(), new EndpointId("id"), new NetworkIdentifier("machine"), new Mock<ISendCommandsToRemoteEndpoints>().Object, new Mock<INotifyOfRemoteEndpointEvents>().Object, systemDiagnostics), t, TaskCreationOptions.None, new CurrentThreadTaskScheduler()), offline.Object, new NetworkIdentifier("mymachine"), new DatasetActivationProposal()); Func<DatasetActivationRequest, CancellationToken, IEnumerable<DistributionPlan>> distributor = (r, c) => new List<DistributionPlan> { plan }; var proxyLayer = new Mock<IProxyCompositionLayer>(); var project = new Project( timeline, distributor, d => new DatasetStorageProxy( d, new GroupSelector( new Mock<IConnectGroups>().Object, proxyLayer.Object), proxyLayer.Object), new Mock<ICollectNotifications>().Object, systemDiagnostics); var summary = string.Empty; project.OnSummaryChanged += (s, e) => { summary = e.Value; }; project.Summary = "MyNewName"; Assert.AreEqual(project.Summary, summary); // Set the summary again, to the same thing. This // shouldn't notify summary = string.Empty; project.Summary = "MyNewName"; Assert.AreEqual(string.Empty, summary); }
public void SaveWithNullPersistenceInformation() { ITimeline timeline = null; timeline = new Timeline(t => BuildStorage(timeline, t)); var systemDiagnostics = new SystemDiagnostics((p, s) => { }, null); var offline = new Mock<IDatasetOfflineInformation>(); var plan = new DistributionPlan( (p, t, r) => new Task<DatasetOnlineInformation>( () => new DatasetOnlineInformation( new DatasetId(), new EndpointId("id"), new NetworkIdentifier("machine"), new Mock<ISendCommandsToRemoteEndpoints>().Object, new Mock<INotifyOfRemoteEndpointEvents>().Object, systemDiagnostics), t), offline.Object, new NetworkIdentifier("mymachine"), new DatasetActivationProposal()); Func<DatasetActivationRequest, CancellationToken, IEnumerable<DistributionPlan>> distributor = (r, c) => new List<DistributionPlan> { plan }; var proxyLayer = new Mock<IProxyCompositionLayer>(); var project = new Project( timeline, distributor, d => new DatasetStorageProxy( d, new GroupSelector( new Mock<IConnectGroups>().Object, proxyLayer.Object), proxyLayer.Object), new Mock<ICollectNotifications>().Object, systemDiagnostics); Assert.Throws<ArgumentNullException>(() => project.Save(null)); }
public void RollForwardWithDeletesOnly() { ITimeline timeline = null; timeline = new Timeline(t => BuildStorage(timeline, t)); var systemDiagnostics = new SystemDiagnostics((p, s) => { }, null); var offline = new Mock<IDatasetOfflineInformation>(); var plan = new DistributionPlan( (p, t, r) => Task<DatasetOnlineInformation>.Factory.StartNew( () => new DatasetOnlineInformation( new DatasetId(), new EndpointId("id"), new NetworkIdentifier("machine"), new Mock<ISendCommandsToRemoteEndpoints>().Object, new Mock<INotifyOfRemoteEndpointEvents>().Object, systemDiagnostics), t, TaskCreationOptions.None, new CurrentThreadTaskScheduler()), offline.Object, new NetworkIdentifier("mymachine"), new DatasetActivationProposal()); Func<DatasetActivationRequest, CancellationToken, IEnumerable<DistributionPlan>> distributor = (r, c) => new List<DistributionPlan> { plan }; var proxyLayer = new Mock<IProxyCompositionLayer>(); var project = new Project( timeline, distributor, d => new DatasetStorageProxy( d, new GroupSelector( new Mock<IConnectGroups>().Object, proxyLayer.Object), proxyLayer.Object), new Mock<ICollectNotifications>().Object, systemDiagnostics); // Create a 'binary' tree of datasets. This should create the following tree: // X // / \ // / \ // / \ // / \ // / \ // X X // / \ / \ // / \ / \ // / \ / \ // X X X X // / \ / \ // X X X X var children = new List<DatasetId>(); var datasets = new Queue<IProxyDataset>(); var root = project.BaseDataset(); datasets.Enqueue(root); int count = 0; while (count < 10) { var creationInformation = new DatasetCreationInformation() { CreatedOnRequestOf = DatasetCreator.User, CanBecomeParent = true, CanBeAdopted = false, CanBeCopied = false, CanBeDeleted = true, LoadFrom = new Mock<IPersistenceInformation>().Object, }; var parent = datasets.Dequeue(); var newChildren = parent.CreateNewChildren(new DatasetCreationInformation[] { creationInformation, creationInformation }); foreach (var child in newChildren) { datasets.Enqueue(child); children.Add(child.Id); count++; } } var marks = new List<TimeMarker>(); marks.Add(project.History.Mark()); for (int i = children.Count - 1; i > -1; i--) { var child = project.Dataset(children[i]); child.Delete(); marks.Add(project.History.Mark()); } project.History.RollBackTo(marks[0]); for (int i = 1; i < marks.Count; i++) { project.History.RollForwardTo(marks[i]); Assert.AreEqual(children.Count - i + 1, project.NumberOfDatasets); for (int j = 0; j < children.Count; j++) { var child = project.Dataset(children[j]); if (j < children.Count - i) { Assert.IsTrue(child.IsValid); } else { Assert.IsNull(child); } } } }