// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; using System.Collections.Generic; using System.Linq; using Microsoft.Coyote.Cli; using Microsoft.Coyote.Logging; using Microsoft.Coyote.Rewriting; using Microsoft.Coyote.SystematicTesting; namespace Microsoft.Coyote { /// /// The entry point to the Coyote tool. /// internal class Program { private static int Main(string[] args) { var parser = new CommandLineParser(args); if (!parser.IsSuccessful) { return (int)ExitCode.Error; } parser.SetTestCommandHandler(RunTest); parser.SetReplayCommandHandler(ReplayTest); parser.SetRewriteCommandHandler(RewriteAssemblies); return (int)parser.InvokeCommand(); } /// /// Runs the test specified in the configuration. /// private static ExitCode RunTest(Configuration configuration) { using var logWriter = new LogWriter(configuration, true); try { // Load the configuration of the assembly to be tested. LoadAssemblyConfiguration(configuration.AssemblyToBeAnalyzed, logWriter); logWriter.LogImportant(". Testing {0}.", configuration.AssemblyToBeAnalyzed); using TestingEngine engine = new TestingEngine(configuration, logWriter); AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; engine.Run(); string directory = OutputFileManager.CreateOutputDirectory(configuration); string fileName = OutputFileManager.GetResolvedFileName(configuration.AssemblyToBeAnalyzed, directory); // Emit the test reports. logWriter.LogImportant("... Emitting execution trace reports:"); if (engine.TryEmitReports(directory, fileName, out IEnumerable reportPaths)) { foreach (var path in reportPaths) { logWriter.LogImportant("..... Writing {0}", path); } } else { logWriter.LogImportant("..... No test reports available."); } // Emit the coverage reports. logWriter.LogImportant("... Emitting coverage reports:"); if (engine.TryEmitCoverageReports(directory, fileName, out reportPaths)) { foreach (var path in reportPaths) { logWriter.LogImportant("..... Writing {0}", path); } } else { logWriter.LogImportant("..... No coverage reports available."); } logWriter.LogImportant(engine.TestReport.GetText(configuration, "...")); logWriter.LogImportant("... Elapsed {0} sec.", engine.Profiler.Results()); return GetExitCodeFromTestReport(engine.TestReport); } catch (Exception ex) { logWriter.LogError(ex.Message); logWriter.LogDebug(ex.StackTrace); return ExitCode.Error; } } /// /// Replays an execution that is specified in the configuration. /// private static ExitCode ReplayTest(Configuration configuration) { using var logWriter = new LogWriter(configuration, true); try { // Load the configuration of the assembly to be replayed. LoadAssemblyConfiguration(configuration.AssemblyToBeAnalyzed, logWriter); logWriter.LogImportant(". Reproducing trace in {0}.", configuration.AssemblyToBeAnalyzed); using TestingEngine engine = new TestingEngine(configuration, logWriter); AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; engine.Run(); // Emit the report. if (engine.TestReport.NumOfFoundBugs > 0) { logWriter.LogImportant(engine.GetReport()); } logWriter.LogImportant("... Elapsed {0} sec.", engine.Profiler.Results()); return GetExitCodeFromTestReport(engine.TestReport); } catch (Exception ex) { logWriter.LogError(ex.Message); logWriter.LogDebug(ex.StackTrace); return ExitCode.Error; } } /// /// Rewrites the assemblies specified in the configuration. /// private static ExitCode RewriteAssemblies(Configuration configuration, RewritingOptions options) { using var logWriter = new LogWriter(configuration, true); try { if (options.AssemblyPaths.Count is 1) { logWriter.LogImportant(". Rewriting {0}.", options.AssemblyPaths.First()); } else { logWriter.LogImportant(". Rewriting the assemblies specified in {0}.", options.AssembliesDirectory); } var profiler = new Profiler(); RewritingEngine.Run(options, configuration, logWriter, profiler); logWriter.LogImportant("... Elapsed {0} sec.", profiler.Results()); } catch (Exception ex) { if (ex is AggregateException aex) { ex = aex.Flatten().InnerException; } logWriter.LogError(ex.Message); logWriter.LogDebug(ex.StackTrace); return ExitCode.Error; } return ExitCode.Success; } /// /// Loads the configuration of the specified assembly. /// private static void LoadAssemblyConfiguration(string assemblyFile, LogWriter logWriter) { // Load config file and absorb its settings. try { var configFile = System.Configuration.ConfigurationManager.OpenExeConfiguration(assemblyFile); var settings = configFile.AppSettings.Settings; foreach (var key in settings.AllKeys) { if (System.Configuration.ConfigurationManager.AppSettings.Get(key) is null) { System.Configuration.ConfigurationManager.AppSettings.Set(key, settings[key].Value); } else { System.Configuration.ConfigurationManager.AppSettings.Add(key, settings[key].Value); } } } catch (System.Configuration.ConfigurationErrorsException ex) { logWriter.LogError(ex.Message); logWriter.LogDebug(ex.StackTrace); } } /// /// Callback invoked when an unhandled exception occurs. /// private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs args) => Environment.Exit((int)ExitCode.InternalError); private static ExitCode GetExitCodeFromTestReport(TestReport report) => report.InternalErrors.Count > 0 ? ExitCode.InternalError : report.NumOfFoundBugs > 0 ? ExitCode.BugFound : ExitCode.Success; } }