2023-08-22 22:42:41 +00:00
// 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.
2023-08-17 17:58:40 +00:00
using System ;
using System.IO ;
using System.Linq ;
2023-08-17 18:02:37 +00:00
using System.Collections ;
using System.Collections.Generic ;
2023-08-21 21:34:59 +00:00
using System.Text.RegularExpressions ;
2023-08-17 18:02:37 +00:00
using System.Formats.Tar ;
using System.Threading.Tasks ;
2023-08-17 17:58:40 +00:00
using Xunit ;
using Xunit.Abstractions ;
2023-08-17 23:30:26 +00:00
namespace Microsoft.DotNet.SourceBuild.SmokeTests ;
[Trait("Category", "SdkContent")]
2023-08-22 22:42:41 +00:00
public class ArtifactsSizeTest : SmokeTests
2023-08-17 17:58:40 +00:00
{
2023-08-22 22:26:10 +00:00
private static readonly string BaselineFilePath = BaselineHelper . GetBaselineFilePath ( $"ArtifactsSizes/{Config.TargetRid}.txt" ) ;
private static readonly Dictionary < string , long > BaselineFileContent = new Dictionary < string , long > ( ) ;
private static readonly Regex BuildVersionPattern = new ( @"\b\d+\.\d+\.\d+[-@](alpha|preview|rc|rtm)\.\d(\.\d+\.\d+)?\b" ) ;
2023-08-23 16:30:53 +00:00
private const int SizeThresholdPercentage = 25 ;
2023-08-21 21:34:59 +00:00
2023-08-17 23:30:26 +00:00
2023-08-22 22:42:41 +00:00
public ArtifactsSizeTest ( ITestOutputHelper outputHelper ) : base ( outputHelper )
2023-08-17 17:58:40 +00:00
{
2023-08-23 16:30:53 +00:00
if ( File . Exists ( BaselineFilePath ) )
2023-08-22 22:26:10 +00:00
{
string [ ] baselineFileContent = File . ReadAllLines ( BaselineFilePath ) ;
2023-08-23 16:30:53 +00:00
foreach ( string entry in baselineFileContent )
2023-08-22 22:26:10 +00:00
{
2023-08-24 22:26:13 +00:00
string [ ] splitEntry = entry . Split ( ':' , StringSplitOptions . TrimEntries ) ;
BaselineFileContent [ splitEntry [ 0 ] ] = long . Parse ( splitEntry [ 1 ] ) ;
2023-08-22 22:26:10 +00:00
}
}
2023-08-22 22:42:41 +00:00
else
{
Assert . False ( true , $"Baseline file `{BaselineFilePath}' does not exist. Please create the baseline file then rerun the test." ) ;
}
2023-08-17 23:30:26 +00:00
}
2023-08-17 17:58:40 +00:00
2023-08-21 21:34:59 +00:00
[SkippableFact(new[] { Config . SourceBuiltArtifactsPathEnv , Config . SdkTarballPathEnv , Config . TargetRidEnv } , skipOnNullOrWhiteSpace : true ) ]
2023-08-22 22:42:41 +00:00
public void CompareArtifactsToBaseline ( )
2023-08-17 23:30:26 +00:00
{
Assert . NotNull ( Config . SourceBuiltArtifactsPath ) ;
Assert . NotNull ( Config . SdkTarballPath ) ;
2023-08-21 21:34:59 +00:00
Assert . NotNull ( Config . TargetRid ) ;
2023-08-17 17:58:40 +00:00
2023-08-25 20:32:16 +00:00
IEnumerable < TarEntry > artifactsTarEntries = Utilities . GetTarballContent ( Config . SourceBuiltArtifactsPath )
. Where ( entry = > entry . EntryType = = TarEntryType . RegularFile )
. Where ( entry = > ! entry . Name . Contains ( "SourceBuildReferencePackages" ) ) ;
IEnumerable < TarEntry > sdkTarEntries = Utilities . GetTarballContent ( Config . SdkTarballPath )
. Where ( entry = > entry . EntryType = = TarEntryType . RegularFile )
. Where ( entry = > ! entry . Name . Contains ( "SourceBuildReferencePackages" ) ) ;
2023-08-17 17:58:40 +00:00
2023-08-24 22:26:13 +00:00
Dictionary < string , int > fileNameCountMap = new Dictionary < string , int > ( ) ;
2023-08-22 22:26:10 +00:00
( string FilePath , long Bytes ) [ ] tarEntries = sdkTarEntries . Concat ( artifactsTarEntries )
2023-08-21 21:34:59 +00:00
. Select ( entry = >
{
2023-08-24 22:26:13 +00:00
string result = ProcessEntryName ( entry . Name , fileNameCountMap ) ;
2023-08-22 22:42:41 +00:00
return ( FilePath : result , Bytes : entry . Length ) ;
2023-08-21 21:34:59 +00:00
} )
2023-08-22 22:26:10 +00:00
. OrderBy ( entry = > entry . FilePath )
2023-08-17 23:30:26 +00:00
. ToArray ( ) ;
2023-08-17 17:58:40 +00:00
2023-08-22 22:42:41 +00:00
foreach ( var entry in tarEntries )
2023-08-17 23:30:26 +00:00
{
2023-08-23 16:30:53 +00:00
if ( ! BaselineFileContent . TryGetValue ( entry . FilePath , out long baselineBytes ) )
2023-08-17 17:58:40 +00:00
{
2023-08-22 22:42:41 +00:00
OutputHelper . LogWarningMessage ( $"{entry.FilePath} does not exist in baseline. Adding it to the baseline file" ) ;
2023-08-17 23:30:26 +00:00
}
else
{
2023-08-23 16:30:53 +00:00
CompareFileSizes ( entry . FilePath , entry . Bytes , baselineBytes ) ;
2023-08-17 17:58:40 +00:00
}
}
2023-08-22 23:06:41 +00:00
try
{
string actualFilePath = Path . Combine ( DotNetHelper . LogsDirectory , $"Updated_ArtifactsSizes_{Config.TargetRid}.txt" ) ;
File . WriteAllLines ( actualFilePath , tarEntries . Select ( entry = > $"{entry.FilePath}: {entry.Bytes}" ) ) ;
}
catch ( IOException ex )
{
throw new InvalidOperationException ( $"An error occurred while copying the baselines file: {BaselineFilePath}" , ex ) ;
}
2023-08-17 23:30:26 +00:00
}
2023-08-24 22:26:13 +00:00
private string ProcessEntryName ( string originalName , Dictionary < string , int > fileNameCountMap )
{
string result = BaselineHelper . RemoveRids ( originalName ) ;
result = BaselineHelper . RemoveVersions ( result ) ;
string pattern = @"x\.y\.z" ;
MatchCollection matches = Regex . Matches ( result , pattern ) ;
if ( matches . Count > 0 )
{
int count = fileNameCountMap . TryGetValue ( result , out int value ) ? value : 0 ;
fileNameCountMap [ result ] = count + 1 ;
if ( count > 0 )
{
int lastIndex = matches [ matches . Count - 1 ] . Index ;
result = result . Substring ( 0 , lastIndex ) + $"x.y.z-{count}" + result . Substring ( lastIndex + pattern . Length - 2 ) ;
}
}
return result ;
}
2023-08-22 22:26:10 +00:00
private void CompareFileSizes ( string filePath , long fileSize , long baselineSize )
2023-08-17 23:30:26 +00:00
{
if ( fileSize = = 0 & & baselineSize ! = 0 )
2023-08-24 22:26:13 +00:00
{
2023-08-22 22:42:41 +00:00
OutputHelper . LogWarningMessage ( $"'{filePath}' is now 0 bytes. It was {baselineSize} bytes" ) ;
2023-08-24 22:26:13 +00:00
}
2023-08-17 23:30:26 +00:00
else if ( fileSize ! = 0 & & baselineSize = = 0 )
2023-08-24 22:26:13 +00:00
{
2023-08-22 22:42:41 +00:00
OutputHelper . LogWarningMessage ( $"'{filePath}' is no longer 0 bytes. It is now {fileSize} bytes" ) ;
2023-08-24 22:26:13 +00:00
}
2023-08-23 16:30:53 +00:00
else if ( baselineSize ! = 0 & & Math . Abs ( ( ( fileSize - baselineSize ) / ( double ) baselineSize ) * 100 ) > = SizeThresholdPercentage )
2023-08-24 22:26:13 +00:00
{
2023-08-23 16:30:53 +00:00
OutputHelper . LogWarningMessage ( $"'{filePath}' increased in size by more than {SizeThresholdPercentage}%. It was originally {baselineSize} bytes and is now {fileSize} bytes" ) ;
2023-08-24 22:26:13 +00:00
}
2023-08-17 23:30:26 +00:00
}
2023-08-25 20:32:16 +00:00
}