Beispiel #1
0
 public OrToolsSatGeneratedSyntaxTreeRegistryTests(ITestOutputHelper outputHelper)
     : base(outputHelper)
 {
     // ReSharper disable once RedundantEmptyObjectOrCollectionInitializer
     Registry = new OrToolsSatGeneratedSyntaxTreeRegistry {
     };
 }
Beispiel #2
0
        /// <summary>
        /// Loads the <paramref name="registry"/> and Purges Generated Code given
        /// <paramref name="serviceManager"/>.
        /// </summary>
        /// <param name="serviceManager"></param>
        /// <param name="registry"></param>
        private void LoadAndPurgeGeneratedCode(CodeGenerationServiceManager serviceManager
                                               , out OrToolsSatGeneratedSyntaxTreeRegistry registry)
        {
            if (DebugMessagesSwitch)
            {
                Writer.WriteLine("Loading and purging registry.");
            }

            /* Should not need to re-load any ServiceManager Registries,
             * since this is done inherently by the SM itself. */
            registry = serviceManager.Registry;

            if (!registry.Any())
            {
                if (DebugMessagesSwitch)
                {
                    Writer.WriteLine("There are no Registry entries.");
                }

                return;
            }

            // Work around output or reference parameters.
            var local            = registry;
            var outputDirectory  = local.OutputDirectory;
            var anUpdateOccurred = local.GoogleOrToolsVersion < GoogleOrToolsVersion;

            // Purge All if any of them are missing.
            bool ShouldPurgeRegistry(GeneratedSyntaxTreeDescriptor descriptor)
            {
                /* Easy Purge to make by Version first, followed closely by whether
                 * any previously registered files have themselves disappeared. */

                bool WhetherSomeFilesAreMissing()
                {
                    var someFilesAreMissing = descriptor.GeneratedAssetKeys
                                              .Select(y => Path.Combine(outputDirectory, y.RenderGeneratedFileName()))
                                              .Any(filePath => !File.Exists(filePath));

                    return(someFilesAreMissing);
                }

                var should = anUpdateOccurred || WhetherSomeFilesAreMissing();

                if (DebugMessagesSwitch)
                {
                    Writer.WriteLine($"Should{(should ? " " : " not ")}purge generated code.");
                }

                return(should);
            }

            registry.PurgeWhere(ShouldPurgeRegistry);
        }
Beispiel #3
0
        /// <inheritdoc />
        protected override JObject SerializeRegistry(OrToolsSatGeneratedSyntaxTreeRegistry registry)
        {
            var       @object    = base.SerializeRegistry(registry);
            const int fieldCount = 3;

            @object.Add(
                new JProperty(nameof(registry.GoogleOrToolsVersion)
                              , registry.GoogleOrToolsVersion.ToString(fieldCount))
                );
            return(@object);
        }
        private void VerifySingleCodeGenerationPass(out CodeGenerationConsoleManager manager
                                                    , out OrToolsSatGeneratedSyntaxTreeRegistry registry)
        {
            // TODO: TBD: for a single pass, it might make sense to refactor this block of code... rinse and repeat for other scenarios...
            VerifyManager((m, a) => ConfigureDefaultArguments(a), DefaultErrorLevel, out manager, out registry);

            var local = registry;

            Path.Combine(registry.OutputDirectory, RegistryFileName).AssertFileExists();

            void VerifyGeneratedFileExists(Guid key) => Path.Combine(local.OutputDirectory, key.RenderGeneratedFileName()).AssertFileExists();

            registry.AssertNotNull().AssertNotEmpty().SelectMany(x => x.GeneratedAssetKeys)
            .ToList().ForEach(VerifyGeneratedFileExists);
        }
        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);
            }
        }
        internal void VerifyManager(ConfigureManagerCallback configure, int expectedErrorLevel
                                    , out CodeGenerationConsoleManager manager, out OrToolsSatGeneratedSyntaxTreeRegistry registry)
        {
            manager = Manager.AssertNotNull();

            configure.AssertNotNull().Invoke(manager, Arguments);

            manager.TryParseOrShowHelp(Arguments.ToArray()).AssertTrue();

            manager.Run(out var actualErrorLevel);

            // Whatever the outcome was, obtain that Registry.
            registry = manager.Registry;

            actualErrorLevel.AssertEqual(expectedErrorLevel);

            // ReSharper disable once SwitchStatementMissingSomeCases
            switch (expectedErrorLevel)
            {
            case DefaultErrorLevel:
                registry.AssertNotNull().AssertNotEmpty();
                break;

            case ErrorGeneratingCode:
                // TODO: TBD: may elaborate on what this means, 'error' ...
                break;

            case MustSpecifyOutputDirectory:
                manager.OutputDirectoryVar.Value.AssertTrue(IsNullOrEmpty);
                registry.AssertNull();
                break;

            case MustSpecifyRegistryFileName:
                manager.OutputDirectoryVar.Value.AssertNotNull().AssertNotEmpty();
                manager.GeneratedCodeRegistryFileVar.Value.AssertTrue(IsNullOrEmpty);
                registry.AssertNull();
                break;
            }
        }
