/// <summary> /// Rewrites the assemblies specified in the configuration. /// </summary> private static void RewriteAssemblies(Configuration configuration, RewritingOptions options) { try { if (options.AssemblyPaths.Count is 1) { Console.WriteLine($". Rewriting {options.AssemblyPaths.First()}"); } else { Console.WriteLine($". Rewriting the assemblies specified in {options.AssembliesDirectory}"); } var profiler = new Profiler(); RewritingEngine.Run(options, configuration, profiler); Console.WriteLine($". Done rewriting in {profiler.Results()} sec"); } catch (Exception ex) { if (ex is AggregateException aex) { ex = aex.Flatten().InnerException; } Error.ReportAndExit(configuration.IsDebugVerbosityEnabled ? ex.ToString() : ex.Message); } }
/// <summary> /// Rewrites the assemblies specified in the configuration. /// </summary> private static void RewriteAssemblies(Configuration configuration, RewritingOptions options) { try { string assemblyDir = null; var fileList = new HashSet <string>(); if (!string.IsNullOrEmpty(configuration.AssemblyToBeAnalyzed)) { var fullPath = Path.GetFullPath(configuration.AssemblyToBeAnalyzed); Console.WriteLine($". Rewriting {fullPath}"); assemblyDir = Path.GetDirectoryName(fullPath); fileList.Add(fullPath); } else if (Directory.Exists(configuration.RewritingOptionsPath)) { assemblyDir = Path.GetFullPath(configuration.RewritingOptionsPath); Console.WriteLine($". Rewriting the assemblies specified in {assemblyDir}"); } RewritingOptions config = options; if (!string.IsNullOrEmpty(assemblyDir)) { // Create a new RewritingOptions object from command line args only. config.AssembliesDirectory = assemblyDir; config.OutputDirectory = assemblyDir; config.AssemblyPaths = fileList; } else { // Load options from JSON file. config = RewritingOptions.ParseFromJSON(configuration.RewritingOptionsPath); Console.WriteLine($". Rewriting the assemblies specified in {configuration.RewritingOptionsPath}"); config.PlatformVersion = configuration.PlatformVersion; // Allow command line options to override the JSON configuration file options. config.Merge(options); } RewritingEngine.Run(configuration, config); } catch (Exception ex) { Debug.WriteLine(ex.StackTrace); Error.ReportAndExit(ex.Message); } }
/// <summary> /// Write the rewriting options in a JSON file. /// </summary> private static void ProduceRewritingOptions(string path, IDictionary <int, HashSet <string> > assemblyFrequencies) { foreach (var(info, file) in GetTestProjectInfos(path)) { var supportedAssemblies = new HashSet <string>(); foreach (var assembly in info.Assemblies) { if (!info.UnsupportedAssemblies.Contains(assembly)) { supportedAssemblies.Add(assembly); } } bool skip = false; foreach (var assembly in info.TestAssemblies) { if (!supportedAssemblies.Contains(assembly)) { skip = true; break; } } if (skip) { continue; } var directoryPath = Path.GetDirectoryName(file); var options = new RewritingOptions { AssembliesPath = ".", OutputPath = Path.Combine(".", "rewritten"), IsRewritingUnitTests = true }; foreach (var assembly in supportedAssemblies) { options.Assemblies.Add(assembly); } string reportFile = $"{Path.Combine(directoryPath, RewritingOptions.FileName)}"; Console.WriteLine($"... Writing rewriting options to '{reportFile}'"); string report = JsonSerializer.Serialize(options, GetJsonSerializerOptions()); File.WriteAllText(reportFile, report); } }
public void TestJsonConfigurationDifferentOutputDirectory() { string configDirectory = GetJsonConfigurationDirectory("Configurations"); string configPath = Path.Combine(configDirectory, "test1.coyote.json"); Assert.True(File.Exists(configPath)); var options = RewritingOptions.ParseFromJSON(configPath); Assert.NotNull(options); options.PlatformVersion = GetPlatformVersion(); Assert.Equal(Path.Combine(configDirectory, "Input"), options.AssembliesDirectory); Assert.Equal(Path.Combine(configDirectory, "Input", "Output"), options.OutputDirectory); Assert.False(options.IsReplacingAssemblies); Assert.Single(options.AssemblyPaths); Assert.Equal(Path.Combine(options.AssembliesDirectory, "Test.dll"), options.AssemblyPaths.First()); }
public void TestJsonConfigurationReplacingBinaries() { string configDirectory = GetJsonConfigurationDirectory("Configurations"); string configPath = Path.Combine(configDirectory, "test2.coyote.json"); Assert.True(File.Exists(configPath), "File not found: " + configPath); var options = RewritingOptions.ParseFromJSON(configPath); Assert.NotNull(options); options.PlatformVersion = GetPlatformVersion(); Assert.Equal(configDirectory, options.AssembliesDirectory); Assert.Equal(configDirectory, options.OutputDirectory); Assert.True(options.IsReplacingAssemblies); Assert.Equal(2, options.AssemblyPaths.Count()); Assert.Equal(Path.Combine(options.AssembliesDirectory, "Test1.dll"), options.AssemblyPaths.First()); }
public void TestJsonConfigurationDifferentOutputDirectory() { string configDirectory = GetJsonConfigurationDirectory("Configurations"); string configPath = Path.Combine(configDirectory, "test1.coyote.json"); Assert.True(File.Exists(configPath)); var options = RewritingOptions.ParseFromJSON(configPath); Assert.NotNull(options); options = options.Sanitize(); Assert.Equal(Path.Combine(configDirectory, "Input"), options.AssembliesDirectory); Assert.Equal(Path.Combine(configDirectory, "Input", "Output"), options.OutputDirectory); Assert.False(options.IsReplacingAssemblies()); Assert.Single(options.AssemblyPaths); Assert.Equal(Path.Combine(options.AssembliesDirectory, "Test.dll"), options.AssemblyPaths.First()); // Ensure this option defaults to true. Assert.True(options.IsRewritingConcurrentCollections); }
public void TestJsonConfigurationReplacingBinaries() { string configDirectory = GetJsonConfigurationDirectory("Configurations"); string configPath = Path.Combine(configDirectory, "test2.coyote.json"); Assert.True(File.Exists(configPath), "File not found: " + configPath); var options = RewritingOptions.ParseFromJSON(configPath); Assert.NotNull(options); options = options.Sanitize(); Assert.Equal(configDirectory, options.AssembliesDirectory); Assert.Equal(configDirectory, options.OutputDirectory); Assert.True(options.IsReplacingAssemblies()); Assert.Equal(2, options.AssemblyPaths.Count()); Assert.Equal(Path.Combine(options.AssembliesDirectory, "Test1.dll"), options.AssemblyPaths.First()); // Ensure this option can be set to false. Assert.False(options.IsRewritingConcurrentCollections); }
/// <summary> /// Parses the command line options and returns a configuration. /// </summary> /// <param name="args">The command line arguments to parse.</param> /// <param name="configuration">The Configuration object populated with the parsed command line options.</param> /// <param name="options">The optional rewriting options.</param> internal bool Parse(string[] args, Configuration configuration, RewritingOptions options) { try { var result = this.Parser.ParseArguments(args); if (result != null) { foreach (var arg in result) { UpdateConfigurationWithParsedArgument(configuration, options, arg); } SanitizeConfiguration(configuration); return(true); } } catch (CommandLineException ex) { if ((from arg in ex.Result where arg.LongName == "version" select arg).Any()) { WriteVersion(); Environment.Exit(1); } else { this.Parser.PrintHelp(Console.Out); Error.ReportAndExit(ex.Message); } } catch (Exception ex) { this.Parser.PrintHelp(Console.Out); Error.ReportAndExit(ex.Message); } return(false); }
private static void Main(string[] args) { // Save these so we can force output to happen even if TestingProcess has re-routed it. StdOut = Console.Out; StdError = Console.Error; AppDomain.CurrentDomain.ProcessExit += OnProcessExit; AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; Console.CancelKeyPress += OnProcessCanceled; // Parses the command line options to get the configuration and rewriting options. var configuration = Configuration.Create(); configuration.TelemetryServerPath = typeof(Program).Assembly.Location; var rewritingOptions = RewritingOptions.Create(); var result = CoyoteTelemetryClient.GetOrCreateMachineId().Result; bool firstTime = result.Item2; var options = new CommandLineOptions(); if (!options.Parse(args, configuration, rewritingOptions)) { options.PrintHelp(Console.Out); if (!firstTime && configuration.EnableTelemetry) { CoyoteTelemetryClient.PrintTelemetryMessage(Console.Out); } Environment.Exit(1); } if (!configuration.RunAsParallelBugFindingTask) { if (firstTime) { string version = typeof(Runtime.CoyoteRuntime).Assembly.GetName().Version.ToString(); Console.WriteLine("Welcome to Microsoft Coyote {0}", version); Console.WriteLine("----------------------------{0}", new string('-', version.Length)); if (configuration.EnableTelemetry) { CoyoteTelemetryClient.PrintTelemetryMessage(Console.Out); } TelemetryClient = new CoyoteTelemetryClient(configuration); TelemetryClient.TrackEventAsync("welcome").Wait(); } Console.WriteLine("Microsoft (R) Coyote version {0} for .NET{1}", typeof(CommandLineOptions).Assembly.GetName().Version, GetDotNetVersion()); Console.WriteLine("Copyright (C) Microsoft Corporation. All rights reserved.\n"); } SetEnvironment(configuration); switch (configuration.ToolCommand.ToLower()) { case "test": RunTest(configuration); break; case "replay": ReplayTest(configuration); break; case "rewrite": RewriteAssemblies(configuration, rewritingOptions); break; case "telemetry": RunServer(configuration); break; } }
/// <summary> /// Updates the configuration with the specified parsed argument. /// </summary> private static void UpdateConfigurationWithParsedArgument(Configuration configuration, RewritingOptions options, CommandLineArgument option) { switch (option.LongName) { case "command": configuration.ToolCommand = (string)option.Value; break; case "outdir": configuration.OutputFilePath = (string)option.Value; break; case "debug": configuration.IsDebugVerbosityEnabled = true; Debug.IsEnabled = true; break; case "verbosity": configuration.IsVerbose = true; string verbosity = (string)option.Value; switch (verbosity) { case "quiet": configuration.IsVerbose = false; break; case "detailed": configuration.LogLevel = options.LogLevel = LogSeverity.Informational; break; case "normal": configuration.LogLevel = options.LogLevel = LogSeverity.Warning; break; case "minimal": configuration.LogLevel = options.LogLevel = LogSeverity.Error; break; default: Error.ReportAndExit($"Please give a valid value for 'verbosity' must be one of 'errors', 'warnings', or 'info', but found {verbosity}."); break; } break; case "path": if (configuration.ToolCommand is "test" || configuration.ToolCommand is "replay") { // In the case of 'coyote test' or 'replay', the path is the assembly to be tested. configuration.AssemblyToBeAnalyzed = (string)option.Value; } else if (configuration.ToolCommand is "rewrite") { // In the case of 'coyote rewrite', the path is the JSON configuration file // with the binary rewriting options. string filename = (string)option.Value; if (Directory.Exists(filename)) { // then we want to rewrite a whole folder full of dll's. configuration.RewritingOptionsPath = filename; } else { string extension = Path.GetExtension(filename); if (string.Compare(extension, ".json", StringComparison.OrdinalIgnoreCase) is 0) { configuration.RewritingOptionsPath = filename; } else if (string.Compare(extension, ".dll", StringComparison.OrdinalIgnoreCase) is 0 || string.Compare(extension, ".exe", StringComparison.OrdinalIgnoreCase) is 0) { configuration.AssemblyToBeAnalyzed = filename; } else { Error.ReportAndExit("Please give a valid .dll or JSON configuration file for binary rewriting."); } }
/// <summary> /// Initializes a new instance of the <see cref="CommandLineParser"/> class. /// </summary> internal CommandLineParser(string[] args) { this.Configuration = Configuration.Create(); this.RewritingOptions = RewritingOptions.Create(); this.Arguments = new Dictionary <string, Argument>(); this.Options = new Dictionary <string, Option>(); var allowedVerbosityLevels = new HashSet <string> { "quiet", "minimal", "normal", "detailed" }; var verbosityOption = new Option <string>( aliases: new[] { "-v", "--verbosity" }, getDefaultValue: () => "quiet", description: "Enable verbosity with an optional verbosity level. " + $"Allowed values are {string.Join(", ", allowedVerbosityLevels)}. " + "Skipping the argument sets the verbosity level to 'detailed'.") { ArgumentHelpName = "LEVEL", Arity = ArgumentArity.ZeroOrOne }; var debugOption = new Option <bool>(aliases: new[] { "-d", "--debug" }) { Arity = ArgumentArity.Zero }; // Add validators. verbosityOption.AddValidator(result => ValidateOptionValueIsAllowed(result, allowedVerbosityLevels)); // Create the commands. this.TestCommand = this.CreateTestCommand(this.Configuration); this.ReplayCommand = this.CreateReplayCommand(); this.RewriteCommand = this.CreateRewriteCommand(); // Create the root command. var rootCommand = new RootCommand("The Coyote systematic testing tool.\n\n" + $"Learn how to use Coyote at {LearnAboutCoyoteUrl}.\nLearn what is new at {LearnWhatIsNewUrl}."); this.AddGlobalOption(rootCommand, verbosityOption); this.AddGlobalOption(rootCommand, debugOption); rootCommand.AddCommand(this.TestCommand); rootCommand.AddCommand(this.ReplayCommand); rootCommand.AddCommand(this.RewriteCommand); rootCommand.TreatUnmatchedTokensAsErrors = true; var commandLineBuilder = new CommandLineBuilder(rootCommand); commandLineBuilder.UseDefaults(); var parser = commandLineBuilder.Build(); this.Results = parser.Parse(args); if (this.Results.Errors.Any() || IsHelpRequested(this.Results)) { // There are parsing errors, so invoke the result to print the errors and help message. this.Results.Invoke(); this.IsSuccessful = false; } else { // There were no errors, so use the parsed results to update the default configurations. this.UpdateConfigurations(this.Results); this.IsSuccessful = true; } }