2016-01-14 11:52:54 -08:00
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System ;
using System.IO ;
using System.Linq ;
2016-03-03 22:57:43 -08:00
using FluentAssertions ;
2016-01-14 11:52:54 -08:00
using Microsoft.DotNet.Cli.Utils ;
using Microsoft.DotNet.Tools.Test.Utilities ;
using Xunit ;
2016-02-11 14:17:20 -08:00
using Microsoft.DotNet.TestFramework ;
2016-05-02 11:32:24 -07:00
using System.Diagnostics ;
2016-01-14 11:52:54 -08:00
namespace Microsoft.DotNet.Tools.Builder.Tests
{
public class IncrementalTests : IncrementalTestBase
{
2016-02-11 14:17:20 -08:00
public IncrementalTests ( )
2016-01-14 11:52:54 -08:00
{
2016-02-11 14:17:20 -08:00
MainProject = "TestSimpleIncrementalApp" ;
ExpectedOutput = "Hello World!" + Environment . NewLine ;
}
private TestInstance _testInstance ;
private void CreateTestInstance ( )
{
_testInstance = TestAssetsManager . CreateTestInstance ( "TestSimpleIncrementalApp" )
. WithLockFiles ( ) ;
TestProjectRoot = _testInstance . TestRoot ;
2016-01-14 11:52:54 -08:00
}
[Fact]
2016-01-21 11:04:45 -08:00
public void TestNoIncrementalFlag ( )
2016-01-14 11:52:54 -08:00
{
2016-02-11 14:17:20 -08:00
CreateTestInstance ( ) ;
2016-01-14 11:52:54 -08:00
var buildResult = BuildProject ( ) ;
2016-04-12 17:29:07 -07:00
buildResult . Should ( ) . HaveCompiledProject ( MainProject , _appFrameworkFullName ) ;
2016-01-14 11:52:54 -08:00
2016-01-21 11:04:45 -08:00
buildResult = BuildProject ( noIncremental : true ) ;
2016-04-18 15:51:35 -07:00
buildResult . Should ( ) . HaveCompiledProject ( MainProject , _appFrameworkFullName ) ;
2016-01-14 11:52:54 -08:00
Assert . Contains ( "[Forced Unsafe]" , buildResult . StdOut ) ;
}
[Fact]
public void TestRebuildMissingPdb ( )
{
2016-02-11 14:17:20 -08:00
CreateTestInstance ( ) ;
2016-01-14 11:52:54 -08:00
TestDeleteOutputWithExtension ( "pdb" ) ;
}
[Fact]
public void TestRebuildMissingDll ( )
{
2016-02-11 14:17:20 -08:00
CreateTestInstance ( ) ;
2016-01-14 11:52:54 -08:00
TestDeleteOutputWithExtension ( "dll" ) ;
}
[Fact]
public void TestRebuildMissingXml ( )
{
2016-02-11 14:17:20 -08:00
CreateTestInstance ( ) ;
2016-01-14 11:52:54 -08:00
TestDeleteOutputWithExtension ( "xml" ) ;
}
[Fact]
public void TestNoLockFile ( )
{
2016-02-11 14:17:20 -08:00
CreateTestInstance ( ) ;
2016-01-14 11:52:54 -08:00
var buildResult = BuildProject ( ) ;
2016-04-12 17:29:07 -07:00
buildResult . Should ( ) . HaveCompiledProject ( MainProject , _appFrameworkFullName ) ;
2016-01-14 11:52:54 -08:00
2016-02-11 14:17:20 -08:00
var lockFile = Path . Combine ( TestProjectRoot , "project.lock.json" ) ;
2016-01-14 11:52:54 -08:00
Assert . True ( File . Exists ( lockFile ) ) ;
File . Delete ( lockFile ) ;
Assert . False ( File . Exists ( lockFile ) ) ;
2016-02-11 15:55:37 -08:00
buildResult = BuildProject ( expectBuildFailure : true ) ;
2016-02-09 10:05:07 -08:00
Assert . Contains ( "does not have a lock file" , buildResult . StdErr ) ;
2016-05-20 09:19:05 +02:00
Assert . Contains ( "dotnet restore" , buildResult . StdErr ) ;
2016-01-14 11:52:54 -08:00
}
2016-03-03 22:57:43 -08:00
[Fact]
public void TestModifiedVersionFile ( )
{
CreateTestInstance ( ) ;
2016-04-12 17:29:07 -07:00
BuildProject ( ) . Should ( ) . HaveCompiledProject ( MainProject , _appFrameworkFullName ) ;
2016-03-03 22:57:43 -08:00
2016-04-18 15:51:35 -07:00
// change version file
2016-03-03 22:57:43 -08:00
var versionFile = Path . Combine ( GetIntermediaryOutputPath ( ) , ".SDKVersion" ) ;
File . Exists ( versionFile ) . Should ( ) . BeTrue ( ) ;
File . AppendAllText ( versionFile , "text" ) ;
2016-04-18 15:51:35 -07:00
// assert rebuilt
2016-04-12 17:29:07 -07:00
BuildProject ( ) . Should ( ) . HaveCompiledProject ( MainProject , _appFrameworkFullName ) ;
2016-03-03 22:57:43 -08:00
}
[Fact]
public void TestNoVersionFile ( )
{
CreateTestInstance ( ) ;
2016-04-12 17:29:07 -07:00
BuildProject ( ) . Should ( ) . HaveCompiledProject ( MainProject , _appFrameworkFullName ) ;
2016-03-03 22:57:43 -08:00
2016-04-18 15:51:35 -07:00
// delete version file
2016-03-03 22:57:43 -08:00
var versionFile = Path . Combine ( GetIntermediaryOutputPath ( ) , ".SDKVersion" ) ;
File . Exists ( versionFile ) . Should ( ) . BeTrue ( ) ;
File . Delete ( versionFile ) ;
File . Exists ( versionFile ) . Should ( ) . BeFalse ( ) ;
2016-04-18 15:51:35 -07:00
// assert build skipped due to no version file
BuildProject ( ) . Should ( ) . HaveSkippedProjectCompilation ( MainProject , _appFrameworkFullName ) ;
// the version file should have been regenerated during the build, even if compilation got skipped
File . Exists ( versionFile ) . Should ( ) . BeTrue ( ) ;
}
[Fact]
public void TestRebuildDeletedSource ( )
{
CreateTestInstance ( ) ;
BuildProject ( ) . Should ( ) . HaveCompiledProject ( MainProject , _appFrameworkFullName ) ;
var sourceFile = Path . Combine ( GetProjectDirectory ( MainProject ) , "Program2.cs" ) ;
File . Delete ( sourceFile ) ;
Assert . False ( File . Exists ( sourceFile ) ) ;
// second build; should get rebuilt since we deleted a source file
BuildProject ( ) . Should ( ) . HaveCompiledProject ( MainProject , _appFrameworkFullName ) ;
// third build; incremental cache should have been regenerated and project skipped
BuildProject ( ) . Should ( ) . HaveSkippedProjectCompilation ( MainProject , _appFrameworkFullName ) ;
}
[Fact]
public void TestRebuildRenamedSource ( )
{
CreateTestInstance ( ) ;
var buildResult = BuildProject ( ) ;
buildResult . Should ( ) . HaveCompiledProject ( MainProject , _appFrameworkFullName ) ;
var sourceFile = Path . Combine ( GetProjectDirectory ( MainProject ) , "Program2.cs" ) ;
var destinationFile = Path . Combine ( Path . GetDirectoryName ( sourceFile ) , "ProgramNew.cs" ) ;
File . Move ( sourceFile , destinationFile ) ;
Assert . False ( File . Exists ( sourceFile ) ) ;
Assert . True ( File . Exists ( destinationFile ) ) ;
// second build; should get rebuilt since we renamed a source file
buildResult = BuildProject ( ) ;
buildResult . Should ( ) . HaveCompiledProject ( MainProject , _appFrameworkFullName ) ;
// third build; incremental cache should have been regenerated and project skipped
2016-04-12 17:29:07 -07:00
BuildProject ( ) . Should ( ) . HaveSkippedProjectCompilation ( MainProject , _appFrameworkFullName ) ;
2016-04-18 15:51:35 -07:00
}
[Fact]
public void TestRebuildDeletedSourceAfterCliChanged ( )
{
CreateTestInstance ( ) ;
BuildProject ( ) . Should ( ) . HaveCompiledProject ( MainProject , _appFrameworkFullName ) ;
2016-03-03 22:57:43 -08:00
2016-04-18 15:51:35 -07:00
// change version file
var versionFile = Path . Combine ( GetIntermediaryOutputPath ( ) , ".SDKVersion" ) ;
2016-03-03 22:57:43 -08:00
File . Exists ( versionFile ) . Should ( ) . BeTrue ( ) ;
2016-04-18 15:51:35 -07:00
File . AppendAllText ( versionFile , "text" ) ;
// delete a source file
var sourceFile = Path . Combine ( GetProjectDirectory ( MainProject ) , "Program2.cs" ) ;
File . Delete ( sourceFile ) ;
Assert . False ( File . Exists ( sourceFile ) ) ;
// should get rebuilt since we changed version file and deleted source file
BuildProject ( ) . Should ( ) . HaveCompiledProject ( MainProject , _appFrameworkFullName ) ;
// third build; incremental cache should have been regenerated and project skipped
BuildProject ( ) . Should ( ) . HaveSkippedProjectCompilation ( MainProject , _appFrameworkFullName ) ;
2016-03-03 22:57:43 -08:00
}
2016-01-26 14:53:56 -08:00
[Fact]
2016-01-14 11:52:54 -08:00
public void TestRebuildChangedLockFile ( )
{
2016-02-11 14:17:20 -08:00
CreateTestInstance ( ) ;
2016-01-14 11:52:54 -08:00
var buildResult = BuildProject ( ) ;
2016-04-12 17:29:07 -07:00
buildResult . Should ( ) . HaveCompiledProject ( MainProject , _appFrameworkFullName ) ;
2016-01-14 11:52:54 -08:00
2016-02-11 14:17:20 -08:00
var lockFile = Path . Combine ( TestProjectRoot , "project.lock.json" ) ;
2016-01-14 11:52:54 -08:00
TouchFile ( lockFile ) ;
buildResult = BuildProject ( ) ;
2016-04-12 17:29:07 -07:00
buildResult . Should ( ) . HaveCompiledProject ( MainProject , _appFrameworkFullName ) ;
2016-01-14 11:52:54 -08:00
}
2016-01-26 14:53:56 -08:00
[Fact]
2016-01-14 11:52:54 -08:00
public void TestRebuildChangedProjectFile ( )
{
2016-02-11 14:17:20 -08:00
CreateTestInstance ( ) ;
2016-01-14 11:52:54 -08:00
var buildResult = BuildProject ( ) ;
2016-04-12 17:29:07 -07:00
buildResult . Should ( ) . HaveCompiledProject ( MainProject , _appFrameworkFullName ) ;
2016-01-26 14:53:56 -08:00
TouchFile ( GetProjectFile ( MainProject ) ) ;
buildResult = BuildProject ( ) ;
2016-04-12 17:29:07 -07:00
buildResult . Should ( ) . HaveCompiledProject ( MainProject , _appFrameworkFullName ) ;
2016-01-26 14:53:56 -08:00
}
// regression for https://github.com/dotnet/cli/issues/965
[Fact]
public void TestInputWithSameTimeAsOutputCausesProjectToCompile ( )
{
2016-02-11 14:17:20 -08:00
CreateTestInstance ( ) ;
2016-01-26 14:53:56 -08:00
var buildResult = BuildProject ( ) ;
2016-04-12 17:29:07 -07:00
buildResult . Should ( ) . HaveCompiledProject ( MainProject , _appFrameworkFullName ) ;
2016-01-14 11:52:54 -08:00
2016-01-26 14:53:56 -08:00
var outputTimestamp = SetAllOutputItemsToSameTime ( ) ;
// set an input to have the same last write time as an output item
// this should trigger recompilation to account for file systems with second timestamp granularity
// (an input file that changed within the same second as the previous outputs should trigger a rebuild)
File . SetLastWriteTime ( GetProjectFile ( MainProject ) , outputTimestamp ) ;
2016-01-14 11:52:54 -08:00
buildResult = BuildProject ( ) ;
2016-04-12 17:29:07 -07:00
buildResult . Should ( ) . HaveCompiledProject ( MainProject , _appFrameworkFullName ) ;
2016-01-26 14:53:56 -08:00
}
private DateTime SetAllOutputItemsToSameTime ( )
{
var now = DateTime . Now ;
foreach ( var f in Directory . EnumerateFiles ( GetCompilationOutputPath ( ) ) )
{
File . SetLastWriteTime ( f , now ) ;
}
return now ;
2016-01-14 11:52:54 -08:00
}
private void TestDeleteOutputWithExtension ( string extension )
{
var buildResult = BuildProject ( ) ;
2016-04-12 17:29:07 -07:00
buildResult . Should ( ) . HaveCompiledProject ( MainProject , _appFrameworkFullName ) ;
2016-01-14 11:52:54 -08:00
2016-02-03 10:57:25 -08:00
Reporter . Verbose . WriteLine ( $"Files in {GetBinRoot()}" ) ;
foreach ( var file in Directory . EnumerateFiles ( GetBinRoot ( ) ) )
2016-01-14 11:52:54 -08:00
{
Reporter . Verbose . Write ( $"\t {file}" ) ;
}
// delete output files with extensions
2016-02-03 10:57:25 -08:00
foreach ( var outputFile in Directory . EnumerateFiles ( GetBinRoot ( ) ) . Where ( f = >
2016-01-14 11:52:54 -08:00
{
var fileName = Path . GetFileName ( f ) ;
2016-01-26 14:53:56 -08:00
return fileName . StartsWith ( MainProject , StringComparison . OrdinalIgnoreCase ) & &
2016-01-14 11:52:54 -08:00
fileName . EndsWith ( extension , StringComparison . OrdinalIgnoreCase ) ;
} ) )
{
Reporter . Output . WriteLine ( $"Deleted {outputFile}" ) ;
File . Delete ( outputFile ) ;
Assert . False ( File . Exists ( outputFile ) ) ;
}
// second build; should get rebuilt since we deleted an output item
buildResult = BuildProject ( ) ;
2016-04-12 17:29:07 -07:00
buildResult . Should ( ) . HaveCompiledProject ( MainProject , _appFrameworkFullName ) ;
2016-01-14 11:52:54 -08:00
}
}
}