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);
        }
示例#2
0
        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);
        }
示例#3
0
        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);
        }
示例#4
0
        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));
                }
            }
        }