Skip to content

Commit

Permalink
Implementation of interface IPSCmdletAction for test coverage (Azure#…
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
vidai-msft authored Nov 28, 2022
1 parent f8a78a1 commit d5b3360
Show file tree
Hide file tree
Showing 11 changed files with 382 additions and 18 deletions.
1 change: 1 addition & 0 deletions .azure-pipelines/powershell-core.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ variables:
AnalysisTimeoutInMinutes: 120
TestTimeoutInMinutes: 180
BuildAzPredictor: false
EnableTestCoverage: true
PowerShellPlatform: PowerShell Core

trigger: none
Expand Down
1 change: 1 addition & 0 deletions .azure-pipelines/windows-powershell.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ variables:
IsGenerateBased: $[eq(variables['system.pullRequest.targetBranch'], 'generation')]
BuildTimeoutInMinutes: 120
AnalysisTimeoutInMinutes: 120
EnableTestCoverage: true
PowerShellPlatform: Windows PowerShell

trigger: none
Expand Down
4 changes: 4 additions & 0 deletions src/Accounts/Accounts/Account/ConnectAzureRmAccount.cs
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,10 @@ public void OnImport()
AzureSession.Instance.RegisterComponent(nameof(AzureCredentialFactory), () => new AzureCredentialFactory());
AzureSession.Instance.RegisterComponent(nameof(MsalAccessTokenAcquirerFactory), () => new MsalAccessTokenAcquirerFactory());
AzureSession.Instance.RegisterComponent<ISshCredentialFactory>(nameof(ISshCredentialFactory), () => new SshCredentialFactory());
#if DEBUG || TESTCOVERAGE
AzureSession.Instance.RegisterComponent<ITestCoverage>(nameof(ITestCoverage), () => new TestCoverage());
#endif

#if DEBUG
}
catch (Exception) when (TestMockSupport.RunningMocked)
Expand Down
1 change: 1 addition & 0 deletions src/Accounts/Accounts/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
* Enabled caching tokens when logging in with a service principal. This could reduce network traffic and improve performance.
* Upgraded target framework of Microsoft.Identity.Client to net461 [#20189]
* Stored `ServicePrincipalSecret` and `CertificatePassword` into `AzKeyStore`.
* Updated the reference of Azure PowerShell Common to 1.3.65-preview.

## Version 2.10.3
* Updated `Get-AzSubscription` to retrieve subscription by Id rather than listed all the subscriptions from server if subscription Id is provided. [#19115]
Expand Down
2 changes: 2 additions & 0 deletions src/Accounts/Accounts/CommonModule/TelemetryProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ public virtual AzurePSQoSEvent CreateQosEvent(InvocationInfo invocationInfo, str
CommandName = invocationInfo?.MyCommand?.Name,
ModuleVersion = TrimModuleVersion(invocationInfo?.MyCommand?.Module?.Version),
ModuleName = TrimModuleName(invocationInfo?.MyCommand?.ModuleName),
SourceScript = invocationInfo?.ScriptName,
ScriptLineNumber = invocationInfo?.ScriptLineNumber ?? 0,
SessionId = MetricHelper.SessionId,
ParameterSetName = parameterSetName,
InvocationName = invocationInfo?.InvocationName,
Expand Down
8 changes: 8 additions & 0 deletions src/Accounts/Authentication/Config/ConfigInitializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,14 @@ private void RegisterConfigs(IConfigManager configManager)
true,
AzurePSDataCollectionProfile.EnvironmentVariableName,
new[] { AppliesTo.Az }));
#if DEBUG || TESTCOVERAGE
configManager.RegisterConfig(new SimpleTypedConfig<bool>(
ConfigKeys.EnableTestCoverage,
"When enabled, the test framework will generate data during test run as a preliminary for the test coverage calculation",
false,
ConfigKeys.EnableTestCoverage,
new[] { AppliesTo.Az }));
#endif

configManager.RegisterConfig(new EnableInterceptSurveyConfig());
configManager.RegisterConfig(new DisplayBreakingChangeWarningsConfig());
Expand Down
151 changes: 151 additions & 0 deletions src/Accounts/Authentication/TestCoverage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------

using Microsoft.WindowsAzure.Commands.Common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;

namespace Microsoft.Azure.Commands.Common.Authentication
{
public class TestCoverage : ITestCoverage
{
private const string CsvHeaderCommandName = "CommandName";
private const string CsvHeaderParameterSetName = "ParameterSetName";
private const string CsvHeaderParameters = "Parameters";
private const string CsvHeaderSourceScript = "SourceScript";
private const string CsvHeaderScriptLineNumber = "LineNumber";
private const string CsvHeaderIsSuccess = "IsSuccess";
private const string Delimiter = ",";

private readonly IList<string> ExcludedSource = new List<string>
{
"Common.ps1",
"Assert.ps1",
"AzureRM.Resources.ps1",
"AzureRM.Storage.ps1"
};


private static readonly string s_testCoverageRootPath;

private static readonly ReaderWriterLockSlim s_lock = new ReaderWriterLockSlim();

static TestCoverage()
{

var repoRootPath = ProbeRepoDirectory();
if (!string.IsNullOrEmpty(repoRootPath))
{
s_testCoverageRootPath = Path.Combine(repoRootPath, "artifacts", "TestCoverageAnalysis", "Raw");
DirectoryInfo rawDir = new DirectoryInfo(s_testCoverageRootPath);
if (!rawDir.Exists)
{
Directory.CreateDirectory(s_testCoverageRootPath);
}
}
}

private static string ProbeRepoDirectory()
{
string directoryPath = "..";
while (Directory.Exists(directoryPath) && (!Directory.Exists(Path.Combine(directoryPath, "src")) || !Directory.Exists(Path.Combine(directoryPath, "artifacts"))))
{
directoryPath = Path.Combine(directoryPath, "..");
}

string result = Directory.Exists(directoryPath) ? Path.GetFullPath(directoryPath) : null;
return result;
}

private string GenerateCsvHeader()
{
StringBuilder headerBuilder = new StringBuilder();
headerBuilder.Append(CsvHeaderCommandName).Append(Delimiter)
.Append(CsvHeaderParameterSetName).Append(Delimiter)
.Append(CsvHeaderParameters).Append(Delimiter)
.Append(CsvHeaderSourceScript).Append(Delimiter)
.Append(CsvHeaderScriptLineNumber).Append(Delimiter)
.Append(CsvHeaderIsSuccess);

return headerBuilder.ToString();
}

private string GenerateCsvItem(string commandName, string parameterSetName, string parameters, string sourceScript, int scriptLineNumber, bool isSuccess)
{
StringBuilder itemBuilder = new StringBuilder();
itemBuilder.Append(commandName).Append(Delimiter)
.Append(parameterSetName).Append(Delimiter)
.Append(parameters).Append(Delimiter)
.Append(sourceScript).Append(Delimiter)
.Append(scriptLineNumber).Append(Delimiter)
.Append(isSuccess.ToString().ToLowerInvariant());

return itemBuilder.ToString();
}

public void LogRawData(AzurePSQoSEvent qos)
{
#if DEBUG || TESTCOVERAGE
string moduleName = qos.ModuleName;
string commandName = qos.CommandName;
string sourceScript = qos.SourceScript;

if (string.IsNullOrEmpty(moduleName) || string.IsNullOrEmpty(commandName) || ExcludedSource.Contains(sourceScript))
return;

var pattern = @"\\(?:artifacts\\Debug|src)\\(?:Az\.)?(?<ModuleName>[a-zA-Z]+)\\";
var match = Regex.Match(sourceScript, pattern, RegexOptions.IgnoreCase);
var testingModuleName = $"Az.{match.Groups["ModuleName"].Value}";
if (string.Compare(testingModuleName, moduleName, true) != 0)
return;

var csvFilePath = Path.Combine(s_testCoverageRootPath, $"{moduleName}.csv");
StringBuilder csvData = new StringBuilder();

s_lock.EnterWriteLock();
try
{
if (!File.Exists(csvFilePath))
{
var csvHeader = GenerateCsvHeader();
csvData.Append(csvHeader);
}

csvData.AppendLine();
var csvItem = GenerateCsvItem(commandName, qos.ParameterSetName, qos.Parameters, Path.GetFileName(sourceScript), qos.ScriptLineNumber, qos.IsSuccess);
csvData.Append(csvItem);

File.AppendAllText(csvFilePath, csvData.ToString());
}
catch (Exception ex)
{
Console.WriteLine($"##[group]Error occurred when generating raw data of test coverage for module {moduleName}");
Console.WriteLine($"##[error]Error Message: {ex.Message}");
Console.WriteLine($"##[error]Source: {ex.Source}");
Console.WriteLine($"##[error]Stack Trace: {ex.StackTrace}");
Console.WriteLine("##[endgroup]");
}
finally
{
s_lock.ExitWriteLock();
}
#endif
}
}
}
1 change: 1 addition & 0 deletions src/shared/ConfigKeys.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ internal static class ConfigKeys
public const string DisplayBreakingChangeWarning = "DisplayBreakingChangeWarning";
public const string DefaultSubscriptionForLogin = "DefaultSubscriptionForLogin";
public const string EnableDataCollection = "EnableDataCollection";
public const string EnableTestCoverage = "EnableTestCoverage";
}
}
34 changes: 17 additions & 17 deletions tools/Common.Netcore.Dependencies.targets
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,22 @@
<ItemGroup>
<PackageReference Include="Microsoft.Rest.ClientRuntime" Version="2.3.24"/>
<PackageReference Include="Microsoft.Rest.ClientRuntime.Azure" Version="3.3.19"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Aks" Version="1.3.64-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Authentication.Abstractions" Version="1.3.64-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Authorization" Version="1.3.64-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Common" Version="1.3.64-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Compute" Version="1.3.64-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Graph.Rbac" Version="1.3.64-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.KeyVault" Version="1.3.64-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Monitor" Version="1.3.64-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Network" Version="1.3.64-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.PolicyInsights" Version="1.3.64-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.ResourceManager" Version="1.3.64-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Storage" Version="1.3.64-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Storage.Management" Version="1.3.64-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Strategies" Version="1.3.64-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Websites" Version="1.3.64-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Common.Share" Version="1.3.64-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Aks" Version="1.3.65-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Authentication.Abstractions" Version="1.3.65-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Authorization" Version="1.3.65-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Common" Version="1.3.65-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Compute" Version="1.3.65-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Graph.Rbac" Version="1.3.65-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.KeyVault" Version="1.3.65-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Monitor" Version="1.3.65-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Network" Version="1.3.65-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.PolicyInsights" Version="1.3.65-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.ResourceManager" Version="1.3.65-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Storage" Version="1.3.65-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Storage.Management" Version="1.3.65-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Strategies" Version="1.3.65-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Clients.Websites" Version="1.3.65-preview"/>
<PackageReference Include="Microsoft.Azure.PowerShell.Common.Share" Version="1.3.65-preview"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Azure.Core" Version="1.25.0"/>
Expand All @@ -36,7 +36,7 @@
<PackageReference Include="PowerShellStandard.Library" Version="5.1.0" PrivateAssets="All" />
</ItemGroup>
<PropertyGroup>
<StorageToolsPath>$(NugetPackageRoot)\microsoft.azure.powershell.storage\1.3.64-preview\tools\</StorageToolsPath>
<StorageToolsPath>$(NugetPackageRoot)\microsoft.azure.powershell.storage\1.3.65-preview\tools\</StorageToolsPath>
</PropertyGroup>
<ItemGroup Condition="'$(OmitJsonPackage)' != 'true'">
<PackageReference Include="Newtonsoft.Json" Version="10.0.3"/>
Expand Down
Loading

0 comments on commit d5b3360

Please sign in to comment.