private static void VerifyDifferentRegistries(OrToolsSatGeneratedSyntaxTreeRegistry previousRegistry
                                                      , OrToolsSatGeneratedSyntaxTreeRegistry currentRegistry)
        {
            {
                bool DirectoryExists(OrToolsSatGeneratedSyntaxTreeRegistry x) => Directory.Exists(x.OutputDirectory);

                // Do some quick verification of each Registry.
                previousRegistry = previousRegistry.AssertNotNull().AssertNotEmpty().AssertTrue(DirectoryExists);
                currentRegistry  = currentRegistry.AssertNotNull().AssertNotEmpty().AssertTrue(DirectoryExists);
            }

            // Should always Not be the Same.
            previousRegistry.AssertNotSame(currentRegistry);

            var previousKeys = previousRegistry.SelectMany(x => x.GeneratedAssetKeys).AssertNotEmpty().ToList().AssertTrue(x => x.Any());
            var currentKeys  = currentRegistry.SelectMany(x => x.GeneratedAssetKeys).AssertNotEmpty().ToList().AssertTrue(x => x.Any());

            {
                // None of the Second Registry Generated Keys should exist in the First Registry.
                void KeysDoesNotContain(IEnumerable <Guid> collection, Guid y) => collection.AssertDoesNotContain(x => x == y);

                // The two sets of Generated Code ought not to overlap whatsoever.
                currentKeys.ForEach(x => KeysDoesNotContain(previousKeys, x));
                previousKeys.ForEach(x => KeysDoesNotContain(currentKeys, x));
            }

            {
                // Newly Generated Code will exist, should also be able to verify this.
                void FileExists(string path) => path.AssertFileExists();

                currentKeys.Select(x => Path.Combine(currentRegistry.OutputDirectory, x.RenderGeneratedFileName())).ToList().ForEach(FileExists);
            }

            {
                // Previously Generated Code will have been Purged, should be able to verify this.
                void FileDoesNotExist(string path) => path.AssertFileDoesNotExist();

                previousKeys.Select(x => Path.Combine(previousRegistry.OutputDirectory, x.RenderGeneratedFileName())).ToList().ForEach(FileDoesNotExist);
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// We need to Evaluate a further and much more in depth Purge opportunity given the
        /// <paramref name="registry"/> and set of <paramref name="renderedCompilationUnits"/>.
        /// </summary>
        /// <param name="registry"></param>
        /// <param name="renderedCompilationUnits"></param>
        /// <returns></returns>
        private bool TryEvaluateCompilationUnits(OrToolsSatGeneratedSyntaxTreeRegistry registry
                                                 , IRenderedCompilationUnitDictionary renderedCompilationUnits)
        {
            // Pretty sure we want to do an Ordinal comparison here. But this is a function of the Invocation.
            bool StringEquals(string a, string b, StringComparison comparisonType)
            => !(a == null || b == null) &&
            string.Equals(a.Trim(), b.Trim(), comparisonType);

            bool TryDecomposeCompilationUnitSyntaxPair(CompilationUnitSyntaxPair pair
                                                       , out string generatedCode) => !IsNullOrEmpty(generatedCode = pair.Value);

            // This is a bit involved, but this gains us sufficient accounting of Generated Code.
            bool ThereAreCodeGenerationInconsistencies(GeneratedSyntaxTreeDescriptor _)
            {
                var outputDirectory = registry.OutputDirectory;

                var filePaths = Directory.EnumerateFiles(outputDirectory, $"*{g}{cs}", TopDirectoryOnly);

                // Obtain any Previously Existing Baseline Generated Code in the form of a Stream Factory.
                var streamFactories = filePaths.Select(filePath => (Func <Stream>)(
                                                           () => File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)
                                                           ));

                // Which has the added benefit of deferring Stream operations until we absolutely need them.
                foreach (var streamFactory in streamFactories)
                {
                    string baselineGeneratedCode;

                    /* And which also defers the actual delegate formation given the nature of the LINQ
                     * provider. Performance may vary from OS to OS, from Run-time to Run-time. */
                    using (var fs = streamFactory.Invoke())
                    {
                        // Do this operation Once and Only Once for the breadth of the candidate set of Generated Code.
                        using (var sr = new StreamReader(fs))
                        {
                            baselineGeneratedCode = sr.ReadToEndAsync().Result;
                        }
                    }

                    // Continue when Code Generation Consistency checks can be verified.
                    if (renderedCompilationUnits.Any(
                            pair => TryDecomposeCompilationUnitSyntaxPair(pair, out var x) &&
                            StringEquals(x, baselineGeneratedCode, Ordinal)))
                    {
                        continue;
                    }

                    if (DebugMessagesSwitch)
                    {
                        Writer.WriteLine("Generated code inconsistencies discovered, purge required.");
                    }

                    // Otherwise, break, we know the set to be Inconsistent, i.e. re-Generation required.
                    return(true);
                }

                if (DebugMessagesSwitch)
                {
                    Writer.WriteLine("Generated code found to be consistent, purge not required.");
                }

                // If everything checked out, All Consistent, then we are Clear to Bypass Code Generation.
                return(false);
            }

            var areNonePreviouslyGenerated            = !registry.SelectMany(x => x.GeneratedAssetKeys).Any();
            var areObviouslyDifferent                 = registry.SelectMany(x => x.GeneratedAssetKeys).Count() != renderedCompilationUnits.Count;
            var thereAreCodeGenerationInconsistencies = registry.Any(ThereAreCodeGenerationInconsistencies);

            var shouldPurge = areNonePreviouslyGenerated || areObviouslyDifferent || thereAreCodeGenerationInconsistencies;

            registry.PurgeWhere(_ => shouldPurge);

            return(shouldPurge);
        }