public async Task SingleDesignTimeInput_Removed_ShouldntBeChanged() { var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); await VerifyOutput(2, () => { SendDesignTimeInputs(inputs); SendDesignTimeInputs(new DesignTimeInputs(new string[] { }, new string[] { })); }); // First update should include the file Assert.Single(_outputProduced[0].ChangedInputs); Assert.Single(_outputProduced[0].Inputs); Assert.Empty(_outputProduced[0].SharedInputs); Assert.Equal("File1.cs", _outputProduced[0].ChangedInputs[0].File); Assert.False(_outputProduced[0].ChangedInputs[0].IgnoreFileWriteTime); // Second shouldn't Assert.Empty(_outputProduced[1].ChangedInputs); Assert.Empty(_outputProduced[1].Inputs); Assert.Empty(_outputProduced[1].SharedInputs); }
public async Task SingleDesignTimeInput_Changes_ChangedTwice() { var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); await VerifyOutput(2, () => { SendDesignTimeInputs(inputs); SendFileChange("File1.cs"); }); Assert.Single(_outputProduced[0].ChangedInputs); Assert.Single(_outputProduced[0].Inputs); Assert.Empty(_outputProduced[0].SharedInputs); Assert.Equal("File1.cs", _outputProduced[0].ChangedInputs[0].File); Assert.False(_outputProduced[0].ChangedInputs[0].IgnoreFileWriteTime); Assert.Single(_outputProduced[1].ChangedInputs); Assert.Single(_outputProduced[1].Inputs); Assert.Empty(_outputProduced[1].SharedInputs); Assert.Equal("File1.cs", _outputProduced[1].ChangedInputs[0].File); Assert.False(_outputProduced[1].ChangedInputs[0].IgnoreFileWriteTime); }
public async Task SingleDesignTimeInput_AnotherAdded_OneInEachChange() { var inputs = new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { }); await VerifyOutput(2, () => { SendDesignTimeInputs(new DesignTimeInputs(new string[] { "File1.cs" }, new string[] { })); SendDesignTimeInputs(new DesignTimeInputs(new string[] { "File1.cs", "File2.cs" }, new string[] { })); }); Assert.Single(_outputProduced[0].ChangedInputs); Assert.Single(_outputProduced[0].Inputs); Assert.Empty(_outputProduced[0].SharedInputs); Assert.Equal("File1.cs", _outputProduced[0].ChangedInputs[0].File); Assert.False(_outputProduced[0].ChangedInputs[0].IgnoreFileWriteTime); Assert.Single(_outputProduced[1].ChangedInputs); Assert.Equal(2, _outputProduced[1].Inputs.Count); Assert.Empty(_outputProduced[1].SharedInputs); Assert.Equal("File2.cs", _outputProduced[1].ChangedInputs[0].File); Assert.False(_outputProduced[1].ChangedInputs[0].IgnoreFileWriteTime); }
internal void ProcessDataflowChanges(IProjectVersionedValue <ValueTuple <DesignTimeInputs, IProjectSubscriptionUpdate> > input) { DesignTimeInputs inputs = input.Value.Item1; IProjectChangeDescription configChanges = input.Value.Item2.ProjectChanges[ConfigurationGeneral.SchemaName]; // This can't change while we're running, but let's use a local so you don't have to take my word for it DesignTimeInputsDelta?previousState = _currentState; var changedInputs = new List <DesignTimeInputFileChange>(); // On the first call where we receive design time inputs we queue compilation of all of them, knowing that we'll only compile if the file write date requires it if (previousState == null) { AddAllInputsToQueue(false); } else { // If its not the first call... // If a new shared design time input is added, we need to recompile everything regardless of source file modified date // because it could be an old file that is being promoted to a shared input if (inputs.SharedInputs.Except(previousState.SharedInputs, StringComparers.Paths).Any()) { AddAllInputsToQueue(true); } // If the namespace or output path inputs have changed, then we recompile every file regardless of date else if (configChanges.Difference.ChangedProperties.Contains(ConfigurationGeneral.RootNamespaceProperty) || configChanges.Difference.ChangedProperties.Contains(ConfigurationGeneral.ProjectDirProperty) || configChanges.Difference.ChangedProperties.Contains(ConfigurationGeneral.IntermediateOutputPathProperty)) { AddAllInputsToQueue(true); } else { // Otherwise we just queue any new design time inputs, and still do date checks foreach (string file in inputs.Inputs.Except(previousState.Inputs, StringComparers.Paths)) { changedInputs.Add(new DesignTimeInputFileChange(file, ignoreFileWriteTime: false)); } } } string tempPEOutputPath; // Make sure we have the up to date output path string basePath = configChanges.After.Properties[ConfigurationGeneral.ProjectDirProperty]; string objPath = configChanges.After.Properties[ConfigurationGeneral.IntermediateOutputPathProperty]; try { tempPEOutputPath = Path.Combine(basePath, objPath, "TempPE"); } catch (ArgumentException) { // if the path is bad, then we presume we wouldn't be able to act on any files anyway // so we can just clear _latestDesignTimeInputs to ensure file changes aren't processed, and return. // If the path is ever fixed this block will trigger again and all will be right with the world. _currentState = null; return; } // This is our only update to current state, and data flow protects us from overlaps. File changes don't update state _currentState = new DesignTimeInputsDelta(inputs.Inputs, inputs.SharedInputs, changedInputs, tempPEOutputPath); PostToOutput(_currentState); void AddAllInputsToQueue(bool ignoreFileWriteTime) { foreach (string file in inputs.Inputs) { changedInputs.Add(new DesignTimeInputFileChange(file, ignoreFileWriteTime)); } } }
private static DesignTimeInputSnapshot?GenerateOutputData( DesignTimeInputSnapshot?previousState, IProjectVersionedValue <ValueTuple <DesignTimeInputs, IProjectSubscriptionUpdate> > input) { DesignTimeInputs inputs = input.Value.Item1; if (!input.Value.Item2.ProjectChanges.TryGetValue(ConfigurationGeneral.SchemaName, out IProjectChangeDescription configChanges)) { // If this isn't an update we can deal with, just ignore it return(null); } var changedInputs = new List <DesignTimeInputFileChange>(); // On the first call where we receive design time inputs we queue compilation of all of them, knowing that we'll only compile if the file write date requires it if (previousState == null) { AddAllInputsToQueue(false); } else { // If its not the first call... // If a new shared design time input is added, we need to recompile everything regardless of source file modified date // because it could be an old file that is being promoted to a shared input if (inputs.SharedInputs.Except(previousState.SharedInputs, StringComparers.Paths).Any()) { AddAllInputsToQueue(true); } // If the namespace or output path inputs have changed, then we recompile every file regardless of date else if (configChanges.Difference.ChangedProperties.Contains(ConfigurationGeneral.RootNamespaceProperty) || configChanges.Difference.ChangedProperties.Contains(ConfigurationGeneral.ProjectDirProperty) || configChanges.Difference.ChangedProperties.Contains(ConfigurationGeneral.IntermediateOutputPathProperty)) { AddAllInputsToQueue(true); } else { // Otherwise we just queue any new design time inputs, and still do date checks foreach (string file in inputs.Inputs.Except(previousState.Inputs, StringComparers.Paths)) { changedInputs.Add(new DesignTimeInputFileChange(file, ignoreFileWriteTime: false)); } } } string tempPEOutputPath; // Make sure we have the up to date output path. If either of these don't exist, they will be null and we'll handle the ArgumentException below string?basePath = configChanges.After.Properties.GetValueOrDefault(ConfigurationGeneral.ProjectDirProperty); string?objPath = configChanges.After.Properties.GetValueOrDefault(ConfigurationGeneral.IntermediateOutputPathProperty); try { tempPEOutputPath = Path.Combine(basePath, objPath, "TempPE"); } catch (ArgumentException) { // if the path is bad, or we couldn't get part of it, then we presume we wouldn't be able to act on any files // so we can just clear _currentState to ensure file changes aren't processed, and return. // If the path is ever fixed this block will trigger again and all will be right with the world. return(null); } // This is our only update to current state, and data flow protects us from overlaps. File changes don't update state return(new DesignTimeInputSnapshot(inputs.Inputs, inputs.SharedInputs, changedInputs, tempPEOutputPath)); void AddAllInputsToQueue(bool ignoreFileWriteTime) { foreach (string file in inputs.Inputs) { changedInputs.Add(new DesignTimeInputFileChange(file, ignoreFileWriteTime)); } } }