Skip to content

Commit d5b3360

Browse files
authored
Implementation of interface IPSCmdletAction for test coverage (#19904)
* Added implementation for interface ITestCoverage and registered in Azure session * Updated the reference of Azure PowerShell Common to 1.3.65-preview * Renamed environment variable from Azure_PS_TestCoverage to EnableTestCoverage
1 parent f8a78a1 commit d5b3360

File tree

11 files changed

+382
-18
lines changed

11 files changed

+382
-18
lines changed

.azure-pipelines/powershell-core.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ variables:
1717
AnalysisTimeoutInMinutes: 120
1818
TestTimeoutInMinutes: 180
1919
BuildAzPredictor: false
20+
EnableTestCoverage: true
2021
PowerShellPlatform: PowerShell Core
2122

2223
trigger: none

.azure-pipelines/windows-powershell.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ variables:
99
IsGenerateBased: $[eq(variables['system.pullRequest.targetBranch'], 'generation')]
1010
BuildTimeoutInMinutes: 120
1111
AnalysisTimeoutInMinutes: 120
12+
EnableTestCoverage: true
1213
PowerShellPlatform: Windows PowerShell
1314

1415
trigger: none

src/Accounts/Accounts/Account/ConnectAzureRmAccount.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,10 @@ public void OnImport()
754754
AzureSession.Instance.RegisterComponent(nameof(AzureCredentialFactory), () => new AzureCredentialFactory());
755755
AzureSession.Instance.RegisterComponent(nameof(MsalAccessTokenAcquirerFactory), () => new MsalAccessTokenAcquirerFactory());
756756
AzureSession.Instance.RegisterComponent<ISshCredentialFactory>(nameof(ISshCredentialFactory), () => new SshCredentialFactory());
757+
#if DEBUG || TESTCOVERAGE
758+
AzureSession.Instance.RegisterComponent<ITestCoverage>(nameof(ITestCoverage), () => new TestCoverage());
759+
#endif
760+
757761
#if DEBUG
758762
}
759763
catch (Exception) when (TestMockSupport.RunningMocked)

