// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Runtime.InteropServices; using System.Text; using RuntimeEnvironment = Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment; namespace Reporting { public class Reporter { private Run run; private Os os; private Build build; private List tests = new List(); protected IEnvironment environment; private Reporter() { } public void AddTest(Test test) { if (tests.Any(t => t.Name.Equals(test.Name))) throw new Exception($"Duplicate test name, {test.Name}"); tests.Add(test); } /// /// Get a Reporter. Relies on environment variables. /// /// Optional environment variable provider /// A Reporter instance or null if the environment is incorrect. public static Reporter CreateReporter(IEnvironment environment = null) { var ret = new Reporter(); ret.environment = environment == null ? new EnvironmentProvider() : environment; if (ret.InLab) { ret.Init(); } return ret; } private void Init() { run = new Run { CorrelationId = environment.GetEnvironmentVariable("HELIX_CORRELATION_ID"), PerfRepoHash = environment.GetEnvironmentVariable("PERFLAB_PERFHASH"), Name = environment.GetEnvironmentVariable("PERFLAB_RUNNAME"), Queue = environment.GetEnvironmentVariable("PERFLAB_QUEUE"), }; Boolean.TryParse(environment.GetEnvironmentVariable("PERFLAB_HIDDEN"), out bool hidden); run.Hidden = hidden; var configs = environment.GetEnvironmentVariable("PERFLAB_CONFIGS"); if (!String.IsNullOrEmpty(configs)) // configs should be optional. { foreach (var kvp in configs.Split(';')) { var split = kvp.Split('='); run.Configurations.Add(split[0], split[1]); } } os = new Os() { Name = $"{RuntimeEnvironment.OperatingSystem} {RuntimeEnvironment.OperatingSystemVersion}", Architecture = RuntimeInformation.OSArchitecture.ToString(), Locale = CultureInfo.CurrentUICulture.ToString() }; build = new Build { Repo = environment.GetEnvironmentVariable("PERFLAB_REPO"), Branch = environment.GetEnvironmentVariable("PERFLAB_BRANCH"), Architecture = environment.GetEnvironmentVariable("PERFLAB_BUILDARCH"), Locale = environment.GetEnvironmentVariable("PERFLAB_LOCALE"), GitHash = environment.GetEnvironmentVariable("PERFLAB_HASH"), BuildName = environment.GetEnvironmentVariable("PERFLAB_BUILDNUM"), TimeStamp = DateTime.Parse(environment.GetEnvironmentVariable("PERFLAB_BUILDTIMESTAMP")), }; build.AdditionalData["productVersion"] = environment.GetEnvironmentVariable("DOTNET_VERSION"); } public string GetJson() { if (!InLab) { return null; } var jsonobj = new { build, os, run, tests }; var settings = new JsonSerializerSettings(); var resolver = new DefaultContractResolver(); resolver.NamingStrategy = new CamelCaseNamingStrategy() { ProcessDictionaryKeys = false }; settings.ContractResolver = resolver; return JsonConvert.SerializeObject(jsonobj, Formatting.Indented, settings); } public string WriteResultTable() { StringBuilder ret = new StringBuilder(); foreach (var test in tests) { var defaultCounter = test.Counters.Single(c => c.DefaultCounter); var topCounters = test.Counters.Where(c => c.TopCounter && !c.DefaultCounter); var restCounters = test.Counters.Where(c => !(c.TopCounter || c.DefaultCounter)); var counterWidth = Math.Max(test.Counters.Max(c => c.Name.Length) + 1, 15); var resultWidth = Math.Max(test.Counters.Max(c => c.Results.Max().ToString("F3").Length + c.MetricName.Length) + 2, 15); ret.AppendLine(test.Name); ret.AppendLine($"{LeftJustify("Metric", counterWidth)}|{LeftJustify("Average",resultWidth)}|{LeftJustify("Min", resultWidth)}|{LeftJustify("Max",resultWidth)}"); ret.AppendLine($"{new String('-', counterWidth)}|{new String('-', resultWidth)}|{new String('-', resultWidth)}|{new String('-', resultWidth)}"); ret.AppendLine(Print(defaultCounter, counterWidth, resultWidth)); foreach(var counter in topCounters) { ret.AppendLine(Print(counter, counterWidth, resultWidth)); } foreach (var counter in restCounters) { ret.AppendLine(Print(counter, counterWidth, resultWidth)); } } return ret.ToString(); } private string Print(Counter counter, int counterWidth, int resultWidth) { string average = $"{counter.Results.Average():F3} {counter.MetricName}"; string max = $"{counter.Results.Max():F3} {counter.MetricName}"; string min = $"{counter.Results.Min():F3} {counter.MetricName}"; return $"{LeftJustify(counter.Name, counterWidth)}|{LeftJustify(average, resultWidth)}|{LeftJustify(min, resultWidth)}|{LeftJustify(max, resultWidth)}"; } private string LeftJustify(string str, int width) { return String.Format("{0,-" + width + "}", str); } public bool InLab => environment.GetEnvironmentVariable("PERFLAB_INLAB")?.Equals("1") ?? false; } }