Beispiel #7
0
        // ReSharper disable once MemberCanBeMadeStatic.Local
        private int?GenerateCode()
        {
            void EnsureDirectoryExists(string path)
            {
                if (Directory.Exists(path))
                {
                    return;
                }

                Directory.CreateDirectory(path);
            }

            try
            {
                string outputDirectory           = OutputDirectoryVar;
                string generatedCodeRegistryFile = GeneratedCodeRegistryFileVar;

                EnsureDirectoryExists(outputDirectory);

                if (DebugMessagesSwitch)
                {
                    Writer.WriteLine($"Output Directory is '{outputDirectory}', registry file name is '{generatedCodeRegistryFile}'.");
                }

                var serviceManager = new CodeGenerationServiceManager(outputDirectory, generatedCodeRegistryFile);

                LoadAndPurgeGeneratedCode(serviceManager, out var registry);

                Registry = registry;

                registry.AssumesTrue(x => ReferenceEquals(x, serviceManager.Registry));

                // ReSharper disable once RedundantEmptyObjectOrCollectionInitializer
                var service = new SatParameterCodeGeneratorService {
                };

                var descriptor = service.Descriptor;

                service.CodeGenerationVisitor.Visit(descriptor);

                var compilationUnits = service.CodeGenerationVisitor.CompilationUnits;

                if (DebugMessagesSwitch)
                {
                    Writer.WriteLine(
                        $"There are {compilationUnits.Count} potential new compilation units as compared"
                        + $" to {registry.SelectMany(x => x.GeneratedAssetKeys).Count()} old ones."
                        );
                }

                // TODO: TBD: this is what we are really talking about...
                // TODO: TBD: we need to find an exact match, otherwise, we reject the current set and regenerate...
                // TODO: TBD: no need to go ad-hoc compiling any previously generated code... that is entirely unnecessary.
                var renderedCompilationUnits = compilationUnits.ToDictionary(
                    x => x.Key
                    , x => x.Value.NormalizeWhitespace().ToFullString()
                    );

                if (TryEvaluateCompilationUnits(registry, renderedCompilationUnits))
                {
                    if (DebugMessagesSwitch)
                    {
                        Writer.WriteLine(
                            "Evaluation complete, there are"
                            + $" {renderedCompilationUnits.Count} rendered compilation units."
                            );
                    }

                    var generatedDescriptor = GeneratedSyntaxTreeDescriptor.Create();

                    foreach (var(key, renderedCompilationUnit) in renderedCompilationUnits)
                    {
                        var filePath = Path.Combine(outputDirectory, key.RenderGeneratedFileName());

                        using (var fs = File.Open(filePath, FileMode.CreateNew, FileAccess.Write, FileShare.Read))
                        {
                            using (var sw = new StreamWriter(fs))
                            {
                                sw.WriteLine(renderedCompilationUnit);
                            }
                        }

                        generatedDescriptor.GeneratedAssetKeys.Add(key);
                    }

                    registry.Add(generatedDescriptor);

                    if (DebugMessagesSwitch)
                    {
                        Writer.WriteLine(
                            "Service Manager Registry instance"
                            + $" is{(ReferenceEquals(serviceManager.Registry, registry) ? " " : " not ")}the same."
                            );
                    }

                    if (DebugMessagesSwitch)
                    {
                        // TODO: TBD: do we need exposure of the SM Registry?
                        Writer.WriteLine(
                            $"There are {generatedDescriptor.GeneratedAssetKeys.Count} generated items and"
                            + $" {serviceManager.Registry.SelectMany(x => x.GeneratedAssetKeys).Count()} total entries to save."
                            );
                    }

                    serviceManager.TrySave();
                }
            }
            catch (Exception ex)
            {
                ReportException(ex);
                return(ErrorGeneratingCode);
            }

            return(null);
        }
Beispiel #8
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);
        }
 private void VerifySingleCodeGenerationPass(out OrToolsSatGeneratedSyntaxTreeRegistry registry)
 => VerifySingleCodeGenerationPass(out _, out registry);