Dotnet publish tests
This commit is contained in:
parent
cd357cb273
commit
07eb7ef28f
43 changed files with 1826 additions and 129 deletions
|
@ -130,20 +130,13 @@ Download it from https://www.cmake.org
|
||||||
if (!$?) {
|
if (!$?) {
|
||||||
Write-Host "Command failed: " cmd /c "$PSScriptRoot\build\build_appdeps.cmd" "$Stage2Dir"
|
Write-Host "Command failed: " cmd /c "$PSScriptRoot\build\build_appdeps.cmd" "$Stage2Dir"
|
||||||
Exit 1
|
Exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# Smoke test stage2
|
|
||||||
$env:DOTNET_HOME = "$Stage2Dir"
|
$env:DOTNET_HOME = "$Stage2Dir"
|
||||||
& "$PSScriptRoot\test\smoke-test.ps1"
|
# Run tests on stage2 dotnet tools
|
||||||
|
& "$PSScriptRoot\test\runtests.ps1"
|
||||||
if (!$?) {
|
if (!$?) {
|
||||||
Write-Host "Command failed: $PSScriptRoot\test\smoke-test.ps1"
|
Write-Host "Command failed: $PSScriptRoot\test\runtests.ps1"
|
||||||
Exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# E2E Test of stage2
|
|
||||||
& "$PSScriptRoot\test\e2e-test.ps1"
|
|
||||||
if (!$?) {
|
|
||||||
Write-Host "Command failed: $PSScriptRoot\test\e2e-test.ps1"
|
|
||||||
Exit 1
|
Exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -114,10 +114,6 @@ DOTNET_HOME=$STAGE2_DIR DOTNET_TOOLS=$STAGE2_DIR $REPOROOT/scripts/build/build_a
|
||||||
COMMIT_ID=$(git rev-parse HEAD)
|
COMMIT_ID=$(git rev-parse HEAD)
|
||||||
echo $COMMIT_ID > $STAGE2_DIR/.commit
|
echo $COMMIT_ID > $STAGE2_DIR/.commit
|
||||||
|
|
||||||
# Smoke-test the output
|
|
||||||
header "Testing stage2 ..."
|
|
||||||
DOTNET_HOME=$STAGE2_DIR DOTNET_TOOLS=$STAGE2_DIR $DIR/test/smoke-test.sh
|
|
||||||
|
|
||||||
# E2E test on the output
|
# E2E test on the output
|
||||||
header "Testing stage2 End to End ..."
|
header "Testing stage2..."
|
||||||
DOTNET_HOME=$STAGE2_DIR DOTNET_TOOLS=$STAGE2_DIR $DIR/test/e2e-test.sh
|
DOTNET_HOME=$STAGE2_DIR DOTNET_TOOLS=$STAGE2_DIR $DIR/test/runtests.sh
|
||||||
|
|
|
@ -6,7 +6,7 @@ REM Licensed under the MIT license. See LICENSE file in the project root for ful
|
||||||
SETLOCAL
|
SETLOCAL
|
||||||
SET ERRORLEVEL=
|
SET ERRORLEVEL=
|
||||||
|
|
||||||
"%~dp0dnx\dnx" "%~dp0dnx\Microsoft.Dnx.Tooling\Microsoft.Dnx.Tooling.dll" restore %*
|
"%~dp0dnx\dnx" "%~dp0dnx\lib\Microsoft.Dnx.Tooling\Microsoft.Dnx.Tooling.dll" restore %*
|
||||||
|
|
||||||
exit /b %ERRORLEVEL%
|
exit /b %ERRORLEVEL%
|
||||||
ENDLOCAL
|
ENDLOCAL
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
|
|
||||||
. "$PSScriptRoot\..\_common.ps1"
|
|
||||||
|
|
||||||
# Restore and compile the test app
|
|
||||||
dotnet restore "$RepoRoot\test\E2E" --runtime "osx.10.10-x64" --runtime "ubuntu.14.04-x64" --runtime "win7-x64"
|
|
||||||
if (!$?) {
|
|
||||||
Write-Host "Command failed: dotnet restore"
|
|
||||||
Exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
dotnet publish --framework dnxcore50 --runtime "$Rid" --output "$RepoRoot\artifacts\$Rid\e2etest" "$RepoRoot\test\E2E"
|
|
||||||
if (!$?) {
|
|
||||||
Write-Host "Command failed: dotnet publish"
|
|
||||||
Exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
## Temporary Workaround for Native Compilation
|
|
||||||
## Need x64 Native Tools Dev Prompt Env Vars
|
|
||||||
## Tracked Here: https://github.com/dotnet/cli/issues/301
|
|
||||||
pushd "$env:VS140COMNTOOLS\..\..\VC"
|
|
||||||
cmd /c "vcvarsall.bat x64&set" |
|
|
||||||
foreach {
|
|
||||||
if ($_ -match "=") {
|
|
||||||
$v = $_.split("=", 2); set-item -force -literalpath "ENV:\$($v[0])" -value "$($v[1])"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
popd
|
|
||||||
|
|
||||||
# Run the app and check the exit code
|
|
||||||
pushd "$RepoRoot\artifacts\$Rid\e2etest"
|
|
||||||
mv E2E.exe corehost.exe -Force
|
|
||||||
& "corehost.exe" "xunit.console.netcore.exe" "E2E.dll" -xml ..\..\e2etest.xml
|
|
||||||
if (!$?) {
|
|
||||||
Write-Host "E2E Test Failure"
|
|
||||||
popd
|
|
||||||
Exit 1
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
popd
|
|
||||||
}
|
|
68
scripts/test/runtests.ps1
Normal file
68
scripts/test/runtests.ps1
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
. "$PSScriptRoot\..\_common.ps1"
|
||||||
|
|
||||||
|
$TestBinRoot = "$RepoRoot\artifacts\tests"
|
||||||
|
|
||||||
|
$TestProjects = @(
|
||||||
|
"E2E",
|
||||||
|
"Microsoft.DotNet.Tools.Publish.Tests"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Publish each test project
|
||||||
|
$TestProjects | ForEach-Object {
|
||||||
|
dotnet publish --framework "dnxcore50" --runtime "$Rid" --output "$TestBinRoot" --configuration "$Configuration" "$RepoRoot\test\$_"
|
||||||
|
if (!$?) {
|
||||||
|
Write-Host Command failed: dotnet publish --framework "dnxcore50" --runtime "$Rid" --output "$TestBinRoot" --configuration "$Configuration" "$RepoRoot\test\$_"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
## Temporary Workaround for Native Compilation
|
||||||
|
## Need x64 Native Tools Dev Prompt Env Vars
|
||||||
|
## Tracked Here: https://github.com/dotnet/cli/issues/301
|
||||||
|
pushd "$env:VS140COMNTOOLS\..\..\VC"
|
||||||
|
cmd /c "vcvarsall.bat x64&set" |
|
||||||
|
foreach {
|
||||||
|
if ($_ -match "=") {
|
||||||
|
$v = $_.split("=", 2); set-item -force -literalpath "ENV:\$($v[0])" -value "$($v[1])"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
popd
|
||||||
|
|
||||||
|
# copy TestProjects folder which is used by the test cases
|
||||||
|
mkdir -Force "$TestBinRoot\TestProjects"
|
||||||
|
cp -rec -Force "$RepoRoot\test\TestProjects\*" "$TestBinRoot\TestProjects"
|
||||||
|
|
||||||
|
$failCount = 0
|
||||||
|
$failingTests = @()
|
||||||
|
|
||||||
|
pushd "$TestBinRoot"
|
||||||
|
|
||||||
|
# Run each test project
|
||||||
|
$TestProjects | ForEach-Object {
|
||||||
|
& "corerun.exe" "xunit.console.netcore.exe" "$_.dll" -xml "$_.xml" -notrait category=failing
|
||||||
|
$exitCode = $LastExitCode
|
||||||
|
if ($exitCode -ne 0) {
|
||||||
|
$failingTests += "$_"
|
||||||
|
}
|
||||||
|
|
||||||
|
$failCount += $exitCode
|
||||||
|
}
|
||||||
|
|
||||||
|
popd
|
||||||
|
|
||||||
|
if ($failCount -ne 0) {
|
||||||
|
Write-Host -ForegroundColor Red "The following tests failed."
|
||||||
|
$failingTests | ForEach-Object {
|
||||||
|
Write-Host -ForegroundColor Red "$_.dll failed. Logs in '$TestBinRoot\$_.xml'"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Host -ForegroundColor Green "All the tests passed!"
|
||||||
|
}
|
||||||
|
|
||||||
|
Exit $failCount
|
|
@ -16,12 +16,30 @@ DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
|
||||||
|
|
||||||
source "$DIR/../_common.sh"
|
source "$DIR/../_common.sh"
|
||||||
|
|
||||||
rm "$REPOROOT/test/E2E/project.lock.json"
|
|
||||||
dotnet restore --quiet "$REPOROOT/test/E2E" --runtime "osx.10.10-x64" --runtime "ubuntu.14.04-x64" --runtime "win7-x64"
|
TestBinRoot="$REPOROOT/artifacts/tests"
|
||||||
dotnet publish --framework dnxcore50 --runtime "$RID" --output "$REPOROOT/artifacts/$RID/e2etest" "$REPOROOT/test/E2E"
|
|
||||||
|
TestProjects=( \
|
||||||
|
E2E \
|
||||||
|
Microsoft.DotNet.Tools.Publish.Tests \
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
for project in ${TestProjects[@]}
|
||||||
|
do
|
||||||
|
dotnet publish --framework "dnxcore50" --runtime "$RID" --output "$TestBinRoot" --configuration "$CONFIGURATION" "$REPOROOT/test/$project"
|
||||||
|
done
|
||||||
|
|
||||||
|
# copy TestProjects folder which is used by the test cases
|
||||||
|
mkdir -p "$TestBinRoot/TestProjects"
|
||||||
|
cp -R $REPOROOT/test/TestProjects/* $TestBinRoot/TestProjects
|
||||||
|
|
||||||
# set -e will abort if the exit code of this is non-zero
|
# set -e will abort if the exit code of this is non-zero
|
||||||
pushd "$REPOROOT/artifacts/$RID/e2etest"
|
pushd "$TestBinRoot"
|
||||||
mv ./E2E ./corehost
|
|
||||||
./corehost xunit.console.netcore.exe E2E.dll
|
for project in ${TestProjects[@]}
|
||||||
|
do
|
||||||
|
./corerun "xunit.console.netcore.exe" "$project.dll" -xml "project.xml"
|
||||||
|
done
|
||||||
|
|
||||||
popd
|
popd
|
|
@ -1,25 +0,0 @@
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
|
|
||||||
. "$PSScriptRoot\..\_common.ps1"
|
|
||||||
|
|
||||||
# Restore and compile the test app
|
|
||||||
dotnet restore "$RepoRoot\test\TestApp" --runtime "osx.10.10-x64" --runtime "ubuntu.14.04-x64" --runtime "win7-x64"
|
|
||||||
dotnet compile "$RepoRoot\test\TestApp" --output "$RepoRoot\artifacts\$Rid\smoketest"
|
|
||||||
|
|
||||||
# Run the app and check the exit code
|
|
||||||
& "$RepoRoot\artifacts\$Rid\smoketest\TestApp.exe"
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
throw "Test App failed to run"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check that a compiler error is reported
|
|
||||||
$oldErrorAction = $ErrorActionPreference
|
|
||||||
$ErrorActionPreference="SilentlyContinue"
|
|
||||||
dotnet compile "$RepoRoot\test\compile\failing\SimpleCompilerError" --framework "$Tfm" 2>$null >$null
|
|
||||||
if ($LASTEXITCODE -eq 0) {
|
|
||||||
throw "Compiler error didn't cause non-zero exit code!"
|
|
||||||
}
|
|
||||||
$ErrorActionPreference = $oldErrorAction
|
|
|
@ -1,35 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
SOURCE="${BASH_SOURCE[0]}"
|
|
||||||
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
|
|
||||||
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
|
|
||||||
SOURCE="$(readlink "$SOURCE")"
|
|
||||||
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
|
|
||||||
done
|
|
||||||
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
|
|
||||||
REPOROOT="$( cd -P "$DIR/../.." && pwd )"
|
|
||||||
|
|
||||||
source "$DIR/../_common.sh"
|
|
||||||
|
|
||||||
rm "$REPOROOT/test/TestApp/project.lock.json"
|
|
||||||
dotnet restore "$REPOROOT/test/TestApp" --runtime "osx.10.10-x64" --runtime "ubuntu.14.04-x64" --runtime "win7-x64"
|
|
||||||
dotnet compile "$REPOROOT/test/TestApp" --output "$REPOROOT/artifacts/$RID/smoketest"
|
|
||||||
|
|
||||||
# set -e will abort if the exit code of this is non-zero
|
|
||||||
$REPOROOT/artifacts/$RID/smoketest/TestApp
|
|
||||||
|
|
||||||
# Check that a compiler error is reported
|
|
||||||
set +e
|
|
||||||
dotnet compile "$REPOROOT/test/compile/failing/SimpleCompilerError" --framework "$TFM" 2>/dev/null >/dev/null
|
|
||||||
rc=$?
|
|
||||||
if [ $rc == 0 ]; then
|
|
||||||
error "Compiler failure test failed! The compiler did not fail to compile!"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
set -e
|
|
|
@ -0,0 +1,176 @@
|
||||||
|
// 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.Runtime.InteropServices;
|
||||||
|
using Microsoft.DotNet.Tools.Test.Utilities;
|
||||||
|
using Xunit;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.DotNet.ProjectModel;
|
||||||
|
|
||||||
|
namespace Microsoft.DotNet.Tools.Publish.Tests
|
||||||
|
{
|
||||||
|
public class PublishTests : TestBase
|
||||||
|
{
|
||||||
|
private string _testProjectsRoot = @"TestProjects";
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> PublishOptions
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new[]
|
||||||
|
{
|
||||||
|
new object[] { "", "", "", "" },
|
||||||
|
new object[] { "dnxcore50", "", "", "" },
|
||||||
|
new object[] { "", RuntimeIdentifier.Current, "", "" },
|
||||||
|
new object[] { "", "", "Release", "" },
|
||||||
|
new object[] { "", "", "", "some/dir"},
|
||||||
|
//new object[] { "", "", "", "\"some/dir/with spaces\"" },
|
||||||
|
new object[] { "dnxcore50", RuntimeIdentifier.Current, "Debug", "some/dir" },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData("PublishOptions")]
|
||||||
|
public void PublishOptionsTest(string framework, string runtime, string config, string outputDir)
|
||||||
|
{
|
||||||
|
// create unique directories in the 'temp' folder
|
||||||
|
var root = Temp.CreateDirectory();
|
||||||
|
var testAppDir = root.CreateDirectory("TestApp");
|
||||||
|
var testLibDir = root.CreateDirectory("TestLibrary");
|
||||||
|
|
||||||
|
//copy projects to the temp dir
|
||||||
|
CopyProjectToTempDir(Path.Combine(_testProjectsRoot, "TestApp"), testAppDir);
|
||||||
|
CopyProjectToTempDir(Path.Combine(_testProjectsRoot, "TestLibrary"), testLibDir);
|
||||||
|
|
||||||
|
RunRestore(testAppDir.Path);
|
||||||
|
RunRestore(testLibDir.Path);
|
||||||
|
|
||||||
|
// run publish
|
||||||
|
outputDir = string.IsNullOrEmpty(outputDir) ? "" : Path.Combine(root.Path, outputDir);
|
||||||
|
var testProject = GetProjectPath(testAppDir);
|
||||||
|
var publishCommand = new PublishCommand(testProject, output: outputDir);
|
||||||
|
publishCommand.Execute().Should().Pass();
|
||||||
|
|
||||||
|
// verify the output executable generated
|
||||||
|
var publishedDir = publishCommand.GetOutputDirectory();
|
||||||
|
var outputExe = publishCommand.GetOutputExecutable();
|
||||||
|
var outputPdb = Path.ChangeExtension(outputExe, "pdb");
|
||||||
|
|
||||||
|
// lets make sure that the output exe is runnable
|
||||||
|
var outputExePath = Path.Combine(publishedDir.FullName, publishCommand.GetOutputExecutable());
|
||||||
|
var command = new TestCommand(outputExePath);
|
||||||
|
command.Execute("").Should().ExitWith(100);
|
||||||
|
|
||||||
|
// the pdb should also be published
|
||||||
|
publishedDir.Should().HaveFile(outputPdb);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[ActiveIssue(491)]
|
||||||
|
public void ProjectWithContentsTest()
|
||||||
|
{
|
||||||
|
// create unique directories in the 'temp' folder
|
||||||
|
var testDir = Temp.CreateDirectory();
|
||||||
|
var testAppDir = Path.Combine(_testProjectsRoot, "TestAppWithContents");
|
||||||
|
|
||||||
|
// copy projects to the temp dir
|
||||||
|
CopyProjectToTempDir(testAppDir, testDir);
|
||||||
|
|
||||||
|
RunRestore(testDir.Path);
|
||||||
|
|
||||||
|
// run publish
|
||||||
|
var testProject = GetProjectPath(testDir);
|
||||||
|
var publishCommand = new PublishCommand(testProject);
|
||||||
|
publishCommand.Execute().Should().Pass();
|
||||||
|
|
||||||
|
// make sure that the output dir has the content files
|
||||||
|
publishCommand.GetOutputDirectory().Should().HaveFile("testcontentfile.txt");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BeforeRestoreTest()
|
||||||
|
{
|
||||||
|
// create unique directories in the 'temp' folder
|
||||||
|
var root = Temp.CreateDirectory();
|
||||||
|
var testAppDir = root.CreateDirectory("TestApp");
|
||||||
|
var testLibDir = root.CreateDirectory("TestLibrary");
|
||||||
|
|
||||||
|
// copy projects to the temp dir
|
||||||
|
CopyProjectToTempDir(Path.Combine(_testProjectsRoot, "TestApp"), testAppDir);
|
||||||
|
CopyProjectToTempDir(Path.Combine(_testProjectsRoot, "TestLibrary"), testLibDir);
|
||||||
|
|
||||||
|
var testProject = GetProjectPath(testAppDir);
|
||||||
|
var publishCommand = new PublishCommand(testProject);
|
||||||
|
publishCommand.Execute().Should().Fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LibraryPublishTest()
|
||||||
|
{
|
||||||
|
// create unique directories in the 'temp' folder
|
||||||
|
var root = Temp.CreateDirectory();
|
||||||
|
var testLibDir = root.CreateDirectory("TestLibrary");
|
||||||
|
|
||||||
|
//copy projects to the temp dir
|
||||||
|
CopyProjectToTempDir(Path.Combine(_testProjectsRoot, "TestLibrary"), testLibDir);
|
||||||
|
|
||||||
|
RunRestore(testLibDir.Path);
|
||||||
|
|
||||||
|
var testProject = GetProjectPath(testLibDir);
|
||||||
|
var publishCommand = new PublishCommand(testProject);
|
||||||
|
publishCommand.Execute().Should().Pass();
|
||||||
|
|
||||||
|
publishCommand.GetOutputDirectory().Should().NotHaveFile("TestLibrary.exe");
|
||||||
|
publishCommand.GetOutputDirectory().Should().HaveFile("TestLibrary.dll");
|
||||||
|
publishCommand.GetOutputDirectory().Should().HaveFile("TestLibrary.pdb");
|
||||||
|
// dependencies should also be copied
|
||||||
|
publishCommand.GetOutputDirectory().Should().HaveFile("System.Runtime.dll");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CompilationFailedTest()
|
||||||
|
{
|
||||||
|
var testDir = Temp.CreateDirectory();
|
||||||
|
var compileFailDir = Path.Combine(_testProjectsRoot, "CompileFail");
|
||||||
|
|
||||||
|
CopyProjectToTempDir(compileFailDir, testDir);
|
||||||
|
|
||||||
|
RunRestore(testDir.Path);
|
||||||
|
|
||||||
|
var testProject = GetProjectPath(testDir);
|
||||||
|
var publishCommand = new PublishCommand(testProject);
|
||||||
|
|
||||||
|
publishCommand.Execute().Should().Fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CopyProjectToTempDir(string projectDir, TempDirectory tempDir)
|
||||||
|
{
|
||||||
|
// copy all the files to temp dir
|
||||||
|
foreach (var file in Directory.EnumerateFiles(projectDir))
|
||||||
|
{
|
||||||
|
// never copy project.lock.json. All the tests are expected to call 'dotnet restore'
|
||||||
|
if (file.ToLower().EndsWith("project.lock.json"))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
tempDir.CopyFile(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetProjectPath(TempDirectory projectDir)
|
||||||
|
{
|
||||||
|
return Path.Combine(projectDir.Path, "project.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RunRestore(string args)
|
||||||
|
{
|
||||||
|
var restoreCommand = new RestoreCommand();
|
||||||
|
restoreCommand.Execute($"--quiet {args}").Should().Pass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||||
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>386d412c-003c-47b1-8258-0e35865cb7c4</ProjectGuid>
|
||||||
|
<RootNamespace>Microsoft.DotNet.Tools.Publish.Tests</RootNamespace>
|
||||||
|
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||||
|
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'" />
|
||||||
|
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
</Project>
|
24
test/Microsoft.DotNet.Tools.Publish.Tests/project.json
Normal file
24
test/Microsoft.DotNet.Tools.Publish.Tests/project.json
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"version": "1.0.0-*",
|
||||||
|
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.0.1-*",
|
||||||
|
"Microsoft.NETCore.Runtime": "1.0.1-*",
|
||||||
|
"Microsoft.NETCore.TestHost" : "1.0.0-*",
|
||||||
|
"System.Console": "4.0.0-beta-*",
|
||||||
|
"System.Runtime": "4.0.21-beta-*",
|
||||||
|
"System.AppContext": "4.0.1-beta-*",
|
||||||
|
|
||||||
|
"xunit": "2.1.0",
|
||||||
|
"xunit.console.netcore": "1.0.2-prerelease-00101",
|
||||||
|
"xunit.netcore.extensions": "1.0.0-prerelease-*",
|
||||||
|
"xunit.runner.utility": "2.1.0",
|
||||||
|
|
||||||
|
"Microsoft.DotNet.Tools.Tests.Utilities": { "target": "project" }
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
"frameworks": {
|
||||||
|
"dnxcore50": { }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
// 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 FluentAssertions;
|
||||||
|
using FluentAssertions.Execution;
|
||||||
|
using Microsoft.DotNet.Cli.Utils;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Microsoft.DotNet.Tools.Test.Utilities
|
||||||
|
{
|
||||||
|
public class CommandResultAssertions
|
||||||
|
{
|
||||||
|
private CommandResult _commandResult;
|
||||||
|
|
||||||
|
public CommandResultAssertions(CommandResult commandResult)
|
||||||
|
{
|
||||||
|
_commandResult = commandResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AndConstraint<CommandResultAssertions> ExitWith(int expectedExitCode)
|
||||||
|
{
|
||||||
|
Execute.Assertion.ForCondition(_commandResult.ExitCode == expectedExitCode)
|
||||||
|
.FailWith("Expected command to exit with {0} but it exited with {1}.", expectedExitCode, _commandResult.ExitCode);
|
||||||
|
return new AndConstraint<CommandResultAssertions>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AndConstraint<CommandResultAssertions> Pass()
|
||||||
|
{
|
||||||
|
Execute.Assertion.ForCondition(_commandResult.ExitCode == 0)
|
||||||
|
.FailWith("Expected command to pass but it exited with {0}.", _commandResult.ExitCode);
|
||||||
|
return new AndConstraint<CommandResultAssertions>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AndConstraint<CommandResultAssertions> Fail()
|
||||||
|
{
|
||||||
|
Execute.Assertion.ForCondition(_commandResult.ExitCode != 0)
|
||||||
|
.FailWith("Expected command to fail but it exited with {0}.", _commandResult.ExitCode);
|
||||||
|
return new AndConstraint<CommandResultAssertions>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AndConstraint<CommandResultAssertions> HaveStdOut()
|
||||||
|
{
|
||||||
|
Execute.Assertion.ForCondition(!string.IsNullOrEmpty(_commandResult.StdOut))
|
||||||
|
.FailWith("Command did not output anything to stdout");
|
||||||
|
return new AndConstraint<CommandResultAssertions>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AndConstraint<CommandResultAssertions> HaveStdErr()
|
||||||
|
{
|
||||||
|
Execute.Assertion.ForCondition(!string.IsNullOrEmpty(_commandResult.StdErr))
|
||||||
|
.FailWith("Command did not output anything to stderr");
|
||||||
|
return new AndConstraint<CommandResultAssertions>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AndConstraint<CommandResultAssertions> NotHaveStdOut()
|
||||||
|
{
|
||||||
|
Execute.Assertion.ForCondition(string.IsNullOrEmpty(_commandResult.StdOut))
|
||||||
|
.FailWith("Expected command to not output to stdout but found - {0}{1}", Environment.NewLine, _commandResult.StdOut);
|
||||||
|
return new AndConstraint<CommandResultAssertions>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AndConstraint<CommandResultAssertions> NotHaveStdErr()
|
||||||
|
{
|
||||||
|
Execute.Assertion.ForCondition(string.IsNullOrEmpty(_commandResult.StdErr))
|
||||||
|
.FailWith("Expected command to not output to stderr but found - {0}{1}", Environment.NewLine, _commandResult.StdErr);
|
||||||
|
return new AndConstraint<CommandResultAssertions>(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
// 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 Microsoft.DotNet.Cli.Utils;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using FluentAssertions;
|
||||||
|
|
||||||
|
namespace Microsoft.DotNet.Tools.Test.Utilities
|
||||||
|
{
|
||||||
|
public static class CommandResultExtensions
|
||||||
|
{
|
||||||
|
public static CommandResultAssertions Should(this CommandResult commandResult)
|
||||||
|
{
|
||||||
|
return new CommandResultAssertions(commandResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
// 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 FluentAssertions;
|
||||||
|
using FluentAssertions.Execution;
|
||||||
|
using Microsoft.DotNet.Cli.Utils;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Microsoft.DotNet.Tools.Test.Utilities
|
||||||
|
{
|
||||||
|
public class DirectoryInfoAssertions
|
||||||
|
{
|
||||||
|
private DirectoryInfo _dirInfo;
|
||||||
|
|
||||||
|
public DirectoryInfoAssertions(DirectoryInfo dir)
|
||||||
|
{
|
||||||
|
_dirInfo = dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AndConstraint<DirectoryInfoAssertions> Exist()
|
||||||
|
{
|
||||||
|
_dirInfo.Exists.Should().BeTrue();
|
||||||
|
Execute.Assertion.ForCondition(_dirInfo.Exists)
|
||||||
|
.FailWith("Expected directory {0} does not exist.", _dirInfo.FullName);
|
||||||
|
return new AndConstraint<DirectoryInfoAssertions>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AndConstraint<DirectoryInfoAssertions> HaveFile(string expectedFile)
|
||||||
|
{
|
||||||
|
var file = _dirInfo.EnumerateFiles(expectedFile, SearchOption.TopDirectoryOnly).SingleOrDefault();
|
||||||
|
Execute.Assertion.ForCondition(file != null)
|
||||||
|
.FailWith("Expected File {0} cannot be found in directory {1}.", expectedFile, _dirInfo.FullName);
|
||||||
|
return new AndConstraint<DirectoryInfoAssertions>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AndConstraint<DirectoryInfoAssertions> NotHaveFile(string expectedFile)
|
||||||
|
{
|
||||||
|
var file = _dirInfo.EnumerateFiles(expectedFile, SearchOption.TopDirectoryOnly).SingleOrDefault();
|
||||||
|
Execute.Assertion.ForCondition(file == null)
|
||||||
|
.FailWith("File {0} should not be found in directory {1}.", expectedFile, _dirInfo.FullName);
|
||||||
|
return new AndConstraint<DirectoryInfoAssertions>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AndConstraint<DirectoryInfoAssertions> HaveFiles(IEnumerable<string> expectedFiles)
|
||||||
|
{
|
||||||
|
foreach (var expectedFile in expectedFiles)
|
||||||
|
{
|
||||||
|
HaveFile(expectedFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AndConstraint<DirectoryInfoAssertions>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AndConstraint<DirectoryInfoAssertions> HaveDirectory(string expectedDir)
|
||||||
|
{
|
||||||
|
var dir = _dirInfo.EnumerateDirectories(expectedDir, SearchOption.TopDirectoryOnly).SingleOrDefault();
|
||||||
|
Execute.Assertion.ForCondition(dir != null)
|
||||||
|
.FailWith("Expected directory {0} cannot be found inside directory {1}.", expectedDir, _dirInfo.FullName);
|
||||||
|
|
||||||
|
return new AndConstraint<DirectoryInfoAssertions>(new DirectoryInfoAssertions(dir));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Microsoft.DotNet.Tools.Test.Utilities
|
||||||
|
{
|
||||||
|
public static class DirectoryInfoExtensions
|
||||||
|
{
|
||||||
|
public static DirectoryInfoAssertions Should(this DirectoryInfo dir)
|
||||||
|
{
|
||||||
|
return new DirectoryInfoAssertions(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.DotNet.Cli.Utils;
|
||||||
|
using Microsoft.DotNet.ProjectModel;
|
||||||
|
using Microsoft.DotNet.Tools.Test.Utilities;
|
||||||
|
|
||||||
|
namespace Microsoft.DotNet.Tools.Publish.Tests
|
||||||
|
{
|
||||||
|
public sealed class PublishCommand : TestCommand
|
||||||
|
{
|
||||||
|
private Project _project;
|
||||||
|
private string _path;
|
||||||
|
private string _framework;
|
||||||
|
private string _runtime;
|
||||||
|
private string _config;
|
||||||
|
private string _output;
|
||||||
|
|
||||||
|
public PublishCommand(string projectPath, string framework="", string runtime="", string output="", string config="")
|
||||||
|
: base("dotnet")
|
||||||
|
{
|
||||||
|
_path = projectPath;
|
||||||
|
_project = ProjectReader.GetProject(projectPath);
|
||||||
|
_framework = framework;
|
||||||
|
_runtime = runtime;
|
||||||
|
_output = output;
|
||||||
|
_config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public override CommandResult Execute(string args="")
|
||||||
|
{
|
||||||
|
args = $"publish {BuildArgs()} {args}";
|
||||||
|
return base.Execute(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ProjectName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _project.Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string BuildRelativeOutputPath()
|
||||||
|
{
|
||||||
|
// lets try to build an approximate output path
|
||||||
|
string config = string.IsNullOrEmpty(_config) ? "Debug" : _config;
|
||||||
|
string framework = string.IsNullOrEmpty(_framework) ?
|
||||||
|
_project.GetTargetFrameworks().First().FrameworkName.GetShortFolderName() : _framework;
|
||||||
|
string runtime = string.IsNullOrEmpty(_runtime) ? RuntimeIdentifier.Current : _runtime;
|
||||||
|
string output = Path.Combine("bin", config, framework, runtime);
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DirectoryInfo GetOutputDirectory()
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(_output))
|
||||||
|
{
|
||||||
|
return new DirectoryInfo(_output);
|
||||||
|
}
|
||||||
|
|
||||||
|
string output = Path.Combine(_project.ProjectDirectory, BuildRelativeOutputPath());
|
||||||
|
return new DirectoryInfo(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetOutputExecutable()
|
||||||
|
{
|
||||||
|
var result = _project.Name;
|
||||||
|
result += RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : "";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string BuildArgs()
|
||||||
|
{
|
||||||
|
return $"{_path} {GetFrameworkOption()} {GetRuntimeOption()} {GetOutputOption()} {GetConfigOption()}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetFrameworkOption()
|
||||||
|
{
|
||||||
|
return string.IsNullOrEmpty(_framework) ? "" : $"-f {_framework}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetRuntimeOption()
|
||||||
|
{
|
||||||
|
return string.IsNullOrEmpty(_runtime) ? "" : $"-r {_runtime}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetOutputOption()
|
||||||
|
{
|
||||||
|
return string.IsNullOrEmpty(_output) ? "" : $"-o {_output}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetConfigOption()
|
||||||
|
{
|
||||||
|
return string.IsNullOrEmpty(_config) ? "" : $"-c {_output}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
// 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 Microsoft.DotNet.Cli.Utils;
|
||||||
|
using Microsoft.DotNet.Tools.Test.Utilities;
|
||||||
|
|
||||||
|
namespace Microsoft.DotNet.Tools.Publish.Tests
|
||||||
|
{
|
||||||
|
public sealed class RestoreCommand : TestCommand
|
||||||
|
{
|
||||||
|
public RestoreCommand()
|
||||||
|
: base("dotnet")
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override CommandResult Execute(string args="")
|
||||||
|
{
|
||||||
|
args = $"restore {args}";
|
||||||
|
return base.Execute(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
// 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 Microsoft.DotNet.Cli.Utils;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
|
||||||
|
namespace Microsoft.DotNet.Tools.Test.Utilities
|
||||||
|
{
|
||||||
|
public class TestCommand
|
||||||
|
{
|
||||||
|
protected string _command;
|
||||||
|
|
||||||
|
public TestCommand(string command)
|
||||||
|
{
|
||||||
|
_command = command;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual CommandResult Execute(string args)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Executing - {_command} {args}");
|
||||||
|
var commandResult = Command.Create(_command, args)
|
||||||
|
.ForwardStdErr()
|
||||||
|
.ForwardStdOut()
|
||||||
|
.Execute();
|
||||||
|
|
||||||
|
return commandResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||||
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>e4f46eab-b5a5-4e60-9b9d-40a1fadbf45c</ProjectGuid>
|
||||||
|
<RootNamespace>Microsoft.DotNet.Tools.Test.Utilities</RootNamespace>
|
||||||
|
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||||
|
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
</Project>
|
|
@ -0,0 +1,30 @@
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
namespace Microsoft.DotNet.Tools.Test.Utilities
|
||||||
|
{
|
||||||
|
public sealed class DisposableDirectory : TempDirectory, IDisposable
|
||||||
|
{
|
||||||
|
public DisposableDirectory(TempRoot root)
|
||||||
|
: base(root)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (Path != null && Directory.Exists(Path))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Directory.Delete(Path, recursive: true);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
// 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.Runtime.InteropServices;
|
||||||
|
using Microsoft.Win32.SafeHandles;
|
||||||
|
|
||||||
|
namespace Microsoft.DotNet.Tools.Test.Utilities
|
||||||
|
{
|
||||||
|
public sealed class DisposableFile : TempFile, IDisposable
|
||||||
|
{
|
||||||
|
public DisposableFile(string path)
|
||||||
|
: base(path)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public DisposableFile(string prefix = null, string extension = null, string directory = null, string callerSourcePath = null, int callerLineNumber = 0)
|
||||||
|
: base(prefix, extension, directory, callerSourcePath, callerLineNumber)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (Path != null && File.Exists(Path))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.Delete(Path);
|
||||||
|
}
|
||||||
|
catch (UnauthorizedAccessException)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// the file might still be memory-mapped, delete on close:
|
||||||
|
DeleteFileOnClose(Path);
|
||||||
|
}
|
||||||
|
catch (IOException ex)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(string.Format(@"
|
||||||
|
The file '{0}' seems to have been opened in a way that prevents us from deleting it on close.
|
||||||
|
Is the file loaded as an assembly (e.g. via Assembly.LoadFile)?
|
||||||
|
|
||||||
|
{1}: {2}", Path, ex.GetType().Name, ex.Message), ex);
|
||||||
|
}
|
||||||
|
catch (UnauthorizedAccessException)
|
||||||
|
{
|
||||||
|
// We should ignore this exception if we got it the second time,
|
||||||
|
// the most important reason is that the file has already been
|
||||||
|
// scheduled for deletion and will be deleted when all handles
|
||||||
|
// are closed.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", PreserveSig = false)]
|
||||||
|
private static extern void SetFileInformationByHandle(SafeFileHandle handle, int fileInformationClass, ref uint fileDispositionInfoDeleteFile, int bufferSize);
|
||||||
|
|
||||||
|
private const int FileDispositionInfo = 4;
|
||||||
|
|
||||||
|
internal static void PrepareDeleteOnCloseStreamForDisposal(FileStream stream)
|
||||||
|
{
|
||||||
|
// tomat: Set disposition to "delete" on the stream, so to avoid ForeFront EndPoint
|
||||||
|
// Protection driver scanning the file. Note that after calling this on a file that's open with DeleteOnClose,
|
||||||
|
// the file can't be opened again, not even by the same process.
|
||||||
|
uint trueValue = 1;
|
||||||
|
SetFileInformationByHandle(stream.SafeFileHandle, FileDispositionInfo, ref trueValue, sizeof(uint));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Marks given file for automatic deletion when all its handles are closed.
|
||||||
|
/// Note that after doing this the file can't be opened again, not even by the same process.
|
||||||
|
/// </summary>
|
||||||
|
internal static void DeleteFileOnClose(string fullPath)
|
||||||
|
{
|
||||||
|
using (var stream = new FileStream(fullPath, FileMode.Open, FileAccess.ReadWrite, FileShare.Delete | FileShare.ReadWrite, 8, FileOptions.DeleteOnClose))
|
||||||
|
{
|
||||||
|
PrepareDeleteOnCloseStreamForDisposal(stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,183 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
namespace Microsoft.DotNet.Tools.Test.Utilities
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Implements a few file name utilities that are needed by the compiler.
|
||||||
|
/// In general the compiler is not supposed to understand the format of the paths.
|
||||||
|
/// In rare cases it needs to check if a string is a valid file name or change the extension
|
||||||
|
/// (embedded resources, netmodules, output name).
|
||||||
|
/// The APIs are intentionally limited to cover just these rare cases. Do not add more APIs.
|
||||||
|
/// </summary>
|
||||||
|
internal static class FileNameUtilities
|
||||||
|
{
|
||||||
|
private const string DirectorySeparatorStr = "\\";
|
||||||
|
internal const char DirectorySeparatorChar = '\\';
|
||||||
|
internal const char AltDirectorySeparatorChar = '/';
|
||||||
|
internal const char VolumeSeparatorChar = ':';
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if the string represents an unqualified file name.
|
||||||
|
/// The name may contain any characters but directory and volume separators.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">Path.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// True if <paramref name="path"/> is a simple file name, false if it is null or includes a directory specification.
|
||||||
|
/// </returns>
|
||||||
|
internal static bool IsFileName(string path)
|
||||||
|
{
|
||||||
|
return IndexOfFileName(path) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the offset in <paramref name="path"/> where the dot that starts an extension is, or -1 if the path doesn't have an extension.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Returns 0 for path ".foo".
|
||||||
|
/// Returns -1 for path "foo.".
|
||||||
|
/// </remarks>
|
||||||
|
private static int IndexOfExtension(string path)
|
||||||
|
{
|
||||||
|
if (path == null)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int length = path.Length;
|
||||||
|
int i = length;
|
||||||
|
|
||||||
|
while (--i >= 0)
|
||||||
|
{
|
||||||
|
char c = path[i];
|
||||||
|
if (c == '.')
|
||||||
|
{
|
||||||
|
if (i != length - 1)
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == DirectorySeparatorChar || c == AltDirectorySeparatorChar || c == VolumeSeparatorChar)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an extension of the specified path string.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The same functionality as <see cref="System.IO.Path.GetExtension(string)"/> but doesn't throw an exception
|
||||||
|
/// if there are invalid characters in the path.
|
||||||
|
/// </remarks>
|
||||||
|
internal static string GetExtension(string path)
|
||||||
|
{
|
||||||
|
if (path == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = IndexOfExtension(path);
|
||||||
|
return (index >= 0) ? path.Substring(index) : string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes extension from path.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Returns "foo" for path "foo.".
|
||||||
|
/// Returns "foo.." for path "foo...".
|
||||||
|
/// </remarks>
|
||||||
|
private static string RemoveExtension(string path)
|
||||||
|
{
|
||||||
|
if (path == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = IndexOfExtension(path);
|
||||||
|
if (index >= 0)
|
||||||
|
{
|
||||||
|
return path.Substring(0, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// trim last ".", if present
|
||||||
|
if (path.Length > 0 && path[path.Length - 1] == '.')
|
||||||
|
{
|
||||||
|
return path.Substring(0, path.Length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns path with the extension changed to <paramref name="extension"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// Equivalent of <see cref="System.IO.Path.ChangeExtension(string, string)"/>
|
||||||
|
///
|
||||||
|
/// If <paramref name="path"/> is null, returns null.
|
||||||
|
/// If path does not end with an extension, the new extension is appended to the path.
|
||||||
|
/// If extension is null, equivalent to <see cref="RemoveExtension"/>.
|
||||||
|
/// </returns>
|
||||||
|
internal static string ChangeExtension(string path, string extension)
|
||||||
|
{
|
||||||
|
if (path == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pathWithoutExtension = RemoveExtension(path);
|
||||||
|
if (extension == null || path.Length == 0)
|
||||||
|
{
|
||||||
|
return pathWithoutExtension;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extension.Length == 0 || extension[0] != '.')
|
||||||
|
{
|
||||||
|
return pathWithoutExtension + "." + extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pathWithoutExtension + extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the position in given path where the file name starts.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>-1 if path is null.</returns>
|
||||||
|
internal static int IndexOfFileName(string path)
|
||||||
|
{
|
||||||
|
if (path == null)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = path.Length - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
char ch = path[i];
|
||||||
|
if (ch == DirectorySeparatorChar || ch == AltDirectorySeparatorChar || ch == VolumeSeparatorChar)
|
||||||
|
{
|
||||||
|
return i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get file name from path.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Unlike <see cref="System.IO.Path.GetFileName"/> doesn't check for invalid path characters.</remarks>
|
||||||
|
internal static string GetFileName(string path)
|
||||||
|
{
|
||||||
|
int fileNameStart = IndexOfFileName(path);
|
||||||
|
return (fileNameStart <= 0) ? path : path.Substring(fileNameStart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Microsoft.DotNet.Tools.Test.Utilities
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The collection of extension methods for the <see cref="ImmutableArray{T}"/> type
|
||||||
|
/// </summary>
|
||||||
|
public static class ImmutableArrayTestExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Writes read-only array of bytes to the specified file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="bytes">Data to write to the file.</param>
|
||||||
|
/// <param name="path">File path.</param>
|
||||||
|
internal static void WriteToFile(this ImmutableArray<byte> bytes, string path)
|
||||||
|
{
|
||||||
|
Debug.Assert(!bytes.IsDefault);
|
||||||
|
|
||||||
|
const int bufferSize = 4096;
|
||||||
|
using (FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, bufferSize))
|
||||||
|
{
|
||||||
|
// PERF: Consider using an ObjectPool<byte[]> here
|
||||||
|
byte[] buffer = new byte[Math.Min(bufferSize, bytes.Length)];
|
||||||
|
|
||||||
|
int offset = 0;
|
||||||
|
while (offset < bytes.Length)
|
||||||
|
{
|
||||||
|
int length = Math.Min(bufferSize, bytes.Length - offset);
|
||||||
|
bytes.CopyTo(offset, buffer, 0, length);
|
||||||
|
fileStream.Write(buffer, 0, length);
|
||||||
|
offset += length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
namespace Microsoft.DotNet.Tools.Test.Utilities
|
||||||
|
{
|
||||||
|
internal enum PathKind
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Null or empty.
|
||||||
|
/// </summary>
|
||||||
|
Empty,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "file"
|
||||||
|
/// </summary>
|
||||||
|
Relative,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ".\file"
|
||||||
|
/// </summary>
|
||||||
|
RelativeToCurrentDirectory,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "..\file"
|
||||||
|
/// </summary>
|
||||||
|
RelativeToCurrentParent,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "\dir\file"
|
||||||
|
/// </summary>
|
||||||
|
RelativeToCurrentRoot,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "C:dir\file"
|
||||||
|
/// </summary>
|
||||||
|
RelativeToDriveDirectory,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "C:\file" or "\\machine" (UNC).
|
||||||
|
/// </summary>
|
||||||
|
Absolute,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,379 @@
|
||||||
|
// 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.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Microsoft.DotNet.Tools.Test.Utilities
|
||||||
|
{
|
||||||
|
// Contains path parsing utilities.
|
||||||
|
// We need our own because System.IO.Path is insufficient for our purposes
|
||||||
|
// For example we need to be able to work with invalid paths or paths containing wildcards
|
||||||
|
internal static class PathUtilities
|
||||||
|
{
|
||||||
|
// We consider '/' a directory separator on Unix like systems.
|
||||||
|
// On Windows both / and \ are equally accepted.
|
||||||
|
internal static readonly char DirectorySeparatorChar = IsUnixLikePlatform ? '/' : '\\';
|
||||||
|
internal static readonly char AltDirectorySeparatorChar = '/';
|
||||||
|
internal static readonly string DirectorySeparatorStr = new string(DirectorySeparatorChar, 1);
|
||||||
|
internal const char VolumeSeparatorChar = ':';
|
||||||
|
|
||||||
|
private static bool IsUnixLikePlatform
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Path.DirectorySeparatorChar == '/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool IsDirectorySeparator(char c)
|
||||||
|
{
|
||||||
|
return c == DirectorySeparatorChar || c == AltDirectorySeparatorChar;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string TrimTrailingSeparators(string s)
|
||||||
|
{
|
||||||
|
int lastSeparator = s.Length;
|
||||||
|
while (lastSeparator > 0 && IsDirectorySeparator(s[lastSeparator - 1]))
|
||||||
|
{
|
||||||
|
lastSeparator = lastSeparator - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastSeparator != s.Length)
|
||||||
|
{
|
||||||
|
s = s.Substring(0, lastSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string GetExtension(string path)
|
||||||
|
{
|
||||||
|
return FileNameUtilities.GetExtension(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string ChangeExtension(string path, string extension)
|
||||||
|
{
|
||||||
|
return FileNameUtilities.ChangeExtension(path, extension);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string RemoveExtension(string path)
|
||||||
|
{
|
||||||
|
return FileNameUtilities.ChangeExtension(path, extension: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string GetFileName(string path)
|
||||||
|
{
|
||||||
|
return FileNameUtilities.GetFileName(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get directory name from path.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Unlike <see cref="System.IO.Path.GetDirectoryName"/> it
|
||||||
|
/// doesn't check for invalid path characters,
|
||||||
|
/// doesn't strip any trailing directory separators (TODO: tomat),
|
||||||
|
/// doesn't recognize UNC structure \\computer-name\share\directory-name\file-name (TODO: tomat).
|
||||||
|
/// </remarks>
|
||||||
|
/// <returns>Prefix of path that represents a directory. </returns>
|
||||||
|
internal static string GetDirectoryName(string path)
|
||||||
|
{
|
||||||
|
int fileNameStart = FileNameUtilities.IndexOfFileName(path);
|
||||||
|
if (fileNameStart < 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return path.Substring(0, fileNameStart);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static PathKind GetPathKind(string path)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(path))
|
||||||
|
{
|
||||||
|
return PathKind.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "C:\"
|
||||||
|
// "\\machine" (UNC)
|
||||||
|
// "/etc" (Unix)
|
||||||
|
if (IsAbsolute(path))
|
||||||
|
{
|
||||||
|
return PathKind.Absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "."
|
||||||
|
// ".."
|
||||||
|
// ".\"
|
||||||
|
// "..\"
|
||||||
|
if (path.Length > 0 && path[0] == '.')
|
||||||
|
{
|
||||||
|
if (path.Length == 1 || IsDirectorySeparator(path[1]))
|
||||||
|
{
|
||||||
|
return PathKind.RelativeToCurrentDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path[1] == '.')
|
||||||
|
{
|
||||||
|
if (path.Length == 2 || IsDirectorySeparator(path[2]))
|
||||||
|
{
|
||||||
|
return PathKind.RelativeToCurrentParent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsUnixLikePlatform)
|
||||||
|
{
|
||||||
|
// "\"
|
||||||
|
// "\foo"
|
||||||
|
if (path.Length >= 1 && IsDirectorySeparator(path[0]))
|
||||||
|
{
|
||||||
|
return PathKind.RelativeToCurrentRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "C:foo"
|
||||||
|
|
||||||
|
if (path.Length >= 2 && path[1] == VolumeSeparatorChar && (path.Length <= 2 || !IsDirectorySeparator(path[2])))
|
||||||
|
{
|
||||||
|
return PathKind.RelativeToDriveDirectory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// "foo.dll"
|
||||||
|
return PathKind.Relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool IsAbsolute(string path)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(path))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsUnixLikePlatform)
|
||||||
|
{
|
||||||
|
return path[0] == DirectorySeparatorChar;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "C:\"
|
||||||
|
if (IsDriveRootedAbsolutePath(path))
|
||||||
|
{
|
||||||
|
// Including invalid paths (e.g. "*:\")
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "\\machine\share"
|
||||||
|
// Including invalid/incomplete UNC paths (e.g. "\\foo")
|
||||||
|
return path.Length >= 2 &&
|
||||||
|
IsDirectorySeparator(path[0]) &&
|
||||||
|
IsDirectorySeparator(path[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if given path is absolute and starts with a drive specification ("C:\").
|
||||||
|
/// </summary>
|
||||||
|
private static bool IsDriveRootedAbsolutePath(string path)
|
||||||
|
{
|
||||||
|
Debug.Assert(!IsUnixLikePlatform);
|
||||||
|
return path.Length >= 3 && path[1] == VolumeSeparatorChar && IsDirectorySeparator(path[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a prefix of given path which is the root of the path.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// Root of an absolute path or null if the path isn't absolute or has invalid format (e.g. "\\").
|
||||||
|
/// It may or may not end with a directory separator (e.g. "C:\", "C:\foo", "\\machine\share", etc.) .
|
||||||
|
/// </returns>
|
||||||
|
internal static string GetPathRoot(string path)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(path))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int length = GetPathRootLength(path);
|
||||||
|
return (length != -1) ? path.Substring(0, length) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetPathRootLength(string path)
|
||||||
|
{
|
||||||
|
Debug.Assert(!string.IsNullOrEmpty(path));
|
||||||
|
|
||||||
|
if (IsUnixLikePlatform)
|
||||||
|
{
|
||||||
|
if (IsDirectorySeparator(path[0]))
|
||||||
|
{
|
||||||
|
// "/*"
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// "C:\"
|
||||||
|
if (IsDriveRootedAbsolutePath(path))
|
||||||
|
{
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsDirectorySeparator(path[0]))
|
||||||
|
{
|
||||||
|
// "\\machine\share"
|
||||||
|
return GetUncPathRootLength(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates the length of root of an UNC path.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// "\\server\share" is root of UNC path "\\server\share\dir1\dir2\file".
|
||||||
|
/// </remarks>
|
||||||
|
private static int GetUncPathRootLength(string path)
|
||||||
|
{
|
||||||
|
Debug.Assert(IsDirectorySeparator(path[0]));
|
||||||
|
|
||||||
|
// root:
|
||||||
|
// [directory-separator]{2,}[^directory-separator]+[directory-separator]+[^directory-separator]+
|
||||||
|
|
||||||
|
int serverIndex = IndexOfNonDirectorySeparator(path, 1);
|
||||||
|
if (serverIndex < 2)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int separator = IndexOfDirectorySeparator(path, serverIndex);
|
||||||
|
if (separator == -1)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int shareIndex = IndexOfNonDirectorySeparator(path, separator);
|
||||||
|
if (shareIndex == -1)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rootEnd = IndexOfDirectorySeparator(path, shareIndex);
|
||||||
|
return rootEnd == -1 ? path.Length : rootEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int IndexOfDirectorySeparator(string path, int start)
|
||||||
|
{
|
||||||
|
for (int i = start; i < path.Length; i++)
|
||||||
|
{
|
||||||
|
if (IsDirectorySeparator(path[i]))
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int IndexOfNonDirectorySeparator(string path, int start)
|
||||||
|
{
|
||||||
|
for (int i = start; i < path.Length; i++)
|
||||||
|
{
|
||||||
|
if (!IsDirectorySeparator(path[i]))
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Combines an absolute path with a relative.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="root">Absolute root path.</param>
|
||||||
|
/// <param name="relativePath">Relative path.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// An absolute combined path, or null if <paramref name="relativePath"/> is
|
||||||
|
/// absolute (e.g. "C:\abc", "\\machine\share\abc"),
|
||||||
|
/// relative to the current root (e.g. "\abc"),
|
||||||
|
/// or relative to a drive directory (e.g. "C:abc\def").
|
||||||
|
/// </returns>
|
||||||
|
/// <seealso cref="CombinePossiblyRelativeAndRelativePaths"/>
|
||||||
|
internal static string CombineAbsoluteAndRelativePaths(string root, string relativePath)
|
||||||
|
{
|
||||||
|
Debug.Assert(IsAbsolute(root));
|
||||||
|
|
||||||
|
return CombinePossiblyRelativeAndRelativePaths(root, relativePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Combine two paths, the first of which may be absolute.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rootOpt">First path: absolute, relative, or null.</param>
|
||||||
|
/// <param name="relativePath">Second path: relative and non-null.</param>
|
||||||
|
/// <returns>null, if <paramref name="rootOpt"/> is null; a combined path, otherwise.</returns>
|
||||||
|
/// <seealso cref="CombineAbsoluteAndRelativePaths"/>
|
||||||
|
internal static string CombinePossiblyRelativeAndRelativePaths(string rootOpt, string relativePath)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(rootOpt))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (GetPathKind(relativePath))
|
||||||
|
{
|
||||||
|
case PathKind.Empty:
|
||||||
|
return rootOpt;
|
||||||
|
|
||||||
|
case PathKind.Absolute:
|
||||||
|
case PathKind.RelativeToCurrentRoot:
|
||||||
|
case PathKind.RelativeToDriveDirectory:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CombinePathsUnchecked(rootOpt, relativePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string CombinePathsUnchecked(string root, string relativePath)
|
||||||
|
{
|
||||||
|
Debug.Assert(!string.IsNullOrEmpty(root));
|
||||||
|
|
||||||
|
char c = root[root.Length - 1];
|
||||||
|
if (!IsDirectorySeparator(c) && c != VolumeSeparatorChar)
|
||||||
|
{
|
||||||
|
return root + DirectorySeparatorStr + relativePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
return root + relativePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string RemoveTrailingDirectorySeparator(string path)
|
||||||
|
{
|
||||||
|
if (path.Length > 0 && IsDirectorySeparator(path[path.Length - 1]))
|
||||||
|
{
|
||||||
|
return path.Substring(0, path.Length - 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether an assembly reference is considered an assembly file path or an assembly name.
|
||||||
|
/// used, for example, on values of /r and #r.
|
||||||
|
/// </summary>
|
||||||
|
internal static bool IsFilePath(string assemblyDisplayNameOrPath)
|
||||||
|
{
|
||||||
|
Debug.Assert(assemblyDisplayNameOrPath != null);
|
||||||
|
|
||||||
|
string extension = FileNameUtilities.GetExtension(assemblyDisplayNameOrPath);
|
||||||
|
return string.Equals(extension, ".dll", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(extension, ".exe", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| assemblyDisplayNameOrPath.IndexOf(DirectorySeparatorChar) != -1
|
||||||
|
|| assemblyDisplayNameOrPath.IndexOf(AltDirectorySeparatorChar) != -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
// 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.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Microsoft.DotNet.Tools.Test.Utilities
|
||||||
|
{
|
||||||
|
public class TempDirectory
|
||||||
|
{
|
||||||
|
private readonly string _path;
|
||||||
|
private readonly TempRoot _root;
|
||||||
|
|
||||||
|
protected TempDirectory(TempRoot root)
|
||||||
|
: this(CreateUniqueDirectory(TempRoot.Root), root)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private TempDirectory(string path, TempRoot root)
|
||||||
|
{
|
||||||
|
Debug.Assert(path != null);
|
||||||
|
Debug.Assert(root != null);
|
||||||
|
|
||||||
|
_path = path;
|
||||||
|
_root = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string CreateUniqueDirectory(string basePath)
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
string dir = System.IO.Path.Combine(basePath, Guid.NewGuid().ToString());
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(dir);
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
catch (IOException)
|
||||||
|
{
|
||||||
|
// retry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Path
|
||||||
|
{
|
||||||
|
get { return _path; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a file in this directory.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">File name.</param>
|
||||||
|
public TempFile CreateFile(string name)
|
||||||
|
{
|
||||||
|
string filePath = System.IO.Path.Combine(_path, name);
|
||||||
|
TempRoot.CreateStream(filePath);
|
||||||
|
return _root.AddFile(new DisposableFile(filePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a file in this directory that is a copy of the specified file.
|
||||||
|
/// </summary>
|
||||||
|
public TempFile CopyFile(string originalPath)
|
||||||
|
{
|
||||||
|
string name = System.IO.Path.GetFileName(originalPath);
|
||||||
|
string filePath = System.IO.Path.Combine(_path, name);
|
||||||
|
File.Copy(originalPath, filePath);
|
||||||
|
return _root.AddFile(new DisposableFile(filePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a subdirectory in this directory.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Directory name or unrooted directory path.</param>
|
||||||
|
public TempDirectory CreateDirectory(string name)
|
||||||
|
{
|
||||||
|
string dirPath = System.IO.Path.Combine(_path, name);
|
||||||
|
Directory.CreateDirectory(dirPath);
|
||||||
|
return new TempDirectory(dirPath, _root);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetCurrentDirectory()
|
||||||
|
{
|
||||||
|
Directory.SetCurrentDirectory(_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return _path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
// 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.Collections.Immutable;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Microsoft.DotNet.Tools.Test.Utilities
|
||||||
|
{
|
||||||
|
public class TempFile
|
||||||
|
{
|
||||||
|
private readonly string _path;
|
||||||
|
|
||||||
|
internal TempFile(string path)
|
||||||
|
{
|
||||||
|
Debug.Assert(PathUtilities.IsAbsolute(path));
|
||||||
|
_path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal TempFile(string prefix, string extension, string directory, string callerSourcePath, int callerLineNumber)
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (prefix == null)
|
||||||
|
{
|
||||||
|
prefix = System.IO.Path.GetFileName(callerSourcePath) + "_" + callerLineNumber.ToString() + "_";
|
||||||
|
}
|
||||||
|
|
||||||
|
_path = System.IO.Path.Combine(directory ?? TempRoot.Root, prefix + Guid.NewGuid() + (extension ?? ".tmp"));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TempRoot.CreateStream(_path);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (PathTooLongException)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch (DirectoryNotFoundException)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch (IOException)
|
||||||
|
{
|
||||||
|
// retry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileStream Open(FileAccess access = FileAccess.ReadWrite)
|
||||||
|
{
|
||||||
|
return new FileStream(_path, FileMode.Open, access);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Path
|
||||||
|
{
|
||||||
|
get { return _path; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public TempFile WriteAllText(string content, Encoding encoding)
|
||||||
|
{
|
||||||
|
File.WriteAllText(_path, content, encoding);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TempFile WriteAllText(string content)
|
||||||
|
{
|
||||||
|
File.WriteAllText(_path, content);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<TempFile> WriteAllTextAsync(string content, Encoding encoding)
|
||||||
|
{
|
||||||
|
using (var sw = new StreamWriter(File.Create(_path), encoding))
|
||||||
|
{
|
||||||
|
await sw.WriteAsync(content).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<TempFile> WriteAllTextAsync(string content)
|
||||||
|
{
|
||||||
|
return WriteAllTextAsync(content, Encoding.UTF8);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TempFile WriteAllBytes(byte[] content)
|
||||||
|
{
|
||||||
|
File.WriteAllBytes(_path, content);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TempFile WriteAllBytes(ImmutableArray<byte> content)
|
||||||
|
{
|
||||||
|
content.WriteToFile(_path);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ReadAllText()
|
||||||
|
{
|
||||||
|
return File.ReadAllText(_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TempFile CopyContentFrom(string path)
|
||||||
|
{
|
||||||
|
return WriteAllBytes(File.ReadAllBytes(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return _path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Microsoft.DotNet.Tools.Test.Utilities
|
||||||
|
{
|
||||||
|
public sealed class TempRoot : IDisposable
|
||||||
|
{
|
||||||
|
private readonly List<IDisposable> _temps = new List<IDisposable>();
|
||||||
|
public static readonly string Root;
|
||||||
|
|
||||||
|
static TempRoot()
|
||||||
|
{
|
||||||
|
Root = Path.Combine(Path.GetTempPath(), "DotnetCLITests");
|
||||||
|
Directory.CreateDirectory(Root);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_temps != null)
|
||||||
|
{
|
||||||
|
DisposeAll(_temps);
|
||||||
|
_temps.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DisposeAll(IEnumerable<IDisposable> temps)
|
||||||
|
{
|
||||||
|
foreach (var temp in temps)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (temp != null)
|
||||||
|
{
|
||||||
|
temp.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TempDirectory CreateDirectory()
|
||||||
|
{
|
||||||
|
var dir = new DisposableDirectory(this);
|
||||||
|
_temps.Add(dir);
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TempFile CreateFile(string prefix = null, string extension = null, string directory = null, [CallerFilePath]string callerSourcePath = null, [CallerLineNumber]int callerLineNumber = 0)
|
||||||
|
{
|
||||||
|
return AddFile(new DisposableFile(prefix, extension, directory, callerSourcePath, callerLineNumber));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DisposableFile AddFile(DisposableFile file)
|
||||||
|
{
|
||||||
|
_temps.Add(file);
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void CreateStream(string fullPath)
|
||||||
|
{
|
||||||
|
using (var file = new FileStream(fullPath, FileMode.CreateNew)) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
49
test/Microsoft.DotNet.Tools.Tests.Utilities/TestBase.cs
Normal file
49
test/Microsoft.DotNet.Tools.Tests.Utilities/TestBase.cs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Microsoft.DotNet.Tools.Test.Utilities
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base class for all unit test classes.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class TestBase : IDisposable
|
||||||
|
{
|
||||||
|
private TempRoot _temp;
|
||||||
|
|
||||||
|
protected TestBase()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetUniqueName()
|
||||||
|
{
|
||||||
|
return Guid.NewGuid().ToString("D");
|
||||||
|
}
|
||||||
|
|
||||||
|
public TempRoot Temp
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_temp == null)
|
||||||
|
{
|
||||||
|
_temp = new TempRoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Dispose()
|
||||||
|
{
|
||||||
|
if (_temp != null)
|
||||||
|
{
|
||||||
|
_temp.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
test/Microsoft.DotNet.Tools.Tests.Utilities/project.json
Normal file
22
test/Microsoft.DotNet.Tools.Tests.Utilities/project.json
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"version": "1.0.0-*",
|
||||||
|
"description": "Microsoft.DotNet.Tools.Tests.Utilities Class Library",
|
||||||
|
|
||||||
|
"dependencies": {
|
||||||
|
"System.Collections": "4.0.11-*",
|
||||||
|
"System.Collections.Immutable": "1.1.38-*",
|
||||||
|
"System.Linq": "4.0.1-*",
|
||||||
|
"System.Threading": "4.0.11-*",
|
||||||
|
"System.IO.FileSystem": "4.0.1-*",
|
||||||
|
"System.IO": "4.0.11-*",
|
||||||
|
"System.Runtime.InteropServices": "4.0.21-*",
|
||||||
|
"FluentAssertions": "4.0.0",
|
||||||
|
|
||||||
|
"Microsoft.DotNet.Cli.Utils": { "target": "project" }
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
"frameworks": {
|
||||||
|
"dnxcore50": { }
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,9 +8,10 @@ namespace TestApp
|
||||||
{
|
{
|
||||||
public class Program
|
public class Program
|
||||||
{
|
{
|
||||||
public static void Main(string[] args)
|
public static int Main(string[] args)
|
||||||
{
|
{
|
||||||
Console.WriteLine(TestLibrary.Helper.GetMessage());
|
Console.WriteLine(TestLibrary.Helper.GetMessage());
|
||||||
|
return 100;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
13
test/TestProjects/TestAppWithContents/Program.cs
Normal file
13
test/TestProjects/TestAppWithContents/Program.cs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace ConsoleApplication
|
||||||
|
{
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static int Main(string[] args)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Hello World!");
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
test/TestProjects/TestAppWithContents/project.json
Normal file
18
test/TestProjects/TestAppWithContents/project.json
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"version": "1.0.0-*",
|
||||||
|
"compilationOptions": {
|
||||||
|
"emitEntryPoint": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Runtime": "1.0.1-beta-*",
|
||||||
|
"System.IO": "4.0.10-beta-*",
|
||||||
|
"System.Console": "4.0.0-beta-*",
|
||||||
|
"System.Runtime": "4.0.21-beta-*"
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
"frameworks": {
|
||||||
|
"dnxcore50": { }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
This is to test if contents are copied correctly by the dotnet tools
|
Loading…
Add table
Add a link
Reference in a new issue