2015-12-09 09:57:45 -08:00
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System ;
using System.IO ;
using System.Linq ;
2016-03-10 00:47:13 -08:00
using System.Threading ;
using System.Threading.Tasks ;
using Microsoft.DotNet.ProjectModel.Graph ;
2016-02-29 21:34:48 -08:00
using Microsoft.DotNet.TestFramework ;
2016-02-17 16:49:34 -08:00
using Microsoft.DotNet.Tools.Test.Utilities ;
2016-02-29 21:34:48 -08:00
using Microsoft.Extensions.Logging ;
2016-02-17 16:49:34 -08:00
using Microsoft.Extensions.PlatformAbstractions ;
2015-12-09 09:57:45 -08:00
using Newtonsoft.Json ;
using Newtonsoft.Json.Linq ;
using Xunit ;
namespace Microsoft.DotNet.ProjectModel.Server.Tests
{
2016-02-29 21:34:48 -08:00
public class DthTests : TestBase
2015-12-09 09:57:45 -08:00
{
2016-02-29 21:34:48 -08:00
private readonly TestAssetsManager _testAssetsManager ;
private readonly ILoggerFactory _loggerFactory ;
2016-03-10 00:47:13 -08:00
2016-02-29 21:34:48 -08:00
public DthTests ( )
2015-12-09 09:57:45 -08:00
{
2016-02-29 21:34:48 -08:00
_loggerFactory = new LoggerFactory ( ) ;
var testVerbose = Environment . GetEnvironmentVariable ( "DOTNET_TEST_VERBOSE" ) ;
if ( testVerbose = = "2" )
{
_loggerFactory . AddConsole ( LogLevel . Trace ) ;
}
else if ( testVerbose = = "1" )
{
_loggerFactory . AddConsole ( LogLevel . Information ) ;
}
2016-03-10 00:47:13 -08:00
else if ( testVerbose = = "0" )
2016-02-29 21:34:48 -08:00
{
_loggerFactory . AddConsole ( LogLevel . Warning ) ;
}
2016-03-21 15:18:28 -07:00
else
{
_loggerFactory . AddConsole ( LogLevel . Error ) ;
}
2016-03-10 00:47:13 -08:00
2016-02-29 21:34:48 -08:00
_testAssetsManager = new TestAssetsManager (
Path . Combine ( RepoRoot , "TestAssets" , "ProjectModelServer" , "DthTestProjects" , "src" ) ) ;
2015-12-09 09:57:45 -08:00
}
[Fact]
public void DthStartup_GetProjectInformation ( )
{
2016-02-29 21:34:48 -08:00
var projectPath = Path . Combine ( _testAssetsManager . AssetsRoot , "EmptyConsoleApp" ) ;
2015-12-09 09:57:45 -08:00
Assert . NotNull ( projectPath ) ;
2016-02-29 21:34:48 -08:00
using ( var server = new DthTestServer ( _loggerFactory ) )
2016-03-21 15:21:17 -07:00
using ( var client = new DthTestClient ( server , _loggerFactory ) )
2015-12-09 09:57:45 -08:00
{
client . Initialize ( projectPath ) ;
var projectInformation = client . DrainTillFirst ( MessageTypes . ProjectInformation )
. EnsureSource ( server , client )
. RetrievePayloadAs < JObject > ( )
. AssertProperty ( "Name" , "EmptyConsoleApp" ) ;
projectInformation . RetrievePropertyAs < JArray > ( "Configurations" )
. AssertJArrayCount ( 2 )
. AssertJArrayContains ( "Debug" )
. AssertJArrayContains ( "Release" ) ;
var frameworkShortNames = projectInformation . RetrievePropertyAs < JArray > ( "Frameworks" )
. AssertJArrayCount ( 2 )
. Select ( f = > f [ "ShortName" ] . Value < string > ( ) ) ;
2016-03-01 17:35:32 -06:00
Assert . Contains ( "netstandardapp1.5" , frameworkShortNames ) ;
2015-12-09 09:57:45 -08:00
Assert . Contains ( "dnx451" , frameworkShortNames ) ;
}
}
[Theory]
[InlineData(4, 4)]
[InlineData(5, 4)]
[InlineData(3, 3)]
public void DthStartup_ProtocolNegotiation ( int requestVersion , int expectVersion )
{
2016-02-29 21:34:48 -08:00
using ( var server = new DthTestServer ( _loggerFactory ) )
2016-03-21 15:21:17 -07:00
using ( var client = new DthTestClient ( server , _loggerFactory ) )
2015-12-09 09:57:45 -08:00
{
client . SetProtocolVersion ( requestVersion ) ;
var response = client . DrainTillFirst ( MessageTypes . ProtocolVersion , TimeSpan . FromDays ( 1 ) ) ;
response . EnsureSource ( server , client ) ;
Assert . Equal ( expectVersion , response . Payload [ "Version" ] ? . Value < int > ( ) ) ;
}
}
2016-01-11 22:26:34 -08:00
[Fact]
2015-12-09 09:57:45 -08:00
public void DthStartup_ProtocolNegotiation_ZeroIsNoAllowed ( )
{
2016-02-29 21:34:48 -08:00
using ( var server = new DthTestServer ( _loggerFactory ) )
2016-03-21 15:21:17 -07:00
using ( var client = new DthTestClient ( server , _loggerFactory ) )
2015-12-09 09:57:45 -08:00
{
client . SetProtocolVersion ( 0 ) ;
Assert . Throws < TimeoutException > ( ( ) = >
{
client . DrainTillFirst ( MessageTypes . ProtocolVersion , timeout : TimeSpan . FromSeconds ( 1 ) ) ;
} ) ;
}
}
[Theory]
[InlineData("Project", "UnresolvedProjectSample", "EmptyLibrary", "Project")]
[InlineData("Package", "UnresolvedPackageSample", "NoSuchPackage", null)]
2016-01-29 01:49:56 -08:00
[InlineData("Package", "IncompatiblePackageSample", "Microsoft.Web.Administration", "Package")]
2015-12-09 09:57:45 -08:00
public void DthCompilation_Initialize_UnresolvedDependency ( string referenceType ,
string testProjectName ,
string expectedUnresolvedDependency ,
string expectedUnresolvedType )
{
2016-02-17 16:49:34 -08:00
if ( PlatformServices . Default . Runtime . OperatingSystemPlatform = = Platform . Linux )
{
Console . WriteLine ( "Test is skipped on Linux" ) ;
return ;
}
2016-03-10 00:47:13 -08:00
2016-02-29 21:34:48 -08:00
var projectPath = Path . Combine ( _testAssetsManager . AssetsRoot , testProjectName ) ;
2015-12-09 09:57:45 -08:00
Assert . NotNull ( projectPath ) ;
2016-02-29 21:34:48 -08:00
using ( var server = new DthTestServer ( _loggerFactory ) )
2016-03-21 15:21:17 -07:00
using ( var client = new DthTestClient ( server , _loggerFactory ) )
2015-12-09 09:57:45 -08:00
{
client . Initialize ( projectPath ) ;
2016-01-14 16:24:59 -08:00
var referencesMessage = client . DrainTillFirst ( MessageTypes . References , TimeSpan . FromDays ( 1 ) )
2015-12-09 09:57:45 -08:00
. EnsureSource ( server , client ) ;
if ( referenceType = = "Project" )
{
var expectedUnresolvedProjectPath = Path . Combine ( Path . GetDirectoryName ( projectPath ) ,
expectedUnresolvedDependency ,
Project . FileName ) ;
referencesMessage . RetrievePayloadAs < JObject > ( )
. RetrievePropertyAs < JArray > ( "ProjectReferences" )
. AssertJArrayCount ( 1 )
. RetrieveArraryElementAs < JObject > ( 0 )
. AssertProperty ( "Name" , expectedUnresolvedDependency )
. AssertProperty ( "Path" , expectedUnresolvedProjectPath )
. AssertProperty < JToken > ( "WrappedProjectPath" , prop = > ! prop . HasValues ) ;
}
else if ( referenceType = = "Package" )
{
referencesMessage . RetrievePayloadAs < JObject > ( )
. RetrievePropertyAs < JArray > ( "ProjectReferences" )
. AssertJArrayCount ( 0 ) ;
}
2016-01-14 16:24:59 -08:00
var unresolveDependency = client . DrainTillFirst ( MessageTypes . Dependencies )
. EnsureSource ( server , client )
. RetrieveDependency ( expectedUnresolvedDependency ) ;
unresolveDependency . AssertProperty ( "Name" , expectedUnresolvedDependency )
. AssertProperty ( "DisplayName" , expectedUnresolvedDependency )
. AssertProperty ( "Resolved" , false )
. AssertProperty ( "Type" , expectedUnresolvedType ) ;
if ( expectedUnresolvedType = = "Project" )
{
unresolveDependency . AssertProperty ( "Path" , Path . Combine ( Path . GetDirectoryName ( projectPath ) ,
expectedUnresolvedDependency ,
Project . FileName ) ) ;
}
else
{
Assert . False ( unresolveDependency [ "Path" ] . HasValues ) ;
}
2015-12-09 09:57:45 -08:00
}
}
[Fact]
public void DthNegative_BrokenProjectPathInLockFile ( )
{
2016-02-29 21:34:48 -08:00
using ( var server = new DthTestServer ( _loggerFactory ) )
2016-03-21 15:21:17 -07:00
using ( var client = new DthTestClient ( server , _loggerFactory ) )
2015-12-09 09:57:45 -08:00
{
// After restore the project is copied to another place so that
// the relative path in project lock file is invalid.
2016-02-29 21:34:48 -08:00
var movedProjectPath = _testAssetsManager . CreateTestInstance ( "BrokenProjectPathSample" )
. WithLockFiles ( )
. TestRoot ;
2015-12-09 09:57:45 -08:00
client . Initialize ( movedProjectPath ) ;
client . DrainTillFirst ( "DependencyDiagnostics" )
. RetrieveDependencyDiagnosticsCollection ( )
. RetrieveDependencyDiagnosticsErrorAt ( 0 )
. AssertProperty < string > ( "FormattedMessage" , message = > message . Contains ( "error NU1002" ) )
. RetrievePropertyAs < JObject > ( "Source" )
. AssertProperty ( "Name" , "EmptyLibrary" ) ;
client . DrainTillFirst ( "Dependencies" )
. RetrieveDependency ( "EmptyLibrary" )
. AssertProperty < JArray > ( "Errors" , errorsArray = > errorsArray . Count = = 1 )
. AssertProperty < JArray > ( "Warnings" , warningsArray = > warningsArray . Count = = 0 )
. AssertProperty ( "Name" , "EmptyLibrary" )
. AssertProperty ( "Resolved" , false ) ;
}
}
[Fact(Skip = "Require dotnet restore integration test")]
public void DthDependencies_UpdateGlobalJson_RefreshDependencies ( )
{
2016-02-29 21:34:48 -08:00
var assets = new TestAssetsManager ( Path . Combine ( AppContext . BaseDirectory , "TestAssets" , "ProjectModelServer" ) ) ;
var projectPath = assets . CreateTestInstance ( "DthUpdateSearchPathSample" ) . WithLockFiles ( ) . TestRoot ;
2015-12-09 09:57:45 -08:00
Assert . True ( Directory . Exists ( projectPath ) ) ;
2016-02-29 21:34:48 -08:00
using ( var server = new DthTestServer ( _loggerFactory ) )
2016-03-21 15:21:17 -07:00
using ( var client = new DthTestClient ( server , _loggerFactory ) )
2015-12-09 09:57:45 -08:00
{
var testProject = Path . Combine ( projectPath , "home" , "src" , "MainProject" ) ;
client . Initialize ( testProject ) ;
client . DrainTillFirst ( "ProjectInformation" )
. RetrievePayloadAs < JObject > ( )
. RetrievePropertyAs < JArray > ( "ProjectSearchPaths" )
. AssertJArrayCount ( 2 ) ;
client . DrainTillFirst ( "DependencyDiagnostics" )
. RetrievePayloadAs < JObject > ( )
. AssertProperty < JArray > ( "Errors" , array = > array . Count = = 0 )
. AssertProperty < JArray > ( "Warnings" , array = > array . Count = = 0 ) ;
client . DrainTillFirst ( "Dependencies" )
. RetrieveDependency ( "Newtonsoft.Json" )
. AssertProperty ( "Type" , "Project" )
. AssertProperty ( "Resolved" , true )
. AssertProperty < JArray > ( "Errors" , array = > array . Count = = 0 , _ = > "Dependency shouldn't contain any error." ) ;
// Overwrite the global.json to remove search path to ext
File . WriteAllText (
Path . Combine ( projectPath , "home" , GlobalSettings . FileName ) ,
JsonConvert . SerializeObject ( new { project = new string [ ] { "src" } } ) ) ;
client . SendPayLoad ( testProject , "RefreshDependencies" ) ;
client . DrainTillFirst ( "ProjectInformation" )
. RetrievePayloadAs < JObject > ( )
. RetrievePropertyAs < JArray > ( "ProjectSearchPaths" )
. AssertJArrayCount ( 1 )
. AssertJArrayElement ( 0 , Path . Combine ( projectPath , "home" , "src" ) ) ;
client . DrainTillFirst ( "DependencyDiagnostics" )
. RetrieveDependencyDiagnosticsCollection ( )
. RetrieveDependencyDiagnosticsErrorAt < JObject > ( 0 )
. AssertProperty ( "ErrorCode" , "NU1010" ) ;
client . DrainTillFirst ( "Dependencies" )
. RetrieveDependency ( "Newtonsoft.Json" )
. AssertProperty ( "Type" , "" )
. AssertProperty ( "Resolved" , false )
. RetrievePropertyAs < JArray > ( "Errors" )
. AssertJArrayCount ( 1 )
. RetrieveArraryElementAs < JObject > ( 0 )
. AssertProperty ( "ErrorCode" , "NU1010" ) ;
}
}
2016-01-14 16:04:27 -08:00
[Fact]
public void DthStartup_OpenProjectBeforeRestore ( )
{
2016-02-29 21:34:48 -08:00
var projectPath = _testAssetsManager . CreateTestInstance ( "EmptyConsoleApp" ) . TestRoot ;
2016-01-14 16:04:27 -08:00
2016-02-29 21:34:48 -08:00
using ( var server = new DthTestServer ( _loggerFactory ) )
2016-03-21 15:21:17 -07:00
using ( var client = new DthTestClient ( server , _loggerFactory ) )
2016-01-14 16:04:27 -08:00
{
client . Initialize ( projectPath ) ;
var messages = client . DrainAllMessages ( ) ;
Assert . False ( messages . Any ( msg = > msg . MessageType = = MessageTypes . Error ) ) ;
var dependencyDiagnostics = messages . Where ( msg = > msg . MessageType = = MessageTypes . DependencyDiagnostics ) ;
Assert . Equal ( 2 , dependencyDiagnostics . Count ( ) ) ;
foreach ( var message in dependencyDiagnostics )
{
message . RetrievePayloadAs < JObject > ( )
. RetrievePropertyAs < JArray > ( "Errors" )
. AssertJArrayContains < JObject > ( error = > error [ "ErrorCode" ] . Value < string > ( ) = = ErrorCodes . NU1009 ) ;
}
}
}
2016-02-29 10:46:13 -08:00
[Fact]
public void InvalidProjectJson ( )
{
2016-02-29 22:09:38 -08:00
var testAssetsPath = Path . Combine ( RepoRoot , "TestAssets" , "ProjectModelServer" ) ;
var assetsManager = new TestAssetsManager ( testAssetsPath ) ;
var testSource = assetsManager . CreateTestInstance ( "IncorrectProjectJson" ) . TestRoot ;
2016-03-10 00:47:13 -08:00
2016-02-29 10:46:13 -08:00
using ( var server = new DthTestServer ( _loggerFactory ) )
2016-03-21 15:21:17 -07:00
using ( var client = new DthTestClient ( server , _loggerFactory ) )
2016-02-29 10:46:13 -08:00
{
client . Initialize ( Path . Combine ( _testAssetsManager . AssetsRoot , "EmptyLibrary" ) ) ;
2016-02-29 22:09:38 -08:00
client . Initialize ( testSource ) ;
2016-02-29 10:46:13 -08:00
// Error for invalid project.json
var messages = client . DrainAllMessages ( ) ;
messages . Single ( msg = > msg . MessageType = = MessageTypes . Error )
. Payload . AsJObject ( )
2016-02-29 22:09:38 -08:00
. AssertProperty < string > ( "Path" , v = > v . Contains ( "IncorrectProjectJson" ) ) ;
2016-02-29 10:46:13 -08:00
// Successfully initialize the other project
messages . Single ( msg = > msg . MessageType = = MessageTypes . ProjectInformation )
. Payload . AsJObject ( )
. AssertProperty < string > ( "Name" , v = > string . Equals ( v , "EmptyLibrary" , StringComparison . Ordinal ) ) ;
// Successfully initialize another project afterwards
client . Initialize ( Path . Combine ( _testAssetsManager . AssetsRoot , "EmptyConsoleApp" ) ) ;
messages = client . DrainAllMessages ( ) ;
messages . Single ( msg = > msg . MessageType = = MessageTypes . ProjectInformation )
. Payload . AsJObject ( )
. AssertProperty < string > ( "Name" , v = > string . Equals ( v , "EmptyConsoleApp" , StringComparison . Ordinal ) ) ;
}
}
[Fact]
public void InvalidGlobalJson ( )
{
var testAssetsPath = Path . Combine ( RepoRoot , "TestAssets" , "ProjectModelServer" ) ;
var assetsManager = new TestAssetsManager ( testAssetsPath ) ;
var testSource = assetsManager . CreateTestInstance ( "IncorrectGlobalJson" ) ;
using ( var server = new DthTestServer ( _loggerFactory ) )
2016-03-21 15:21:17 -07:00
using ( var client = new DthTestClient ( server , _loggerFactory ) )
2016-02-29 10:46:13 -08:00
{
client . Initialize ( Path . Combine ( testSource . TestRoot , "src" , "Project1" ) ) ;
var messages = client . DrainAllMessages ( ) ;
messages . ContainsMessage ( MessageTypes . Error )
. Single ( ) . Payload . AsJObject ( )
. AssertProperty < string > ( "Path" , v = > v . Contains ( "InvalidGlobalJson" ) ) ;
}
}
2016-03-10 00:47:13 -08:00
2016-03-03 13:12:33 -08:00
[Fact]
public void RecoverFromGlobalError ( )
{
var testProject = _testAssetsManager . CreateTestInstance ( "EmptyConsoleApp" )
. WithLockFiles ( )
. TestRoot ;
2016-03-10 00:47:13 -08:00
2016-03-03 13:12:33 -08:00
using ( var server = new DthTestServer ( _loggerFactory ) )
2016-03-21 15:21:17 -07:00
using ( var client = new DthTestClient ( server , _loggerFactory ) )
2016-03-03 13:12:33 -08:00
{
var projectFile = Path . Combine ( testProject , Project . FileName ) ;
var content = File . ReadAllText ( projectFile ) ;
File . WriteAllText ( projectFile , content + "}" ) ;
2016-03-10 00:47:13 -08:00
2016-03-03 13:12:33 -08:00
client . Initialize ( testProject ) ;
var messages = client . DrainAllMessages ( ) ;
messages . ContainsMessage ( MessageTypes . Error ) ;
2016-03-10 00:47:13 -08:00
2016-03-03 13:12:33 -08:00
File . WriteAllText ( projectFile , content ) ;
client . SendPayLoad ( testProject , MessageTypes . FilesChanged ) ;
2016-03-09 08:15:18 -08:00
var clearError = client . DrainTillFirst ( MessageTypes . Error ) ;
clearError . Payload . AsJObject ( ) . AssertProperty ( "Message" , null as string ) ;
2016-03-03 13:12:33 -08:00
}
}
2016-03-10 00:47:13 -08:00
[Theory]
[InlineData(500, true)]
[InlineData(3000, false)]
public void WaitForLockFileReleased ( int occupyFileFor , bool expectSuccess )
{
var testProject = _testAssetsManager . CreateTestInstance ( "EmptyConsoleApp" )
. WithLockFiles ( )
. TestRoot ;
using ( var server = new DthTestServer ( _loggerFactory ) )
2016-03-21 15:21:17 -07:00
using ( var client = new DthTestClient ( server , _loggerFactory ) )
2016-03-10 00:47:13 -08:00
{
var lockFilePath = Path . Combine ( testProject , LockFile . FileName ) ;
var lockFileContent = File . ReadAllText ( lockFilePath ) ;
var fs = new FileStream ( lockFilePath , FileMode . Create , FileAccess . Write , FileShare . None ) ;
// Test the platform
// A sharing violation is expected in following code. Otherwise the FileSteam is not implemented correctly.
Assert . ThrowsAny < IOException > ( ( ) = >
{
new FileStream ( lockFilePath , FileMode . Open , FileAccess . Read , FileShare . Read ) ;
} ) ;
var task = Task . Run ( ( ) = >
{
// WorkspaceContext will try to open the lock file for 3 times with 500 ms interval in between.
Thread . Sleep ( occupyFileFor ) ;
fs . Dispose ( ) ;
} ) ;
client . Initialize ( testProject ) ;
var messages = client . DrainAllMessages ( ) ;
if ( expectSuccess )
{
messages . AssertDoesNotContain ( MessageTypes . Error ) ;
}
else
{
messages . ContainsMessage ( MessageTypes . Error ) ;
}
}
}
2015-12-09 09:57:45 -08:00
}
}