src/Accounts/Accounts/ChangeLog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
* Enabled caching tokens when logging in with a service principal. This could reduce network traffic and improve performance.
2323
* Upgraded target framework of Microsoft.Identity.Client to net461 [#20189]
2424
* Stored `ServicePrincipalSecret` and `CertificatePassword` into `AzKeyStore`.
25+
* Updated the reference of Azure PowerShell Common to 1.3.65-preview.
2526

2627
## Version 2.10.3
2728
* Updated `Get-AzSubscription` to retrieve subscription by Id rather than listed all the subscriptions from server if subscription Id is provided. [#19115]

src/Accounts/Accounts/CommonModule/TelemetryProvider.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ public virtual AzurePSQoSEvent CreateQosEvent(InvocationInfo invocationInfo, str
143143
CommandName = invocationInfo?.MyCommand?.Name,
144144
ModuleVersion = TrimModuleVersion(invocationInfo?.MyCommand?.Module?.Version),
145145
ModuleName = TrimModuleName(invocationInfo?.MyCommand?.ModuleName),
146+
SourceScript = invocationInfo?.ScriptName,
147+
ScriptLineNumber = invocationInfo?.ScriptLineNumber ?? 0,
146148
SessionId = MetricHelper.SessionId,
147149
ParameterSetName = parameterSetName,
148150
InvocationName = invocationInfo?.InvocationName,

src/Accounts/Authentication/Config/ConfigInitializer.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,14 @@ private void RegisterConfigs(IConfigManager configManager)
190190
true,
191191
AzurePSDataCollectionProfile.EnvironmentVariableName,
192192
new[] { AppliesTo.Az }));
193+
#if DEBUG || TESTCOVERAGE
194+
configManager.RegisterConfig(new SimpleTypedConfig<bool>(
195+
ConfigKeys.EnableTestCoverage,
196+
"When enabled, the test framework will generate data during test run as a preliminary for the test coverage calculation",
197+
false,
198+
ConfigKeys.EnableTestCoverage,
199+
new[] { AppliesTo.Az }));
200+
#endif
193201

194202
configManager.RegisterConfig(new EnableInterceptSurveyConfig());
195203
configManager.RegisterConfig(new DisplayBreakingChangeWarningsConfig());
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
// ----------------------------------------------------------------------------------
2+
//
3+
// Copyright Microsoft Corporation
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
// ----------------------------------------------------------------------------------
14+
15+
using Microsoft.WindowsAzure.Commands.Common;
16+
using System;
17+
using System.Collections.Generic;
18+
using System.IO;
19+
using System.Runtime.InteropServices;
20+
using System.Text;
21+
using System.Text.RegularExpressions;
22+
using System.Threading;
23+
24+
namespace Microsoft.Azure.Commands.Common.Authentication
25+
{
26+
public class TestCoverage : ITestCoverage
27+
{
28+
private const string CsvHeaderCommandName = "CommandName";
29+
private const string CsvHeaderParameterSetName = "ParameterSetName";
30+
private const string CsvHeaderParameters = "Parameters";
31+
private const string CsvHeaderSourceScript = "SourceScript";
32+
private const string CsvHeaderScriptLineNumber = "LineNumber";
33+
private const string CsvHeaderIsSuccess = "IsSuccess";
34+
private const string Delimiter = ",";
35+
36+
private readonly IList<string> ExcludedSource = new List<string>
37+
{
38+
"Common.ps1",
39+
"Assert.ps1",
40+
"AzureRM.Resources.ps1",
41+
"AzureRM.Storage.ps1"
42+
};
43+
44+
45+
private static readonly string s_testCoverageRootPath;
46+
47+
private static readonly ReaderWriterLockSlim s_lock = new ReaderWriterLockSlim();
48+
49+
static TestCoverage()
50+
{
51+
52+
var repoRootPath = ProbeRepoDirectory();
53+
if (!string.IsNullOrEmpty(repoRootPath))
54+
{
55+
s_testCoverageRootPath = Path.Combine(repoRootPath, "artifacts", "TestCoverageAnalysis", "Raw");
56+
DirectoryInfo rawDir = new DirectoryInfo(s_testCoverageRootPath);
57+
if (!rawDir.Exists)
58+
{
59+
Directory.CreateDirectory(s_testCoverageRootPath);
60+
}
61+
}
62+
}
63+
64+
private static string ProbeRepoDirectory()
65+
{
66+
string directoryPath = "..";
67+
while (Directory.Exists(directoryPath) && (!Directory.Exists(Path.Combine(directoryPath, "src")) || !Directory.Exists(Path.Combine(directoryPath, "artifacts"))))
68+
{
69+
directoryPath = Path.Combine(directoryPath, "..");
70+
}
71+
72+
string result = Directory.Exists(directoryPath) ? Path.GetFullPath(directoryPath) : null;
73+
return result;
74+
}
75+
76+
private string GenerateCsvHeader()
77+
{
78+
StringBuilder headerBuilder = new StringBuilder();
79+
headerBuilder.Append(CsvHeaderCommandName).Append(Delimiter)
80+
.Append(CsvHeaderParameterSetName).Append(Delimiter)
81+
.Append(CsvHeaderParameters).Append(Delimiter)
82+
.Append(CsvHeaderSourceScript).Append(Delimiter)
83+
.Append(CsvHeaderScriptLineNumber).Append(Delimiter)
84+
.Append(CsvHeaderIsSuccess);
85+
86+
return headerBuilder.ToString();
87+
}
88+
89+
private string GenerateCsvItem(string commandName, string parameterSetName, string parameters, string sourceScript, int scriptLineNumber, bool isSuccess)
90+
{
91+
StringBuilder itemBuilder = new StringBuilder();
92+
itemBuilder.Append(commandName).Append(Delimiter)
93+
.Append(parameterSetName).Append(Delimiter)
94+
.Append(parameters).Append(Delimiter)
95+
.Append(sourceScript).Append(Delimiter)
96+
.Append(scriptLineNumber).Append(Delimiter)
97+
.Append(isSuccess.ToString().ToLowerInvariant());
98+
99+
return itemBuilder.ToString();
100+
}
101+
102+
public void LogRawData(AzurePSQoSEvent qos)
103+
{
104+
#if DEBUG || TESTCOVERAGE
105+
string moduleName = qos.ModuleName;
106+
string commandName = qos.CommandName;
107+
string sourceScript = qos.SourceScript;
108+
109+
if (string.IsNullOrEmpty(moduleName) || string.IsNullOrEmpty(commandName) || ExcludedSource.Contains(sourceScript))
110+
return;
111+
112+
var pattern = @"\\(?:artifacts\\Debug|src)\\(?:Az\.)?(?<ModuleName>[a-zA-Z]+)\\";
113+
var match = Regex.Match(sourceScript, pattern, RegexOptions.IgnoreCase);
114+
var testingModuleName = $"Az.{match.Groups["ModuleName"].Value}";
115+
if (string.Compare(testingModuleName, moduleName, true) != 0)
116+
return;
117+
118+
var csvFilePath = Path.Combine(s_testCoverageRootPath, $"{moduleName}.csv");
119+
StringBuilder csvData = new StringBuilder();
120+
121+
s_lock.EnterWriteLock();
122+
try
123+
{
124+
if (!File.Exists(csvFilePath))
125+
{
126+
var csvHeader = GenerateCsvHeader();
127+
csvData.Append(csvHeader);
128+
}
129+
130+
csvData.AppendLine();
131+
var csvItem = GenerateCsvItem(commandName, qos.ParameterSetName, qos.Parameters, Path.GetFileName(sourceScript), qos.ScriptLineNumber, qos.IsSuccess);
132+
csvData.Append(csvItem);
133+
134+
File.AppendAllText(csvFilePath, csvData.ToString());
135+
}
136+
catch (Exception ex)
137+
{
138+
Console.WriteLine($"##[group]Error occurred when generating raw data of test coverage for module {moduleName}");
139+
Console.WriteLine($"##[error]Error Message: {ex.Message}");
140+
Console.WriteLine($"##[error]Source: {ex.Source}");
141+
Console.WriteLine($"##[error]Stack Trace: {ex.StackTrace}");
142+
Console.WriteLine("##[endgroup]");
143+
}
144+
finally
145+
{
146+
s_lock.ExitWriteLock();
147+
}
148+
#endif
149+
}
150+
}
151+
}

src/shared/ConfigKeys.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,6 @@ internal static class ConfigKeys
2828
public const string DisplayBreakingChangeWarning = "DisplayBreakingChangeWarning";
2929
public const string DefaultSubscriptionForLogin = "DefaultSubscriptionForLogin";
3030
public const string EnableDataCollection = "EnableDataCollection";
31+
public const string EnableTestCoverage = "EnableTestCoverage";
3132
}
3233
}

