diff --git a/MusicStore.sln b/MusicStore.sln
index 0c409e95f2..66539db055 100644
--- a/MusicStore.sln
+++ b/MusicStore.sln
@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
-VisualStudioVersion = 15.0.26405.2
+VisualStudioVersion = 15.0.26228.9
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7D749BDA-4638-4517-B66A-D40DEDEEB141}"
ProjectSection(SolutionItems) = preProject
@@ -17,6 +17,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MusicStore.Test", "test\Mus
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MusicStore.E2ETests", "test\MusicStore.E2ETests\MusicStore.E2ETests.csproj", "{72A5F455-121F-4954-BF28-D712C6BE88EA}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{88A30728-49E5-46C5-9CEC-9D8FD346A043}"
+ ProjectSection(SolutionItems) = preProject
+ build\dependencies.props = build\dependencies.props
+ build\repo.targets = build\repo.targets
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
diff --git a/build/dependencies.props b/build/dependencies.props
index 181df87181..95607dedc7 100644
--- a/build/dependencies.props
+++ b/build/dependencies.props
@@ -13,6 +13,7 @@
4.3.0
$(BundledNETStandardPackageVersion)
+ 4.0.0
15.0.0
2.2.0
diff --git a/build/repo.targets b/build/repo.targets
index 6940b72667..f0b024c20b 100644
--- a/build/repo.targets
+++ b/build/repo.targets
@@ -1,10 +1,44 @@
+ $(PrepareDependsOn);UpdateNuGetConfig
$(RepositoryRoot)test\MusicStore.E2ETests\MusicStore.E2ETests.csproj
-
+
+
+
+
+
+
+
+
+
+
+ VSTestTestCaseFilter=E2ETests=NanoServer
+
+
+
+
+
+
+
+
+ VSTestTestCaseFilter=smoketests=usestore
+
+
+
+
+
@@ -12,4 +46,5 @@
+
diff --git a/samples/MusicStore/MusicStore.csproj b/samples/MusicStore/MusicStore.csproj
index 1b97253010..51ff611dfd 100644
--- a/samples/MusicStore/MusicStore.csproj
+++ b/samples/MusicStore/MusicStore.csproj
@@ -7,32 +7,15 @@
netcoreapp2.0
$(DefineConstants);DEMO
true
- win7-x86;win7-x64;linux-x64;osx-x64
+ win7-x86;win7-x64;linux-x64;osx-x64
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/samples/MusicStore/Program.cs b/samples/MusicStore/Program.cs
index 7c491669dd..a0a71b10dc 100644
--- a/samples/MusicStore/Program.cs
+++ b/samples/MusicStore/Program.cs
@@ -53,11 +53,9 @@ namespace MusicStore
builder.ConfigureLogging(factory =>
{
- factory.AddConsole();
-
var logLevel = string.Equals(environment, "Development", StringComparison.Ordinal) ? LogLevel.Information : LogLevel.Warning;
- factory.AddFilter("Console", level => level >= logLevel);
+ factory.AddConsole();
});
var host = builder.Build();
diff --git a/test/MusicStore.E2ETests/MusicStore.E2ETests.csproj b/test/MusicStore.E2ETests/MusicStore.E2ETests.csproj
index c78ca94989..7bda4710aa 100644
--- a/test/MusicStore.E2ETests/MusicStore.E2ETests.csproj
+++ b/test/MusicStore.E2ETests/MusicStore.E2ETests.csproj
@@ -15,11 +15,14 @@
+
+
+
diff --git a/test/MusicStore.E2ETests/SmokeTestsUsingStore/BaseStoreSetupFixture.cs b/test/MusicStore.E2ETests/SmokeTestsUsingStore/BaseStoreSetupFixture.cs
new file mode 100644
index 0000000000..c53658f22e
--- /dev/null
+++ b/test/MusicStore.E2ETests/SmokeTestsUsingStore/BaseStoreSetupFixture.cs
@@ -0,0 +1,53 @@
+using System;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Testing;
+
+namespace E2ETests
+{
+ public class BaseStoreSetupFixture : IDisposable
+ {
+ private readonly IDisposable _logToken;
+ private readonly ILogger _logger;
+ private readonly Store _store;
+
+ public BaseStoreSetupFixture(bool createInDefaultLocation, string loggerName)
+ {
+ if (!Store.IsEnabled())
+ {
+ return;
+ }
+
+ var testLog = AssemblyTestLog.ForAssembly(typeof(BaseStoreSetupFixture).Assembly);
+ ILoggerFactory loggerFactory;
+ _logToken = testLog.StartTestLog(null, loggerName, out loggerFactory, testName: loggerName);
+ _logger = loggerFactory.CreateLogger();
+
+ CreateStoreInDefaultLocation = createInDefaultLocation;
+
+ _logger.LogInformation(
+ "Setting up store in the location: {location}",
+ createInDefaultLocation ? "default" : "custom");
+
+ _store = new Store(loggerFactory);
+
+ StoreDirectory = _store.CreateStore(createInDefaultLocation);
+ }
+
+ public bool CreateStoreInDefaultLocation { get; }
+
+ public string StoreDirectory { get; }
+
+ public void Dispose()
+ {
+ if (_store != null)
+ {
+ _store.Dispose();
+ }
+
+ if (_logToken != null)
+ {
+ _logToken.Dispose();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/MusicStore.E2ETests/SmokeTestsUsingStore/SetupFixtures.cs b/test/MusicStore.E2ETests/SmokeTestsUsingStore/SetupFixtures.cs
new file mode 100644
index 0000000000..97ace4d70d
--- /dev/null
+++ b/test/MusicStore.E2ETests/SmokeTestsUsingStore/SetupFixtures.cs
@@ -0,0 +1,22 @@
+namespace E2ETests
+{
+ public class DefaultLocationSetupFixture : BaseStoreSetupFixture
+ {
+ public DefaultLocationSetupFixture() :
+ base(
+ createInDefaultLocation: true,
+ loggerName: nameof(DefaultLocationSetupFixture))
+ {
+ }
+ }
+
+ public class CustomLocationSetupFixture : BaseStoreSetupFixture
+ {
+ public CustomLocationSetupFixture() :
+ base(
+ createInDefaultLocation: false,
+ loggerName: nameof(CustomLocationSetupFixture))
+ {
+ }
+ }
+}
diff --git a/test/MusicStore.E2ETests/SmokeTestsUsingStore/SmokeTestsUsingStore.cs b/test/MusicStore.E2ETests/SmokeTestsUsingStore/SmokeTestsUsingStore.cs
new file mode 100644
index 0000000000..f8a970ecf3
--- /dev/null
+++ b/test/MusicStore.E2ETests/SmokeTestsUsingStore/SmokeTestsUsingStore.cs
@@ -0,0 +1,92 @@
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Server.IntegrationTesting;
+using Microsoft.AspNetCore.Testing.xunit;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace E2ETests
+{
+ public class SmokeTestsUsingDefaultLocation : IClassFixture
+ {
+ private readonly DefaultLocationSetupFixture _testFixture;
+ private readonly ITestOutputHelper _output;
+
+ public SmokeTestsUsingDefaultLocation(
+ DefaultLocationSetupFixture testFixure,
+ ITestOutputHelper output)
+ {
+ _testFixture = testFixure;
+ _output = output;
+ }
+
+ [EnvironmentVariableSkipCondition(Store.MusicStoreAspNetCoreStoreFeed, null, SkipOnMatch = false)]
+ [ConditionalFact]
+ [Trait("smoketests", "usestore")]
+ [Trait("smoketests", "usestore-defaultlocation")]
+ public async Task DefaultLocation_Kestrel()
+ {
+ var tests = new SmokeTestsUsingStoreHelper(_output);
+ await tests.SmokeTestSuite(
+ ServerType.Kestrel,
+ _testFixture.CreateStoreInDefaultLocation,
+ _testFixture.StoreDirectory);
+ }
+
+ [OSSkipCondition(OperatingSystems.Linux)]
+ [OSSkipCondition(OperatingSystems.MacOSX)]
+ [EnvironmentVariableSkipCondition(Store.MusicStoreAspNetCoreStoreFeed, null, SkipOnMatch = false)]
+ [ConditionalFact]
+ [Trait("smoketests", "usestore")]
+ [Trait("smoketests", "usestore-defaultlocation")]
+ public async Task DefaultLocation_WebListener()
+ {
+ var tests = new SmokeTestsUsingStoreHelper(_output);
+ await tests.SmokeTestSuite(
+ ServerType.WebListener,
+ _testFixture.CreateStoreInDefaultLocation,
+ _testFixture.StoreDirectory);
+ }
+ }
+
+ public class SmokeTestsUsingInCustomLocation : IClassFixture
+ {
+ private readonly CustomLocationSetupFixture _testFixture;
+ private readonly ITestOutputHelper _output;
+
+ public SmokeTestsUsingInCustomLocation(
+ CustomLocationSetupFixture testFixure,
+ ITestOutputHelper output)
+ {
+ _testFixture = testFixure;
+ _output = output;
+ }
+
+ [EnvironmentVariableSkipCondition(Store.MusicStoreAspNetCoreStoreFeed, null, SkipOnMatch = false)]
+ [ConditionalFact]
+ [Trait("smoketests", "usestore")]
+ [Trait("smoketests", "usestore-customlocation")]
+ public async Task CustomLocation_Kestrel()
+ {
+ var tests = new SmokeTestsUsingStoreHelper(_output);
+ await tests.SmokeTestSuite(
+ ServerType.Kestrel,
+ _testFixture.CreateStoreInDefaultLocation,
+ _testFixture.StoreDirectory);
+ }
+
+ [OSSkipCondition(OperatingSystems.Linux)]
+ [OSSkipCondition(OperatingSystems.MacOSX)]
+ [EnvironmentVariableSkipCondition(Store.MusicStoreAspNetCoreStoreFeed, null, SkipOnMatch = false)]
+ [ConditionalFact]
+ [Trait("smoketests", "usestore")]
+ [Trait("smoketests", "usestore-customlocation")]
+ public async Task CustomLocation_WebListener()
+ {
+ var tests = new SmokeTestsUsingStoreHelper(_output);
+ await tests.SmokeTestSuite(
+ ServerType.WebListener,
+ _testFixture.CreateStoreInDefaultLocation,
+ _testFixture.StoreDirectory);
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/MusicStore.E2ETests/SmokeTestsUsingStore/SmokeTestsUsingStoreHelper.cs b/test/MusicStore.E2ETests/SmokeTestsUsingStore/SmokeTestsUsingStoreHelper.cs
new file mode 100644
index 0000000000..4961248e61
--- /dev/null
+++ b/test/MusicStore.E2ETests/SmokeTestsUsingStore/SmokeTestsUsingStoreHelper.cs
@@ -0,0 +1,69 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Server.IntegrationTesting;
+using Microsoft.Extensions.Logging.Testing;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace E2ETests
+{
+ public class SmokeTestsUsingStoreHelper : LoggedTest
+ {
+ public SmokeTestsUsingStoreHelper(ITestOutputHelper output) : base(output)
+ {
+ }
+
+ public async Task SmokeTestSuite(ServerType serverType, bool isStoreInDefaultLocation, string storeDirectory)
+ {
+ var targetFramework = "netcoreapp2.0";
+ var testName = $"SmokeTestsUsingStore_{serverType}";
+ using (StartLog(out var loggerFactory, testName))
+ {
+ var logger = loggerFactory.CreateLogger(nameof(SmokeTestsUsingStoreHelper));
+ var musicStoreDbName = DbUtils.GetUniqueName();
+
+ var deploymentParameters = new DeploymentParameters(
+ Helpers.GetApplicationPath(ApplicationType.Portable), serverType, RuntimeFlavor.CoreClr, RuntimeArchitecture.x64)
+ {
+ EnvironmentName = "SocialTesting",
+ SiteName = "MusicStoreTestSiteUsingStore",
+ PublishApplicationBeforeDeployment = true,
+ PreservePublishedApplicationForDebugging = Helpers.PreservePublishedApplicationForDebugging,
+ TargetFramework = targetFramework,
+ Configuration = Helpers.GetCurrentBuildConfiguration(),
+ ApplicationType = ApplicationType.Portable,
+ UserAdditionalCleanup = parameters =>
+ {
+ DbUtils.DropDatabase(musicStoreDbName, logger);
+ }
+ };
+
+ // Override the connection strings using environment based configuration
+ deploymentParameters.EnvironmentVariables
+ .Add(new KeyValuePair(
+ MusicStoreConfig.ConnectionStringKey,
+ DbUtils.CreateConnectionString(musicStoreDbName)));
+
+ if (!isStoreInDefaultLocation)
+ {
+ deploymentParameters.EnvironmentVariables.Add(
+ new KeyValuePair("DOTNET_SHARED_STORE", storeDirectory));
+ }
+
+ using (var deployer = ApplicationDeployerFactory.Create(deploymentParameters, loggerFactory))
+ {
+ var deploymentResult = await deployer.DeployAsync();
+
+ var mvcCoreDllPath = Path.Combine(deploymentResult.ContentRoot, "Microsoft.AspNetCore.Mvc.Core.dll");
+ var fileInfo = new FileInfo(mvcCoreDllPath);
+ Assert.False(
+ File.Exists(mvcCoreDllPath),
+ $"The file '{fileInfo.Name}.{fileInfo.Extension}' was not expected to be present in the publish directory");
+
+ await SmokeTestHelper.RunTestsAsync(deploymentResult, logger);
+ }
+ }
+ }
+ }
+}
diff --git a/test/MusicStore.E2ETests/SmokeTestsUsingStore/Store.cs b/test/MusicStore.E2ETests/SmokeTestsUsingStore/Store.cs
new file mode 100644
index 0000000000..806d71e998
--- /dev/null
+++ b/test/MusicStore.E2ETests/SmokeTestsUsingStore/Store.cs
@@ -0,0 +1,247 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Threading;
+using Microsoft.AspNetCore.Server.IntegrationTesting;
+using Microsoft.Extensions.CommandLineUtils;
+using Microsoft.Extensions.Logging;
+using NuGet.Configuration;
+using NuGet.Packaging.Core;
+using NuGet.Protocol;
+using NuGet.Protocol.Core.Types;
+using NuGet.Versioning;
+
+namespace E2ETests
+{
+ internal class Store : IDisposable
+ {
+ public const string MusicStoreAspNetCoreStoreFeed = "MUSICSTORE_ASPNETCORE_STORE_FEED";
+ private readonly ILogger _logger;
+ private string _storeDir;
+ private string _tempDir;
+
+ public Store(ILoggerFactory loggerFactory)
+ {
+ _logger = loggerFactory.CreateLogger();
+ }
+
+ public string CreateStore(bool createInDefaultLocation)
+ {
+ var storeParentDir = GetStoreParentDirectory(createInDefaultLocation);
+
+ InstallStore(storeParentDir);
+
+ _storeDir = Path.Combine(storeParentDir, "store");
+
+ return _storeDir;
+ }
+
+ public void Dispose()
+ {
+ if (string.IsNullOrEmpty(_storeDir))
+ {
+ return;
+ }
+
+ if (Helpers.PreservePublishedApplicationForDebugging)
+ {
+ _logger.LogInformation("Skipping deleting the store as it has been disabled");
+ }
+ else
+ {
+ _logger.LogInformation("Deleting the store...");
+
+ //RetryHelper.RetryOperation(
+ // () => Directory.Delete(_storeDir, recursive: true),
+ // e => _logger.LogError($"Failed to delete directory : {e.Message}"),
+ // retryCount: 3,
+ // retryDelayMilliseconds: 100);
+
+ RetryHelper.RetryOperation(
+ () => Directory.Delete(_tempDir, recursive: true),
+ e => _logger.LogError($"Failed to delete directory : {e.Message}"),
+ retryCount: 3,
+ retryDelayMilliseconds: 100);
+ }
+ }
+
+ public static bool IsEnabled()
+ {
+ var storeFeed = Environment.GetEnvironmentVariable(MusicStoreAspNetCoreStoreFeed);
+ return !string.IsNullOrEmpty(storeFeed);
+ }
+
+ private void InstallStore(string storeParentDir)
+ {
+ var packageId = "Build.RS";
+ var storeFeed = Environment.GetEnvironmentVariable(MusicStoreAspNetCoreStoreFeed);
+ if (string.IsNullOrEmpty(storeFeed))
+ {
+ _logger.LogError("The feed for the store package was not provided." +
+ $"Set the environment variable '{MusicStoreAspNetCoreStoreFeed}' and try again.");
+
+ throw new InvalidOperationException(
+ $"The environment variable '{MusicStoreAspNetCoreStoreFeed}' is not defined or is empty.");
+ }
+ else
+ {
+ _logger.LogInformation($"Using the feed {storeFeed} for the store package");
+ }
+
+ // Get the version information from the attribute which is typically the same as the nuget package version
+ // Example:
+ // [assembly: AssemblyInformationalVersion("2.0.0-preview1-24847")]
+ var aspnetCoreHttpAssembly = typeof(Microsoft.AspNetCore.Http.FormCollection).Assembly;
+ var obj = aspnetCoreHttpAssembly.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), inherit: false).FirstOrDefault();
+ var assemblyInformationVersionAttribute = obj as AssemblyInformationalVersionAttribute;
+ if (assemblyInformationVersionAttribute == null)
+ {
+ throw new InvalidOperationException($"Could not find {nameof(assemblyInformationVersionAttribute)} from the assembly {aspnetCoreHttpAssembly.FullName}");
+ }
+
+ _logger.LogInformation($"Downloading package with id {packageId} and version {assemblyInformationVersionAttribute.InformationalVersion} from feed {storeFeed}");
+
+ _tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
+ var sourceRepository = Repository.Factory.GetCoreV2(new PackageSource(storeFeed));
+ var downloadResource = sourceRepository.GetResource();
+ var result = downloadResource.GetDownloadResourceResultAsync(
+ new PackageIdentity(packageId, NuGetVersion.Parse(assemblyInformationVersionAttribute.InformationalVersion)),
+ new PackageDownloadContext(
+ new SourceCacheContext() { NoCache = true, DirectDownload = true },
+ _tempDir,
+ directDownload: true),
+ null,
+ NuGet.Common.NullLogger.Instance,
+ CancellationToken.None)
+ .Result;
+
+ if (result.Status != DownloadResourceResultStatus.Available)
+ {
+ _logger.LogError($"Failed to download the package. Status: {result.Status}");
+ throw new InvalidOperationException("Unable to download the store package");
+ }
+
+ var zipFile = Path.Combine(_tempDir, "Build.RS.zip");
+ using (var targetStream = File.Create(zipFile))
+ {
+ using (result.PackageStream)
+ {
+ result.PackageStream.CopyTo(targetStream);
+ }
+ }
+
+ _logger.LogInformation($"Package downloaded and saved as zip file at {zipFile}");
+
+ var zipFileExtracted = Path.Combine(_tempDir, "extracted");
+ ZipFile.ExtractToDirectory(zipFile, zipFileExtracted);
+
+ _logger.LogInformation($"Package extracted at {zipFileExtracted}");
+
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ string fileNameWithExtension = null;
+ foreach (var file in new DirectoryInfo(zipFileExtracted).GetFiles())
+ {
+ if (file.Name.StartsWith($"{packageId}.winx64"))
+ {
+ using (var zipArchive = ZipFile.Open(file.FullName, ZipArchiveMode.Read))
+ {
+ var mvcCoreDllEntry = zipArchive.Entries
+ .Where(entry => string.Equals(entry.Name, "Microsoft.AspNetCore.Mvc.Core.dll", StringComparison.OrdinalIgnoreCase))
+ .FirstOrDefault();
+ if (mvcCoreDllEntry != null && mvcCoreDllEntry.FullName.Contains(assemblyInformationVersionAttribute.InformationalVersion))
+ {
+ fileNameWithExtension = file.Name;
+ break;
+ }
+ }
+ }
+ }
+
+ if (string.IsNullOrEmpty(fileNameWithExtension))
+ {
+ throw new InvalidOperationException(
+ $"Could not find a store zip file with version {assemblyInformationVersionAttribute.InformationalVersion}");
+ }
+
+ var storeZipFile = Path.Combine(zipFileExtracted, fileNameWithExtension);
+ ZipFile.ExtractToDirectory(storeZipFile, storeParentDir);
+ _logger.LogInformation($"Extracted the store zip file '{storeZipFile}' to '{storeParentDir}'");
+ }
+ else
+ {
+ string packageIdPrefix;
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ packageIdPrefix = $"{packageId}.linux";
+ }
+ else
+ {
+ packageIdPrefix = $"{packageId}.osx";
+ }
+
+ string fileNameWithExtension = null;
+ foreach (var file in new DirectoryInfo(zipFileExtracted).GetFiles())
+ {
+ if (file.Name.StartsWith(packageIdPrefix)
+ && !string.Equals($"{packageIdPrefix}.tar.gz", file.Name, StringComparison.OrdinalIgnoreCase))
+ {
+ fileNameWithExtension = file.FullName;
+ break;
+ }
+ }
+
+ if (string.IsNullOrEmpty(fileNameWithExtension))
+ {
+ throw new InvalidOperationException(
+ $"Could not find a store zip file with version {assemblyInformationVersionAttribute.InformationalVersion}");
+ }
+
+ Directory.CreateDirectory(storeParentDir);
+
+ var startInfo = new ProcessStartInfo()
+ {
+ FileName = "tar",
+ Arguments = $"xvzf {fileNameWithExtension}",
+ WorkingDirectory = storeParentDir,
+ UseShellExecute = false,
+ CreateNoWindow = true,
+ RedirectStandardError = true,
+ RedirectStandardOutput = true,
+ RedirectStandardInput = true
+ };
+ var tarProcess = new Process() { StartInfo = startInfo };
+ tarProcess.EnableRaisingEvents = true;
+ tarProcess.StartAndCaptureOutAndErrToLogger("tar", _logger);
+
+ if (tarProcess.HasExited && tarProcess.ExitCode != 0)
+ {
+ var message = $"Error occurred while extracting the file '{fileNameWithExtension}' in working directory '{storeParentDir}'";
+ _logger.LogError(message);
+ throw new InvalidOperationException(message);
+ }
+ }
+ }
+
+ private string GetStoreParentDirectory(bool createInDefaultLocation)
+ {
+ string storeParentDir;
+ if (createInDefaultLocation)
+ {
+ // On Windows: ..\.dotnet\x64\dotnet.exe
+ // On Linux : ../.dotnet/dotnet
+ var dotnetDir = new FileInfo(DotNetMuxer.MuxerPath).Directory.FullName;
+ storeParentDir = dotnetDir;
+ }
+ else
+ {
+ storeParentDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
+ }
+ return storeParentDir;
+ }
+ }
+}