diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 000000000..acdab74f7
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,31 @@
+
+[*.{c,c++,cc,cp,cpp,cu,cuh,cxx,h,hh,hpp,hxx,inc,inl,ino,ipp,mpp,proto,tpp}]
+indent_style=tab
+indent_size=tab
+tab_width=4
+
+[*.{asax,ascx,aspx,cs,cshtml,css,htm,html,js,jsx,master,razor,skin,ts,tsx,vb,xaml,xamlx,xoml}]
+indent_style=space
+indent_size=4
+tab_width=4
+
+[*.{appxmanifest,build,config,csproj,dbml,discomap,dtd,json,jsproj,lsproj,njsproj,nuspec,proj,props,resjson,resw,resx,StyleCop,targets,tasks,vbproj,xml,xsd}]
+indent_style=space
+indent_size=2
+tab_width=2
+
+[*]
+
+# Microsoft .NET properties
+csharp_new_line_before_members_in_object_initializers=false
+csharp_preferred_modifier_order=public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion
+csharp_style_var_elsewhere=true:hint
+csharp_style_var_for_built_in_types=true:hint
+csharp_style_var_when_type_is_apparent=true:hint
+dotnet_style_predefined_type_for_locals_parameters_members=true:hint
+dotnet_style_predefined_type_for_member_access=true:hint
+dotnet_style_qualification_for_event=false:warning
+dotnet_style_qualification_for_field=false:warning
+dotnet_style_qualification_for_method=false:warning
+dotnet_style_qualification_for_property=false:warning
+dotnet_style_require_accessibility_modifiers=for_non_interface_members:hint
diff --git a/.gitignore b/.gitignore
index 1c4634087..a0336fe33 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,7 +9,6 @@
*.user
*.userosscache
*.sln.docstates
-*NCrunch*
.trydotnet-*
# User-specific files (MonoDevelop/Xamarin Studio)
diff --git a/DotNetTry.sln.DotSettings b/DotNetTry.sln.DotSettings
new file mode 100644
index 000000000..7fa67efcf
--- /dev/null
+++ b/DotNetTry.sln.DotSettings
@@ -0,0 +1,2 @@
+
+ True
\ No newline at end of file
diff --git a/DotNetTry.v3.ncrunchsolution b/DotNetTry.v3.ncrunchsolution
new file mode 100644
index 000000000..10420ac91
--- /dev/null
+++ b/DotNetTry.v3.ncrunchsolution
@@ -0,0 +1,6 @@
+
+
+ True
+ True
+
+
\ No newline at end of file
diff --git a/MLS.Agent.Tests/ApiContracts/ApiOutputContractTests.cs b/MLS.Agent.Tests/ApiContracts/ApiOutputContractTests.cs
index c096a9329..a1092994f 100644
--- a/MLS.Agent.Tests/ApiContracts/ApiOutputContractTests.cs
+++ b/MLS.Agent.Tests/ApiContracts/ApiOutputContractTests.cs
@@ -9,10 +9,8 @@
using Microsoft.DotNet.Try.Protocol;
using Microsoft.DotNet.Try.Protocol.Tests;
using Recipes;
-using WorkspaceServer.Tests;
using Xunit;
using Xunit.Abstractions;
-using Package = WorkspaceServer.Packaging.Package;
namespace MLS.Agent.Tests.ApiContracts
{
@@ -31,12 +29,11 @@ public ApiOutputContractTests(ITestOutputHelper output) : base(output)
[Fact]
public async Task The_Run_contract_for_compiling_code_has_not_been_broken()
{
- var package = await Package.Copy(await Default.ConsoleWorkspace());
var viewport = ViewportCode();
var requestJson = new WorkspaceRequest(
new Workspace(
- workspaceType: package.Name,
+ workspaceType: "console",
buffers: new[]
{
EntrypointCode(),
@@ -55,12 +52,11 @@ public async Task The_Run_contract_for_compiling_code_has_not_been_broken()
[Fact]
public async Task The_Run_contract_for_noncompiling_code_has_not_been_broken()
{
- var package = await Package.Copy(await Default.ConsoleWorkspace());
var viewport = ViewportCode("doesn't compile");
var request = new WorkspaceRequest(
new Workspace(
- workspaceType: package.Name,
+ workspaceType: "console",
buffers: new[]
{
EntrypointCode(),
@@ -81,12 +77,11 @@ public async Task The_Run_contract_for_noncompiling_code_has_not_been_broken()
[Fact]
public async Task The_Compile_contract_for_compiling_code_has_not_been_broken()
{
- var package = await Package.Copy(await Default.ConsoleWorkspace());
var viewport = ViewportCode();
var requestJson = new WorkspaceRequest(
new Workspace(
- workspaceType: package.Name,
+ workspaceType: "console",
buffers: new[]
{
EntrypointCode(),
@@ -116,12 +111,11 @@ public async Task The_Compile_contract_for_compiling_code_has_not_been_broken()
[Fact]
public async Task The_Compile_contract_for_noncompiling_code_has_not_been_broken()
{
- var package = await Package.Copy(await Default.ConsoleWorkspace());
var viewport = ViewportCode("doesn't compile");
var request = new WorkspaceRequest(
new Workspace(
- workspaceType: package.Name,
+ workspaceType: "console",
buffers: new[]
{
EntrypointCode(),
@@ -142,12 +136,11 @@ public async Task The_Compile_contract_for_noncompiling_code_has_not_been_broken
[Fact]
public async Task The_Completions_contract_has_not_been_broken()
{
- var package = await Package.Copy(await Default.ConsoleWorkspace());
var viewport = ViewportCode("Console.Ou$$");
var requestJson = new WorkspaceRequest(
new Workspace(
- workspaceType: package.Name,
+ workspaceType: "console",
buffers: new[]
{
EntrypointCode(),
@@ -166,12 +159,11 @@ public async Task The_Completions_contract_has_not_been_broken()
[Fact]
public async Task The_signature_help_contract_has_not_been_broken()
{
- var package = await Package.Copy(await Default.ConsoleWorkspace());
var viewport = ViewportCode("Console.Write($$);");
var requestJson = new WorkspaceRequest(
new Workspace(
- workspaceType: package.Name,
+ workspaceType: "console",
buffers: new[]
{
EntrypointCode(),
@@ -190,10 +182,9 @@ public async Task The_signature_help_contract_has_not_been_broken()
[Fact]
public async Task The_instrumentation_contract_has_not_been_broken()
{
- var package = await Package.Copy(await Default.ConsoleWorkspace());
var requestJson = new WorkspaceRequest(
new Workspace(
- workspaceType: package.Name,
+ workspaceType: "console",
buffers: new[]
{
EntrypointCode("int a = 1; int b = 2; a = 3; b = a;")
@@ -212,10 +203,9 @@ public async Task The_instrumentation_contract_has_not_been_broken()
[Fact]
public async Task The_run_contract_with_no_instrumentation_has_not_been_broken()
{
- var package = await Package.Copy(await Default.ConsoleWorkspace());
var requestJson = new WorkspaceRequest(
new Workspace(
- workspaceType: package.Name,
+ workspaceType: "console",
buffers: new[]
{
EntrypointCode("int a = 1; int b = 2; a = 3; b = a;")
@@ -248,7 +238,7 @@ public static void Main()
}}
}}".EnforceLF();
- MarkupTestFile.GetPosition(input, out string output, out var position);
+ MarkupTestFile.GetPosition(input, out var output, out var position);
return new Buffer(
"Program.cs",
@@ -282,7 +272,7 @@ public static object Method()
}}
}}".EnforceLF();
- MarkupTestFile.GetPosition(input, out string output, out var position);
+ MarkupTestFile.GetPosition(input, out var output, out var position);
return new Buffer(
"ViewportCode.cs",
diff --git a/MLS.Agent.Tests/ApiViaHttpTests.cs b/MLS.Agent.Tests/ApiViaHttpTests.cs
index 42f1f097b..aa57fbbc5 100644
--- a/MLS.Agent.Tests/ApiViaHttpTests.cs
+++ b/MLS.Agent.Tests/ApiViaHttpTests.cs
@@ -27,6 +27,7 @@
using HtmlAgilityPack;
using System.Web;
using MLS.Agent.Controllers;
+using WorkspaceServer.Tests.Packaging;
using CodeManipulation = WorkspaceServer.Tests.CodeManipulation;
using SourceFile = Microsoft.DotNet.Try.Protocol.ClientApi.SourceFile;
@@ -82,8 +83,7 @@ public async Task The_compile_endpoint_returns_bad_request_if_workspace_type_is_
public async Task The_workspace_endpoint_compiles_code_using_dotnet_when_a_non_script_workspace_type_is_specified()
{
var output = Guid.NewGuid().ToString();
- var package = await WorkspaceServer.Packaging.Package.Copy(await Default.ConsoleWorkspace());
- var requestJson = Create.SimpleWorkspaceRequestAsJson(output, package.Name);
+ var requestJson = Create.SimpleWorkspaceRequestAsJson(output, "console");
var response = await CallRun(requestJson);
@@ -100,7 +100,7 @@ public async Task The_workspace_endpoint_compiles_code_using_dotnet_when_a_non_s
public async Task The_workspace_endpoint_will_prevent_compiling_if_is_in_language_service_mode()
{
var output = Guid.NewGuid().ToString();
- var package = await WorkspaceServer.Packaging.Package.Copy(await Default.ConsoleWorkspace());
+ var package = await PackageUtilities.Copy(await Default.ConsoleWorkspace());
var requestJson = Create.SimpleWorkspaceRequestAsJson(output, package.Name);
@@ -112,12 +112,12 @@ public async Task The_workspace_endpoint_will_prevent_compiling_if_is_in_languag
[Fact]
public async Task When_a_non_script_workspace_type_is_specified_then_code_fragments_cannot_be_compiled_successfully()
{
- var package = await WorkspaceServer.Packaging.Package.Copy(await Default.ConsoleWorkspace());
+
var requestJson =
new WorkspaceRequest(
Workspace.FromSource(
@"Console.WriteLine(""hello!"");",
- workspaceType: package.Name,
+ workspaceType: "console",
id: "Program.cs")).ToJson();
var response = await CallRun(requestJson);
@@ -201,8 +201,8 @@ public async Task Sending_payloads_that_cannot_be_deserialized_results_in_BadReq
[Fact]
public async Task A_script_snippet_workspace_can_be_used_to_get_completions()
{
- var (processed, position) = WorkspaceServer.Tests.CodeManipulation.ProcessMarkup("Console.$$");
- using (var agent = new AgentService())
+ var (processed, position) = CodeManipulation.ProcessMarkup("Console.$$");
+ using (var agent = new AgentService(StartupOptions.FromCommandLine("hosted")))
{
var json = new WorkspaceRequest(
requestId: "TestRun",
@@ -346,7 +346,6 @@ public static IEnumerable Fibonacci()
}
}".EnforceLF();
#endregion
- var package = await WorkspaceServer.Packaging.Package.Copy(await Default.ConsoleWorkspace());
var (processed, position) = CodeManipulation.ProcessMarkup(generator);
var log = new LogEntryList();
using (LogEvents.Subscribe(log.Add))
@@ -356,7 +355,7 @@ public static IEnumerable Fibonacci()
new WorkspaceRequest(activeBufferId: "generators/FibonacciGenerator.cs",
requestId: "TestRun",
workspace: Workspace.FromSources(
- package.Name,
+ "console",
("Program.cs", program, 0),
("generators/FibonacciGenerator.cs", processed, position)
)).ToJson();
@@ -423,7 +422,6 @@ public static IEnumerable Fibonacci()
}".EnforceLF();
#endregion
- var package = await WorkspaceServer.Packaging.Package.Copy(await Default.ConsoleWorkspace());
var (processed, position) = CodeManipulation.ProcessMarkup(generator);
var log = new LogEntryList();
using (LogEvents.Subscribe(log.Add))
@@ -433,7 +431,7 @@ public static IEnumerable Fibonacci()
new WorkspaceRequest(activeBufferId: "generators/FibonacciGenerator.cs",
requestId: "TestRun",
workspace: Workspace.FromSources(
- package.Name,
+ "console",
("Program.cs", program, 0),
("generators/FibonacciGenerator.cs", processed, position)
)).ToJson();
@@ -500,7 +498,7 @@ public static IEnumerable Fibonacci()
}".EnforceLF();
#endregion
- var package = await WorkspaceServer.Packaging.Package.Copy(await Default.ConsoleWorkspace(), "a space");
+ var package = await PackageUtilities.Copy(await Default.ConsoleWorkspace(), "a space");
var (processed, position) = CodeManipulation.ProcessMarkup(generator);
var log = new LogEntryList();
using (LogEvents.Subscribe(log.Add))
@@ -578,7 +576,6 @@ public static IEnumerable Fibonacci()
}".EnforceLF();
#endregion
- var package = await WorkspaceServer.Packaging.Package.Copy(await Default.ConsoleWorkspace());
var (processed, position) = CodeManipulation.ProcessMarkup(generator);
var log = new LogEntryList();
using (LogEvents.Subscribe(log.Add))
@@ -588,7 +585,7 @@ public static IEnumerable Fibonacci()
new WorkspaceRequest(activeBufferId: "generators/FibonacciGenerator.cs",
requestId: "TestRun",
workspace: Workspace.FromSources(
- package.Name,
+ "console",
("Program.cs", program, 0),
("generators/FibonacciGenerator.cs", processed, position)
)).ToJson();
@@ -613,14 +610,10 @@ public static IEnumerable Fibonacci()
}
}
- [Fact]
+ [Fact(Skip = "WIP aspnet.webapi")]
public async Task When_aspnet_webapi_workspace_request_succeeds_then_output_shows_web_response()
{
- var workspaceType = await Create.WebApiWorkspaceCopy();
- var workspace = WorkspaceFactory.CreateWorkspaceFromDirectory(
- workspaceType.Directory,
- workspaceType.Directory.Name);
-
+ var workspace = new Workspace(workspaceType:"aspnet.webapi", buffers:new []{new Buffer("empty.cs", "")});
var request = new WorkspaceRequest(workspace, httpRequest: new HttpRequest("/api/values", "get"), requestId: "TestRun");
var json = request.ToJson();
@@ -648,10 +641,10 @@ public async Task When_aspnet_webapi_workspace_request_succeeds_then_output_show
"]");
}
- [Fact(Skip = "WIP")]
+ [Fact(Skip = "WIP aspnet.webapi")]
public async Task When_aspnet_webapi_workspace_request_succeeds_then_standard_out_is_available_on_response()
{
- var package = await WorkspaceServer.Packaging.Package.Copy(await Default.WebApiWorkspace());
+ var package = await PackageUtilities.Copy(await Default.WebApiWorkspace());
await package.CreateRoslynWorkspaceForRunAsync(new TimeBudget(10.Minutes()));
var workspace = WorkspaceFactory.CreateWorkspaceFromDirectory(package.Directory, package.Directory.Name);
@@ -669,10 +662,10 @@ public async Task When_aspnet_webapi_workspace_request_succeeds_then_standard_ou
throw new NotImplementedException();
}
- [Fact]
+ [Fact(Skip = "WIP aspnet.webapi")]
public async Task When_aspnet_webapi_workspace_request_fails_then_diagnostics_are_returned()
{
- var package = await WorkspaceServer.Packaging.Package.Copy(await Default.WebApiWorkspace());
+ var package = await PackageUtilities.Copy(await Default.WebApiWorkspace());
await package.CreateRoslynWorkspaceForRunAsync(new TimeBudget(10.Minutes()));
var workspace = WorkspaceFactory.CreateWorkspaceFromDirectory(package.Directory, package.Directory.Name);
var nonCompilingBuffer = new Buffer("broken.cs", "this does not compile", 0);
@@ -696,9 +689,8 @@ public async Task When_aspnet_webapi_workspace_request_fails_then_diagnostics_ar
public async Task When_Run_times_out_in_console_workspace_server_code_then_the_response_code_is_504()
{
var code = @"public class Program { public static void Main() { Console.WriteLine(); } }";
- var package = await WorkspaceServer.Packaging.Package.Copy(await Default.ConsoleWorkspace());
- var workspace = Workspace.FromSource(code.EnforceLF(), package.Name);
+ var workspace = Workspace.FromSource(code.EnforceLF(), "console");
var requestJson = new WorkspaceRequest(workspace).ToJson();
@@ -776,10 +768,19 @@ public async Task When_Run_times_out_in_user_code_then_the_response_code_is_417(
{
Clock.Reset();
- var workspace =
- workspaceType == "script"
- ? Workspace.FromSource(code, "script")
- : Workspace.FromSource(code, (await Create.ConsoleWorkspaceCopy()).Name);
+ Workspace workspace = null;
+ if (workspaceType == "script")
+ {
+ workspace = Workspace.FromSource(code, "script");
+ }
+ else
+ {
+ var package = Create.EmptyWorkspace();
+ var build = await Create.NewPackage(package.Name, package.Directory, Create.ConsoleConfiguration);
+ workspace = Workspace.FromSource(code, build.Name);
+ }
+
+
var requestJson = new WorkspaceRequest(workspace).ToJson();
var response = await CallRun(requestJson, 10000);
@@ -874,12 +875,11 @@ public async Task Can_extract_regions_from_files()
[Fact]
public async Task Returns_200_if_the_package_exists()
{
- var package = await Create.ConsoleWorkspaceCopy();
var packageVersion = "1.0.0";
using(var agent = new AgentService())
{
- var response = await agent.GetAsync($@"/packages/{package.Name}/{packageVersion}");
+ var response = await agent.GetAsync($@"/packages/console/{packageVersion}");
response.StatusCode.Should().Be(HttpStatusCode.OK);
}
}
@@ -900,12 +900,12 @@ public async Task Returns_404_if_the_package_does_not_exist()
[Fact]
public async Task Returns_blazor_false_if_the_package_does_not_contain_blazor_runner()
{
- var package = await Create.ConsoleWorkspaceCopy();
+
var packageVersion = "1.0.0";
using (var agent = new AgentService())
{
- var response = await agent.GetAsync($@"/packages/{package.Name}/{packageVersion}");
+ var response = await agent.GetAsync($@"/packages/console/{packageVersion}");
response.Should().BeSuccessful();
var result = await response.Content.ReadAsStringAsync();
result.FromJsonTo()
diff --git a/MLS.Agent.Tests/ApiViaHttpTestsBase.cs b/MLS.Agent.Tests/ApiViaHttpTestsBase.cs
index 270c2e10a..e674a4dc5 100644
--- a/MLS.Agent.Tests/ApiViaHttpTestsBase.cs
+++ b/MLS.Agent.Tests/ApiViaHttpTestsBase.cs
@@ -63,7 +63,7 @@ private static async Task Call(
StartupOptions options = null)
{
HttpResponseMessage response;
- using (var agent = new AgentService(options))
+ using (var agent = new AgentService(options ?? StartupOptions.FromCommandLine("hosted")))
{
var request = new HttpRequestMessage(
HttpMethod.Post,
diff --git a/MLS.Agent.Tests/Markdown/CodeBlockAnnotationExtensionTests.cs b/MLS.Agent.Tests/Markdown/CodeBlockAnnotationExtensionTests.cs
index 7d00a9fe3..879dd7bca 100644
--- a/MLS.Agent.Tests/Markdown/CodeBlockAnnotationExtensionTests.cs
+++ b/MLS.Agent.Tests/Markdown/CodeBlockAnnotationExtensionTests.cs
@@ -63,7 +63,7 @@ static void MyProgram(string[] args)
("Program.cs", fileContent),
("sample.csproj", "")
};
- var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor, PackageRegistry.CreateForHostedMode()).Build();
+ var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor, Default.PackageFinder).Build();
var document =
$@"```{language} --source-file Program.cs
```";
@@ -81,7 +81,7 @@ public async Task Does_not_insert_code_when_specified_language_is_not_csharp()
var testDir = TestAssets.SampleConsole;
var directoryAccessor = new InMemoryDirectoryAccessor(testDir);
- var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor, PackageRegistry.CreateForHostedMode()).Build();
+ var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor, Default.PackageFinder).Build();
var document = @"
```js --source-file Program.cs
console.log(""Hello World"");
@@ -100,7 +100,7 @@ public async Task Does_not_insert_code_when_csharp_is_specified_but_no_additiona
var testDir = TestAssets.SampleConsole;
var directoryAccessor = new InMemoryDirectoryAccessor(testDir);
- var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor, PackageRegistry.CreateForHostedMode()).Build();
+ var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor, Default.PackageFinder).Build();
var document = @"
```cs
Console.WriteLine(""Hello World"");
@@ -117,7 +117,7 @@ public async Task Error_messsage_is_displayed_when_the_linked_file_does_not_exis
{
("sample.csproj", "")
};
- var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor, PackageRegistry.CreateForHostedMode()).Build();
+ var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor, Default.PackageFinder).Build();
var document =
@"```cs --source-file DOESNOTEXIST
```";
@@ -133,7 +133,7 @@ public async Task Error_message_is_displayed_when_no_project_is_specified_and_no
{
("Program.cs", "")
};
- var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor, PackageRegistry.CreateForHostedMode()).Build();
+ var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor, Default.PackageFinder).Build();
var document =
@"```cs --source-file Program.cs
```";
@@ -155,7 +155,7 @@ public async Task Error_message_is_displayed_when_a_project_is_specified_but_the
var document =
$@"```cs --project {projectPath} --source-file Program.cs
```";
- var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor, PackageRegistry.CreateForHostedMode()).Build();
+ var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor, Default.PackageFinder).Build();
var html = (await pipeline.RenderHtmlAsync(document)).EnforceLF();
html.Should().Contain($"Project not found: ./{projectPath}");
@@ -172,7 +172,7 @@ public async Task Sets_the_trydotnet_package_attribute_using_the_passed_project_
("src/sample/sample.csproj", "")
};
- var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor, PackageRegistry.CreateForHostedMode()).Build();
+ var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor, Default.PackageFinder).Build();
var package = "../src/sample/sample.csproj";
var document =
@@ -277,7 +277,7 @@ static void MyProgram(string[] args)
var document =
@"```cs --source-file Program.cs --region codeRegion
```";
- var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor, PackageRegistry.CreateForHostedMode()).Build();
+ var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor, Default.PackageFinder).Build();
var html = (await pipeline.RenderHtmlAsync(document)).EnforceLF();
var htmlDocument = new HtmlDocument();
@@ -307,7 +307,7 @@ public async Task Sets_the_trydotnet_filename_using_the_filename_specified_in_th
var document =
$@"```cs --source-file {filename} --region codeRegion
```";
- var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor, PackageRegistry.CreateForHostedMode()).Build();
+ var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor, Default.PackageFinder).Build();
var html = (await pipeline.RenderHtmlAsync(document)).EnforceLF();
var htmlDocument = new HtmlDocument();
@@ -337,7 +337,7 @@ public async Task Sets_the_trydotnet_filename_using_the_filename_specified_in_th
var document =
$@"```cs --source-file {sourceFile} --destination-file {destinationFile} --region codeRegion
```";
- var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor, PackageRegistry.CreateForHostedMode()).Build();
+ var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor, Default.PackageFinder).Build();
var html = (await pipeline.RenderHtmlAsync(document)).EnforceLF();
var htmlDocument = new HtmlDocument();
@@ -366,7 +366,7 @@ public async Task Sets_the_trydotnet_region_using_the_region_passed_in_the_markd
var document =
$@"```cs --source-file Program.cs --region {region}
```";
- var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor, PackageRegistry.CreateForHostedMode()).Build();
+ var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor, Default.PackageFinder).Build();
var html = (await pipeline.RenderHtmlAsync(document)).EnforceLF();
var htmlDocument = new HtmlDocument();
@@ -391,7 +391,7 @@ public async Task If_the_specified_region_does_not_exist_then_an_error_message_i
var document =
$@"```cs --source-file Program.cs --region {region}
```";
- var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor, PackageRegistry.CreateForHostedMode()).Build();
+ var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor, Default.PackageFinder).Build();
var html = (await pipeline.RenderHtmlAsync(document)).EnforceLF();
var htmlDocument = new HtmlDocument();
@@ -422,7 +422,7 @@ public async Task If_the_specified_region_exists_more_than_once_then_an_error_is
var document =
$@"```cs --source-file Program.cs --region {region}
```";
- var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor, PackageRegistry.CreateForHostedMode()).Build();
+ var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor, Default.PackageFinder).Build();
var html = (await pipeline.RenderHtmlAsync(document)).EnforceLF();
var htmlDocument = new HtmlDocument();
@@ -446,7 +446,7 @@ public async Task Sets_the_trydotnet_session_using_the_session_passed_in_the_mar
$@"```cs --source-file Program.cs --session {session}
```";
var pipeline = new MarkdownPipelineBuilder()
- .UseCodeBlockAnnotations(directoryAccessor, PackageRegistry.CreateForHostedMode())
+ .UseCodeBlockAnnotations(directoryAccessor, Default.PackageFinder)
.Build();
var html = (await pipeline.RenderHtmlAsync(document)).EnforceLF();
@@ -473,7 +473,7 @@ public async Task Sets_the_trydotnet_session_to_a_default_value_when_a_session_i
@"```cs --source-file Program.cs
```";
var pipeline = new MarkdownPipelineBuilder()
- .UseCodeBlockAnnotations(directoryAccessor, PackageRegistry.CreateForHostedMode())
+ .UseCodeBlockAnnotations(directoryAccessor, Default.PackageFinder)
.Build();
var html = (await pipeline.RenderHtmlAsync(document)).EnforceLF();
diff --git a/MLS.Agent.Tests/Markdown/DirectoryAccessorTests.cs b/MLS.Agent.Tests/Markdown/DirectoryAccessorTests.cs
index 5cf737c7d..128ff70e4 100644
--- a/MLS.Agent.Tests/Markdown/DirectoryAccessorTests.cs
+++ b/MLS.Agent.Tests/Markdown/DirectoryAccessorTests.cs
@@ -10,6 +10,7 @@
using WorkspaceServer;
using WorkspaceServer.Packaging;
using WorkspaceServer.Tests;
+using WorkspaceServer.Tests.Packaging;
using WorkspaceServer.Tests.TestUtility;
using Xunit;
using static Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment;
@@ -41,6 +42,25 @@ public void It_can_retrieve_all_files_recursively()
.Contain(new RelativeFilePath("Subdirectory/Tutorial.md"));
}
+ [Fact]
+ public void It_can_retrieve_all_files_at_root()
+ {
+ var directory = GetDirectory(TestAssets.SampleConsole);
+
+ var files = directory.GetAllFiles();
+
+ files.Should()
+ .Contain(new RelativeFilePath("BasicConsoleApp.csproj"))
+ .And
+ .Contain(new RelativeFilePath("Program.cs"))
+ .And
+ .Contain(new RelativeFilePath("Readme.md"))
+ .And
+ .NotContain(new RelativeFilePath("Subdirectory/AnotherProgram.cs"))
+ .And
+ .NotContain(new RelativeFilePath("Subdirectory/Tutorial.md"));
+ }
+
[Fact]
public void GetAllFilesRecursively_does_not_return_directories()
{
@@ -244,7 +264,7 @@ public class FileSystemDirectoryAccessorTests : DirectoryAccessorTests
{
public override IDirectoryAccessor CreateDirectory([CallerMemberName]string testName = null)
{
- var directory = Package.CreateDirectory(testName);
+ var directory = PackageUtilities.CreateDirectory(testName);
return new FileSystemDirectoryAccessor(directory);
}
diff --git a/MLS.Agent.Tests/Markdown/MarkdownFileTests.cs b/MLS.Agent.Tests/Markdown/MarkdownFileTests.cs
index ab21a9790..db3631d68 100644
--- a/MLS.Agent.Tests/Markdown/MarkdownFileTests.cs
+++ b/MLS.Agent.Tests/Markdown/MarkdownFileTests.cs
@@ -11,7 +11,6 @@
using MLS.Agent.CommandLine;
using MLS.Agent.Markdown;
using WorkspaceServer.Tests;
-using WorkspaceServer;
using WorkspaceServer.Tests.TestUtility;
using Xunit;
using Xunit.Abstractions;
@@ -40,7 +39,7 @@ public async Task Renders_html_content_for_the_files_in_the_root_path()
[Fact]
public async Task Renders_html_content_for_files_in_subdirectories()
{
- var html = await RenderHtml(("Subdirectory/Tutorial.md", "This is a sample *tutorial file*"));
+ var html = await RenderHtml(("SubDirectory/Tutorial.md", "This is a sample *tutorial file*"));
html.Should().Contain("tutorial file");
}
@@ -136,7 +135,7 @@ public async Task Should_parse_markdown_file_and_set_package_with_fully_resolved
```")
};
- var project = new MarkdownProject(dirAccessor, PackageRegistry.CreateForHostedMode());
+ var project = new MarkdownProject(dirAccessor, Default.PackageFinder);
project.TryGetMarkdownFile(new RelativeFilePath("docs/Readme.md"), out var markdownFile).Should().BeTrue();
var htmlDocument = new HtmlDocument();
htmlDocument.LoadHtml((await markdownFile.ToHtmlContentAsync()).ToString());
@@ -425,8 +424,8 @@ public async Task Non_editable_code_inserts_code_present_in_markdown()
[Fact]
public async Task Package_option_defaults_to_startup_options()
{
- var expectedPackage = "console";
- var expectedPackageVersion = "1.2.3";
+ const string expectedPackage = "console";
+ const string expectedPackageVersion = "1.2.3";
var defaultCodeBlockAnnotations = new StartupOptions(
package: expectedPackage,
@@ -440,7 +439,7 @@ public async Task Package_option_defaults_to_startup_options()
"),
("Program.cs", "")
},
- new PackageRegistry(),
+ Default.PackageFinder,
defaultCodeBlockAnnotations
);
@@ -464,7 +463,7 @@ protected async Task RenderHtml(params (string, string)[] project)
var markdownProject = new MarkdownProject(
directoryAccessor,
- new PackageRegistry());
+ Default.PackageFinder);
var markdownFile = markdownProject.GetAllMarkdownFiles().Single();
var html = (await markdownFile.ToHtmlContentAsync()).ToString();
diff --git a/MLS.Agent.Tests/Markdown/MarkdownProjectTests.cs b/MLS.Agent.Tests/Markdown/MarkdownProjectTests.cs
index 6c0d604a1..c4b856831 100644
--- a/MLS.Agent.Tests/Markdown/MarkdownProjectTests.cs
+++ b/MLS.Agent.Tests/Markdown/MarkdownProjectTests.cs
@@ -29,7 +29,7 @@ public void Returns_list_of_all_relative_paths_to_all_markdown_files()
("Program.cs", "")
};
- var project = new MarkdownProject(dirAccessor, PackageRegistry.CreateForHostedMode());
+ var project = new MarkdownProject(dirAccessor, Default.PackageFinder);
var files = project.GetAllMarkdownFiles();
@@ -46,7 +46,7 @@ public void Returns_false_for_nonexistent_file()
{
var workingDir = TestAssets.SampleConsole;
var dirAccessor = new InMemoryDirectoryAccessor(workingDir);
- var project = new MarkdownProject(dirAccessor, PackageRegistry.CreateForHostedMode());
+ var project = new MarkdownProject(dirAccessor, Default.PackageFinder);
var path = new RelativeFilePath("DOESNOTEXIST");
project.TryGetMarkdownFile(path, out _).Should().BeFalse();
@@ -70,7 +70,7 @@ public async Task Returns_all_projects_referenced_from_all_markdown_files()
("../Project1/Console1.csproj", @""),
("../Project2/Console2.csproj", @"")
},
- PackageRegistry.CreateForHostedMode());
+ Default.PackageFinder);
var markdownFiles = project.GetAllMarkdownFiles();
diff --git a/MLS.Agent.Tests/WorkspaceDiscoveryTests.cs b/MLS.Agent.Tests/WorkspaceDiscoveryTests.cs
index 67f7f203c..5eb9bf049 100644
--- a/MLS.Agent.Tests/WorkspaceDiscoveryTests.cs
+++ b/MLS.Agent.Tests/WorkspaceDiscoveryTests.cs
@@ -7,6 +7,7 @@
using System.IO;
using System.Threading.Tasks;
using Clockwise;
+using FluentAssertions.Extensions;
using Microsoft.DotNet.Try.Protocol;
using Microsoft.DotNet.Try.Protocol.Tests;
using MLS.Agent.CommandLine;
@@ -46,13 +47,16 @@ public async Task Local_tool_workspace_can_be_discovered()
[Fact]
public async Task Project_file_path_workspace_can_be_discovered_and_run_with_buffer_inlining()
{
- var workspace = (await Create.ConsoleWorkspaceCopy()).Directory;
+ var package = Create.EmptyWorkspace("a space");
+ var build = await Create.NewPackage(package.Name, package.Directory, Create.ConsoleConfiguration) as IHaveADirectory;
+
+ var workspace = build.Directory;
var csproj = workspace.GetFiles("*.csproj")[0];
var programCs = workspace.GetFiles("*.cs")[0];
var output = Guid.NewGuid().ToString();
var ws = new Workspace(
- files: new[] { new File(programCs.FullName, SourceCodeProvider.ConsoleProgramSingleRegion) },
+ files: new[] { new File(programCs.FullName, SourceCodeProvider.ConsoleProgramSingleRegion) },
buffers: new[] { new Buffer(new BufferId(programCs.FullName, "alpha"), $"Console.WriteLine(\"{output}\");") },
workspaceType: csproj.FullName);
@@ -66,24 +70,19 @@ public async Task Project_file_path_workspace_can_be_discovered_and_run_with_buf
result.ShouldSucceedWithOutput(output);
}
- private async Task<(string packageName, DirectoryInfo addSource)> CreateLocalTool(IConsole console)
+ private async Task<(string packageName, DirectoryInfo packageLocation)> CreateLocalTool(IConsole console)
{
// Keep project name short to work around max path issues
var projectName = Guid.NewGuid().ToString("N").Substring(0, 8);
+ var build = await Create.NewPackage(projectName, Create.ConsoleConfiguration) as IHaveADirectory;
- var copy = Create.EmptyWorkspace(
- initializer: new PackageInitializer(
- "console",
- projectName));
-
- await copy.CreateRoslynWorkspaceForRunAsync(new Budget());
-
+ var ws = await ((ICreateWorkspaceForRun)build).CreateRoslynWorkspaceForRunAsync(new TimeBudget(30.Seconds()));
var packageLocation = new DirectoryInfo(
- Path.Combine(copy.Directory.FullName, "pack-output"));
+ Path.Combine(build.Directory.FullName, "pack-output"));
var packageName = await PackCommand.Do(
new PackOptions(
- copy.Directory,
+ build.Directory,
outputDirectory: packageLocation,
enableBlazor: false),
console);
diff --git a/MLS.Agent/MLS.Agent.v3.ncrunchproject b/MLS.Agent/MLS.Agent.v3.ncrunchproject
new file mode 100644
index 000000000..65923ee2a
--- /dev/null
+++ b/MLS.Agent/MLS.Agent.v3.ncrunchproject
@@ -0,0 +1,8 @@
+
+
+
+ ..\docs\**.*
+ ..\MLS.Styles\**.*
+
+
+
\ No newline at end of file
diff --git a/MLS.Agent/Markdown/MarkdownProject.cs b/MLS.Agent/Markdown/MarkdownProject.cs
index f033b6329..58a6e33df 100644
--- a/MLS.Agent/Markdown/MarkdownProject.cs
+++ b/MLS.Agent/Markdown/MarkdownProject.cs
@@ -5,6 +5,8 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Reactive.Disposables;
+using System.Threading.Tasks;
using Markdig;
using Microsoft.DotNet.Try.Markdown;
using Recipes;
@@ -40,6 +42,11 @@ public IEnumerable GetAllFilesRecursively()
return Enumerable.Empty();
}
+ public IEnumerable GetAllFiles()
+ {
+ return Enumerable.Empty();
+ }
+
public IEnumerable GetAllDirectoriesRecursively()
{
return Enumerable.Empty();
@@ -55,6 +62,11 @@ public IDirectoryAccessor GetDirectoryAccessorForRelativePath(RelativeDirectoryP
return this;
}
+ public Task TryLockAsync()
+ {
+ return Task.FromResult(Disposable.Empty);
+ }
+
public void WriteAllText(RelativeFilePath path, string text)
{
}
diff --git a/WorkspaceServer.Tests/AspNetWorkspaceTests.cs b/WorkspaceServer.Tests/AspNetWorkspaceTests.cs
index 9148d4daf..5081e6a68 100644
--- a/WorkspaceServer.Tests/AspNetWorkspaceTests.cs
+++ b/WorkspaceServer.Tests/AspNetWorkspaceTests.cs
@@ -36,9 +36,11 @@ public AspNetWorkspaceTests(ITestOutputHelper output)
[Fact]
public async Task Run_starts_the_kestrel_server_and_provides_a_WebServer_feature_that_can_receive_requests()
{
- var (server, package) = await GetRunnerAndWorkspace();
+ var registry = Default.PackageFinder;
+ var server = new RoslynWorkspaceServer(registry);
+ var package = await registry.Get("aspnet.webapi");
- var workspace = WorkspaceFactory.CreateWorkspaceFromDirectory(package.Directory, package.Name);
+ var workspace = WorkspaceFactory.CreateWorkspaceFromDirectory(package.Directory, "aspnet.webapi");
using (var runResult = await server.Run(new WorkspaceRequest(workspace, "Program.cs")))
{
@@ -52,15 +54,5 @@ public async Task Run_starts_the_kestrel_server_and_provides_a_WebServer_feature
result.Should().Equal("value1", "value2");
}
}
-
- protected async Task<(ICodeRunner server, Package workspace)> GetRunnerAndWorkspace(
- [CallerMemberName] string testName = null)
- {
- var package = await Create.WebApiWorkspaceCopy(testName);
-
- var server = new RoslynWorkspaceServer(new PackageRegistry());
-
- return (server, package);
- }
}
}
diff --git a/WorkspaceServer.Tests/Create.cs b/WorkspaceServer.Tests/Create.cs
index d4db9a38a..674ede4e9 100644
--- a/WorkspaceServer.Tests/Create.cs
+++ b/WorkspaceServer.Tests/Create.cs
@@ -8,38 +8,80 @@
using System.Reactive.Concurrency;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
+using Clockwise;
using Microsoft.DotNet.Try.Protocol;
using Microsoft.DotNet.Try.Protocol.Tests;
using MLS.Agent.CommandLine;
using Recipes;
using WorkspaceServer.Packaging;
+using WorkspaceServer.Tests.Packaging;
using Package = WorkspaceServer.Packaging.Package;
namespace WorkspaceServer.Tests
{
public static class Create
{
+ public static Action ConsoleConfiguration { get; } = packageBuilder =>
+ {
+ packageBuilder.CreateUsingDotnet("console");
+ packageBuilder.TrySetLanguageVersion("8.0");
+ packageBuilder.AddPackageReference("Newtonsoft.Json");
+ };
+
+ public static Task NewPackage(string name, Action configure = null, bool createRebuildablePackage = false)
+ {
+ if (name == null) throw new ArgumentNullException(nameof(name));
+ var package = EmptyWorkspace(name);
+ return NewPackage(package.Name, package.Directory, configure, createRebuildablePackage);
+ }
+
+ public static async Task NewPackage(string name, DirectoryInfo directory, Action configure = null, bool createRebuildablePackage = false)
+ {
+ if (string.IsNullOrWhiteSpace(name))
+ {
+ throw new ArgumentException("Value cannot be null or whitespace.", nameof(name));
+ }
+ if (directory == null)
+ {
+ throw new ArgumentNullException(nameof(directory));
+ }
+
+ var packageBuilder = new PackageBuilder(name)
+ {
+ Directory = directory,
+ CreateRebuildablePackage = createRebuildablePackage
+ };
+
+
+ configure?.Invoke(packageBuilder);
+ var package = packageBuilder.GetPackage();
+
+ await package.EnsureReady(new Budget());
+
+ return package;
+ }
+
public static async Task ConsoleWorkspaceCopy([CallerMemberName] string testName = null, bool isRebuildable = false, IScheduler buildThrottleScheduler = null) =>
- await Package.Copy(
+ await PackageUtilities.Copy(
await Default.ConsoleWorkspace(),
testName,
isRebuildable,
buildThrottleScheduler);
public static async Task WebApiWorkspaceCopy([CallerMemberName] string testName = null) =>
- await Package.Copy(
+ await PackageUtilities.Copy(
await Default.WebApiWorkspace(),
testName);
public static async Task XunitWorkspaceCopy([CallerMemberName] string testName = null) =>
- await Package.Copy(
+ await PackageUtilities.Copy(
await Default.XunitWorkspace(),
testName);
public static async Task NetstandardWorkspaceCopy(
[CallerMemberName] string testName = null,
DirectoryInfo parentDirectory = null) =>
- await Package.Copy(
+ await PackageUtilities.Copy(
await Default.NetstandardWorkspace(),
testName,
parentDirectory: parentDirectory);
@@ -48,10 +90,10 @@ public static Package EmptyWorkspace([CallerMemberName] string testName = null,
{
if (!isRebuildablePackage)
{
- return new NonrebuildablePackage(directory: Package.CreateDirectory(testName), initializer: initializer);
+ return new NonrebuildablePackage(directory: PackageUtilities.CreateDirectory(testName), initializer: initializer);
}
- return new RebuildablePackage(directory: Package.CreateDirectory(testName), initializer: initializer);
+ return new RebuildablePackage(directory: PackageUtilities.CreateDirectory(testName), initializer: initializer);
}
public static async Task<(string packageName, DirectoryInfo addSource)> NupkgWithBlazorEnabled([CallerMemberName] string testName = null)
diff --git a/WorkspaceServer.Tests/Default.cs b/WorkspaceServer.Tests/Default.cs
index fd5e9fc8e..418331a0d 100644
--- a/WorkspaceServer.Tests/Default.cs
+++ b/WorkspaceServer.Tests/Default.cs
@@ -1,6 +1,8 @@
// 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.Threading.Tasks;
using WorkspaceServer.Packaging;
@@ -8,14 +10,14 @@ namespace WorkspaceServer.Tests
{
public static class Default
{
- private static readonly PackageRegistry DefaultPackages = PackageRegistry.CreateForHostedMode();
+ public static PackageRegistry PackageFinder { get; } = PackageRegistry.CreateForHostedMode();
- public static async Task ConsoleWorkspace() => await DefaultPackages.Get("console");
+ public static async Task ConsoleWorkspace() => await PackageFinder.Get("console");
- public static async Task WebApiWorkspace() => await DefaultPackages.Get("aspnet.webapi");
+ public static async Task WebApiWorkspace() => await PackageFinder.Get("aspnet.webapi");
- public static async Task XunitWorkspace() => await DefaultPackages.Get("xunit");
+ public static async Task XunitWorkspace() => await PackageFinder.Get("xunit");
- public static async Task NetstandardWorkspace() => await DefaultPackages.Get("blazor-console");
+ public static async Task NetstandardWorkspace() => await PackageFinder.Get("blazor-console");
}
}
\ No newline at end of file
diff --git a/WorkspaceServer.Tests/InMemoryDirectoryAccessor.cs b/WorkspaceServer.Tests/InMemoryDirectoryAccessor.cs
index e02fc874b..50391552d 100644
--- a/WorkspaceServer.Tests/InMemoryDirectoryAccessor.cs
+++ b/WorkspaceServer.Tests/InMemoryDirectoryAccessor.cs
@@ -6,12 +6,15 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Threading.Tasks;
using Microsoft.DotNet.Try.Markdown;
+using Recipes;
namespace WorkspaceServer.Tests
{
public class InMemoryDirectoryAccessor : IDirectoryAccessor, IEnumerable
{
+ private readonly AsyncLock _lock = new AsyncLock();
private readonly DirectoryInfo _rootDirToAddFiles;
private Dictionary _files = new Dictionary(
@@ -144,11 +147,17 @@ public IDirectoryAccessor GetDirectoryAccessorForRelativePath(RelativeDirectoryP
{
var newPath = WorkingDirectory.Combine(relativePath);
return new InMemoryDirectoryAccessor(newPath)
- {
- _files = _files
- };
+ {
+ _files = _files
+ };
+ }
+
+ public Task TryLockAsync()
+ {
+ return Task.FromResult(_lock.LockAsync());
}
+
public IEnumerable GetAllDirectoriesRecursively()
{
return _files.Keys
@@ -157,6 +166,15 @@ public IEnumerable GetAllDirectoriesRecursively()
Path.GetRelativePath(WorkingDirectory.FullName, key.FullName)));
}
+ public IEnumerable GetAllFiles()
+ {
+ return _files.Keys
+ .OfType()
+ .Where(key => key.Directory.FullName == WorkingDirectory.FullName)
+ .Select(key => new RelativeFilePath(
+ Path.GetRelativePath(WorkingDirectory.FullName, key.FullName)));
+ }
+
public IEnumerable GetAllFilesRecursively()
{
return _files.Keys
diff --git a/WorkspaceServer.Tests/NetstandardWorkspaceTests.cs b/WorkspaceServer.Tests/NetstandardWorkspaceTests.cs
index 694dfde9f..aca877d3b 100644
--- a/WorkspaceServer.Tests/NetstandardWorkspaceTests.cs
+++ b/WorkspaceServer.Tests/NetstandardWorkspaceTests.cs
@@ -4,22 +4,16 @@
using System;
using System.Linq;
using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Text;
using System.Threading.Tasks;
using Clockwise;
using FluentAssertions;
using Microsoft.DotNet.Try.Protocol;
using Pocket;
-using WorkspaceServer.Models.Execution;
using WorkspaceServer.Servers.Roslyn;
using WorkspaceServer.Tests.CodeSamples;
-using WorkspaceServer.Packaging;
using Xunit;
using Xunit.Abstractions;
-using static Pocket.Logger;
using Buffer = Microsoft.DotNet.Try.Protocol.Buffer;
-using Package = WorkspaceServer.Packaging.Package;
namespace WorkspaceServer.Tests
{
@@ -38,10 +32,10 @@ public NetstandardWorkspaceTests(ITestOutputHelper output)
[Fact]
public async Task When_run_fails_to_compile_then_diagnostics_are_aligned_with_buffer_span()
{
- var (server, build) = await GetRunnerAndWorkspace();
+ var server = GetCodeCompiler();
var workspace = new Workspace(
- workspaceType: build.Name,
+ workspaceType: "blazor-console",
files: new[] { new File("Program.cs", SourceCodeProvider.ConsoleProgramSingleRegion) },
buffers: new[] { new Buffer("Program.cs@alpha", @"Console.WriteLine(banana);", 0) });
@@ -60,10 +54,10 @@ public async Task When_run_fails_to_compile_then_diagnostics_are_aligned_with_bu
[Fact]
public async Task Compile_with_active_buffer_id_includes_diagnostics_on_edge_of_region()
{
- var (server, build) = await GetRunnerAndWorkspace();
+ var server = GetCodeCompiler();
var workspace = new Workspace(
- workspaceType: build.Name,
+ workspaceType: "blazor-console",
files: new[] { new File("Program.cs", "using System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nnamespace MyCodeSample\r\n{\r\npublic class Program\r\n {\r\n public static void Main()\r\n {\r\n #region code\r\n #endregion\r\n }\r\n }\r\n}") },
buffers: new[] { new Buffer("Program.cs@code", @"var x = 3", 0) });
@@ -81,10 +75,10 @@ public async Task Compile_with_active_buffer_id_includes_diagnostics_on_edge_of_
[Fact]
public async Task Compile_can_succeed_and_run()
{
- var (server, build) = await GetRunnerAndWorkspace();
+ var server = GetCodeCompiler();
var workspace = new Workspace(
- workspaceType: build.Name,
+ workspaceType: "blazor-console",
files: new[] { new File("Program.cs", SourceCodeProvider.ConsoleProgramSingleRegion) },
buffers: new[] { new Buffer("Program.cs@alpha", @"Console.WriteLine(2);", 0) });
@@ -93,7 +87,7 @@ public async Task Compile_can_succeed_and_run()
result.Succeeded.Should().BeTrue();
- var bytes = System.Convert.FromBase64String(result.Base64Assembly);
+ var bytes = Convert.FromBase64String(result.Base64Assembly);
var assembly = Assembly.Load(bytes);
var main = assembly.GetTypes().
SelectMany(t => t.GetMethods())
@@ -102,14 +96,6 @@ public async Task Compile_can_succeed_and_run()
main.Invoke(null, new [] { new string[] { } });
}
- protected async Task<(ICodeCompiler server, Package workspace)> GetRunnerAndWorkspace(
- [CallerMemberName] string testName = null)
- {
- var workspace = await Create.NetstandardWorkspaceCopy(testName);
-
- var server = new RoslynWorkspaceServer(workspace);
-
- return (server, workspace);
- }
+ protected ICodeCompiler GetCodeCompiler() => new RoslynWorkspaceServer(Default.PackageFinder);
}
}
diff --git a/WorkspaceServer.Tests/PackageRegistryTests.cs b/WorkspaceServer.Tests/PackageRegistryTests.cs
index 48d3d0614..49a8f8c42 100644
--- a/WorkspaceServer.Tests/PackageRegistryTests.cs
+++ b/WorkspaceServer.Tests/PackageRegistryTests.cs
@@ -4,26 +4,27 @@
using System.Threading.Tasks;
using FluentAssertions;
using WorkspaceServer.Packaging;
+using WorkspaceServer.Tests.Packaging;
using Xunit;
namespace WorkspaceServer.Tests
{
public class PackageRegistryTests
{
- private readonly PackageRegistry registry = new PackageRegistry();
+ private readonly PackageRegistry _registry = new PackageRegistry();
[Fact(Skip = "Cache is disabled for the moment")]
public async Task PackageRegistry_will_return_same_instance_of_a_package()
{
// FIX: (PackageRegistry_will_return_same_instance_of_a_package)
- var packageName = Package.CreateDirectory(nameof(PackageRegistry_will_return_same_instance_of_a_package)).Name;
+ var packageName = PackageUtilities.CreateDirectory(nameof(PackageRegistry_will_return_same_instance_of_a_package)).Name;
- registry.Add(packageName,
+ _registry.Add(packageName,
options => options.CreateUsingDotnet("console"));
- var package1 = await registry.Get(packageName);
- var package2 = await registry.Get(packageName);
+ var package1 = await _registry.Get(packageName);
+ var package2 = await _registry.Get(packageName);
package1.Should().BeSameAs(package2);
}
diff --git a/WorkspaceServer.Tests/PackageTests.cs b/WorkspaceServer.Tests/PackageTests.cs
index f099bd52a..79fe3afe7 100644
--- a/WorkspaceServer.Tests/PackageTests.cs
+++ b/WorkspaceServer.Tests/PackageTests.cs
@@ -13,20 +13,21 @@
using Xunit.Abstractions;
using WorkspaceServer.Packaging;
using System.Threading;
+using WorkspaceServer.Tests.Packaging;
namespace WorkspaceServer.Tests
{
public partial class PackageTests : IDisposable
{
- private readonly CompositeDisposable disposables = new CompositeDisposable();
+ private readonly CompositeDisposable _disposables = new CompositeDisposable();
public PackageTests(ITestOutputHelper output)
{
- disposables.Add(output.SubscribeToPocketLogger());
- disposables.Add(VirtualClock.Start());
+ _disposables.Add(output.SubscribeToPocketLogger());
+ _disposables.Add(VirtualClock.Start());
}
- public void Dispose() => disposables.Dispose();
+ public void Dispose() => _disposables.Dispose();
[Fact]
public async Task A_package_is_not_initialized_more_than_once()
@@ -76,7 +77,7 @@ public async Task A_package_copy_is_not_reinitialized_if_the_source_was_already_
await original.CreateRoslynWorkspaceForLanguageServicesAsync(new TimeBudget(30.Seconds()));
- var copy = await Package.Copy(original);
+ var copy = await PackageUtilities.Copy(original);
await copy.CreateRoslynWorkspaceForLanguageServicesAsync(new TimeBudget(30.Seconds()));
@@ -176,11 +177,11 @@ await Task.WhenAll(
[Theory]
[InlineData("console", false)]
[InlineData("nodatime.api", false)]
- [InlineData("blazor-console", true, Skip = "Requires package design changes")]
- [InlineData("blazor-nodatime.api", true, Skip = "Requires package design changes")]
+ //[InlineData("blazor-console", true, Skip = "Requires package design changes")]
+ //[InlineData("blazor-nodatime.api", true, Skip = "Requires package design changes")]
public async Task CanSupportBlazor_indicates_whether_the_package_supports_Blazor(string packageName, bool expected)
{
- var registry = PackageRegistry.CreateForHostedMode();
+ var registry = Default.PackageFinder;
var package = await registry.Get(packageName);
package.CanSupportBlazor.Should().Be(expected);
}
diff --git a/WorkspaceServer.Tests/PackageTests2.cs b/WorkspaceServer.Tests/PackageTests2.cs
index 6786886c1..37bf02644 100644
--- a/WorkspaceServer.Tests/PackageTests2.cs
+++ b/WorkspaceServer.Tests/PackageTests2.cs
@@ -19,8 +19,8 @@ public void It_can_have_assets_added_to_it()
var directoryAccessor = new InMemoryDirectoryAccessor();
var package = new Package2("the-package", directoryAccessor);
-
- var projectAsset = new ProjectAsset(directoryAccessor);
+ directoryAccessor.Add(("mainProject.csproj",""));
+ var projectAsset = new ProjectAsset(directoryAccessor, "mainProject.csproj");
package.Add(projectAsset);
package.Assets.Should().Contain(a => a == projectAsset);
@@ -30,10 +30,10 @@ public void It_can_have_assets_added_to_it()
public void An_asset_must_be_in_a_subdirectory_of_the_package()
{
var directoryAccessor = new InMemoryDirectoryAccessor();
-
+ directoryAccessor.Add(("./2/mainProject.csproj", ""));
var package = new Package2("1", directoryAccessor.GetDirectoryAccessorForRelativePath("1"));
- var projectAsset = new ProjectAsset(directoryAccessor.GetDirectoryAccessorForRelativePath("2"));
+ var projectAsset = new ProjectAsset(directoryAccessor.GetDirectoryAccessorForRelativePath("2"), "mainProject.csproj");
package.Invoking(p => p.Add(projectAsset)).Should()
.Throw()
diff --git a/WorkspaceServer.Tests/Packaging/PackageUtilities.cs b/WorkspaceServer.Tests/Packaging/PackageUtilities.cs
new file mode 100644
index 000000000..7b992a2a1
--- /dev/null
+++ b/WorkspaceServer.Tests/Packaging/PackageUtilities.cs
@@ -0,0 +1,89 @@
+// 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.Reactive.Concurrency;
+using System.Threading.Tasks;
+using Clockwise;
+using MLS.Agent.Tools;
+using WorkspaceServer.Packaging;
+
+namespace WorkspaceServer.Tests.Packaging
+{
+ public static class PackageUtilities
+ {
+ private static readonly object CreateDirectoryLock = new object();
+
+ public static async Task Copy(
+ Package fromPackage,
+ string folderNameStartsWith = null,
+ bool isRebuildable = false,
+ IScheduler buildThrottleScheduler = null,
+ DirectoryInfo parentDirectory = null)
+ {
+ if (fromPackage == null)
+ {
+ throw new ArgumentNullException(nameof(fromPackage));
+ }
+
+ await fromPackage.EnsureReady(new Budget());
+
+ folderNameStartsWith = folderNameStartsWith ?? fromPackage.Name;
+ parentDirectory = parentDirectory ?? fromPackage.Directory.Parent;
+
+ var destination =
+ CreateDirectory(folderNameStartsWith,
+ parentDirectory);
+
+ fromPackage.Directory.CopyTo(destination);
+
+ var binLogs = destination.GetFiles("*.binlog");
+
+ foreach (var fileInfo in binLogs)
+ {
+ fileInfo.Delete();
+ }
+
+ Package copy;
+ if (isRebuildable)
+ {
+ copy = new RebuildablePackage(directory: destination, name: destination.Name, buildThrottleScheduler: buildThrottleScheduler);
+ }
+ else
+ {
+ copy = new NonrebuildablePackage(directory: destination, name: destination.Name, buildThrottleScheduler: buildThrottleScheduler);
+ }
+
+ return copy;
+ }
+
+ public static DirectoryInfo CreateDirectory(
+ string folderNameStartsWith,
+ DirectoryInfo parentDirectory = null)
+ {
+ if (string.IsNullOrWhiteSpace(folderNameStartsWith))
+ {
+ throw new ArgumentException("Value cannot be null or whitespace.", nameof(folderNameStartsWith));
+ }
+
+ parentDirectory = parentDirectory ?? Package.DefaultPackagesDirectory;
+
+ DirectoryInfo created;
+
+ lock (CreateDirectoryLock)
+ {
+ if (!parentDirectory.Exists)
+ {
+ parentDirectory.Create();
+ }
+
+ var existingFolders = parentDirectory.GetDirectories($"{folderNameStartsWith}.*");
+
+ created = parentDirectory.CreateSubdirectory($"{folderNameStartsWith}.{existingFolders.Length + 1}");
+ }
+
+ return created;
+ }
+ }
+}
diff --git a/WorkspaceServer.Tests/PipelineStepTests.cs b/WorkspaceServer.Tests/PipelineStepTests.cs
new file mode 100644
index 000000000..6fd839a43
--- /dev/null
+++ b/WorkspaceServer.Tests/PipelineStepTests.cs
@@ -0,0 +1,191 @@
+// 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.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using FluentAssertions;
+using FluentAssertions.Extensions;
+using WorkspaceServer.Packaging;
+using Xunit;
+
+namespace WorkspaceServer.Tests
+{
+ public class PipelineStepTests
+ {
+ [Fact]
+ public async Task It_produces_a_new_value_when_there_is_none()
+ {
+ var producer = new PipelineStep(() => Task.FromResult("something"));
+ var value = await producer.GetLatestAsync();
+ value.Should().Be("something");
+ }
+
+ [Fact]
+ public async Task It_produces_a_new_value_when_invalidated()
+ {
+ var seed = 0;
+ var producer = new PipelineStep(() => Task.FromResult(Interlocked.Increment(ref seed)));
+ var value = await producer.GetLatestAsync();
+ value.Should().Be(1);
+
+ producer.Invalidate();
+ var newValue = await producer.GetLatestAsync();
+ newValue.Should().Be(2);
+ }
+
+ [Fact]
+ public async Task It_propagates_exception()
+ {
+ var producer = new PipelineStep( () => throw new InvalidOperationException());
+ producer.Awaiting(p => p.GetLatestAsync())
+ .Should()
+ .Throw();
+
+ }
+
+ [Fact]
+ public async Task It_remains_invalid_if_exceptions_are_thrown()
+ {
+ var seed = 0;
+ var producer = new PipelineStep(() =>
+ {
+ var next = Interlocked.Increment(ref seed);
+ if (next == 1)
+ {
+ throw new InvalidOperationException();
+ }
+ return Task.FromResult(next);
+ });
+
+ producer.Awaiting(p => p.GetLatestAsync())
+ .Should()
+ .Throw();
+
+ var value = await producer.GetLatestAsync();
+ value.Should().Be(2);
+
+ }
+
+ [Fact]
+ public async Task It_does_not_produces_a_new_value_when_invalidated_until_asked_for_latest_value()
+ {
+ var seed = 0;
+ var producer = new PipelineStep(() => Task.FromResult(Interlocked.Increment(ref seed)));
+ var value = await producer.GetLatestAsync();
+ value.Should().Be(1);
+ producer.Invalidate();
+ seed.Should().Be(1);
+ }
+
+ [Fact]
+ public async Task It_retains_the_latest_value()
+ {
+ var seed = 0;
+ var producer = new PipelineStep(() => Task.FromResult(Interlocked.Increment(ref seed)));
+ var value1 = await producer.GetLatestAsync();
+ var value2 = await producer.GetLatestAsync();
+ var value3 = await producer.GetLatestAsync();
+ value1.Should().Be(1);
+ value2.Should().Be(1);
+ value3.Should().Be(1);
+ }
+
+ [Fact]
+ public async Task It_returns_same_value_to_concurrent_requests()
+ {
+ var seed = 0;
+ var barrier = new Barrier(3);
+ var producer = new PipelineStep(() =>
+ {
+ barrier.SignalAndWait(2.Seconds());
+ return Task.FromResult(++seed);
+ });
+
+ var values = await Task.WhenAll( Enumerable.Range(0, 3)
+ .AsParallel()
+ .Select((_) => producer.GetLatestAsync()));
+ values.Should().HaveCount(3).And.OnlyContain(i => i == 1);
+ }
+
+ [Fact]
+ public async Task When_invalidated_while_producing_a_value_the_consumer_waiting_will_wait_for_latest_production_to_be_finished()
+ {
+ var seed = 0;
+ var consumerBarrier = new Barrier(2);
+ var producerBarrier = new Barrier(2);
+
+ var producer = new PipelineStep(() =>
+ {
+ // will require all consumer to reach this point to move on
+ producerBarrier.SignalAndWait(2.Seconds());
+ return Task.FromResult(Interlocked.Increment(ref seed));
+ });
+
+ var firstConsumer = Task.Run(() =>
+ {
+ var task = producer.GetLatestAsync();
+ // block waiting for the other consumer
+ consumerBarrier.SignalAndWait(2.Seconds());
+ return task;
+ }
+ );
+
+ var secondConsumer = Task.Run(() =>
+ {
+ // now both consumer reached the barrier
+ consumerBarrier.SignalAndWait(2.Seconds());
+ producer.Invalidate();
+ // let the firs request fire
+ producerBarrier.RemoveParticipant();
+ // second request after invalidation
+ var task = producer.GetLatestAsync();
+ return task;
+ }
+ );
+
+ var values = await Task.WhenAll(firstConsumer, secondConsumer);
+ values.Should().HaveCount(2).And.OnlyContain(i => i == 2);
+
+ }
+
+ [Fact]public async Task Sequence_of_steps_produce_a_value()
+ {
+ var step1 = new PipelineStep(()=>Task.FromResult(1));
+ var step2 = step1.Then( (number) => Task.FromResult($"{number} {number}") );
+ var value1 = await step2.GetLatestAsync();
+ value1.Should().Be("1 1");
+ }
+
+ [Fact]
+ public async Task Invalidating_a_step_in_a_sequence_causes_only_that_step_to_re_evaluate()
+ {
+ var seed1 = 0;
+ var step1 = new PipelineStep(() => Task.FromResult(Interlocked.Increment(ref seed1)));
+ var seed2 = 0;
+ var step2 = step1.Then((number) => Task.FromResult($"{number} {Interlocked.Increment(ref seed2)}"));
+ await step2.GetLatestAsync();
+
+ step2.Invalidate();
+ var value = await step2.GetLatestAsync();
+ value.Should().Be("1 2");
+ }
+
+ [Fact]
+ public async Task Invalidating_a_step_in_a_sequence_causes_all_successor_to_evaluate()
+ {
+ var seed1 = 0;
+ var seed2 = 0;
+ var seed3 = 0;
+ var step1 = new PipelineStep(() => Task.FromResult(Interlocked.Increment(ref seed1)));
+ var step2 = step1.Then((number) => Task.FromResult($"{number} {Interlocked.Increment(ref seed2)}"));
+ var step3 = step2.Then((text) => Task.FromResult($"{text} {Interlocked.Increment(ref seed3)}"));
+ await step3.GetLatestAsync();
+
+ step2.Invalidate();
+ var value = await step3.GetLatestAsync();
+ value.Should().Be("1 2 2");
+ }
+ }
+}
\ No newline at end of file
diff --git a/WorkspaceServer.Tests/RebuildablePackageTests.cs b/WorkspaceServer.Tests/RebuildablePackageTests.cs
index 58f2e3839..bc473ad89 100644
--- a/WorkspaceServer.Tests/RebuildablePackageTests.cs
+++ b/WorkspaceServer.Tests/RebuildablePackageTests.cs
@@ -35,20 +35,22 @@ public async Task If_a_new_file_is_added_the_workspace_includes_the_file()
var ws = await package.CreateRoslynWorkspaceForRunAsync(new TimeBudget(30.Seconds()));
var newFile = Path.Combine(package.Directory.FullName, "Sample.cs");
- ws.CurrentSolution.Projects.First().Documents.Should().NotContain(d => d.FilePath == newFile);
+ ws.CurrentSolution.Projects.First().Documents.Select(d => d.FilePath).Should().NotContain(filePath => filePath == newFile);
File.WriteAllText(newFile, "//this is a new file");
ws = await package.CreateRoslynWorkspaceForRunAsync(new TimeBudget(30.Seconds()));
- ws.CurrentSolution.Projects.First().Documents.Should().Contain(d => d.FilePath == newFile);
+ ws.CurrentSolution.Projects.First().Documents.Select(d => d.FilePath).Should().Contain(filePath => filePath == newFile);
}
[Fact]
public async Task If_the_project_file_is_changed_then_the_workspace_reflects_the_changes()
{
- var package = (RebuildablePackage)await Create.ConsoleWorkspaceCopy(isRebuildable: true);
- var ws = await package.CreateRoslynWorkspaceForRunAsync(new TimeBudget(30.Seconds()));
+ var package = Create.EmptyWorkspace();
+ var build = await Create.NewPackage(package.Name, package.Directory, Create.ConsoleConfiguration) as ICreateWorkspaceForRun;
+
+ var ws = await build.CreateRoslynWorkspaceForRunAsync(new TimeBudget(30.Seconds()));
var references = ws.CurrentSolution.Projects.First().MetadataReferences;
references.Should().NotContain(reference =>
@@ -67,17 +69,19 @@ public async Task If_the_project_file_is_changed_then_the_workspace_reflects_the
[Fact]
public async Task If_an_existing_file_is_deleted_then_the_workspace_does_not_include_the_file()
{
- var package = (RebuildablePackage)await Create.ConsoleWorkspaceCopy(isRebuildable: true);
- var ws = await package.CreateRoslynWorkspaceForRunAsync(new TimeBudget(30.Seconds()));
+ var package = Create.EmptyWorkspace();
+ var build = await Create.NewPackage(package.Name, package.Directory, Create.ConsoleConfiguration, true) as ICreateWorkspaceForRun;
+
+ var ws = await build.CreateRoslynWorkspaceForRunAsync(new TimeBudget(30.Seconds()));
var existingFile = Path.Combine(package.Directory.FullName, "Program.cs");
- ws.CurrentSolution.Projects.First().Documents.Should().Contain(d => d.FilePath == existingFile);
+ ws.CurrentSolution.Projects.First().Documents.Select(d => d.FilePath).Should().Contain(filePath => filePath == existingFile);
File.Delete(existingFile);
await Task.Delay(1000);
- ws = await package.CreateRoslynWorkspaceForRunAsync(new TimeBudget(30.Seconds()));
- ws.CurrentSolution.Projects.First().Documents.Should().NotContain(d => d.FilePath == existingFile);
+ ws = await build.CreateRoslynWorkspaceForRunAsync(new TimeBudget(30.Seconds()));
+ ws.CurrentSolution.Projects.First().Documents.Select(d => d.FilePath).Should().NotContain(filePath => filePath == existingFile);
}
[Fact]
@@ -113,8 +117,8 @@ public async Task If_a_build_is_in_progress_and_another_request_comes_in_both_ar
var workspaces = await Task.WhenAll(workspace1, workspace2);
- workspaces[0].CurrentSolution.Projects.First().Documents.Should().Contain(p => p.FilePath.EndsWith("Sample.cs"));
- workspaces[1].CurrentSolution.Projects.First().Documents.Should().Contain(p => p.FilePath.EndsWith("Sample.cs"));
+ workspaces[0].CurrentSolution.Projects.First().Documents.Select(d => d.FilePath).Should().Contain(filePath => filePath.EndsWith("Sample.cs"));
+ workspaces[1].CurrentSolution.Projects.First().Documents.Select(d => d.FilePath).Should().Contain(filePath => filePath.EndsWith("Sample.cs"));
}
}
}
diff --git a/WorkspaceServer.Tests/RoslynWorkspaceServerConsoleProjectTests.cs b/WorkspaceServer.Tests/RoslynWorkspaceServerConsoleProjectTests.cs
index da321d7af..f8dc8d2ff 100644
Binary files a/WorkspaceServer.Tests/RoslynWorkspaceServerConsoleProjectTests.cs and b/WorkspaceServer.Tests/RoslynWorkspaceServerConsoleProjectTests.cs differ
diff --git a/WorkspaceServer.Tests/RoslynWorkspaceServerScriptDiagnosticsTests.cs b/WorkspaceServer.Tests/RoslynWorkspaceServerScriptDiagnosticsTests.cs
index 4fbbc98ae..250cdb91a 100644
--- a/WorkspaceServer.Tests/RoslynWorkspaceServerScriptDiagnosticsTests.cs
+++ b/WorkspaceServer.Tests/RoslynWorkspaceServerScriptDiagnosticsTests.cs
@@ -1,8 +1,6 @@
// 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.Runtime.CompilerServices;
using System.Threading.Tasks;
using FluentAssertions;
using Microsoft.DotNet.Try.Protocol;
@@ -22,11 +20,6 @@ public RoslynWorkspaceServerScriptDiagnosticsTests(ITestOutputHelper output) : b
{
}
- protected override Task<(ICodeRunner runner, Package workspace)> GetRunnerAndWorkspaceBuild(string testName = null)
- {
- return Task.FromResult(((ICodeRunner)new ScriptingWorkspaceServer(),(Package) new NonrebuildablePackage("script")));
- }
-
[Fact]
public async Task Get_diagnostics()
{
@@ -41,8 +34,10 @@ public async Task Get_diagnostics()
result.Diagnostics.Should().Contain(diagnostics => diagnostics.Message == "(1,1): error CS0103: The name \'addd\' does not exist in the current context");
}
- protected override ILanguageService GetLanguageService(
- [CallerMemberName] string testName = null) => new RoslynWorkspaceServer(
- PackageRegistry.CreateForHostedMode());
+ protected override ILanguageService GetLanguageService() => new RoslynWorkspaceServer(Default.PackageFinder);
+
+ protected override ICodeCompiler GetCodeCompiler() => new RoslynWorkspaceServer(Default.PackageFinder);
+
+ protected override ICodeRunner GetCodeRunner() => new RoslynWorkspaceServer(Default.PackageFinder);
}
}
\ No newline at end of file
diff --git a/WorkspaceServer.Tests/RoslynWorkspaceServerScriptIntellisenseTests.cs b/WorkspaceServer.Tests/RoslynWorkspaceServerScriptIntellisenseTests.cs
index 5c79645cc..c928f7fbf 100644
--- a/WorkspaceServer.Tests/RoslynWorkspaceServerScriptIntellisenseTests.cs
+++ b/WorkspaceServer.Tests/RoslynWorkspaceServerScriptIntellisenseTests.cs
@@ -1,37 +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 System;
using System.Linq;
-using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using FluentAssertions;
using Microsoft.DotNet.Try.Protocol;
using Microsoft.DotNet.Try.Protocol.Tests;
-using WorkspaceServer.Servers.Roslyn;
-using WorkspaceServer.Servers.Scripting;
-using WorkspaceServer.Packaging;
using Xunit;
using Xunit.Abstractions;
using Buffer = Microsoft.DotNet.Try.Protocol.Buffer;
-using Package = WorkspaceServer.Packaging.Package;
namespace WorkspaceServer.Tests
{
- public class RoslynWorkspaceServerScriptIntellisenseTests : WorkspaceServerTestsCore
+ public class RoslynWorkspaceServerScriptIntellisenseTests : RoslynWorkspaceServerTestsCore
{
public RoslynWorkspaceServerScriptIntellisenseTests(ITestOutputHelper output) : base(output)
{
}
- protected override Task<(ICodeRunner runner, Package workspace)> GetRunnerAndWorkspaceBuild(string testName = null)
- {
- return Task.FromResult(((ICodeRunner)new ScriptingWorkspaceServer(),(Package) new NonrebuildablePackage("script")));
- }
- protected override ILanguageService GetLanguageService(
- [CallerMemberName] string testName = null) => new RoslynWorkspaceServer(
- PackageRegistry.CreateForHostedMode());
-
[Fact]
public async Task Get_signature_help_for_invalid_location_return_empty()
{
diff --git a/WorkspaceServer.Tests/RoslynWorkspaceServerConsoleProjectDiagnosticsTests.cs b/WorkspaceServer.Tests/RoslynWorkspaceServerTestsConsoleProjectDiagnosticsTests.cs
similarity index 85%
rename from WorkspaceServer.Tests/RoslynWorkspaceServerConsoleProjectDiagnosticsTests.cs
rename to WorkspaceServer.Tests/RoslynWorkspaceServerTestsConsoleProjectDiagnosticsTests.cs
index ff1cae474..1f4cba49a 100644
--- a/WorkspaceServer.Tests/RoslynWorkspaceServerConsoleProjectDiagnosticsTests.cs
+++ b/WorkspaceServer.Tests/RoslynWorkspaceServerTestsConsoleProjectDiagnosticsTests.cs
@@ -1,23 +1,21 @@
// 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.Runtime.CompilerServices;
using System.Threading.Tasks;
using FluentAssertions;
using Microsoft.DotNet.Try.Protocol;
using Microsoft.DotNet.Try.Protocol.Tests;
-using WorkspaceServer.Servers.Roslyn;
using Xunit;
using Xunit.Abstractions;
using Buffer = Microsoft.DotNet.Try.Protocol.Buffer;
-using Package = WorkspaceServer.Packaging.Package;
namespace WorkspaceServer.Tests
{
- public class RoslynWorkspaceServerConsoleProjectDiagnosticsTests : WorkspaceServerTestsCore
+ public class RoslynWorkspaceServerTestsConsoleProjectDiagnosticsTests : RoslynWorkspaceServerTestsCore
{
- public RoslynWorkspaceServerConsoleProjectDiagnosticsTests(ITestOutputHelper output) : base(output)
+
+
+ public RoslynWorkspaceServerTestsConsoleProjectDiagnosticsTests(ITestOutputHelper output) : base(output)
{
}
@@ -160,15 +158,6 @@ public static IEnumerable Fibonacci()
result.Diagnostics.Should().Contain(diagnostics => diagnostics.Message == "generators/FibonacciGenerator.cs(14,17): error CS0246: The type or namespace name \'adddd\' could not be found (are you missing a using directive or an assembly reference?)");
}
- protected override Task<(ICodeRunner runner, Package workspace)> GetRunnerAndWorkspaceBuild(
- [CallerMemberName] string testName = null)
- {
- throw new NotImplementedException();
- }
-
- protected override ILanguageService GetLanguageService([CallerMemberName] string testName = null)
- {
- return new RoslynWorkspaceServer(PackageRegistry.CreateForHostedMode());
- }
+
}
}
\ No newline at end of file
diff --git a/WorkspaceServer.Tests/RoslynWorkspaceServerConsoleProjectIntellisenseTests.cs b/WorkspaceServer.Tests/RoslynWorkspaceServerTestsConsoleProjectIntellisenseTests.cs
similarity index 95%
rename from WorkspaceServer.Tests/RoslynWorkspaceServerConsoleProjectIntellisenseTests.cs
rename to WorkspaceServer.Tests/RoslynWorkspaceServerTestsConsoleProjectIntellisenseTests.cs
index 756455e17..6047d8f59 100644
--- a/WorkspaceServer.Tests/RoslynWorkspaceServerConsoleProjectIntellisenseTests.cs
+++ b/WorkspaceServer.Tests/RoslynWorkspaceServerTestsConsoleProjectIntellisenseTests.cs
@@ -1,14 +1,12 @@
// 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.Linq;
-using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using FluentAssertions;
using Microsoft.DotNet.Try.Protocol;
using Microsoft.DotNet.Try.Protocol.Tests;
-using WorkspaceServer.Servers.Roslyn;
+using WorkspaceServer.Tests.Packaging;
using Xunit;
using Xunit.Abstractions;
using Buffer = Microsoft.DotNet.Try.Protocol.Buffer;
@@ -16,9 +14,9 @@
namespace WorkspaceServer.Tests
{
- public class RoslynWorkspaceServerConsoleProjectIntellisenseTests : WorkspaceServerTestsCore
+ public class RoslynWorkspaceServerTestsConsoleProjectIntellisenseTests : RoslynWorkspaceServerTestsCore
{
- public RoslynWorkspaceServerConsoleProjectIntellisenseTests(ITestOutputHelper output) : base(output)
+ public RoslynWorkspaceServerTestsConsoleProjectIntellisenseTests(ITestOutputHelper output) : base(output)
{
}
@@ -188,11 +186,10 @@ public static IEnumerable Fibonacci()
}".EnforceLF();
#endregion
-
- var package = await Package.Copy(await Default.ConsoleWorkspace());
+
var (processed, position) = CodeManipulation.ProcessMarkup(generator);
- var workspace = new Workspace(workspaceType: package.Name, buffers: new[]
+ var workspace = new Workspace(workspaceType: "console", buffers: new[]
{
new Buffer("Program.cs", program),
new Buffer("generators/FibonacciGenerator.cs", processed, position)
@@ -697,7 +694,7 @@ public static IEnumerable Fibonacci()
#endregion
var (processed, position) = CodeManipulation.ProcessMarkup(generator);
- var package = await Package.Copy(await Default.ConsoleWorkspace());
+ var package = await PackageUtilities.Copy(await Default.ConsoleWorkspace());
var workspace = new Workspace(workspaceType: package.Name, buffers: new[]
{
new Buffer("Program.cs", program),
@@ -710,7 +707,7 @@ public static IEnumerable Fibonacci()
result.Signatures.Should().NotBeNullOrEmpty();
- var sample = result.Signatures.Where(e => e.Label == "void Console.WriteLine(string format, params object[] arg)").First();
+ var sample = result.Signatures.First(e => e.Label == "void Console.WriteLine(string format, params object[] arg)");
sample.Documentation.Value.Should().Contain("Writes the text representation of the specified array of objects, followed by the current line terminator, to the standard output stream using the specified format information.");
sample.Parameters.Should().HaveCount(2);
@@ -722,16 +719,5 @@ public static IEnumerable Fibonacci()
sample.Parameters.ElementAt(1).Label.Should().Be("params object[] arg");
sample.Parameters.ElementAt(1).Documentation.Value.Should().Contain("An array of objects to write using format.");
}
-
- protected override Task<(ICodeRunner runner, Package workspace)> GetRunnerAndWorkspaceBuild(
- [CallerMemberName] string testName = null)
- {
- throw new NotImplementedException();
- }
-
- protected override ILanguageService GetLanguageService([CallerMemberName] string testName = null)
- {
- return new RoslynWorkspaceServer(PackageRegistry.CreateForHostedMode());
- }
}
}
diff --git a/WorkspaceServer.Tests/RoslynWorkspaceServerTestsCore.cs b/WorkspaceServer.Tests/RoslynWorkspaceServerTestsCore.cs
new file mode 100644
index 000000000..2884b21c3
--- /dev/null
+++ b/WorkspaceServer.Tests/RoslynWorkspaceServerTestsCore.cs
@@ -0,0 +1,22 @@
+// 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 WorkspaceServer.Packaging;
+using WorkspaceServer.Servers.Roslyn;
+using Xunit.Abstractions;
+
+namespace WorkspaceServer.Tests
+{
+ public abstract class RoslynWorkspaceServerTestsCore : WorkspaceServerTestsCore
+ {
+ protected RoslynWorkspaceServerTestsCore(ITestOutputHelper output) : base(output)
+ {
+ }
+
+ protected override ILanguageService GetLanguageService() => new RoslynWorkspaceServer(Default.PackageFinder);
+
+ protected override ICodeCompiler GetCodeCompiler() => new RoslynWorkspaceServer(Default.PackageFinder);
+
+ protected override ICodeRunner GetCodeRunner() => new RoslynWorkspaceServer(Default.PackageFinder);
+ }
+}
\ No newline at end of file
diff --git a/WorkspaceServer.Tests/ScriptingWorkspaceServerTests.cs b/WorkspaceServer.Tests/ScriptingWorkspaceServerTests.cs
index 5b5c963aa..d6cb34b54 100644
--- a/WorkspaceServer.Tests/ScriptingWorkspaceServerTests.cs
+++ b/WorkspaceServer.Tests/ScriptingWorkspaceServerTests.cs
@@ -24,16 +24,17 @@ public ScriptingWorkspaceServerTests(ITestOutputHelper output) : base(output)
{
}
- protected override Workspace CreateWorkspaceWithMainContaining(string text, Package package) =>
+ protected override Workspace CreateWorkspaceWithMainContaining(string text) =>
Workspace.FromSource(text, workspaceType: "script");
+
- protected override Task<(ICodeRunner runner, Package workspace)> GetRunnerAndWorkspaceBuild(
- [CallerMemberName] string testName = null) =>
- Task.FromResult<(ICodeRunner , Package )>((new ScriptingWorkspaceServer(), new NonrebuildablePackage("script")));
-
- protected override ILanguageService GetLanguageService([CallerMemberName] string testName = null) =>
+ protected override ILanguageService GetLanguageService() =>
throw new NotImplementedException();
+ protected override ICodeCompiler GetCodeCompiler() => throw new NotImplementedException();
+
+ protected override ICodeRunner GetCodeRunner() => new ScriptingWorkspaceServer();
+
[Fact]
public async Task Response_shows_fragment_return_value()
{
@@ -42,7 +43,7 @@ public async Task Response_shows_fragment_return_value()
var person = new { Name = ""Jeff"", Age = 20 };
$""{person.Name} is {person.Age} year(s) old""", "script");
- var (server, build) = await GetRunnerAndWorkspaceBuild();
+ var server = GetCodeRunner();
var result = await server.Run(new WorkspaceRequest(workspace));
@@ -52,7 +53,7 @@ public async Task Response_shows_fragment_return_value()
{
Succeeded = true,
Output = new string[] { },
- Exception = (string) null,
+ Exception = (string)null,
ReturnValue = $"Jeff is 20 year(s) old",
}, config => config.ExcludingMissingMembers());
}
@@ -62,7 +63,7 @@ public async Task Response_indicates_when_compile_is_unsuccessful()
{
var workspace = Workspace.FromSource(@"
Console.WriteLine(banana);", "script");
- var (server, build) = await GetRunnerAndWorkspaceBuild();
+ var server = GetCodeRunner();
var result = await server.Run(new WorkspaceRequest(workspace));
@@ -92,7 +93,7 @@ public static void Main()
workspaceType: "script",
usings: new[] { "System.Threading" });
- var (server, build) = await GetRunnerAndWorkspaceBuild();
+ var server = GetCodeRunner();
var result = await server.Run(new WorkspaceRequest(workspace));
@@ -100,7 +101,7 @@ public static void Main()
{
Succeeded = true,
Output = new[] { "Hello there!", "" },
- Exception = (string) null,
+ Exception = (string)null,
}, config => config.ExcludingMissingMembers());
}
@@ -117,7 +118,7 @@ public static void Main(params int[] args)
Console.WriteLine(""Hello there!"");
}
}", workspaceType: "script");
- var (server, build) = await GetRunnerAndWorkspaceBuild();
+ var server = GetCodeRunner();
var result = await server.Run(new WorkspaceRequest(workspace));
@@ -137,7 +138,7 @@ public static void Main()
Console.WriteLine(""Hello there!"");
}
}", workspaceType: "script");
- var (server, build) = await GetRunnerAndWorkspaceBuild();
+ var server = GetCodeRunner();
var result = await server.Run(new WorkspaceRequest(workspace));
@@ -164,7 +165,7 @@ static void Main(string[] args)
workspaceType: "script",
files: new[] { new File("Main.cs", fileCode) },
buffers: new[] { new Buffer(@"Main.cs@toReplace", @"Console.WriteLine(""Hello there!"");", 0) });
- var (server, build) = await GetRunnerAndWorkspaceBuild();
+ var server = GetCodeRunner();
var result = await server.Run(new WorkspaceRequest(workspace));
@@ -190,7 +191,7 @@ static void Main(string[] args)
files: new[] { new File("Main.cs", fileCode) },
buffers: new[] { new Buffer(@"Main.cs@toReplace", @"Console.WriteLine(banana);", 0) });
- var (server, build) = await GetRunnerAndWorkspaceBuild();
+ var server = GetCodeRunner();
var result = await server.Run(new WorkspaceRequest(workspace));
result.Should().BeEquivalentTo(new
diff --git a/WorkspaceServer.Tests/WebServerTests.cs b/WorkspaceServer.Tests/WebServerTests.cs
index 40e5bcc44..5c10dd74d 100644
--- a/WorkspaceServer.Tests/WebServerTests.cs
+++ b/WorkspaceServer.Tests/WebServerTests.cs
@@ -13,6 +13,7 @@
using Recipes;
using WorkspaceServer.Features;
using WorkspaceServer.Packaging;
+using WorkspaceServer.Tests.Packaging;
using Xunit;
using Xunit.Abstractions;
@@ -32,7 +33,7 @@ public WebServerTests(ITestOutputHelper output)
[Fact]
public async Task Multiple_WebServer_instances_can_be_run_concurrently_in_the_same_folder()
{
- var workspace = await Package.Copy(await Default.WebApiWorkspace());
+ var workspace = await PackageUtilities.Copy(await Default.WebApiWorkspace());
using (var webServer1 = new WebServer(workspace))
using (var webServer2 = new WebServer(workspace))
{
@@ -47,7 +48,7 @@ public async Task Multiple_WebServer_instances_can_be_run_concurrently_in_the_sa
[Fact]
public async Task EnsureStarted_returns_the_web_server_base_uri()
{
- var workspace = await Package.Copy(await Default.WebApiWorkspace());
+ var workspace = await PackageUtilities.Copy(await Default.WebApiWorkspace());
using (var webServer = new WebServer(workspace))
{
@@ -62,7 +63,7 @@ public async Task EnsureStarted_returns_the_web_server_base_uri()
[Fact]
public async Task WebServer_lifecycle_events_can_be_viewed_via_StandardOutput()
{
- var workspace = await Package.Copy(await Default.WebApiWorkspace());
+ var workspace = await PackageUtilities.Copy(await Default.WebApiWorkspace());
var log = new StringBuilder();
using (var webServer = new WebServer(workspace))
diff --git a/WorkspaceServer.Tests/WorkspaceServer.Tests.csproj b/WorkspaceServer.Tests/WorkspaceServer.Tests.csproj
index 141b0a7ce..2a68da2c0 100644
--- a/WorkspaceServer.Tests/WorkspaceServer.Tests.csproj
+++ b/WorkspaceServer.Tests/WorkspaceServer.Tests.csproj
@@ -21,6 +21,10 @@
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
diff --git a/WorkspaceServer.Tests/WorkspaceServerRegistryTests.cs b/WorkspaceServer.Tests/WorkspaceServerRegistryTests.cs
index 58f2b974e..3d29aa613 100644
--- a/WorkspaceServer.Tests/WorkspaceServerRegistryTests.cs
+++ b/WorkspaceServer.Tests/WorkspaceServerRegistryTests.cs
@@ -12,34 +12,33 @@
using Xunit;
using Xunit.Abstractions;
using FluentAssertions.Extensions;
-using Microsoft.DotNet.Try.Protocol;
using WorkspaceServer.Packaging;
-using Package = WorkspaceServer.Packaging.Package;
+using WorkspaceServer.Tests.Packaging;
namespace WorkspaceServer.Tests
{
public class WorkspaceServerRegistryTests : IDisposable
{
- private readonly CompositeDisposable disposables = new CompositeDisposable();
- private readonly PackageRegistry registry = PackageRegistry.CreateForHostedMode();
+ private readonly CompositeDisposable _disposables = new CompositeDisposable();
+ private readonly PackageRegistry _registry = Default.PackageFinder;
public WorkspaceServerRegistryTests(ITestOutputHelper output)
{
- disposables.Add(output.SubscribeToPocketLogger());
- disposables.Add(VirtualClock.Start());
+ _disposables.Add(output.SubscribeToPocketLogger());
+ _disposables.Add(VirtualClock.Start());
}
- public void Dispose() => disposables.Dispose();
+ public void Dispose() => _disposables.Dispose();
- [Fact]
+ [Fact(Skip = "this api is deprecated, to be replaced")]
public async Task Workspaces_can_be_registered_to_be_created_using_dotnet_new()
{
- var packageName = Package.CreateDirectory(nameof(Workspaces_can_be_registered_to_be_created_using_dotnet_new)).Name;
+ var packageName = PackageUtilities.CreateDirectory(nameof(Workspaces_can_be_registered_to_be_created_using_dotnet_new)).Name;
- registry.Add(packageName,
+ _registry.Add(packageName,
options => options.CreateUsingDotnet("console"));
- var package = await registry.Get(packageName);
+ var package = await _registry.Get(packageName);
var workspace = await package.CreateRoslynWorkspaceForRunAsync(new TimeBudget(30.Seconds()));
@@ -47,51 +46,12 @@ public async Task Workspaces_can_be_registered_to_be_created_using_dotnet_new()
project.MetadataReferences.Count.Should().BeGreaterThan(0);
}
- [Fact]
- public async Task NuGet_packages_can_be_added_during_initialization()
- {
- var workspaceId = Package.CreateDirectory(nameof(NuGet_packages_can_be_added_during_initialization)).Name;
-
- registry.Add(workspaceId,
- options =>
- {
- options.CreateUsingDotnet("console");
- options.AddPackageReference("Twilio", "5.9.2");
- });
-
- var workspaceServer = new RoslynWorkspaceServer(registry);
-
- var workspace = Workspace.FromSource(
- @"
-using System;
-using Twilio.Clients;
-using Twilio.Rest.Api.V2010.Account;
-using Twilio.Types;
-
-namespace Twilio_try.dot.net_sample
-{
- class Program
- {
- static void Main()
- {
- var sendFromPhoneNumber = new PhoneNumber(""TWILIO_PHONE_NUMBER"");
- var sendToPhoneNumber = new PhoneNumber(""RECIPIENT_PHONE_NUMBER"");
- }
- }
-}",
- workspaceType: workspaceId);
-
- var result = await workspaceServer.Run(new WorkspaceRequest(workspace));
-
- result.Succeeded.Should().BeTrue(because: "compilation can't succeed unless the NuGet package has been restored.");
- }
-
[Fact]
public async Task GetWorkspace_will_check_workspaces_directory_if_requested_workspace_was_not_registered()
{
var unregisteredWorkspace = await Default.ConsoleWorkspace();
- var package = await registry.Get(unregisteredWorkspace.Name);
+ var package = await _registry.Get(unregisteredWorkspace.Name);
var workspace = await package.CreateRoslynWorkspaceForRunAsync(new TimeBudget(30.Seconds()));
@@ -102,7 +62,7 @@ public async Task GetWorkspace_will_check_workspaces_directory_if_requested_work
public async Task When_workspace_was_not_registered_then_GetWorkspaceServer_will_return_a_working_server()
{
var unregisteredWorkspace = await Default.ConsoleWorkspace();
- var server = new RoslynWorkspaceServer(registry);
+ var server = new RoslynWorkspaceServer(_registry);
var workspaceRequest = WorkspaceRequestFactory.CreateRequestFromDirectory(unregisteredWorkspace.Directory, unregisteredWorkspace.Name);
@@ -114,20 +74,20 @@ public async Task When_workspace_was_not_registered_then_GetWorkspaceServer_will
[Fact]
public async Task Workspace_can_be_registered_in_directory_other_than_the_default()
{
- var parentDirectory = Package.CreateDirectory(nameof(Workspace_can_be_registered_in_directory_other_than_the_default));
+ var parentDirectory = PackageUtilities.CreateDirectory(nameof(Workspace_can_be_registered_in_directory_other_than_the_default));
var workspaceName = "a";
var childDirectory = parentDirectory.CreateSubdirectory(workspaceName);
- registry.Add(
+ _registry.Add(
workspaceName,
builder =>
{
builder.Directory = childDirectory;
});
- var workspace = await registry.Get(workspaceName);
+ var workspace = await _registry.Get(workspaceName);
workspace.Directory.Should().Be(childDirectory);
}
diff --git a/WorkspaceServer.Tests/WorkspaceServerTests.cs b/WorkspaceServer.Tests/WorkspaceServerTests.cs
index 90964a75b..4906b6e1f 100644
--- a/WorkspaceServer.Tests/WorkspaceServerTests.cs
+++ b/WorkspaceServer.Tests/WorkspaceServerTests.cs
@@ -12,7 +12,6 @@
using Xunit.Abstractions;
using static Pocket.Logger;
using DiagnosticSeverity = Microsoft.DotNet.Try.Protocol.DiagnosticSeverity;
-using Package = WorkspaceServer.Packaging.Package;
using Workspace = Microsoft.DotNet.Try.Protocol.Workspace;
namespace WorkspaceServer.Tests
@@ -20,21 +19,19 @@ namespace WorkspaceServer.Tests
public abstract class WorkspaceServerTests : WorkspaceServerTestsCore
{
protected abstract Workspace CreateWorkspaceWithMainContaining(
- string text,
- Package package);
+ string text);
[Fact]
public async Task Diagnostic_logs_do_not_show_up_in_captured_console_output()
{
using (LogEvents.Subscribe(e => Console.WriteLine(e.ToLogString())))
{
- var (server, build) = await GetRunnerAndWorkspaceBuild();
+ var server = GetCodeRunner();
var result = await server.Run(
new WorkspaceRequest(
CreateWorkspaceWithMainContaining(
- "Console.WriteLine(\"hi!\");",
- build))
+ "Console.WriteLine(\"hi!\");"))
);
result.Output
@@ -52,7 +49,7 @@ protected WorkspaceServerTests(ITestOutputHelper output) : base(output)
[Fact]
public async Task Response_indicates_when_compile_is_successful_and_signature_is_like_a_console_app()
{
- var (server, build) = await GetRunnerAndWorkspaceBuild();
+ var server = GetCodeRunner();
var workspace = Workspace.FromSource(@"
using System;
@@ -63,7 +60,7 @@ public static void Main()
{
}
}
-", workspaceType: build.Name);
+", workspaceType: "console");
var result = await server.Run(new WorkspaceRequest(workspace));
@@ -76,8 +73,8 @@ public static void Main()
public async Task Response_shows_program_output_when_compile_is_successful_and_signature_is_like_a_console_app()
{
var output = nameof(Response_shows_program_output_when_compile_is_successful_and_signature_is_like_a_console_app);
-
- var (server, build) = await GetRunnerAndWorkspaceBuild();
+
+ var server = GetCodeRunner();
var workspace = Workspace.FromSource($@"
using System;
@@ -88,7 +85,7 @@ public static void Main()
{{
Console.WriteLine(""{output}"");
}}
-}}", workspaceType: build.Name);
+}}", workspaceType: "console");
var result = await server.Run(new WorkspaceRequest(workspace));
@@ -99,12 +96,12 @@ public static void Main()
[Fact]
public async Task Response_shows_program_output_when_compile_is_successful_and_signature_is_a_fragment_containing_console_output()
{
- var (server, build) = await GetRunnerAndWorkspaceBuild();
+ var server = GetCodeRunner();
var request = CreateWorkspaceWithMainContaining(@"
var person = new { Name = ""Jeff"", Age = 20 };
var s = $""{person.Name} is {person.Age} year(s) old"";
-Console.Write(s);", build);
+Console.Write(s);");
var result = await server.Run(new WorkspaceRequest(request));
@@ -115,10 +112,10 @@ public async Task Response_shows_program_output_when_compile_is_successful_and_s
[Fact]
public async Task When_compile_is_unsuccessful_then_no_exceptions_are_shown()
{
- var (server, build) = await GetRunnerAndWorkspaceBuild();
+ var server = GetCodeRunner();
var request = CreateWorkspaceWithMainContaining(@"
-Console.WriteLine(banana);", build);
+Console.WriteLine(banana);");
var result = await server.Run(new WorkspaceRequest(request));
result.Succeeded.Should().BeFalse();
@@ -128,10 +125,10 @@ public async Task When_compile_is_unsuccessful_then_no_exceptions_are_shown()
[Fact]
public async Task When_compile_is_unsuccessful_then_diagnostics_are_displayed_in_output()
{
- var (server, build) = await GetRunnerAndWorkspaceBuild();
+ var server = GetCodeRunner();
var request = CreateWorkspaceWithMainContaining(@"
-Console.WriteLine(banana);", build);
+Console.WriteLine(banana);");
var result = await server.Run(new WorkspaceRequest(request));
result.Succeeded.Should().BeFalse();
@@ -143,13 +140,13 @@ public async Task When_compile_is_unsuccessful_then_diagnostics_are_displayed_in
[Fact]
public async Task Multi_line_console_output_is_captured_correctly_a()
{
- var (server, build) = await GetRunnerAndWorkspaceBuild();
+ var server = GetCodeRunner();
var request = CreateWorkspaceWithMainContaining(@"
Console.WriteLine(1);
Console.WriteLine(2);
Console.WriteLine(3);
-Console.WriteLine(4);", build);
+Console.WriteLine(4);");
var result = await server.Run(new WorkspaceRequest(request));
@@ -160,13 +157,13 @@ public async Task Multi_line_console_output_is_captured_correctly_a()
[Fact]
public async Task Multi_line_console_output_is_captured_correctly()
{
- var (server, build) = await GetRunnerAndWorkspaceBuild();
+ var server = GetCodeRunner();
var request = CreateWorkspaceWithMainContaining(@"
Console.WriteLine(1);
Console.WriteLine(2);
Console.WriteLine(3);
-Console.WriteLine(4);", build);
+Console.WriteLine(4);");
var result = await server.Run(new WorkspaceRequest(request));
@@ -178,14 +175,14 @@ public async Task Multi_line_console_output_is_captured_correctly()
public async Task Whitespace_is_preserved_in_multi_line_output()
{
- var (server, build) = await GetRunnerAndWorkspaceBuild();
-
+ var server = GetCodeRunner();
+
var request = CreateWorkspaceWithMainContaining(@"
Console.WriteLine();
Console.WriteLine(1);
Console.WriteLine();
Console.WriteLine();
-Console.WriteLine(2);", build);
+Console.WriteLine(2);");
var result = await server.Run(new WorkspaceRequest(request));
@@ -195,14 +192,14 @@ public async Task Whitespace_is_preserved_in_multi_line_output()
[Fact]
public async Task Multi_line_console_output_is_captured_correctly_when_an_exception_is_thrown()
{
- var (server, build) = await GetRunnerAndWorkspaceBuild();
+ var server = GetCodeRunner();
var request = CreateWorkspaceWithMainContaining(@"
Console.WriteLine(1);
Console.WriteLine(2);
throw new Exception(""oops!"");
Console.WriteLine(3);
-Console.WriteLine(4);", build);
+Console.WriteLine(4);");
var timeBudget = new TimeBudget(10.Minutes());
@@ -217,9 +214,9 @@ public async Task Multi_line_console_output_is_captured_correctly_when_an_except
[Fact]
public async Task When_the_users_code_throws_on_first_line_then_it_is_returned_as_an_exception_property()
{
- var (server, build) = await GetRunnerAndWorkspaceBuild();
+ var server = GetCodeRunner();
- var request = CreateWorkspaceWithMainContaining(@"throw new Exception(""oops!"");", build);
+ var request = CreateWorkspaceWithMainContaining(@"throw new Exception(""oops!"");");
var result = await server.Run(new WorkspaceRequest(request));
@@ -230,10 +227,10 @@ public async Task When_the_users_code_throws_on_first_line_then_it_is_returned_a
[Fact]
public async Task When_the_users_code_throws_on_subsequent_line_then_it_is_returned_as_an_exception_property()
{
- var (server, build) = await GetRunnerAndWorkspaceBuild();
+ var server = GetCodeRunner();
var request = CreateWorkspaceWithMainContaining(@"
-throw new Exception(""oops!"");", build);
+throw new Exception(""oops!"");");
var result = await server.Run(new WorkspaceRequest(request));
@@ -244,7 +241,7 @@ public async Task When_the_users_code_throws_on_subsequent_line_then_it_is_retur
[Fact]
public async Task When_a_public_void_Main_with_no_parameters_is_present_it_is_invoked()
{
- var (server, build) = await GetRunnerAndWorkspaceBuild();
+ var server = GetCodeRunner();
var workspace = Workspace.FromSource(@"
using System;
@@ -255,7 +252,7 @@ public static void Main()
{
Console.WriteLine(""Hello there!"");
}
-}", workspaceType: build.Name);
+}", workspaceType: "console");
var result = await server.Run(new WorkspaceRequest(workspace));
@@ -265,8 +262,8 @@ public static void Main()
[Fact]
public async Task When_a_public_void_Main_with_parameters_is_present_it_is_invoked()
{
- var (server, build) = await GetRunnerAndWorkspaceBuild();
-
+ var server = GetCodeRunner();
+
var workspace = Workspace.FromSource(@"
using System;
@@ -276,7 +273,7 @@ public static void Main(params string[] args)
{
Console.WriteLine(""Hello there!"");
}
-}", workspaceType: build.Name);
+}", workspaceType: "console");
var result = await server.Run(new WorkspaceRequest(workspace));
@@ -286,7 +283,7 @@ public static void Main(params string[] args)
[Fact]
public async Task When_an_internal_void_Main_with_no_parameters_is_present_it_is_invoked()
{
- var (server, build) = await GetRunnerAndWorkspaceBuild();
+ var server = GetCodeRunner();
var workspace = Workspace.FromSource(@"
using System;
@@ -297,7 +294,7 @@ static void Main()
{
Console.WriteLine(""Hello there!"");
}
-}", workspaceType: build.Name);
+}", workspaceType:"console");
var result = await server.Run(new WorkspaceRequest(workspace));
@@ -309,7 +306,7 @@ static void Main()
[Fact]
public async Task When_an_internal_void_Main_with_parameters_is_present_it_is_invoked()
{
- var (server, build) = await GetRunnerAndWorkspaceBuild();
+ var server = GetCodeRunner();
var workspace = Workspace.FromSource(@"
using System;
@@ -320,7 +317,7 @@ static void Main(string[] args)
{
Console.WriteLine(""Hello there!"");
}
-}", workspaceType: build.Name);
+}", workspaceType: "console");
var result = await server.Run(new WorkspaceRequest(workspace));
@@ -334,7 +331,7 @@ public async Task Response_shows_warnings_with_successful_compilation()
{
var output = nameof(Response_shows_warnings_with_successful_compilation);
- var (server, build) = await GetRunnerAndWorkspaceBuild();
+ var server = GetCodeRunner();
var workspace = CreateWorkspaceWithMainContaining($@"
using System;
@@ -347,7 +344,7 @@ public static void Main()
var a = 0;
Console.WriteLine(""{output}"");
}}
-}}", build);
+}}");
var result = await server.Run(new WorkspaceRequest(workspace));
@@ -361,7 +358,7 @@ public async Task Response_shows_warnings_when_compilation_fails()
{
var output = nameof(Response_shows_warnings_when_compilation_fails);
- var (server, build) = await GetRunnerAndWorkspaceBuild();
+ var server = GetCodeRunner();
var workspace = CreateWorkspaceWithMainContaining($@"
using System;
@@ -373,7 +370,7 @@ public static void Main()
var a = 0;
Console.WriteLine(""{output}"")
}}
-}}", build);
+}}");
var result = await server.Run(new WorkspaceRequest(workspace));
diff --git a/WorkspaceServer.Tests/WorkspaceServerTestsCore.cs b/WorkspaceServer.Tests/WorkspaceServerTestsCore.cs
index 3eda61362..abfb493e3 100644
--- a/WorkspaceServer.Tests/WorkspaceServerTestsCore.cs
+++ b/WorkspaceServer.Tests/WorkspaceServerTestsCore.cs
@@ -2,11 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
-using System.Linq;
-using System.Runtime.CompilerServices;
-using System.Threading.Tasks;
using Pocket;
-using WorkspaceServer.Packaging;
using Xunit.Abstractions;
namespace WorkspaceServer.Tests
@@ -21,11 +17,11 @@ protected WorkspaceServerTestsCore(ITestOutputHelper output)
}
public void Dispose() => _disposables.Dispose();
-
- protected abstract Task<(ICodeRunner runner, Package workspace)> GetRunnerAndWorkspaceBuild(
- [CallerMemberName] string testName = null);
- protected abstract ILanguageService GetLanguageService(
- [CallerMemberName] string testName = null);
+ protected abstract ILanguageService GetLanguageService();
+
+ protected abstract ICodeCompiler GetCodeCompiler();
+
+ protected abstract ICodeRunner GetCodeRunner();
}
}
\ No newline at end of file
diff --git a/WorkspaceServer/FileSystemDirectoryAccessor.cs b/WorkspaceServer/FileSystemDirectoryAccessor.cs
index 48e2d01a9..f589c7fd2 100644
--- a/WorkspaceServer/FileSystemDirectoryAccessor.cs
+++ b/WorkspaceServer/FileSystemDirectoryAccessor.cs
@@ -5,7 +5,9 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Threading.Tasks;
using Microsoft.DotNet.Try.Markdown;
+using WorkspaceServer.Packaging;
using WorkspaceServer.Servers.Roslyn;
namespace WorkspaceServer
@@ -75,6 +77,12 @@ public IDirectoryAccessor GetDirectoryAccessorForRelativePath(RelativeDirectoryP
return new FileSystemDirectoryAccessor(new DirectoryInfo(absolutePath));
}
+ public Task TryLockAsync()
+ {
+ return FileLock.TryCreateAsync(this);
+ }
+
+
public IEnumerable GetAllDirectoriesRecursively()
{
var directories = _rootDirectory.GetDirectories("*", SearchOption.AllDirectories);
@@ -91,6 +99,14 @@ public IEnumerable GetAllFilesRecursively()
new RelativeFilePath(PathUtilities.GetRelativePath(_rootDirectory.FullName, f.FullName)));
}
+ public IEnumerable GetAllFiles()
+ {
+ var files = _rootDirectory.GetFiles("*", SearchOption.TopDirectoryOnly);
+
+ return files.Select(f =>
+ new RelativeFilePath(PathUtilities.GetRelativePath(_rootDirectory.FullName, f.FullName)));
+ }
+
public override string ToString() => _rootDirectory.FullName;
}
}
diff --git a/WorkspaceServer/IDirectoryAccessor.cs b/WorkspaceServer/IDirectoryAccessor.cs
index 4262f3e8b..cfbf76372 100644
--- a/WorkspaceServer/IDirectoryAccessor.cs
+++ b/WorkspaceServer/IDirectoryAccessor.cs
@@ -1,8 +1,10 @@
// 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.Threading.Tasks;
using Microsoft.DotNet.Try.Markdown;
namespace WorkspaceServer
@@ -21,10 +23,14 @@ public interface IDirectoryAccessor
IEnumerable GetAllFilesRecursively();
+ IEnumerable GetAllFiles();
+
IEnumerable GetAllDirectoriesRecursively();
FileSystemInfo GetFullyQualifiedPath(RelativePath path);
IDirectoryAccessor GetDirectoryAccessorForRelativePath(RelativeDirectoryPath path);
+
+ Task TryLockAsync();
}
}
\ No newline at end of file
diff --git a/WorkspaceServer/PackageRegistry.cs b/WorkspaceServer/PackageRegistry.cs
index 6fa0d0717..2ede19a40 100644
--- a/WorkspaceServer/PackageRegistry.cs
+++ b/WorkspaceServer/PackageRegistry.cs
@@ -9,12 +9,11 @@
using System.Linq;
using System.Threading.Tasks;
using Clockwise;
-using Microsoft.CodeAnalysis.Operations;
using WorkspaceServer.Packaging;
namespace WorkspaceServer
{
- public class PackageRegistry :
+ public class PackageRegistry :
IPackageFinder,
IEnumerable>
{
@@ -54,7 +53,7 @@ private PackageRegistry(
_strategies.Add(strategy);
}
-
+
_packageFinders = packageFinders?.ToList() ?? GetDefaultPackageFinders().ToList();
}
@@ -88,9 +87,9 @@ public async Task Get(string packageName, Budget budget = null)
// FIX: (Get) move this into the cache
var package = await GetPackage2(descriptor);
- if (package == null || !(package is T))
+ if (!(package is T))
{
- package = await GetPackageFromPackageBuilder(packageName, budget, descriptor);
+ package = await GetPackageFromPackageBuilder(packageName, budget, descriptor);
}
return (T)package;
@@ -101,9 +100,21 @@ private Task GetPackage2(PackageDescriptor descriptor)
{
return _packages2.GetOrAdd(descriptor, async descriptor2 =>
{
- foreach (var packgeFinder in _packageFinders)
+ foreach (var packageFinder in _packageFinders)
{
- if (await packgeFinder.Find(descriptor) is T pkg)
+ var package = await packageFinder.Find(descriptor);
+ if (package != null)
+ {
+ if (package is Package2 package2)
+ {
+ var packageAsset = package2.Assets.OfType().FirstOrDefault();
+ if (packageAsset != null)
+ {
+ return packageAsset;
+ }
+ }
+ }
+ if (package is T pkg)
{
return pkg;
}
@@ -113,8 +124,7 @@ private Task GetPackage2(PackageDescriptor descriptor)
});
}
- private Task GetPackageFromPackageBuilder(string packageName, Budget budget, PackageDescriptor descriptor)
- where T : IPackage
+ private Task GetPackageFromPackageBuilder(string packageName, Budget budget, PackageDescriptor descriptor)
{
return _packages.GetOrAdd(packageName, async name =>
{
@@ -143,7 +153,7 @@ public static PackageRegistry CreateForTryMode(DirectoryInfo project, PackageSou
{
var finders = GetDefaultPackageFinders().Append(new WebAssemblyAssetFinder(Package.DefaultPackagesDirectory, addSource));
var registry = new PackageRegistry(
- true,
+ true,
addSource,
finders,
additionalStrategies: new LocalToolInstallingPackageDiscoveryStrategy(Package.DefaultPackagesDirectory, addSource));
@@ -214,7 +224,7 @@ public static PackageRegistry CreateForHostedMode()
packageBuilder.AddPackageReference("Newtonsoft.Json");
packageBuilder.EnableBlazor(registry);
});
-
+
registry.Add("blazor-ms.logging",
packageBuilder =>
{
diff --git a/WorkspaceServer/Packaging/FileLock.cs b/WorkspaceServer/Packaging/FileLock.cs
new file mode 100644
index 000000000..10294295f
--- /dev/null
+++ b/WorkspaceServer/Packaging/FileLock.cs
@@ -0,0 +1,48 @@
+// 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.Threading.Tasks;
+
+namespace WorkspaceServer.Packaging
+{
+ public class FileLock
+ {
+ public static async Task TryCreateAsync(FileInfo lockFile)
+ {
+ if (lockFile == null)
+ {
+ throw new ArgumentNullException(nameof(lockFile));
+ }
+
+ const int waitAmount = 100;
+ var attemptCount = 1;
+ do
+ {
+ await Task.Delay(waitAmount * attemptCount);
+ attemptCount++;
+
+ try
+ {
+ return File.Create(lockFile.FullName, 1, FileOptions.DeleteOnClose);
+ }
+ catch (IOException)
+ {
+
+ }
+ } while (attemptCount <= 10);
+
+ throw new IOException($"Cannot acquire file lock {lockFile.FullName}");
+ }
+
+ public static Task TryCreateAsync(IDirectoryAccessor directoryAccessor)
+ {
+ if (directoryAccessor == null)
+ {
+ throw new ArgumentNullException(nameof(directoryAccessor));
+ }
+ return TryCreateAsync(directoryAccessor.GetFullyQualifiedFilePath(".trydotnet-lock"));
+ }
+ }
+}
\ No newline at end of file
diff --git a/WorkspaceServer/Packaging/IPackage.cs b/WorkspaceServer/Packaging/IPackage.cs
index aef995a0a..e788ef398 100644
--- a/WorkspaceServer/Packaging/IPackage.cs
+++ b/WorkspaceServer/Packaging/IPackage.cs
@@ -1,7 +1,6 @@
// 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.Threading.Tasks;
using Clockwise;
@@ -29,12 +28,17 @@ public interface IMightSupportBlazor : IPackage
bool CanSupportBlazor { get; }
}
- public interface ICreateWorkspaceForRun : IPackage
+ public interface ICreateWorkspace : IPackage
+ {
+ Task CreateRoslynWorkspaceAsync(Budget budget);
+ }
+
+ public interface ICreateWorkspaceForRun : IPackage, ICreateWorkspace
{
Task CreateRoslynWorkspaceForRunAsync(Budget budget);
}
- public interface ICreateWorkspaceForLanguageServices : IPackage
+ public interface ICreateWorkspaceForLanguageServices : IPackage, ICreateWorkspace
{
Task CreateRoslynWorkspaceForLanguageServicesAsync(Budget budget);
}
diff --git a/WorkspaceServer/Packaging/Package.cs b/WorkspaceServer/Packaging/Package.cs
index 9117b0a12..c96dfb0e6 100644
--- a/WorkspaceServer/Packaging/Package.cs
+++ b/WorkspaceServer/Packaging/Package.cs
@@ -35,7 +35,7 @@ public abstract class Package :
ICreateWorkspaceForRun
{
internal const string DesignTimeBuildBinlogFileName = "package_designTimeBuild.binlog";
- private static readonly object _createDirectoryLock = new object();
+
private static readonly ConcurrentDictionary _packageBuildSemaphores = new ConcurrentDictionary();
private static readonly ConcurrentDictionary _packagePublishSemaphores = new ConcurrentDictionary();
@@ -515,91 +515,16 @@ protected async Task Publish()
}
}
- public static async Task Copy(
- Package fromPackage,
- string folderNameStartsWith = null,
- bool isRebuildable = false,
- IScheduler buildThrottleScheduler = null,
- DirectoryInfo parentDirectory = null)
- {
- if (fromPackage == null)
- {
- throw new ArgumentNullException(nameof(fromPackage));
- }
-
- await fromPackage.EnsureReady(new Budget());
-
- folderNameStartsWith = folderNameStartsWith ?? fromPackage.Name;
- parentDirectory = parentDirectory ?? fromPackage.Directory.Parent;
-
- var destination =
- CreateDirectory(folderNameStartsWith,
- parentDirectory);
-
- fromPackage.Directory.CopyTo(destination);
-
- var binLogs = destination.GetFiles("*.binlog");
-
- foreach (var fileInfo in binLogs)
- {
- fileInfo.Delete();
- }
-
- Package copy;
- if (isRebuildable)
- {
- copy = new RebuildablePackage(directory: destination, name: destination.Name, buildThrottleScheduler: buildThrottleScheduler)
- {
- IsDirectoryCreated = true
- };
- }
- else
- {
- copy = new NonrebuildablePackage(directory: destination, name: destination.Name, buildThrottleScheduler: buildThrottleScheduler)
- {
- IsDirectoryCreated = true
- };
- }
-
- Log.Info(
- "Copied workspace {from} to {to}",
- fromPackage,
- copy);
+
- return copy;
- }
-
- public static DirectoryInfo CreateDirectory(
- string folderNameStartsWith,
- DirectoryInfo parentDirectory = null)
+ public override string ToString()
{
- if (string.IsNullOrWhiteSpace(folderNameStartsWith))
- {
- throw new ArgumentException("Value cannot be null or whitespace.", nameof(folderNameStartsWith));
- }
-
- parentDirectory = parentDirectory ?? DefaultPackagesDirectory;
-
- DirectoryInfo created;
-
- lock (_createDirectoryLock)
- {
- if (!parentDirectory.Exists)
- {
- parentDirectory.Create();
- }
-
- var existingFolders = parentDirectory.GetDirectories($"{folderNameStartsWith}.*");
-
- created = parentDirectory.CreateSubdirectory($"{folderNameStartsWith}.{existingFolders.Length + 1}");
- }
-
- return created;
+ return $"{Name} ({Directory.FullName})";
}
- public override string ToString()
+ public Task CreateRoslynWorkspaceAsync(Budget budget)
{
- return $"{Name} ({Directory.FullName})";
+ return CreateRoslynWorkspaceForRunAsync(budget);
}
protected SyntaxTree CreateInstrumentationEmitterSyntaxTree()
diff --git a/WorkspaceServer/Packaging/Package2.cs b/WorkspaceServer/Packaging/Package2.cs
index ca21909da..742a1f984 100644
--- a/WorkspaceServer/Packaging/Package2.cs
+++ b/WorkspaceServer/Packaging/Package2.cs
@@ -9,8 +9,7 @@
namespace WorkspaceServer.Packaging
{
- public class Package2 :
- IPackage,
+ public class Package2 :
IHaveADirectory,
IHaveADirectoryAccessor,
IMightSupportBlazor
diff --git a/WorkspaceServer/Packaging/PackageAsset.cs b/WorkspaceServer/Packaging/PackageAsset.cs
index 530548d43..9b29afbd9 100644
--- a/WorkspaceServer/Packaging/PackageAsset.cs
+++ b/WorkspaceServer/Packaging/PackageAsset.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
-using System.IO;
namespace WorkspaceServer.Packaging
{
@@ -16,14 +15,6 @@ protected PackageAsset(IDirectoryAccessor directoryAccessor)
public IDirectoryAccessor DirectoryAccessor { get; }
}
- public class ProjectAsset : PackageAsset
- {
-
- public ProjectAsset(IDirectoryAccessor directoryAccessor) : base(directoryAccessor)
- {
- }
- }
-
public class WebAssemblyAsset : PackageAsset
{
public WebAssemblyAsset(IDirectoryAccessor directoryAccessor) : base(directoryAccessor)
diff --git a/WorkspaceServer/Packaging/PackageBase.cs b/WorkspaceServer/Packaging/PackageBase.cs
index 14580a1d3..2f8379a00 100644
--- a/WorkspaceServer/Packaging/PackageBase.cs
+++ b/WorkspaceServer/Packaging/PackageBase.cs
@@ -4,7 +4,6 @@
using System;
using System.IO;
using Clockwise;
-using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using MLS.Agent.Tools;
@@ -15,10 +14,9 @@
namespace WorkspaceServer.Packaging
{
public abstract class PackageBase :
- IPackage,
IHaveADirectory,
- IMightSupportBlazor
- , IHaveADirectoryAccessor
+ IMightSupportBlazor,
+ IHaveADirectoryAccessor
{
IDirectoryAccessor IHaveADirectoryAccessor.Directory => new FileSystemDirectoryAccessor(Directory);
@@ -46,8 +44,7 @@ protected PackageBase(
public string Name { get; }
public DirectoryInfo Directory { get; set; }
-
- protected bool IsDirectoryCreated { get; set; }
+
protected Task EnsureCreated() => _lazyCreation.ValueAsync();
diff --git a/WorkspaceServer/Packaging/PackageBuilder.cs b/WorkspaceServer/Packaging/PackageBuilder.cs
index 1889d033c..1d7eebc49 100644
--- a/WorkspaceServer/Packaging/PackageBuilder.cs
+++ b/WorkspaceServer/Packaging/PackageBuilder.cs
@@ -33,7 +33,7 @@ public PackageBuilder(string packageName, IPackageInitializer packageInitializer
public DirectoryInfo Directory { get; set; }
- public bool CreateRebuildablePackage { get; internal set; }
+ public bool CreateRebuildablePackage { get; set; }
public bool BlazorSupported { get; private set; }
diff --git a/WorkspaceServer/Packaging/PipelineStep.cs b/WorkspaceServer/Packaging/PipelineStep.cs
new file mode 100644
index 000000000..f954bd2ee
--- /dev/null
+++ b/WorkspaceServer/Packaging/PipelineStep.cs
@@ -0,0 +1,124 @@
+// 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.Threading.Tasks;
+
+namespace WorkspaceServer.Packaging
+{
+ public interface IPipelineStep
+ {
+ void Invalidate();
+ }
+
+ public class PipelineStep : IPipelineStep
+ {
+ private readonly Func> _createValue;
+ private TaskCompletionSource _latestValue = new TaskCompletionSource();
+ private Task _inFlight;
+ private readonly object _lock = new object();
+ private bool _invalidated = true;
+ private Guid _operationId;
+ private IPipelineStep _nextStep;
+
+ public PipelineStep(Func> createValue)
+ {
+ if (createValue == null) throw new ArgumentNullException(nameof(createValue));
+
+ _createValue = async () =>
+ {
+ await Task.Yield();
+ return await createValue();
+ };
+ }
+
+ public PipelineStep Then(Func> nextStep)
+ {
+ var newStep= new PipelineStep(async () =>
+ {
+ var previousStepValue = await GetLatestAsync();
+ return await nextStep(previousStepValue);
+ });
+
+ _nextStep = newStep;
+ return newStep;
+ }
+
+
+
+ public Task GetLatestAsync()
+ {
+ lock (_lock)
+ {
+ if (_invalidated)
+ {
+ if (_inFlight == null)
+ {
+ _latestValue = new TaskCompletionSource();
+ _inFlight = _createValue();
+ }
+ else
+ {
+ _inFlight = _inFlight
+ .ContinueWith(t => _createValue().Result);
+ }
+ var newId = Guid.NewGuid();
+ _operationId = newId;
+ _inFlight.ContinueWith(t =>
+ {
+ lock (_lock)
+ {
+ switch (t.Status)
+ {
+ case TaskStatus.Faulted:
+
+ if (_operationId == newId)
+ {
+ _latestValue.SetException(t.Exception.InnerException);
+ }
+
+ _inFlight = null;
+ _invalidated = true;
+
+ break;
+ case TaskStatus.RanToCompletion:
+
+ if (_operationId == newId)
+ {
+ _latestValue.SetResult(t.Result);
+ }
+
+ _inFlight = null;
+ _invalidated = false;
+
+ break;
+ }
+ }
+ });
+ _invalidated = false;
+ return _latestValue.Task;
+
+ }
+ else
+ {
+ return _latestValue.Task;
+ }
+ }
+
+ }
+
+ public void Invalidate()
+ {
+ lock (_lock)
+ {
+ if (!_invalidated)
+ {
+ _invalidated = true;
+
+ }
+ }
+
+ _nextStep?.Invalidate();
+ }
+ }
+}
\ No newline at end of file
diff --git a/WorkspaceServer/Packaging/ProjectAsset.cs b/WorkspaceServer/Packaging/ProjectAsset.cs
new file mode 100644
index 000000000..aeafaae87
--- /dev/null
+++ b/WorkspaceServer/Packaging/ProjectAsset.cs
@@ -0,0 +1,226 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Buildalyzer;
+using Clockwise;
+using Microsoft.CodeAnalysis;
+using MLS.Agent.Tools;
+using Pocket;
+using WorkspaceServer.Servers.Roslyn;
+using static Pocket.Logger;
+
+namespace WorkspaceServer.Packaging
+{
+ public class ProjectAsset : PackageAsset,
+ ICreateWorkspaceForLanguageServices,
+ ICreateWorkspaceForRun,
+ IHaveADirectory
+ {
+ private const string FullBuildBinlogFileName = "package_fullBuild.binlog";
+ private readonly FileInfo _projectFile;
+ private readonly FileInfo _lastBuildErrorLogFile;
+ private readonly PipelineStep _projectBuildStep;
+ private readonly PipelineStep _workspaceStep;
+ private readonly PipelineStep _cleanupStep;
+
+ public string Name { get; }
+
+ public DirectoryInfo Directory { get; }
+
+ public ProjectAsset(IDirectoryAccessor directoryAccessor, string csprojFileName = null) : base(directoryAccessor)
+ {
+ if (directoryAccessor == null)
+ {
+ throw new ArgumentNullException(nameof(directoryAccessor));
+ }
+
+ if (string.IsNullOrWhiteSpace(csprojFileName))
+ {
+ var firstProject = DirectoryAccessor.GetAllFiles().Single(f => f.Extension == ".csproj");
+ _projectFile = DirectoryAccessor.GetFullyQualifiedFilePath(firstProject.FileName);
+ }
+ else
+ {
+ _projectFile = DirectoryAccessor.GetFullyQualifiedFilePath(csprojFileName);
+ }
+
+ Directory = DirectoryAccessor.GetFullyQualifiedRoot();
+ Name = _projectFile?.Name ?? Directory?.Name;
+ _lastBuildErrorLogFile = directoryAccessor.GetFullyQualifiedFilePath(".trydotnet-builderror");
+ _cleanupStep = new PipelineStep(LoadResultOrCleanAsync);
+ _projectBuildStep = _cleanupStep.Then(BuildProjectAsync);
+ _workspaceStep = _projectBuildStep.Then(BuildWorkspaceAsync);
+ }
+
+ private async Task LoadResultOrCleanAsync()
+ {
+ using (await DirectoryAccessor.TryLockAsync())
+ {
+ var binLog = this.FindLatestBinLog();
+ if (binLog != null)
+ {
+ var results = await TryLoadAnalyzerResultsAsync(binLog);
+ var result = results?.FirstOrDefault(p => p.ProjectFilePath == _projectFile.FullName);
+
+ var didCompile = DidPerformCoreCompile(result);
+ if (result != null)
+ {
+ if (result.Succeeded && didCompile)
+ {
+ return result;
+ }
+ }
+ }
+
+ binLog?.DoWhenFileAvailable(() => binLog.Delete());
+ var toClean = Directory.GetDirectories("obj");
+ foreach (var directoryInfo in toClean)
+ {
+ directoryInfo.Delete(true);
+ }
+
+ return null;
+ }
+ }
+
+ private bool DidPerformCoreCompile(AnalyzerResult result)
+ {
+ if (result == null)
+ {
+ return false;
+ }
+
+ var sourceCount = result.SourceFiles?.Length ?? 0;
+ var compilerInputs = result.GetCompileInputs()?.Length ?? 0;
+
+ return compilerInputs > 0 && sourceCount > 0;
+ }
+
+ private Task BuildWorkspaceAsync(AnalyzerResult result)
+ {
+ if (result.TryGetWorkspace(out var ws))
+ {
+ var projectId = ws.CurrentSolution.ProjectIds.FirstOrDefault();
+ var references = result.References;
+ var metadataReferences = references.GetMetadataReferences();
+ var solution = ws.CurrentSolution;
+ solution = solution.WithProjectMetadataReferences(projectId, metadataReferences);
+ ws.TryApplyChanges(solution);
+ return Task.FromResult(ws);
+ }
+ throw new InvalidOperationException("Failed creating workspace");
+ }
+
+ private async Task BuildProjectAsync(AnalyzerResult result)
+ {
+ if (result != null)
+ {
+ return result;
+ }
+
+ using (await DirectoryAccessor.TryLockAsync())
+ {
+
+ using (var operation = Log.OnEnterAndConfirmOnExit())
+ {
+ try
+ {
+ operation.Info("Attempting building package {name}", Name);
+ await DotnetBuild();
+ operation.Info("Workspace built");
+ operation.Succeed();
+ }
+ catch (Exception exception)
+ {
+ operation.Error("Exception building workspace", exception);
+ throw;
+ }
+
+ var binLog = this.FindLatestBinLog();
+
+ if (binLog == null)
+ {
+ throw new InvalidOperationException("Failed to build");
+ }
+
+ var results = await TryLoadAnalyzerResultsAsync(binLog);
+
+ if (results?.Count == 0)
+ {
+ throw new InvalidOperationException("The build log seems to contain no solutions or projects");
+ }
+
+ result = results?.FirstOrDefault(p => p.ProjectFilePath == _projectFile.FullName);
+
+ if (result?.Succeeded == true)
+ {
+ return result;
+ }
+
+ throw new InvalidOperationException("Failed to build");
+ }
+ }
+ }
+
+ private async Task TryLoadAnalyzerResultsAsync(FileInfo binLog)
+ {
+ AnalyzerResults results = null;
+ await binLog.DoWhenFileAvailable(() =>
+ {
+ var manager = new AnalyzerManager();
+ results = manager.Analyze(binLog.FullName);
+ });
+ return results;
+ }
+
+ public Task CreateRoslynWorkspaceAsync(Budget budget)
+ {
+ return _workspaceStep.GetLatestAsync();
+ }
+
+ public Task CreateRoslynWorkspaceForRunAsync(Budget budget)
+ {
+ return CreateRoslynWorkspaceAsync(budget);
+ }
+
+ public Task CreateRoslynWorkspaceForLanguageServicesAsync(Budget budget)
+ {
+ return CreateRoslynWorkspaceAsync(budget);
+ }
+
+
+ protected async Task DotnetBuild()
+ {
+ using (var operation = Log.OnEnterAndConfirmOnExit())
+ {
+ var args = $"/bl:{FullBuildBinlogFileName}";
+ if (_projectFile?.Exists == true)
+ {
+ args = $@"""{_projectFile.FullName}"" {args}";
+ }
+
+ operation.Info("Building package {name} in {directory}", Name, Directory);
+
+ var result = await new Dotnet(Directory).Build(args: args);
+
+ if (result.ExitCode != 0)
+ {
+ File.WriteAllText(
+ _lastBuildErrorLogFile.FullName,
+ string.Join(Environment.NewLine, result.Error));
+ }
+ else if (_lastBuildErrorLogFile.Exists)
+ {
+ _lastBuildErrorLogFile.Delete();
+ }
+
+ result.ThrowOnFailure();
+ operation.Succeed();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/WorkspaceServer/Packaging/ProjectAssetLoader.cs b/WorkspaceServer/Packaging/ProjectAssetLoader.cs
index 6e464dda5..6ec247907 100644
--- a/WorkspaceServer/Packaging/ProjectAssetLoader.cs
+++ b/WorkspaceServer/Packaging/ProjectAssetLoader.cs
@@ -18,7 +18,7 @@ public Task> LoadAsync(Package2 package)
foreach (var csproj in directory.GetAllFilesRecursively()
.Where(f => f.Extension == ".csproj"))
{
- assets.Add(new ProjectAsset(directory.GetDirectoryAccessorForRelativePath(csproj.Directory)));
+ assets.Add(new ProjectAsset(directory.GetDirectoryAccessorForRelativePath(csproj.Directory), csproj.FileName));
}
return
diff --git a/WorkspaceServer/Servers/Roslyn/CompilationUtility.cs b/WorkspaceServer/Servers/Roslyn/CompilationUtility.cs
index bee272fdd..ce7fa62ee 100644
--- a/WorkspaceServer/Servers/Roslyn/CompilationUtility.cs
+++ b/WorkspaceServer/Servers/Roslyn/CompilationUtility.cs
@@ -1,6 +1,7 @@
// 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 Buildalyzer.Workspaces;
@@ -38,6 +39,11 @@ public static IEnumerable FindBinLogs(this IHaveADirectory package) =>
public static async Task WaitForFileAvailable(
this FileInfo file)
{
+ if (file == null)
+ {
+ throw new ArgumentNullException(nameof(file));
+ }
+
const int waitAmount = 100;
var attemptCount = 1;
while (file.Exists && attemptCount <= 10 && !IsAvailable())
@@ -62,6 +68,35 @@ bool IsAvailable()
}
}
+ public static async Task DoWhenFileAvailable(
+ this FileInfo file, Action action)
+ {
+ if (file == null)
+ {
+ throw new ArgumentNullException(nameof(file));
+ }
+
+ if (action == null)
+ {
+ throw new ArgumentNullException(nameof(action));
+ }
+
+ const int waitAmount = 100;
+ var attemptCount = 1;
+ while (file.Exists && attemptCount <= 10)
+ {
+ try
+ {
+ action();
+ break;
+ }
+ catch(IOException){
+ await Task.Delay(waitAmount * attemptCount);
+ attemptCount++;
+ }
+ }
+ }
+
public static FileInfo GetProjectFile(this IHaveADirectory packageBase) =>
packageBase.Directory.GetFiles("*.csproj").FirstOrDefault();
@@ -105,12 +140,7 @@ internal static string GetTargetFramework(this IHaveADirectory ihad)
.GetFiles("*.runtimeconfig.json", SearchOption.AllDirectories)
.FirstOrDefault();
- if (runtimeConfig != null)
- {
- return RuntimeConfig.GetTargetFramework(runtimeConfig);
- }
-
- return "netstandard2.0";
+ return runtimeConfig != null ? RuntimeConfig.GetTargetFramework(runtimeConfig) : "netstandard2.0";
}
}
}
\ No newline at end of file
diff --git a/WorkspaceServer/Servers/Roslyn/PackageExtensions.cs b/WorkspaceServer/Servers/Roslyn/PackageExtensions.cs
index dbcbf8504..2c496b45c 100644
--- a/WorkspaceServer/Servers/Roslyn/PackageExtensions.cs
+++ b/WorkspaceServer/Servers/Roslyn/PackageExtensions.cs
@@ -10,6 +10,7 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.DotNet.Try.Project;
using Microsoft.DotNet.Try.Protocol;
+using WorkspaceServer.Packaging;
using WorkspaceServer.Servers.Roslyn.Instrumentation;
using static System.Environment;
using Package = WorkspaceServer.Packaging.Package;
@@ -162,17 +163,64 @@ await InstrumentationLineMapper.MapLineLocationsRelativeToViewportAsync(
package.GetCompilation(sources, sourceCodeKind, defaultUsings, () => package.CreateRoslynWorkspaceForRunAsync(budget), budget);
public static Task<(Compilation compilation, IReadOnlyCollection documents)> GetCompilationForLanguageServices(
- this Package package,
+ this ICreateWorkspace package,
IReadOnlyCollection sources,
SourceCodeKind sourceCodeKind,
IEnumerable defaultUsings,
Budget budget) =>
- package.GetCompilation(sources, sourceCodeKind, defaultUsings, () => package.CreateRoslynWorkspaceForLanguageServicesAsync(budget), budget);
+ package.GetCompilation(sources, sourceCodeKind, defaultUsings, () => package.CreateRoslynWorkspaceAsync(budget), budget);
private static Document GetActiveDocument(IEnumerable documents, BufferId activeBufferId)
{
return documents.First(d => d.Name.Equals(activeBufferId.FileName));
}
+
+ public static async Task<(Compilation compilation, IReadOnlyCollection documents)> GetCompilation(
+ this IPackage package,
+ IReadOnlyCollection sources,
+ SourceCodeKind sourceCodeKind,
+ IEnumerable defaultUsings,
+ Func> workspaceFactory,
+ Budget budget)
+ {
+ var workspace = await workspaceFactory();
+
+ var currentSolution = workspace.CurrentSolution;
+ var project = currentSolution.Projects.First();
+ var projectId = project.Id;
+ foreach (var source in sources)
+ {
+ if (currentSolution.Projects
+ .SelectMany(p => p.Documents)
+ .FirstOrDefault(d => d.IsMatch(source)) is Document document)
+ {
+ // there's a pre-existing document, so overwrite its contents
+ document = document.WithText(source.Text);
+ document = document.WithSourceCodeKind(sourceCodeKind);
+ currentSolution = document.Project.Solution;
+ }
+ else
+ {
+ var docId = DocumentId.CreateNewId(projectId, $"{package.Name}.Document");
+
+ currentSolution = currentSolution.AddDocument(docId, source.Name, source.Text);
+ currentSolution = currentSolution.WithDocumentSourceCodeKind(docId, sourceCodeKind);
+ }
+ }
+
+
+ project = currentSolution.GetProject(projectId);
+ var usings = defaultUsings?.ToArray() ?? Array.Empty();
+ if (usings.Length > 0)
+ {
+ var options = (CSharpCompilationOptions)project.CompilationOptions;
+ project = project.WithCompilationOptions(options.WithUsings(usings));
+ }
+
+ var compilation = await project.GetCompilationAsync().CancelIfExceeds(budget);
+
+ return (compilation, project.Documents.ToArray());
+ }
}
}
diff --git a/WorkspaceServer/Servers/Roslyn/RoslynWorkspaceServer.cs b/WorkspaceServer/Servers/Roslyn/RoslynWorkspaceServer.cs
index f0dc3c292..3b25661d2 100644
--- a/WorkspaceServer/Servers/Roslyn/RoslynWorkspaceServer.cs
+++ b/WorkspaceServer/Servers/Roslyn/RoslynWorkspaceServer.cs
@@ -32,7 +32,7 @@ public class RoslynWorkspaceServer : ILanguageService, ICodeRunner, ICodeCompile
{
private readonly IPackageFinder _packageFinder;
private const int defaultBudgetInSeconds = 30;
- private readonly ConcurrentDictionary locks = new ConcurrentDictionary();
+ private static readonly ConcurrentDictionary locks = new ConcurrentDictionary();
private readonly IWorkspaceTransformer _transformer = new BufferInliningTransformer();
private static readonly string UserCodeCompleted = nameof(UserCodeCompleted);
@@ -49,7 +49,7 @@ public RoslynWorkspaceServer(IPackageFinder packageRegistry)
public async Task GetCompletionList(WorkspaceRequest request, Budget budget)
{
budget = budget ?? new TimeBudget(TimeSpan.FromSeconds(defaultBudgetInSeconds));
- var package = await _packageFinder.Find(request.Workspace.WorkspaceType);
+ var package = await _packageFinder.Find(request.Workspace.WorkspaceType);
var processed = await _transformer.TransformAsync(request.Workspace);
var sourceFiles = processed.GetSourceFiles();
@@ -119,7 +119,7 @@ public async Task GetSignatureHelp(WorkspaceRequest request
{
budget = budget ?? new TimeBudget(TimeSpan.FromSeconds(defaultBudgetInSeconds));
- var package = await _packageFinder.Find(request.Workspace.WorkspaceType);
+ var package = await _packageFinder.Find(request.Workspace.WorkspaceType);
var processed = await _transformer.TransformAsync(request.Workspace);
@@ -160,7 +160,7 @@ public async Task GetDiagnostics(WorkspaceRequest request, Bud
{
budget = budget ?? new TimeBudget(TimeSpan.FromSeconds(defaultBudgetInSeconds));
- var package = await _packageFinder.Find(request.Workspace.WorkspaceType);
+ var package = await _packageFinder.Find(request.Workspace.WorkspaceType);
var workspace = await _transformer.TransformAsync(request.Workspace);