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
{
string [ ] splitEntry = entry . Split ( ':' ) ;
BaselineFileContent [ splitEntry [ 0 ] . Trim ( ) ] = long . Parse ( splitEntry [ 1 ] . Trim ( ) ) ;
}
}
2023-08-22 22:42:41 +00:00
else
{
Assert . True ( Directory . Exists ( BaselineHelper . GetBaselineFilePath ( "ArtifactsSizes/" ) ) ) ;
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-17 23:30:26 +00:00
IEnumerable < TarEntry > artifactsTarEntries = Utilities . GetTarballContent ( Config . SourceBuiltArtifactsPath ) . Where ( entry = > entry . EntryType = = TarEntryType . RegularFile ) ;
IEnumerable < TarEntry > sdkTarEntries = Utilities . GetTarballContent ( Config . SdkTarballPath ) . Where ( entry = > entry . EntryType = = TarEntryType . RegularFile ) ;
2023-08-17 17:58:40 +00:00
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-22 22:42:41 +00:00
string result = BaselineHelper . RemoveVersions ( entry . Name ) ;
result = BaselineHelper . RemoveRids ( result ) ;
result = BaselineHelper . RemoveNetTfmPaths ( result ) ;
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-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-22 22:42:41 +00:00
OutputHelper . LogWarningMessage ( $"'{filePath}' is now 0 bytes. It was {baselineSize} bytes" ) ;
2023-08-17 23:30:26 +00:00
else if ( fileSize ! = 0 & & baselineSize = = 0 )
2023-08-22 22:42:41 +00:00
OutputHelper . LogWarningMessage ( $"'{filePath}' is no longer 0 bytes. It is now {fileSize} bytes" ) ;
2023-08-23 16:30:53 +00:00
else if ( baselineSize ! = 0 & & Math . Abs ( ( ( fileSize - baselineSize ) / ( double ) baselineSize ) * 100 ) > = SizeThresholdPercentage )
OutputHelper . LogWarningMessage ( $"'{filePath}' increased in size by more than {SizeThresholdPercentage}%. It was originally {baselineSize} bytes and is now {fileSize} bytes" ) ;
2023-08-17 23:30:26 +00:00
}
2023-08-17 17:58:40 +00:00
}