tools/Common.Netcore.Dependencies.targets

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,22 @@
33
<ItemGroup>
44
<PackageReference Include="Microsoft.Rest.ClientRuntime" Version="2.3.24"/>
55
<PackageReference Include="Microsoft.Rest.ClientRuntime.Azure" Version="3.3.19"/>
6-
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Aks" Version="1.3.64-preview"/>
7-
<PackageReference Include="Microsoft.Azure.PowerShell.Authentication.Abstractions" Version="1.3.64-preview"/>
8-
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Authorization" Version="1.3.64-preview"/>
9-
<PackageReference Include="Microsoft.Azure.PowerShell.Common" Version="1.3.64-preview"/>
10-
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Compute" Version="1.3.64-preview"/>
11-
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Graph.Rbac" Version="1.3.64-preview"/>
12-
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.KeyVault" Version="1.3.64-preview"/>
13-
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Monitor" Version="1.3.64-preview"/>
14-
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Network" Version="1.3.64-preview"/>
15-
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.PolicyInsights" Version="1.3.64-preview"/>
16-
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.ResourceManager" Version="1.3.64-preview"/>
17-
<PackageReference Include="Microsoft.Azure.PowerShell.Storage" Version="1.3.64-preview"/>
18-
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Storage.Management" Version="1.3.64-preview"/>
19-
<PackageReference Include="Microsoft.Azure.PowerShell.Strategies" Version="1.3.64-preview"/>
20-
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Websites" Version="1.3.64-preview"/>
21-
<PackageReference Include="Microsoft.Azure.PowerShell.Common.Share" Version="1.3.64-preview"/>
6+
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Aks" Version="1.3.65-preview"/>
7+
<PackageReference Include="Microsoft.Azure.PowerShell.Authentication.Abstractions" Version="1.3.65-preview"/>
8+
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Authorization" Version="1.3.65-preview"/>
9+
<PackageReference Include="Microsoft.Azure.PowerShell.Common" Version="1.3.65-preview"/>
10+
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Compute" Version="1.3.65-preview"/>
11+
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Graph.Rbac" Version="1.3.65-preview"/>
12+
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.KeyVault" Version="1.3.65-preview"/>
13+
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Monitor" Version="1.3.65-preview"/>
14+
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Network" Version="1.3.65-preview"/>
15+
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.PolicyInsights" Version="1.3.65-preview"/>
16+
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.ResourceManager" Version="1.3.65-preview"/>
17+
<PackageReference Include="Microsoft.Azure.PowerShell.Storage" Version="1.3.65-preview"/>
18+
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Storage.Management" Version="1.3.65-preview"/>
19+
<PackageReference Include="Microsoft.Azure.PowerShell.Strategies" Version="1.3.65-preview"/>
20+
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Websites" Version="1.3.65-preview"/>
21+
<PackageReference Include="Microsoft.Azure.PowerShell.Common.Share" Version="1.3.65-preview"/>
2222
</ItemGroup>
2323
<ItemGroup>
2424
<PackageReference Include="Azure.Core" Version="1.25.0"/>
@@ -36,7 +36,7 @@
3636
<PackageReference Include="PowerShellStandard.Library" Version="5.1.0" PrivateAssets="All" />
3737
</ItemGroup>
3838
<PropertyGroup>
39-
<StorageToolsPath>$(NugetPackageRoot)\microsoft.azure.powershell.storage\1.3.64-preview\tools\</StorageToolsPath>
39+
<StorageToolsPath>$(NugetPackageRoot)\microsoft.azure.powershell.storage\1.3.65-preview\tools\</StorageToolsPath>
4040
</PropertyGroup>
4141
<ItemGroup Condition="'$(OmitJsonPackage)' != 'true'">
4242
<PackageReference Include="Newtonsoft.Json" Version="10.0.3"/>

0 commit comments

Comments
 (0)