diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 868448437c2..3f85b7f5d94 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -247,6 +247,19 @@ jobs: projectFile: maui_scenarios.proj channels: - release/7.0 + + # Maui Android scenario benchmarks + - template: /eng/performance/build_machine_matrix.yml + parameters: + jobTemplate: /eng/performance/scenarios.yml + buildMachines: + - win-x64-android-arm64 + isPublic: false + jobParameters: + kind: maui_scenarios_android + projectFile: maui_scenarios_android.proj + channels: + - release/7.0 ################################################ # Scheduled Private jobs diff --git a/eng/performance/build_machine_matrix.yml b/eng/performance/build_machine_matrix.yml index f525325cea7..63b95f58f0e 100644 --- a/eng/performance/build_machine_matrix.yml +++ b/eng/performance/build_machine_matrix.yml @@ -91,4 +91,16 @@ jobs: vmImage: windows-2019 machinePool: Tiger queue: Windows.10.Arm64.Perf.Surf - ${{ insert }}: ${{ parameters.jobParameters }} \ No newline at end of file + ${{ insert }}: ${{ parameters.jobParameters }} + +- ${{ if and(containsValue(parameters.buildMachines, 'win-x64-android-arm64'), not(eq(parameters.isPublic, true))) }}: # Windows ARM64 Pixel only used in private builds currently + - template: ${{ parameters.jobTemplate }} + parameters: + osName: windows + architecture: x64 + osVersion: 19H1 + pool: + vmImage: 'windows-2022' + queue: Windows.10.Amd64.Pixel.Perf + machinePool: Pixel + ${{ insert }}: ${{ parameters.jobParameters }} diff --git a/eng/performance/maui_scenarios_android.proj b/eng/performance/maui_scenarios_android.proj new file mode 100644 index 00000000000..270288b148a --- /dev/null +++ b/eng/performance/maui_scenarios_android.proj @@ -0,0 +1,82 @@ + + + + + + true + 1.0.0-prerelease.21566.2 + %HELIX_CORRELATION_PAYLOAD%\microsoft.dotnet.xharness.cli\$(MicrosoftDotNetXHarnessCLIVersion)\tools\net6.0\any\Microsoft.DotNet.XHarness.CLI.dll + + + + $(Python) post.py + scenarios_out + $(CorrelationPayloadDirectory)$(PreparePayloadOutDirectoryName)\ + $(CorrelationPayloadDirectory)$(PreparePayloadOutDirectoryName)/ + + + + + + + + + + 00:30 + + + + + + mauiandroid + $(ScenariosDir)%(ScenarioDirectoryName) + com.companyname.mauiandroiddefault-Signed + com.companyname.mauiandroiddefault + + + mauiandroidpodcast + $(ScenariosDir)%(ScenarioDirectoryName) + com.Microsoft.NetConf2021.Maui-Signed + com.Microsoft.NetConf2021.Maui + + + mauiblazorandroid + $(ScenariosDir)%(ScenarioDirectoryName) + com.companyname.mauiblazorandroiddefault-Signed + com.companyname.mauiblazorandroiddefault + + + + + + + $(Python) pre.py publish -f $(PERFLAB_Framework)-android -o $(PreparePayloadWorkItemBaseDirectory)%(PreparePayloadWorkItem.ScenarioDirectoryName) -r android-arm64 --self-contained + %(PreparePayloadWorkItem.PayloadDirectory) + + + + + + + + echo on; xcopy %HELIX_CORRELATION_PAYLOAD%\$(PreparePayloadOutDirectoryName)\%(HelixWorkItem.ScenarioDirectoryName) %HELIX_WORKITEM_ROOT%\pub\ /E /I /Y + $(Python) test.py sod --scenario-name "%(Identity)" + + + echo on; xcopy %HELIX_CORRELATION_PAYLOAD%\$(PreparePayloadOutDirectoryName)\%(HelixWorkItem.ScenarioDirectoryName) %HELIX_WORKITEM_ROOT%\pub\ /E /I /Y; ren %HELIX_WORKITEM_ROOT%\pub\%(HelixWorkItem.ApkName).apk %(HelixWorkItem.ApkName).zip; powershell.exe -nologo -noprofile -command "& {Expand-Archive %HELIX_WORKITEM_ROOT%\pub\%(HelixWorkItem.ApkName).zip -DestinationPath %HELIX_WORKITEM_ROOT%\pub\}"; del %HELIX_WORKITEM_ROOT%\pub\%(HelixWorkItem.ApkName).zip + $(Python) test.py sod --scenario-name "%(Identity)" + + + echo on; set XHARNESSPATH=$(XharnessPath); xcopy %HELIX_CORRELATION_PAYLOAD%\$(PreparePayloadOutDirectoryName)\%(HelixWorkItem.ScenarioDirectoryName) %HELIX_WORKITEM_ROOT%\pub\ /E /I /Y + $(Python) test.py devicestartup --device-type android --package-path pub\%(HelixWorkItem.ApkName).apk --package-name %(HelixWorkItem.PackageName) --scenario-name "%(Identity)" + + + echo on; set XHARNESSPATH=$(XharnessPath); xcopy %HELIX_CORRELATION_PAYLOAD%\$(PreparePayloadOutDirectoryName)\%(HelixWorkItem.ScenarioDirectoryName) %HELIX_WORKITEM_ROOT%\pub\ /E /I /Y + $(Python) test.py devicestartup --device-type android --package-path pub\%(HelixWorkItem.ApkName).apk --package-name %(HelixWorkItem.PackageName) --scenario-name "%(Identity)" --disable-animations + + + + + + + diff --git a/scripts/dotnet.py b/scripts/dotnet.py index 77010e0e5c8..58284a3cc9b 100755 --- a/scripts/dotnet.py +++ b/scripts/dotnet.py @@ -5,13 +5,14 @@ """ import ssl +import datetime from argparse import Action, ArgumentParser, ArgumentTypeError, ArgumentError from collections import namedtuple from glob import iglob from json import loads from logging import getLogger from os import chmod, environ, listdir, makedirs, path, pathsep, system -from re import search +from re import search, match, MULTILINE from shutil import rmtree from stat import S_IRWXU from subprocess import CalledProcessError, check_output @@ -611,27 +612,30 @@ def get_commit_date( if not commit_sha: raise ValueError('.NET Commit sha was not defined.') + # Example URL: https://github.com/dotnet/runtime/commit/2d76178d5faa97be86fc8d049c7dbcbdf66dc497.patch url = None - urlformat = 'https://api.github.com/repos/%s/%s/commits/%s' if repository is None: # The origin of the repo where the commit belongs to has changed # between release. Here we attempt to naively guess the repo. core_sdk_frameworks = ChannelMap.get_supported_frameworks() repo = 'core-sdk' if framework in core_sdk_frameworks else 'cli' - url = urlformat % ('dotnet', repo, commit_sha) + url = f'https://github.com/dotnet/{repo}/commit/{commit_sha}.patch' else: owner, repo = get_repository(repository) - url = urlformat % (owner, repo, commit_sha) + url = f'https://github.com/{owner}/{repo}/commit/{commit_sha}.patch' build_timestamp = None - sleep_time = 10 # Start with 10 second sleep timer + sleep_time = 10 # Start with 10 second sleep timer for retrycount in range(5): try: with urlopen(url) as response: getLogger().info("Commit: %s", url) - item = loads(response.read().decode('utf-8')) - build_timestamp = item['commit']['committer']['date'] - break + patch = response.read().decode('utf-8') + dateMatch = search(r'^Date: (.+)$', patch, MULTILINE) + if dateMatch: + build_timestamp = datetime.datetime.strptime(dateMatch.group(1), '%a, %d %b %Y %H:%M:%S %z').astimezone(datetime.timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ') + getLogger().info(f"Got UTC timestamp {build_timestamp} from {dateMatch.group(1)}") + break except URLError as error: getLogger().warning(f"URL Error trying to get commit date from {url}; Reason: {error.reason}; Attempt {retrycount}") sleep(sleep_time) diff --git a/src/scenarios/mauiandroid/pre.py b/src/scenarios/mauiandroid/pre.py index aed0d76c750..ce508b17fe6 100644 --- a/src/scenarios/mauiandroid/pre.py +++ b/src/scenarios/mauiandroid/pre.py @@ -2,48 +2,37 @@ pre-command ''' import sys -import os -from zipfile import ZipFile from performance.logger import setup_loggers, getLogger -from shutil import copyfile +from shared import const +from shared.mauisharedpython import remove_aab_files, install_versioned_maui from shared.precommands import PreCommands -from shared.const import PUBDIR -from argparse import ArgumentParser +from shared.versionmanager import versions_write_json, get_version_from_dll_powershell +from test import EXENAME setup_loggers(True) -parser = ArgumentParser() -parser.add_argument('--unzip', help='Unzip APK and report extracted tree', action='store_true', default=False) -parser.add_argument( - '--apk-name', - dest='apk', - required=True, - type=str, - help='Name of the APK to setup') -args = parser.parse_args() - -if not os.path.exists(PUBDIR): - os.mkdir(PUBDIR) -apkname = args.apk -apknamezip = '%s.zip' % (apkname) -if not os.path.exists(apkname): - getLogger().error('Cannot find %s' % (apkname)) - exit(-1) -if args.unzip: - if not os.path.exists(apknamezip): - copyfile(apkname, apknamezip) - - with ZipFile(apknamezip) as zip: - zip.extractall(os.path.join('.', PUBDIR)) - - assets_dir = os.path.join(PUBDIR, 'assets') - assets_zip = os.path.join(assets_dir, 'assets.zip') - with ZipFile(assets_zip) as zip: - zip.extractall(assets_dir) - - os.remove(assets_zip) -else: - copyfile(apkname, os.path.join(PUBDIR, apkname)) - - - +precommands = PreCommands() +install_versioned_maui(precommands) + +# Setup the Maui folder +precommands.new(template='maui', + output_dir=const.APPDIR, + bin_dir=const.BINDIR, + exename=EXENAME, + working_directory=sys.path[0], + no_restore=False) + +# Build the APK +precommands.execute(['--no-restore', '--source', 'MauiNuGet.config']) + +# Remove the aab files as we don't need them, this saves space +output_dir = const.PUBDIR +if precommands.output: + output_dir = precommands.output +remove_aab_files(output_dir) + +# Copy the MauiVersion to a file so we have it on the machine +maui_version = get_version_from_dll_powershell(rf".\{const.APPDIR}\obj\Release\{precommands.framework}\{precommands.runtime_identifier}\linked\Microsoft.Maui.dll") +version_dict = { "mauiVersion": maui_version } +versions_write_json(version_dict, rf"{output_dir}\versions.json") +print(f"Versions: {version_dict}") \ No newline at end of file diff --git a/src/scenarios/mauiandroid/test.py b/src/scenarios/mauiandroid/test.py index 349ef2ebcc7..5976d343ee1 100644 --- a/src/scenarios/mauiandroid/test.py +++ b/src/scenarios/mauiandroid/test.py @@ -1,11 +1,15 @@ ''' -C# Console app +Mobile Maui App ''' +from shared.const import PUBDIR from shared.runner import TestTraits, Runner +from shared.versionmanager import versions_read_json_file_save_env EXENAME = 'MauiAndroidDefault' -if __name__ == "__main__": +if __name__ == "__main__": + versions_read_json_file_save_env(rf".\{PUBDIR}\versions.json") + traits = TestTraits(exename=EXENAME, guiapp='false', ) diff --git a/src/scenarios/mauiandroidpodcast/post.py b/src/scenarios/mauiandroidpodcast/post.py new file mode 100644 index 00000000000..abdb7d8db9d --- /dev/null +++ b/src/scenarios/mauiandroidpodcast/post.py @@ -0,0 +1,9 @@ +''' +post cleanup script +''' + +from shared.postcommands import clean_directories +from performance.common import remove_directory + +remove_directory("dotnet-podcasts") +clean_directories() diff --git a/src/scenarios/mauiandroidpodcast/pre.py b/src/scenarios/mauiandroidpodcast/pre.py new file mode 100644 index 00000000000..30892a686f7 --- /dev/null +++ b/src/scenarios/mauiandroidpodcast/pre.py @@ -0,0 +1,35 @@ +''' +pre-command +''' +import subprocess +from performance.logger import setup_loggers, getLogger +from shared.precommands import PreCommands +from shared.mauisharedpython import remove_aab_files, install_versioned_maui +from shared.versionmanager import versions_write_json, get_version_from_dll_powershell +from shared import const + +setup_loggers(True) +precommands = PreCommands() +install_versioned_maui(precommands) + +branch = f'{precommands.framework[:6]}' +subprocess.run(['git', 'clone', 'https://github.com/microsoft/dotnet-podcasts.git', '-b', branch, '--single-branch', '--depth', '1']) +subprocess.run(['powershell', '-Command', r'Remove-Item -Path .\\dotnet-podcasts\\.git -Recurse -Force']) # Git files have permission issues, do their deletion separately + +precommands.existing(projectdir='./dotnet-podcasts', projectfile='./src/Mobile/Microsoft.NetConf2021.Maui.csproj') + +# Build the APK +precommands._restore() +precommands.execute(['--no-restore']) + +# Remove the aab files as we don't need them, this saves space +output_dir = const.PUBDIR +if precommands.output: + output_dir = precommands.output +remove_aab_files(output_dir) + +# Copy the MauiVersion to a file so we have it on the machine +maui_version = get_version_from_dll_powershell(rf".\{const.APPDIR}\obj\Release\{precommands.framework}\{precommands.runtime_identifier}\linked\Microsoft.Maui.dll") +version_dict = { "mauiVersion": maui_version } +versions_write_json(version_dict, rf"{output_dir}\versions.json") +print(f"Versions: {version_dict}") \ No newline at end of file diff --git a/src/scenarios/mauiandroidpodcast/test.py b/src/scenarios/mauiandroidpodcast/test.py new file mode 100644 index 00000000000..d715ae7eb3a --- /dev/null +++ b/src/scenarios/mauiandroidpodcast/test.py @@ -0,0 +1,16 @@ +''' +Mobile Maui App +''' +from shared.const import PUBDIR +from shared.runner import TestTraits, Runner +from shared.versionmanager import versions_read_json_file_save_env + +EXENAME = 'MauiAndroidPodcast' + +if __name__ == "__main__": + versions_read_json_file_save_env(rf".\{PUBDIR}\versions.json") + + traits = TestTraits(exename=EXENAME, + guiapp='false', + ) + Runner(traits).run() \ No newline at end of file diff --git a/src/scenarios/mauiblazorandroid/pre.py b/src/scenarios/mauiblazorandroid/pre.py index aed0d76c750..c2c9f71f41d 100644 --- a/src/scenarios/mauiblazorandroid/pre.py +++ b/src/scenarios/mauiblazorandroid/pre.py @@ -2,48 +2,74 @@ pre-command ''' import sys -import os -from zipfile import ZipFile from performance.logger import setup_loggers, getLogger -from shutil import copyfile +from shared import const +from shared.mauisharedpython import remove_aab_files, install_versioned_maui from shared.precommands import PreCommands -from shared.const import PUBDIR -from argparse import ArgumentParser +from shared.versionmanager import versions_write_json, get_version_from_dll_powershell +from test import EXENAME setup_loggers(True) +precommands = PreCommands() +install_versioned_maui(precommands) -parser = ArgumentParser() -parser.add_argument('--unzip', help='Unzip APK and report extracted tree', action='store_true', default=False) -parser.add_argument( - '--apk-name', - dest='apk', - required=True, - type=str, - help='Name of the APK to setup') -args = parser.parse_args() - -if not os.path.exists(PUBDIR): - os.mkdir(PUBDIR) -apkname = args.apk -apknamezip = '%s.zip' % (apkname) -if not os.path.exists(apkname): - getLogger().error('Cannot find %s' % (apkname)) - exit(-1) -if args.unzip: - if not os.path.exists(apknamezip): - copyfile(apkname, apknamezip) - - with ZipFile(apknamezip) as zip: - zip.extractall(os.path.join('.', PUBDIR)) - - assets_dir = os.path.join(PUBDIR, 'assets') - assets_zip = os.path.join(assets_dir, 'assets.zip') - with ZipFile(assets_zip) as zip: - zip.extractall(assets_dir) - - os.remove(assets_zip) -else: - copyfile(apkname, os.path.join(PUBDIR, apkname)) +# Setup the Maui folder +precommands.new(template='maui-blazor', + output_dir=const.APPDIR, + bin_dir=const.BINDIR, + exename=EXENAME, + working_directory=sys.path[0], + no_restore=False) +# Add the index.razor.cs file +with open(f"{const.APPDIR}/Pages/Index.razor.cs", "w") as indexCSFile: + indexCSFile.write(''' + using Microsoft.AspNetCore.Components; + #if ANDROID + using Android.App; + #endif\n\n''' + + f" namespace {EXENAME}.Pages" + +''' + { + public partial class Index + { + protected override void OnAfterRender(bool firstRender) + { + if (firstRender) + { + #if ANDROID + var activity = MainActivity.Context as Activity; + activity.ReportFullyDrawn(); + #else + System.Console.WriteLine(\"__MAUI_Blazor_WebView_OnAfterRender__\"); + #endif + } + } + } + } +''') + +# Replace line in the Android MainActivity.cs file +with open(f"{const.APPDIR}/Platforms/Android/MainActivity.cs", "r") as mainActivityFile: + mainActivityFileLines = mainActivityFile.readlines() + +with open(f"{const.APPDIR}/Platforms/Android/MainActivity.cs", "w") as mainActivityFile: + for line in mainActivityFileLines: + if line.startswith("{"): + mainActivityFile.write("{\npublic static Android.Content.Context Context { get; private set; }\npublic MainActivity() { Context = this; }") + else: + mainActivityFile.write(line) + +# Build the APK +precommands.execute(['--no-restore', '--source', 'MauiNuGet.config']) +output_dir = const.PUBDIR +if precommands.output: + output_dir = precommands.output +remove_aab_files(output_dir) +# Copy the MauiVersion to a file so we have it on the machine +maui_version = get_version_from_dll_powershell(rf".\{const.APPDIR}\obj\Release\{precommands.framework}\{precommands.runtime_identifier}\linked\Microsoft.Maui.dll") +version_dict = { "mauiVersion": maui_version } +versions_write_json(version_dict, rf"{output_dir}\versions.json") +print(f"Versions: {version_dict}") \ No newline at end of file diff --git a/src/scenarios/mauiblazorandroid/test.py b/src/scenarios/mauiblazorandroid/test.py index 743d5eaeb65..9363fdfeb5b 100644 --- a/src/scenarios/mauiblazorandroid/test.py +++ b/src/scenarios/mauiblazorandroid/test.py @@ -1,12 +1,16 @@ ''' -C# Console app +Mobile Maui App ''' +from shared.const import PUBDIR from shared.runner import TestTraits, Runner +from shared.versionmanager import versions_read_json_file_save_env EXENAME = 'MauiBlazorAndroidDefault' if __name__ == "__main__": + versions_read_json_file_save_env(rf".\{PUBDIR}\versions.json") + traits = TestTraits(exename=EXENAME, guiapp='false', ) - Runner(traits).run() + Runner(traits).run() \ No newline at end of file diff --git a/src/scenarios/mauishared/mauisharedpython.py b/src/scenarios/mauishared/mauisharedpython.py new file mode 100644 index 00000000000..127c712bfee --- /dev/null +++ b/src/scenarios/mauishared/mauisharedpython.py @@ -0,0 +1,9 @@ +import subprocess +import os + +# Remove the aab files as we don't need them, this saves space in the correlation payload +def RemoveAABFiles(output_dir="."): + file_list = os.listdir(output_dir) + for file in file_list: + if file.endswith(".aab"): + os.remove(os.path.join(output_dir, file)) diff --git a/src/scenarios/shared/mauisharedpython.py b/src/scenarios/shared/mauisharedpython.py new file mode 100644 index 00000000000..a61be2cfa65 --- /dev/null +++ b/src/scenarios/shared/mauisharedpython.py @@ -0,0 +1,24 @@ +import subprocess +import os +import requests +from shared.precommands import PreCommands + +# Remove the aab files as we don't need them, this saves space in the correlation payload +def remove_aab_files(output_dir="."): + file_list = os.listdir(output_dir) + for file in file_list: + if file.endswith(".aab"): + os.remove(os.path.join(output_dir, file)) + +def install_versioned_maui(precommands): + target_framework_wo_platform = precommands.framework.split('-')[0] + + # Download what we need + with open ("MauiNuGet.config", "wb") as f: + f.write(requests.get(f'https://raw.githubusercontent.com/dotnet/maui/{target_framework_wo_platform}/NuGet.config', allow_redirects=True).content) + + workload_install_args = ['--configfile', 'MauiNuGet.config'] + if int(target_framework_wo_platform.split('.')[0][3:]) > 7: # Use the rollback file for versions greater than 7 + workload_install_args += ['--from-rollback-file', f'https://aka.ms/dotnet/maui/{target_framework_wo_platform}.json'] + + precommands.install_workload('maui', workload_install_args) \ No newline at end of file diff --git a/src/scenarios/shared/runner.py b/src/scenarios/shared/runner.py index 147c1f5b225..86acf00715d 100644 --- a/src/scenarios/shared/runner.py +++ b/src/scenarios/shared/runner.py @@ -440,6 +440,14 @@ def run(self): getLogger().info(f"Animation values successfully set to {animationValue}.") try: + stopAppCmd = [ + adb.stdout.strip(), + 'shell', + 'am', + 'force-stop', + self.packagename + ] + installCmd = xharnesscommand() + [ 'android', 'install', @@ -493,7 +501,7 @@ def run(self): # Actual testing some run stuff getLogger().info("Test run to check if permissions are needed") - activityname = getActivity.stdout + activityname = getActivity.stdout.strip() # -W in the start command waits for the app to finish initial draw. startAppCmd = [ @@ -509,16 +517,8 @@ def run(self): testRun.run() testRunStats = re.findall(runSplitRegex, testRun.stdout) # Split results saving value (List: Starting, Status, LaunchState, Activity, TotalTime, WaitTime) getLogger().info(f"Test run activity: {testRunStats[3]}") - time.sleep(10) # Add delay to ensure app is fully installed and give it some time to settle - stopAppCmd = [ - adb.stdout.strip(), - 'shell', - 'am', - 'force-stop', - self.packagename - ] RunCommand(stopAppCmd, verbose=True).run() if "com.google.android.permissioncontroller" in testRunStats[3]: diff --git a/src/scenarios/shared/versionmanager.py b/src/scenarios/shared/versionmanager.py new file mode 100644 index 00000000000..3eeaf795a5a --- /dev/null +++ b/src/scenarios/shared/versionmanager.py @@ -0,0 +1,31 @@ +''' +Version File Manager +''' +import json +import os +import subprocess + +def versions_write_json(versiondict: dict, outputfile = 'versions.json'): + with open(outputfile, 'w') as file: + json.dump(versiondict, file) + +def versions_read_json(inputfile = 'versions.json'): + with open(inputfile, 'r') as file: + return json.load(file) + +def versions_write_env(versiondict: dict): + for key, value in versiondict.items(): + os.environ[key] = value + +def versions_read_json_file_save_env(inputfile = 'versions.json'): + versions = versions_read_json(inputfile) + print(f"Versions: {versions}") + versions_write_env(versions) + + # Remove the versions.json file if we are in the lab to ensure SOD doesn't pick it up + if "PERFLAB_INLAB" in os.environ and os.environ["PERFLAB_INLAB"] == "1": + os.remove(inputfile) + +def get_version_from_dll_powershell(dll_path: str): + result = subprocess.run(['powershell', '-Command', rf'Get-ChildItem {dll_path} | Select-Object -ExpandProperty VersionInfo | Select-Object -ExpandProperty ProductVersion'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True) + return result.stdout.decode('utf-8').strip() \ No newline at end of file diff --git a/src/tools/Reporting/Reporting.Tests/EnvironmentProviderMock.cs b/src/tools/Reporting/Reporting.Tests/EnvironmentProviderMock.cs index cf33c408fe3..1939998e176 100644 --- a/src/tools/Reporting/Reporting.Tests/EnvironmentProviderMock.cs +++ b/src/tools/Reporting/Reporting.Tests/EnvironmentProviderMock.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections; using System.Collections.Generic; namespace Reporting.Tests @@ -14,6 +15,10 @@ public string GetEnvironmentVariable(string variable) { return Vars?[variable]; } + public IDictionary GetEnvironmentVariables() + { + return Vars; + } } internal class PerfLabEnvironmentProviderMock : EnvironmentProviderMockBase { @@ -23,6 +28,7 @@ public PerfLabEnvironmentProviderMock() { {"PERFLAB_INLAB", "1" }, {"HELIX_CORRELATION_ID","testCorrelationId" }, + {"HELIX_WORKITEM_FRIENDLYNAME","Test Friendly Name" }, {"PERFLAB_PERFHASH","testPerfHash" }, {"PERFLAB_QUEUE","testQueue" }, {"PERFLAB_REPO","testRepo" }, @@ -34,7 +40,7 @@ public PerfLabEnvironmentProviderMock() {"PERFLAB_HIDDEN", "false" }, {"PERFLAB_RUNNAME", "testRunName" }, {"PERFLAB_CONFIGS", "KEY1=VALUE1;KEY2=VALUE2" }, - {"PERFLAB_BUILDTIMESTAMP", new DateTime(1970,1,1).ToString("o") }, + {"PERFLAB_BUILDTIMESTAMP", new DateTime(1970,1,1).ToString("o") } }; } diff --git a/src/tools/Reporting/Reporting.Tests/ReporterTests.cs b/src/tools/Reporting/Reporting.Tests/ReporterTests.cs index c354d23b90f..dca49fe5314 100644 --- a/src/tools/Reporting/Reporting.Tests/ReporterTests.cs +++ b/src/tools/Reporting/Reporting.Tests/ReporterTests.cs @@ -106,6 +106,7 @@ public void JsonCanBeGenerated() var jsonObj = JsonConvert.DeserializeAnonymousType(jsonString, jsonType); Assert.Equal(environment.GetEnvironmentVariable("HELIX_CORRELATION_ID"), jsonObj.Run.CorrelationId); + Assert.Equal(environment.GetEnvironmentVariable("HELIX_WORKITEM_FRIENDLYNAME"), jsonObj.Run.WorkItemName); Assert.Equal(environment.GetEnvironmentVariable("PERFLAB_PERFHASH"), jsonObj.Run.PerfRepoHash); Assert.Equal(environment.GetEnvironmentVariable("PERFLAB_QUEUE"), jsonObj.Run.Queue); Assert.Equal(environment.GetEnvironmentVariable("PERFLAB_REPO"), jsonObj.Build.Repo); diff --git a/src/tools/Reporting/Reporting/EnvironmentProvider.cs b/src/tools/Reporting/Reporting/EnvironmentProvider.cs index 90d28729284..11336a24fce 100644 --- a/src/tools/Reporting/Reporting/EnvironmentProvider.cs +++ b/src/tools/Reporting/Reporting/EnvironmentProvider.cs @@ -11,5 +11,6 @@ namespace Reporting public class EnvironmentProvider : IEnvironment { public string GetEnvironmentVariable(string variable) => Environment.GetEnvironmentVariable(variable); + public System.Collections.IDictionary GetEnvironmentVariables() => Environment.GetEnvironmentVariables(); } } diff --git a/src/tools/Reporting/Reporting/IEnvironment.cs b/src/tools/Reporting/Reporting/IEnvironment.cs index c7dbfb9b002..409edd14285 100644 --- a/src/tools/Reporting/Reporting/IEnvironment.cs +++ b/src/tools/Reporting/Reporting/IEnvironment.cs @@ -2,14 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Generic; -using System.Text; - namespace Reporting { public interface IEnvironment { string GetEnvironmentVariable(string variable); + System.Collections.IDictionary GetEnvironmentVariables(); } } diff --git a/src/tools/Reporting/Reporting/Reporter.cs b/src/tools/Reporting/Reporting/Reporter.cs index ed797a01bec..097a92aaf2d 100644 --- a/src/tools/Reporting/Reporting/Reporter.cs +++ b/src/tools/Reporting/Reporting/Reporter.cs @@ -5,6 +5,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using System; +using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -88,14 +89,25 @@ private void Init() BuildName = environment.GetEnvironmentVariable("PERFLAB_BUILDNUM"), TimeStamp = DateTime.Parse(environment.GetEnvironmentVariable("PERFLAB_BUILDTIMESTAMP")), }; - build.AdditionalData["productVersion"] = environment.GetEnvironmentVariable("DOTNET_VERSION"); - // Additional Data we only want populated if it is available - if (environment.GetEnvironmentVariable("MAUI_VERSION") != null) + foreach (DictionaryEntry entry in environment.GetEnvironmentVariables()) { - build.AdditionalData["mauiVersion"] = environment.GetEnvironmentVariable("MAUI_VERSION"); - } - + if (entry.Key.ToString().Equals("PERFLAB_TARGET_FRAMEWORKS", StringComparison.InvariantCultureIgnoreCase)) + { + build.AdditionalData["targetFrameworks"] = entry.Value.ToString(); + } + else if(entry.Key.ToString().EndsWith("version", true, CultureInfo.InvariantCulture)) + { + // Special case the original two special cases, MAUI_VERSION is only needed because runtime based runs use MAUI_VERSION + if(entry.Key.ToString().Equals("DOTNET_VERSION", StringComparison.InvariantCultureIgnoreCase)){ + build.AdditionalData["productVersion"] = entry.Value.ToString(); + } else if(entry.Key.ToString().Equals("MAUI_VERSION", StringComparison.InvariantCultureIgnoreCase)){ + build.AdditionalData["mauiVersion"] = entry.Value.ToString(); + } else { + build.AdditionalData[entry.Key.ToString()] = entry.Value.ToString(); + } + } + } } public string GetJson() { @@ -159,4 +171,4 @@ private string LeftJustify(string str, int width) public bool InLab => environment.GetEnvironmentVariable("PERFLAB_INLAB")?.Equals("1") ?? false; } -} +} \ No newline at end of file