Merge pull request #1013 from dotnet/release/new_api
merge `release/new_api` to `main`
This commit is contained in:
Коммит
7e0d21e19c
|
@ -378,3 +378,27 @@ docs/\.ionide/
|
|||
#Backup files
|
||||
*.bak
|
||||
NotebookExamples/*/Samples/housing.csv
|
||||
|
||||
src/microsoft-trydotnet-editor/test-results/
|
||||
|
||||
src/microsoft-trydotnet-editor/dist/
|
||||
|
||||
src/microsoft-trydotnet-editor/simulatorGenerator/
|
||||
|
||||
src/microsoft-trydotnet-editor/lib/
|
||||
|
||||
src/microsoft-trydotnet/test-results/
|
||||
|
||||
src/microsoft-trydotnet/dist/
|
||||
|
||||
src/microsoft-trydotnet/simulatorGenerator/
|
||||
|
||||
src/microsoft-trydotnet/lib/
|
||||
|
||||
src/Microsoft.TryDotNet/wwwroot/api/
|
||||
|
||||
src/Microsoft.TryDotNet/wwwroot/css/trydotnet.css
|
||||
|
||||
src/microsoft-learn-mock/dist/
|
||||
|
||||
src/microsoft-learn-mock/site/index.js
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
Not doing this in each project because duplicate PackageReferences causes an Arcade build failure.
|
||||
-->
|
||||
<ItemGroup Condition="$(MSBuildProjectName.EndsWith('.Tests')) AND '$(DisableArcade)' == '1'">
|
||||
<ItemGroup Condition="($(MSBuildProjectName.EndsWith('.Tests')) OR $(MSBuildProjectName.EndsWith('.IntegrationTests'))) AND '$(DisableArcade)' == '1'">
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<NoWarn>$(NoWarn);2003</NoWarn><!-- AssemblyInformationalVersionAttribute contains a non-standard value -->
|
||||
<Deterministic Condition="'$(NCrunch)' == '1'">false</Deterministic>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith('netstandard')) AND '$(OS)' == 'Windows_NT'">
|
||||
<!-- the 2.1.503 F# compiler can produce PDBs that can't properly be converted, see https://github.com/Microsoft/visualfsharp/issues/5976 -->
|
||||
<PublishWindowsPdb>false</PublishWindowsPdb>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="Library.fs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FSharp.Compiler.Service" Version="41.0.1" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Update="FSharp.Core" Version="6.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,130 +0,0 @@
|
|||
namespace FSharpWorkspaceShim
|
||||
|
||||
open System
|
||||
open System.IO
|
||||
open FSharp.Compiler.SourceCodeServices
|
||||
open Microsoft.CodeAnalysis
|
||||
open Microsoft.CodeAnalysis.Text
|
||||
|
||||
module Shim =
|
||||
|
||||
let private checker = FSharpChecker.Create()
|
||||
|
||||
let private getIndex (text: string) (line: int) (column: int) =
|
||||
let mutable index = -1
|
||||
let mutable currentLine = 0
|
||||
let mutable currentColumn = 0
|
||||
text.ToCharArray()
|
||||
|> Array.iteri (fun i c ->
|
||||
if line = currentLine && column = currentColumn then index <- i
|
||||
match c with
|
||||
| '\n' ->
|
||||
currentLine <- currentLine + 1
|
||||
currentColumn <- 0
|
||||
| _ -> currentColumn <- currentColumn + 1)
|
||||
index
|
||||
|
||||
let private newlineProxy = System.String [|char 29|]
|
||||
|
||||
// adapted from https://github.com/dotnet/fsharp/blob/master/src/fsharp/ErrorLogger.fs
|
||||
let private normalizeErrorString (text : string) =
|
||||
if isNull text then nullArg "text"
|
||||
let text = text.Trim()
|
||||
|
||||
let buf = System.Text.StringBuilder()
|
||||
let mutable i = 0
|
||||
while i < text.Length do
|
||||
let delta =
|
||||
match text.[i] with
|
||||
| '\r' when i + 1 < text.Length && text.[i + 1] = '\n' ->
|
||||
// handle \r\n sequence - replace it with one single space
|
||||
buf.Append newlineProxy |> ignore
|
||||
2
|
||||
| '\n' | '\r' ->
|
||||
buf.Append newlineProxy |> ignore
|
||||
1
|
||||
| c ->
|
||||
// handle remaining chars: control - replace with space, others - keep unchanged
|
||||
let c = if Char.IsControl c then ' ' else c
|
||||
buf.Append c |> ignore
|
||||
1
|
||||
i <- i + delta
|
||||
buf.ToString()
|
||||
|
||||
let private newlineifyErrorString (message:string) = message.Replace(newlineProxy, Environment.NewLine)
|
||||
|
||||
// adapted from https://github.com/dotnet/fsharp/blob/master/vsintegration/src/FSharp.Editor/Common/RoslynHelpers.fs
|
||||
let private convertError (error: FSharpErrorInfo) (location: Location) =
|
||||
// Normalize the error message into the same format that we will receive it from the compiler.
|
||||
// This ensures that IntelliSense and Compiler errors in the 'Error List' are de-duplicated.
|
||||
// (i.e the same error does not appear twice, where the only difference is the line endings.)
|
||||
let normalizedMessage = error.Message |> normalizeErrorString |> newlineifyErrorString
|
||||
|
||||
let id = "FS" + error.ErrorNumber.ToString("0000")
|
||||
let emptyString = LocalizableString.op_Implicit("")
|
||||
let description = LocalizableString.op_Implicit(normalizedMessage)
|
||||
let severity = if error.Severity = FSharpErrorSeverity.Error then DiagnosticSeverity.Error else DiagnosticSeverity.Warning
|
||||
let customTags =
|
||||
match error.ErrorNumber with
|
||||
| 1182 -> WellKnownDiagnosticTags.Unnecessary
|
||||
| _ -> null
|
||||
let descriptor = new DiagnosticDescriptor(id, emptyString, description, error.Subcategory, severity, true, emptyString, String.Empty, customTags)
|
||||
Diagnostic.Create(descriptor, location)
|
||||
|
||||
let GetDiagnostics (projectPath: string) (files: string[]) (pathMapSource: string) (pathMapDest: string) =
|
||||
async {
|
||||
let projectOptions = {
|
||||
ProjectFileName = projectPath
|
||||
ProjectId = None
|
||||
SourceFiles = files
|
||||
OtherOptions = [||]
|
||||
ReferencedProjects = [||]
|
||||
IsIncompleteTypeCheckEnvironment = false
|
||||
UseScriptResolutionRules = false
|
||||
LoadTime = DateTime.Now
|
||||
UnresolvedReferences = None
|
||||
OriginalLoadReferences = []
|
||||
ExtraProjectInfo = None
|
||||
Stamp = None
|
||||
}
|
||||
let ensureDirectorySeparator (path: string) =
|
||||
if path.EndsWith(Path.DirectorySeparatorChar |> string) |> not then path + (string Path.DirectorySeparatorChar)
|
||||
else path
|
||||
let pathMapSource = ensureDirectorySeparator pathMapSource
|
||||
let pathMapDest = ensureDirectorySeparator pathMapDest
|
||||
let! results = checker.ParseAndCheckProject projectOptions
|
||||
// adapted from from https://github.com/dotnet/fsharp/blob/master/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs
|
||||
let diagnostics =
|
||||
results.Errors
|
||||
|> Seq.choose (fun error ->
|
||||
if error.StartLineAlternate = 0 || error.EndLineAlternate = 0 then
|
||||
// F# error line numbers are one-based. Compiler returns 0 for global errors (reported by ProjectDiagnosticAnalyzer)
|
||||
None
|
||||
else
|
||||
// Roslyn line numbers are zero-based
|
||||
let linePositionSpan = LinePositionSpan(LinePosition(error.StartLineAlternate - 1, error.StartColumn), LinePosition(error.EndLineAlternate - 1, error.EndColumn))
|
||||
let text = File.ReadAllText(error.FileName)
|
||||
let textSpan =
|
||||
TextSpan.FromBounds(
|
||||
getIndex text (error.StartLineAlternate - 1) error.StartColumn,
|
||||
getIndex text (error.EndLineAlternate - 1) error.EndColumn)
|
||||
|
||||
// F# compiler report errors at end of file if parsing fails. It should be corrected to match Roslyn boundaries
|
||||
let correctedTextSpan =
|
||||
if textSpan.End <= text.Length then
|
||||
textSpan
|
||||
else
|
||||
let start =
|
||||
min textSpan.Start (text.Length - 1)
|
||||
|> max 0
|
||||
|
||||
TextSpan.FromBounds(start, text.Length)
|
||||
|
||||
let filePath =
|
||||
if error.FileName.StartsWith(pathMapSource) then String.Concat(pathMapDest, error.FileName.Substring(pathMapSource.Length))
|
||||
else error.FileName
|
||||
let location = Location.Create(filePath, correctedTextSpan, linePositionSpan)
|
||||
Some(convertError error location))
|
||||
|> Seq.toArray
|
||||
return diagnostics
|
||||
} |> Async.StartAsTask
|
|
@ -1,14 +0,0 @@
|
|||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace Recipes
|
||||
{
|
||||
public static class EnvironmentExtensions
|
||||
{
|
||||
public static bool IsTest(this IHostEnvironment hostingEnvironment) =>
|
||||
hostingEnvironment.EnvironmentName == "test";
|
||||
|
||||
public static IWebHostBuilder UseTestEnvironment(this IWebHostBuilder builder) =>
|
||||
builder.UseEnvironment("test");
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using MLS.Agent;
|
||||
using WorkspaceServer;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Pocket
|
||||
{
|
||||
internal partial class LogEvents
|
||||
{
|
||||
public static IDisposable SubscribeToPocketLogger(this ITestOutputHelper output) =>
|
||||
Subscribe(
|
||||
e => output.WriteLine(e.ToLogString()),
|
||||
new[]
|
||||
{
|
||||
typeof(LogEvents).Assembly,
|
||||
typeof(Startup).Assembly,
|
||||
typeof(ICodeRunner).Assembly,
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Hosting.Server.Features;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using MLS.Agent.CommandLine;
|
||||
using MLS.Agent.Tools;
|
||||
using Pocket;
|
||||
using Recipes;
|
||||
using WorkspaceServer;
|
||||
|
||||
namespace MLS.Agent.Tests
|
||||
{
|
||||
public class AgentService : IDisposable
|
||||
{
|
||||
private readonly IDirectoryAccessor _directoryAccessor;
|
||||
private readonly StartupOptions _options;
|
||||
private readonly CompositeDisposable _disposables = new CompositeDisposable();
|
||||
|
||||
private readonly HttpClient _client;
|
||||
|
||||
public AgentService(StartupOptions options = null, IDirectoryAccessor directoryAccessor = null)
|
||||
{
|
||||
_directoryAccessor = directoryAccessor;
|
||||
_options = options ?? new StartupOptions(
|
||||
production: false,
|
||||
languageService: false);
|
||||
|
||||
var testServer = CreateTestServer();
|
||||
|
||||
_client = testServer.CreateClient();
|
||||
|
||||
_disposables.Add(testServer);
|
||||
_disposables.Add(_client);
|
||||
}
|
||||
|
||||
public FakeBrowserLauncher BrowserLauncher { get; private set; }
|
||||
|
||||
public void Dispose() => _disposables.Dispose();
|
||||
|
||||
private TestServer CreateTestServer()
|
||||
{
|
||||
//We need to set the feature collection to be not null,
|
||||
//so that the ServerFeatures(like the urls to use) can be configured and used
|
||||
var featureCollection = new FeatureCollection();
|
||||
featureCollection.Set<IServerAddressesFeature>(new ServerAddressesFeature());
|
||||
return new TestServer(CreateWebHostBuilder(), featureCollection);
|
||||
}
|
||||
|
||||
private IWebHostBuilder CreateWebHostBuilder()
|
||||
{
|
||||
var builder = new WebHostBuilder()
|
||||
.ConfigureServices(c =>
|
||||
{
|
||||
if (_directoryAccessor != null)
|
||||
{
|
||||
c.AddSingleton(_directoryAccessor);
|
||||
}
|
||||
c.AddSingleton(_options);
|
||||
c.AddSingleton<IBrowserLauncher>(sp =>
|
||||
{
|
||||
BrowserLauncher = new FakeBrowserLauncher();
|
||||
return BrowserLauncher;
|
||||
});
|
||||
})
|
||||
.UseTestEnvironment()
|
||||
.UseStartup<Startup>()
|
||||
.ConfigureUrl(_options.Mode, _options.Port);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request) =>
|
||||
_client.SendAsync(request);
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MLS.Agent.Tests
|
||||
{
|
||||
public static class AgentServiceExtensions
|
||||
{
|
||||
public static Task<HttpResponseMessage> GetAsync(
|
||||
this AgentService service,
|
||||
string uri,
|
||||
string referer = null)
|
||||
{
|
||||
var message = new HttpRequestMessage(HttpMethod.Get, uri);
|
||||
if (referer != null)
|
||||
{
|
||||
message.Headers.Add("referer", referer);
|
||||
}
|
||||
|
||||
return service.SendAsync(message);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,195 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Microsoft.DotNet.Try.Protocol;
|
||||
using Recipes;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace MLS.Agent.Tests.ApiContracts
|
||||
{
|
||||
public class ApiInputContractChangeTests : ApiViaHttpTestsBase
|
||||
{
|
||||
public ApiInputContractChangeTests(ITestOutputHelper output) : base(output)
|
||||
{
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("console")]
|
||||
[InlineData("script")]
|
||||
public async Task Changing_the_completion_request_format_does_not_change_the_response(string workspaceType)
|
||||
{
|
||||
var oldFormatRequest =
|
||||
$@"{{
|
||||
""workspace"": {{
|
||||
""workspaceType"": ""{workspaceType}"",
|
||||
""files"": [],
|
||||
""buffers"": [
|
||||
{{
|
||||
""id"": ""file.cs"",
|
||||
""content"": ""using System;\nusing System.Collections.Generic;\nusing System.Linq;\n\npublic class Program\n{{\n public static void Main()\n {{\n foreach (var i in Fibonacci().Take(20))\n {{\n Console.\n }}\n }}\n\n private static IEnumerable<int> Fibonacci()\n {{\n int current = 1, next = 1;\n\n while (true) \n {{\n yield return current;\n next = current + (current = next);\n }}\n }}\n}}\n"",
|
||||
""position"": 0
|
||||
}}
|
||||
],
|
||||
""usings"": []
|
||||
}},
|
||||
""activeBufferId"": ""file.cs"",
|
||||
""position"": 187
|
||||
}}";
|
||||
|
||||
var newFormatRequest =
|
||||
$@"{{
|
||||
""workspace"": {{
|
||||
""workspaceType"": ""{workspaceType}"",
|
||||
""files"": [],
|
||||
""buffers"": [
|
||||
{{
|
||||
""id"": ""file.cs"",
|
||||
""content"": ""using System;\nusing System.Collections.Generic;\nusing System.Linq;\n\npublic class Program\n{{\n public static void Main()\n {{\n foreach (var i in Fibonacci().Take(20))\n {{\n Console.\n }}\n }}\n\n private static IEnumerable<int> Fibonacci()\n {{\n int current = 1, next = 1;\n\n while (true) \n {{\n yield return current;\n next = current + (current = next);\n }}\n }}\n}}\n"",
|
||||
""position"": 187
|
||||
}}
|
||||
],
|
||||
""usings"": []
|
||||
}},
|
||||
""activeBufferId"": ""file.cs""
|
||||
}}";
|
||||
|
||||
var responseToOldFormatRequest = await CallCompletion(oldFormatRequest);
|
||||
var responseToNewFormatRequest = await CallCompletion(newFormatRequest);
|
||||
|
||||
responseToNewFormatRequest.Should().BeSuccessful();
|
||||
|
||||
responseToNewFormatRequest.Should().BeEquivalentTo(responseToOldFormatRequest);
|
||||
|
||||
var resultOfOldFormatRequest = await responseToOldFormatRequest.DeserializeAs<CompletionResult>();
|
||||
var resultOfNewFormatRequest = await responseToNewFormatRequest.DeserializeAs<CompletionResult>();
|
||||
|
||||
resultOfNewFormatRequest.Items.Should().Contain(i => i.DisplayText == "WriteLine");
|
||||
|
||||
resultOfOldFormatRequest.Should().BeEquivalentTo(resultOfNewFormatRequest);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("console")]
|
||||
[InlineData("script")]
|
||||
public async Task Changing_the_signature_help_request_format_does_not_change_the_response(string workspaceType)
|
||||
{
|
||||
var oldFormatRequest =
|
||||
$@"{{
|
||||
""workspace"": {{
|
||||
""workspaceType"": ""{workspaceType}"",
|
||||
""files"": [],
|
||||
""buffers"": [
|
||||
{{
|
||||
""id"": ""file.cs"",
|
||||
""content"": ""using System;\nusing System.Collections.Generic;\nusing System.Linq;\n\npublic class Program\n{{\n public static void Main()\n {{\n foreach (var i in Fibonacci().Take(20))\n {{\n Console.WriteLine()\n }}\n }}\n\n private static IEnumerable<int> Fibonacci()\n {{\n int current = 1, next = 1;\n\n while (true) \n {{\n yield return current;\n next = current + (current = next);\n }}\n }}\n}}\n"",
|
||||
""position"": 0
|
||||
}}
|
||||
],
|
||||
""usings"": []
|
||||
}},
|
||||
""activeBufferId"": ""file.cs"",
|
||||
""position"": 197
|
||||
}}";
|
||||
|
||||
var newFormatRequest =
|
||||
$@"{{
|
||||
""workspace"": {{
|
||||
""workspaceType"": ""{workspaceType}"",
|
||||
""files"": [],
|
||||
""buffers"": [
|
||||
{{
|
||||
""id"": ""file.cs"",
|
||||
""content"": ""using System;\nusing System.Collections.Generic;\nusing System.Linq;\n\npublic class Program\n{{\n public static void Main()\n {{\n foreach (var i in Fibonacci().Take(20))\n {{\n Console.WriteLine()\n }}\n }}\n\n private static IEnumerable<int> Fibonacci()\n {{\n int current = 1, next = 1;\n\n while (true) \n {{\n yield return current;\n next = current + (current = next);\n }}\n }}\n}}\n"",
|
||||
""position"": 197
|
||||
}}
|
||||
],
|
||||
""usings"": []
|
||||
}},
|
||||
""activeBufferId"": ""file.cs""
|
||||
}}";
|
||||
|
||||
var responseToOldFormatRequest = await CallSignatureHelp(oldFormatRequest);
|
||||
var responseToNewFormatRequest = await CallSignatureHelp(newFormatRequest);
|
||||
|
||||
responseToNewFormatRequest.Should().BeSuccessful();
|
||||
|
||||
responseToNewFormatRequest.Should().BeEquivalentTo(responseToOldFormatRequest);
|
||||
|
||||
var resultOfOldFormatRequest = await responseToOldFormatRequest.DeserializeAs<SignatureHelpResult>();
|
||||
var resultOfNewFormatRequest = await responseToNewFormatRequest.DeserializeAs<SignatureHelpResult>();
|
||||
|
||||
resultOfNewFormatRequest.Signatures.Should().Contain(i => i.Label == "void Console.WriteLine()");
|
||||
|
||||
resultOfOldFormatRequest.Should().BeEquivalentTo(resultOfNewFormatRequest);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("console")]
|
||||
[InlineData("script")]
|
||||
public async Task Changing_the_run_request_format_does_not_change_the_response(string workspaceType)
|
||||
{
|
||||
var oldFormatRequest =
|
||||
$@"{{
|
||||
""workspace"": {{
|
||||
""workspaceType"": ""{workspaceType}"",
|
||||
""files"": [],
|
||||
""buffers"": [
|
||||
{{
|
||||
""id"": ""Program.cs"",
|
||||
""content"": ""using System;\nusing System.Collections.Generic;\nusing System.Linq;\n\npublic class Program\n{{\n public static void Main()\n {{\n foreach (var i in Fibonacci().Take(10))\n {{\n Console.WriteLine(i);\n }}\n }}\n\n private static IEnumerable<int> Fibonacci()\n {{\n int current = 1, next = 1;\n\n while (true) \n {{\n yield return current;\n next = current + (current = next);\n }}\n }}\n}}\n"",
|
||||
""position"": 0
|
||||
}}
|
||||
],
|
||||
""usings"": []
|
||||
}},
|
||||
""activeBufferId"": """",
|
||||
""position"": 197
|
||||
}}";
|
||||
|
||||
var newFormatRequest =
|
||||
$@"{{
|
||||
""workspace"": {{
|
||||
""workspaceType"": ""{workspaceType}"",
|
||||
""files"": [],
|
||||
""buffers"": [
|
||||
{{
|
||||
""id"": ""Program.cs"",
|
||||
""content"": ""using System;\nusing System.Collections.Generic;\nusing System.Linq;\n\npublic class Program\n{{\n public static void Main()\n {{\n foreach (var i in Fibonacci().Take(10))\n {{\n Console.WriteLine(i);\n }}\n }}\n\n private static IEnumerable<int> Fibonacci()\n {{\n int current = 1, next = 1;\n\n while (true) \n {{\n yield return current;\n next = current + (current = next);\n }}\n }}\n}}\n"",
|
||||
""position"": 197
|
||||
}}
|
||||
],
|
||||
""usings"": []
|
||||
}},
|
||||
""activeBufferId"": """"
|
||||
}}";
|
||||
|
||||
var responseToOldFormatRequest = await CallRun(oldFormatRequest);
|
||||
var responseToNewFormatRequest = await CallRun(newFormatRequest);
|
||||
|
||||
responseToNewFormatRequest.Should().BeSuccessful();
|
||||
|
||||
responseToNewFormatRequest.Should().BeEquivalentTo(responseToOldFormatRequest);
|
||||
|
||||
var resultOfOldFormatRequest = await responseToOldFormatRequest.DeserializeAs<RunResult>();
|
||||
var resultOfNewFormatRequest = await responseToNewFormatRequest.DeserializeAs<RunResult>();
|
||||
|
||||
resultOfNewFormatRequest.Output.Should().BeEquivalentTo(
|
||||
"1",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"5",
|
||||
"8",
|
||||
"13",
|
||||
"21",
|
||||
"34",
|
||||
"55",
|
||||
"");
|
||||
|
||||
resultOfOldFormatRequest.Should().BeEquivalentTo(resultOfNewFormatRequest);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"requestId": "TestRun",
|
||||
"base64assembly": "",
|
||||
"succeeded": true,
|
||||
"diagnostics": []
|
||||
}
|
|
@ -1,105 +0,0 @@
|
|||
{
|
||||
"requestId": "TestRun",
|
||||
"base64assembly": null,
|
||||
"succeeded": false,
|
||||
"diagnostics": [
|
||||
{
|
||||
"start": 136,
|
||||
"end": 142,
|
||||
"message": "ViewportCode.cs(9,30): error CS0161: 'Sample.Method()': not all code paths return a value",
|
||||
"severity": 3,
|
||||
"id": "CS0161",
|
||||
"location": "ViewportCode.cs(9,30): error CS0161"
|
||||
},
|
||||
{
|
||||
"start": 184,
|
||||
"end": 189,
|
||||
"message": "ViewportCode.cs(12,13): error CS0103: The name 'doesn' does not exist in the current context",
|
||||
"severity": 3,
|
||||
"id": "CS0103",
|
||||
"location": "ViewportCode.cs(12,13): error CS0103"
|
||||
},
|
||||
{
|
||||
"start": 189,
|
||||
"end": 189,
|
||||
"message": "ViewportCode.cs(12,18): error CS1010: Newline in constant",
|
||||
"severity": 3,
|
||||
"id": "CS1010",
|
||||
"location": "ViewportCode.cs(12,18): error CS1010"
|
||||
},
|
||||
{
|
||||
"start": 189,
|
||||
"end": 189,
|
||||
"message": "ViewportCode.cs(12,18): error CS1012: Too many characters in character literal",
|
||||
"severity": 3,
|
||||
"id": "CS1012",
|
||||
"location": "ViewportCode.cs(12,18): error CS1012"
|
||||
},
|
||||
{
|
||||
"start": 189,
|
||||
"end": 199,
|
||||
"message": "ViewportCode.cs(12,18): error CS1002: ; expected",
|
||||
"severity": 3,
|
||||
"id": "CS1002",
|
||||
"location": "ViewportCode.cs(12,18): error CS1002"
|
||||
},
|
||||
{
|
||||
"start": 199,
|
||||
"end": 199,
|
||||
"message": "ViewportCode.cs(12,28): error CS1002: ; expected",
|
||||
"severity": 3,
|
||||
"id": "CS1002",
|
||||
"location": "ViewportCode.cs(12,28): error CS1002"
|
||||
}
|
||||
],
|
||||
"projectDiagnostics": [
|
||||
{
|
||||
"start": 136,
|
||||
"end": 142,
|
||||
"message": "'Sample.Method()': not all code paths return a value",
|
||||
"severity": 3,
|
||||
"id": "CS0161",
|
||||
"location": "ViewportCode.cs(9,30): error CS0161"
|
||||
},
|
||||
{
|
||||
"start": 184,
|
||||
"end": 189,
|
||||
"message": "The name 'doesn' does not exist in the current context",
|
||||
"severity": 3,
|
||||
"id": "CS0103",
|
||||
"location": "ViewportCode.cs(12,13): error CS0103"
|
||||
},
|
||||
{
|
||||
"start": 189,
|
||||
"end": 189,
|
||||
"message": "Newline in constant",
|
||||
"severity": 3,
|
||||
"id": "CS1010",
|
||||
"location": "ViewportCode.cs(12,18): error CS1010"
|
||||
},
|
||||
{
|
||||
"start": 189,
|
||||
"end": 189,
|
||||
"message": "Too many characters in character literal",
|
||||
"severity": 3,
|
||||
"id": "CS1012",
|
||||
"location": "ViewportCode.cs(12,18): error CS1012"
|
||||
},
|
||||
{
|
||||
"start": 189,
|
||||
"end": 199,
|
||||
"message": "; expected",
|
||||
"severity": 3,
|
||||
"id": "CS1002",
|
||||
"location": "ViewportCode.cs(12,18): error CS1002"
|
||||
},
|
||||
{
|
||||
"start": 199,
|
||||
"end": 199,
|
||||
"message": "; expected",
|
||||
"severity": 3,
|
||||
"id": "CS1002",
|
||||
"location": "ViewportCode.cs(12,28): error CS1002"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"items": [
|
||||
{
|
||||
"displayText": "extern",
|
||||
"kind": "Keyword",
|
||||
"filterText": "extern",
|
||||
"sortText": "extern",
|
||||
"insertText": "extern",
|
||||
"documentation": ""
|
||||
},
|
||||
{
|
||||
"displayText": "using",
|
||||
"kind": "Keyword",
|
||||
"filterText": "using",
|
||||
"sortText": "using",
|
||||
"insertText": "using",
|
||||
"documentation": ""
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,570 +0,0 @@
|
|||
{
|
||||
"items": [
|
||||
{
|
||||
"displayText": "BackgroundColor",
|
||||
"kind": "Property",
|
||||
"filterText": "BackgroundColor",
|
||||
"sortText": "BackgroundColor",
|
||||
"insertText": "BackgroundColor",
|
||||
"documentation": {
|
||||
"value": "Gets or sets the background color of the console.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "Beep",
|
||||
"kind": "Method",
|
||||
"filterText": "Beep",
|
||||
"sortText": "Beep",
|
||||
"insertText": "Beep",
|
||||
"documentation": {
|
||||
"value": "Plays the sound of a beep through the console speaker.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "BufferHeight",
|
||||
"kind": "Property",
|
||||
"filterText": "BufferHeight",
|
||||
"sortText": "BufferHeight",
|
||||
"insertText": "BufferHeight",
|
||||
"documentation": {
|
||||
"value": "Gets or sets the height of the buffer area.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "BufferWidth",
|
||||
"kind": "Property",
|
||||
"filterText": "BufferWidth",
|
||||
"sortText": "BufferWidth",
|
||||
"insertText": "BufferWidth",
|
||||
"documentation": {
|
||||
"value": "Gets or sets the width of the buffer area.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "CancelKeyPress",
|
||||
"kind": "Event",
|
||||
"filterText": "CancelKeyPress",
|
||||
"sortText": "CancelKeyPress",
|
||||
"insertText": "CancelKeyPress",
|
||||
"documentation": {
|
||||
"value": "Occurs when the System.ConsoleModifiers.Control modifier key (Ctrl) and either the System.ConsoleKey.C console key (C) or the Break key are pressed simultaneously (Ctrl+C or Ctrl+Break).",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "CapsLock",
|
||||
"kind": "Property",
|
||||
"filterText": "CapsLock",
|
||||
"sortText": "CapsLock",
|
||||
"insertText": "CapsLock",
|
||||
"documentation": {
|
||||
"value": "Gets a value indicating whether the CAPS LOCK keyboard toggle is turned on or turned off.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "Clear",
|
||||
"kind": "Method",
|
||||
"filterText": "Clear",
|
||||
"sortText": "Clear",
|
||||
"insertText": "Clear",
|
||||
"documentation": {
|
||||
"value": "Clears the console buffer and corresponding console window of display information.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "CursorLeft",
|
||||
"kind": "Property",
|
||||
"filterText": "CursorLeft",
|
||||
"sortText": "CursorLeft",
|
||||
"insertText": "CursorLeft",
|
||||
"documentation": {
|
||||
"value": "Gets or sets the column position of the cursor within the buffer area.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "CursorSize",
|
||||
"kind": "Property",
|
||||
"filterText": "CursorSize",
|
||||
"sortText": "CursorSize",
|
||||
"insertText": "CursorSize",
|
||||
"documentation": {
|
||||
"value": "Gets or sets the height of the cursor within a character cell.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "CursorTop",
|
||||
"kind": "Property",
|
||||
"filterText": "CursorTop",
|
||||
"sortText": "CursorTop",
|
||||
"insertText": "CursorTop",
|
||||
"documentation": {
|
||||
"value": "Gets or sets the row position of the cursor within the buffer area.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "CursorVisible",
|
||||
"kind": "Property",
|
||||
"filterText": "CursorVisible",
|
||||
"sortText": "CursorVisible",
|
||||
"insertText": "CursorVisible",
|
||||
"documentation": {
|
||||
"value": "Gets or sets a value indicating whether the cursor is visible.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "Equals",
|
||||
"kind": "Method",
|
||||
"filterText": "Equals",
|
||||
"sortText": "Equals",
|
||||
"insertText": "Equals",
|
||||
"documentation": {
|
||||
"value": "Determines whether the specified object instances are considered equal.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "Error",
|
||||
"kind": "Property",
|
||||
"filterText": "Error",
|
||||
"sortText": "Error",
|
||||
"insertText": "Error",
|
||||
"documentation": {
|
||||
"value": "Gets the standard error output stream.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "ForegroundColor",
|
||||
"kind": "Property",
|
||||
"filterText": "ForegroundColor",
|
||||
"sortText": "ForegroundColor",
|
||||
"insertText": "ForegroundColor",
|
||||
"documentation": {
|
||||
"value": "Gets or sets the foreground color of the console.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "In",
|
||||
"kind": "Property",
|
||||
"filterText": "In",
|
||||
"sortText": "In",
|
||||
"insertText": "In",
|
||||
"documentation": {
|
||||
"value": "Gets the standard input stream.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "InputEncoding",
|
||||
"kind": "Property",
|
||||
"filterText": "InputEncoding",
|
||||
"sortText": "InputEncoding",
|
||||
"insertText": "InputEncoding",
|
||||
"documentation": {
|
||||
"value": "Gets or sets the encoding the console uses to read input.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "IsErrorRedirected",
|
||||
"kind": "Property",
|
||||
"filterText": "IsErrorRedirected",
|
||||
"sortText": "IsErrorRedirected",
|
||||
"insertText": "IsErrorRedirected",
|
||||
"documentation": {
|
||||
"value": "Gets a value that indicates whether the error output stream has been redirected from the standard error stream.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "IsInputRedirected",
|
||||
"kind": "Property",
|
||||
"filterText": "IsInputRedirected",
|
||||
"sortText": "IsInputRedirected",
|
||||
"insertText": "IsInputRedirected",
|
||||
"documentation": {
|
||||
"value": "Gets a value that indicates whether input has been redirected from the standard input stream.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "IsOutputRedirected",
|
||||
"kind": "Property",
|
||||
"filterText": "IsOutputRedirected",
|
||||
"sortText": "IsOutputRedirected",
|
||||
"insertText": "IsOutputRedirected",
|
||||
"documentation": {
|
||||
"value": "Gets a value that indicates whether output has been redirected from the standard output stream.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "KeyAvailable",
|
||||
"kind": "Property",
|
||||
"filterText": "KeyAvailable",
|
||||
"sortText": "KeyAvailable",
|
||||
"insertText": "KeyAvailable",
|
||||
"documentation": {
|
||||
"value": "Gets a value indicating whether a key press is available in the input stream.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "LargestWindowHeight",
|
||||
"kind": "Property",
|
||||
"filterText": "LargestWindowHeight",
|
||||
"sortText": "LargestWindowHeight",
|
||||
"insertText": "LargestWindowHeight",
|
||||
"documentation": {
|
||||
"value": "Gets the largest possible number of console window rows, based on the current font and screen resolution.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "LargestWindowWidth",
|
||||
"kind": "Property",
|
||||
"filterText": "LargestWindowWidth",
|
||||
"sortText": "LargestWindowWidth",
|
||||
"insertText": "LargestWindowWidth",
|
||||
"documentation": {
|
||||
"value": "Gets the largest possible number of console window columns, based on the current font and screen resolution.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "MoveBufferArea",
|
||||
"kind": "Method",
|
||||
"filterText": "MoveBufferArea",
|
||||
"sortText": "MoveBufferArea",
|
||||
"insertText": "MoveBufferArea",
|
||||
"documentation": {
|
||||
"value": "Copies a specified source area of the screen buffer to a specified destination area.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "NumberLock",
|
||||
"kind": "Property",
|
||||
"filterText": "NumberLock",
|
||||
"sortText": "NumberLock",
|
||||
"insertText": "NumberLock",
|
||||
"documentation": {
|
||||
"value": "Gets a value indicating whether the NUM LOCK keyboard toggle is turned on or turned off.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "OpenStandardError",
|
||||
"kind": "Method",
|
||||
"filterText": "OpenStandardError",
|
||||
"sortText": "OpenStandardError",
|
||||
"insertText": "OpenStandardError",
|
||||
"documentation": {
|
||||
"value": "Acquires the standard error stream.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "OpenStandardInput",
|
||||
"kind": "Method",
|
||||
"filterText": "OpenStandardInput",
|
||||
"sortText": "OpenStandardInput",
|
||||
"insertText": "OpenStandardInput",
|
||||
"documentation": {
|
||||
"value": "Acquires the standard input stream.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "OpenStandardOutput",
|
||||
"kind": "Method",
|
||||
"filterText": "OpenStandardOutput",
|
||||
"sortText": "OpenStandardOutput",
|
||||
"insertText": "OpenStandardOutput",
|
||||
"documentation": {
|
||||
"value": "Acquires the standard output stream.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "Out",
|
||||
"kind": "Property",
|
||||
"filterText": "Out",
|
||||
"sortText": "Out",
|
||||
"insertText": "Out",
|
||||
"documentation": {
|
||||
"value": "Gets the standard output stream.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "OutputEncoding",
|
||||
"kind": "Property",
|
||||
"filterText": "OutputEncoding",
|
||||
"sortText": "OutputEncoding",
|
||||
"insertText": "OutputEncoding",
|
||||
"documentation": {
|
||||
"value": "Gets or sets the encoding the console uses to write output.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "Read",
|
||||
"kind": "Method",
|
||||
"filterText": "Read",
|
||||
"sortText": "Read",
|
||||
"insertText": "Read",
|
||||
"documentation": {
|
||||
"value": "Reads the next character from the standard input stream.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "ReadKey",
|
||||
"kind": "Method",
|
||||
"filterText": "ReadKey",
|
||||
"sortText": "ReadKey",
|
||||
"insertText": "ReadKey",
|
||||
"documentation": {
|
||||
"value": "Obtains the next character or function key pressed by the user. The pressed key is displayed in the console window.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "ReadLine",
|
||||
"kind": "Method",
|
||||
"filterText": "ReadLine",
|
||||
"sortText": "ReadLine",
|
||||
"insertText": "ReadLine",
|
||||
"documentation": {
|
||||
"value": "Reads the next line of characters from the standard input stream.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "ReferenceEquals",
|
||||
"kind": "Method",
|
||||
"filterText": "ReferenceEquals",
|
||||
"sortText": "ReferenceEquals",
|
||||
"insertText": "ReferenceEquals",
|
||||
"documentation": {
|
||||
"value": "Determines whether the specified System.Object instances are the same instance.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "ResetColor",
|
||||
"kind": "Method",
|
||||
"filterText": "ResetColor",
|
||||
"sortText": "ResetColor",
|
||||
"insertText": "ResetColor",
|
||||
"documentation": {
|
||||
"value": "Sets the foreground and background console colors to their defaults.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "SetBufferSize",
|
||||
"kind": "Method",
|
||||
"filterText": "SetBufferSize",
|
||||
"sortText": "SetBufferSize",
|
||||
"insertText": "SetBufferSize",
|
||||
"documentation": {
|
||||
"value": "Sets the height and width of the screen buffer area to the specified values.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "SetCursorPosition",
|
||||
"kind": "Method",
|
||||
"filterText": "SetCursorPosition",
|
||||
"sortText": "SetCursorPosition",
|
||||
"insertText": "SetCursorPosition",
|
||||
"documentation": {
|
||||
"value": "Sets the position of the cursor.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "SetError",
|
||||
"kind": "Method",
|
||||
"filterText": "SetError",
|
||||
"sortText": "SetError",
|
||||
"insertText": "SetError",
|
||||
"documentation": {
|
||||
"value": "Sets the System.Console.Error property to the specified System.IO.TextWriter object.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "SetIn",
|
||||
"kind": "Method",
|
||||
"filterText": "SetIn",
|
||||
"sortText": "SetIn",
|
||||
"insertText": "SetIn",
|
||||
"documentation": {
|
||||
"value": "Sets the System.Console.In property to the specified System.IO.TextReader object.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "SetOut",
|
||||
"kind": "Method",
|
||||
"filterText": "SetOut",
|
||||
"sortText": "SetOut",
|
||||
"insertText": "SetOut",
|
||||
"documentation": {
|
||||
"value": "Sets the System.Console.Out property to target the System.IO.TextWriter object.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "SetWindowPosition",
|
||||
"kind": "Method",
|
||||
"filterText": "SetWindowPosition",
|
||||
"sortText": "SetWindowPosition",
|
||||
"insertText": "SetWindowPosition",
|
||||
"documentation": {
|
||||
"value": "Sets the position of the console window relative to the screen buffer.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "SetWindowSize",
|
||||
"kind": "Method",
|
||||
"filterText": "SetWindowSize",
|
||||
"sortText": "SetWindowSize",
|
||||
"insertText": "SetWindowSize",
|
||||
"documentation": {
|
||||
"value": "Sets the height and width of the console window to the specified values.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "Title",
|
||||
"kind": "Property",
|
||||
"filterText": "Title",
|
||||
"sortText": "Title",
|
||||
"insertText": "Title",
|
||||
"documentation": {
|
||||
"value": "Gets or sets the title to display in the console title bar.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "TreatControlCAsInput",
|
||||
"kind": "Property",
|
||||
"filterText": "TreatControlCAsInput",
|
||||
"sortText": "TreatControlCAsInput",
|
||||
"insertText": "TreatControlCAsInput",
|
||||
"documentation": {
|
||||
"value": "Gets or sets a value indicating whether the combination of the System.ConsoleModifiers.Control modifier key and System.ConsoleKey.C console key (Ctrl+C) is treated as ordinary input or as an interruption that is handled by the operating system.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "WindowHeight",
|
||||
"kind": "Property",
|
||||
"filterText": "WindowHeight",
|
||||
"sortText": "WindowHeight",
|
||||
"insertText": "WindowHeight",
|
||||
"documentation": {
|
||||
"value": "Gets or sets the height of the console window area.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "WindowLeft",
|
||||
"kind": "Property",
|
||||
"filterText": "WindowLeft",
|
||||
"sortText": "WindowLeft",
|
||||
"insertText": "WindowLeft",
|
||||
"documentation": {
|
||||
"value": "Gets or sets the leftmost position of the console window area relative to the screen buffer.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "WindowTop",
|
||||
"kind": "Property",
|
||||
"filterText": "WindowTop",
|
||||
"sortText": "WindowTop",
|
||||
"insertText": "WindowTop",
|
||||
"documentation": {
|
||||
"value": "Gets or sets the top position of the console window area relative to the screen buffer.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "WindowWidth",
|
||||
"kind": "Property",
|
||||
"filterText": "WindowWidth",
|
||||
"sortText": "WindowWidth",
|
||||
"insertText": "WindowWidth",
|
||||
"documentation": {
|
||||
"value": "Gets or sets the width of the console window.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "Write",
|
||||
"kind": "Method",
|
||||
"filterText": "Write",
|
||||
"sortText": "Write",
|
||||
"insertText": "Write",
|
||||
"documentation": {
|
||||
"value": "Writes the text representation of the specified Boolean value to the standard output stream.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayText": "WriteLine",
|
||||
"kind": "Method",
|
||||
"filterText": "WriteLine",
|
||||
"sortText": "WriteLine",
|
||||
"insertText": "WriteLine",
|
||||
"documentation": {
|
||||
"value": "Writes the current line terminator to the standard output stream.",
|
||||
"isTrusted": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestId": "TestRun",
|
||||
"diagnostics": [
|
||||
{
|
||||
"start": 194,
|
||||
"end": 194,
|
||||
"message": "ViewportCode.cs(12,23): error CS1002: ; expected",
|
||||
"severity": 3,
|
||||
"id": "CS1002",
|
||||
"location": "ViewportCode.cs(12,23): error CS1002"
|
||||
},
|
||||
{
|
||||
"start": 192,
|
||||
"end": 194,
|
||||
"message": "ViewportCode.cs(12,21): error CS0117: 'Console' does not contain a definition for 'Ou'",
|
||||
"severity": 3,
|
||||
"id": "CS0117",
|
||||
"location": "ViewportCode.cs(12,21): error CS0117"
|
||||
},
|
||||
{
|
||||
"start": 136,
|
||||
"end": 142,
|
||||
"message": "ViewportCode.cs(9,30): error CS0161: 'Sample.Method()': not all code paths return a value",
|
||||
"severity": 3,
|
||||
"id": "CS0161",
|
||||
"location": "ViewportCode.cs(9,30): error CS0161"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"requestId": "TestRun",
|
||||
"succeeded": true,
|
||||
"output": [
|
||||
"Hello world!",
|
||||
""
|
||||
],
|
||||
"exception": null,
|
||||
"diagnostics": []
|
||||
}
|
|
@ -1,113 +0,0 @@
|
|||
{
|
||||
"requestId": "TestRun",
|
||||
"succeeded": false,
|
||||
"output": [
|
||||
"ViewportCode.cs(12,18): error CS1002: ; expected",
|
||||
"ViewportCode.cs(12,18): error CS1010: Newline in constant",
|
||||
"ViewportCode.cs(12,18): error CS1012: Too many characters in character literal",
|
||||
"ViewportCode.cs(12,28): error CS1002: ; expected",
|
||||
"ViewportCode.cs(12,13): error CS0103: The name 'doesn' does not exist in the current context",
|
||||
"ViewportCode.cs(9,30): error CS0161: 'Sample.Method()': not all code paths return a value"
|
||||
],
|
||||
"exception": null,
|
||||
"diagnostics": [
|
||||
{
|
||||
"start": 136,
|
||||
"end": 142,
|
||||
"message": "ViewportCode.cs(9,30): error CS0161: 'Sample.Method()': not all code paths return a value",
|
||||
"severity": 3,
|
||||
"id": "CS0161",
|
||||
"location": "ViewportCode.cs(9,30): error CS0161"
|
||||
},
|
||||
{
|
||||
"start": 184,
|
||||
"end": 189,
|
||||
"message": "ViewportCode.cs(12,13): error CS0103: The name 'doesn' does not exist in the current context",
|
||||
"severity": 3,
|
||||
"id": "CS0103",
|
||||
"location": "ViewportCode.cs(12,13): error CS0103"
|
||||
},
|
||||
{
|
||||
"start": 189,
|
||||
"end": 189,
|
||||
"message": "ViewportCode.cs(12,18): error CS1010: Newline in constant",
|
||||
"severity": 3,
|
||||
"id": "CS1010",
|
||||
"location": "ViewportCode.cs(12,18): error CS1010"
|
||||
},
|
||||
{
|
||||
"start": 189,
|
||||
"end": 189,
|
||||
"message": "ViewportCode.cs(12,18): error CS1012: Too many characters in character literal",
|
||||
"severity": 3,
|
||||
"id": "CS1012",
|
||||
"location": "ViewportCode.cs(12,18): error CS1012"
|
||||
},
|
||||
{
|
||||
"start": 189,
|
||||
"end": 199,
|
||||
"message": "ViewportCode.cs(12,18): error CS1002: ; expected",
|
||||
"severity": 3,
|
||||
"id": "CS1002",
|
||||
"location": "ViewportCode.cs(12,18): error CS1002"
|
||||
},
|
||||
{
|
||||
"start": 199,
|
||||
"end": 199,
|
||||
"message": "ViewportCode.cs(12,28): error CS1002: ; expected",
|
||||
"severity": 3,
|
||||
"id": "CS1002",
|
||||
"location": "ViewportCode.cs(12,28): error CS1002"
|
||||
}
|
||||
],
|
||||
"projectDiagnostics": [
|
||||
{
|
||||
"start": 136,
|
||||
"end": 142,
|
||||
"message": "'Sample.Method()': not all code paths return a value",
|
||||
"severity": 3,
|
||||
"id": "CS0161",
|
||||
"location": "ViewportCode.cs(9,30): error CS0161"
|
||||
},
|
||||
{
|
||||
"start": 184,
|
||||
"end": 189,
|
||||
"message": "The name 'doesn' does not exist in the current context",
|
||||
"severity": 3,
|
||||
"id": "CS0103",
|
||||
"location": "ViewportCode.cs(12,13): error CS0103"
|
||||
},
|
||||
{
|
||||
"start": 189,
|
||||
"end": 189,
|
||||
"message": "Newline in constant",
|
||||
"severity": 3,
|
||||
"id": "CS1010",
|
||||
"location": "ViewportCode.cs(12,18): error CS1010"
|
||||
},
|
||||
{
|
||||
"start": 189,
|
||||
"end": 189,
|
||||
"message": "Too many characters in character literal",
|
||||
"severity": 3,
|
||||
"id": "CS1012",
|
||||
"location": "ViewportCode.cs(12,18): error CS1012"
|
||||
},
|
||||
{
|
||||
"start": 189,
|
||||
"end": 199,
|
||||
"message": "; expected",
|
||||
"severity": 3,
|
||||
"id": "CS1002",
|
||||
"location": "ViewportCode.cs(12,18): error CS1002"
|
||||
},
|
||||
{
|
||||
"start": 199,
|
||||
"end": 199,
|
||||
"message": "; expected",
|
||||
"severity": 3,
|
||||
"id": "CS1002",
|
||||
"location": "ViewportCode.cs(12,28): error CS1002"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,150 +0,0 @@
|
|||
{
|
||||
"requestId": "TestRun",
|
||||
"succeeded": true,
|
||||
"output": [],
|
||||
"exception": null,
|
||||
"diagnostics": [],
|
||||
"instrumentation": [
|
||||
{
|
||||
"filePosition": {
|
||||
"line": 10,
|
||||
"character": 12,
|
||||
"file": "Program.cs"
|
||||
},
|
||||
"locals": [],
|
||||
"output": {
|
||||
"start": 0,
|
||||
"end": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"filePosition": {
|
||||
"line": 10,
|
||||
"character": 23,
|
||||
"file": "Program.cs"
|
||||
},
|
||||
"locals": [
|
||||
{
|
||||
"name": "a",
|
||||
"value": "1",
|
||||
"declaredAt": {
|
||||
"start": 146,
|
||||
"end": 147
|
||||
}
|
||||
}
|
||||
],
|
||||
"output": {
|
||||
"start": 0,
|
||||
"end": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"filePosition": {
|
||||
"line": 10,
|
||||
"character": 34,
|
||||
"file": "Program.cs"
|
||||
},
|
||||
"locals": [
|
||||
{
|
||||
"name": "b",
|
||||
"value": "2",
|
||||
"declaredAt": {
|
||||
"start": 157,
|
||||
"end": 158
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a",
|
||||
"value": "1",
|
||||
"declaredAt": {
|
||||
"start": 146,
|
||||
"end": 147
|
||||
}
|
||||
}
|
||||
],
|
||||
"output": {
|
||||
"start": 0,
|
||||
"end": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"filePosition": {
|
||||
"line": 10,
|
||||
"character": 41,
|
||||
"file": "Program.cs"
|
||||
},
|
||||
"locals": [
|
||||
{
|
||||
"name": "b",
|
||||
"value": "2",
|
||||
"declaredAt": {
|
||||
"start": 157,
|
||||
"end": 158
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a",
|
||||
"value": "3",
|
||||
"declaredAt": {
|
||||
"start": 146,
|
||||
"end": 147
|
||||
}
|
||||
}
|
||||
],
|
||||
"output": {
|
||||
"start": 0,
|
||||
"end": 0
|
||||
}
|
||||
}
|
||||
],
|
||||
"variableLocations": [
|
||||
{
|
||||
"name": "b",
|
||||
"locations": [
|
||||
{
|
||||
"startLine": 10,
|
||||
"startColumn": 41,
|
||||
"endLine": 10,
|
||||
"endColumn": 42
|
||||
},
|
||||
{
|
||||
"startLine": 10,
|
||||
"startColumn": 27,
|
||||
"endLine": 10,
|
||||
"endColumn": 28
|
||||
}
|
||||
],
|
||||
"declaredAt": {
|
||||
"start": 157,
|
||||
"end": 158
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a",
|
||||
"locations": [
|
||||
{
|
||||
"startLine": 10,
|
||||
"startColumn": 34,
|
||||
"endLine": 10,
|
||||
"endColumn": 35
|
||||
},
|
||||
{
|
||||
"startLine": 10,
|
||||
"startColumn": 45,
|
||||
"endLine": 10,
|
||||
"endColumn": 46
|
||||
},
|
||||
{
|
||||
"startLine": 10,
|
||||
"startColumn": 16,
|
||||
"endLine": 10,
|
||||
"endColumn": 17
|
||||
}
|
||||
],
|
||||
"declaredAt": {
|
||||
"start": 146,
|
||||
"end": 147
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"requestId": "TestRun",
|
||||
"succeeded": true,
|
||||
"output": [],
|
||||
"exception": null,
|
||||
"diagnostics": []
|
||||
}
|
|
@ -1,403 +0,0 @@
|
|||
{
|
||||
"signatures": [
|
||||
{
|
||||
"name": "Write",
|
||||
"label": "void Console.Write(bool value)",
|
||||
"documentation": {
|
||||
"value": "Writes the text representation of the specified Boolean value to the standard output stream.",
|
||||
"isTrusted": false
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "value",
|
||||
"label": "bool value",
|
||||
"documentation": {
|
||||
"value": "**value**: The value to write.",
|
||||
"isTrusted": false
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Write",
|
||||
"label": "void Console.Write(char value)",
|
||||
"documentation": {
|
||||
"value": "Writes the specified Unicode character value to the standard output stream.",
|
||||
"isTrusted": false
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "value",
|
||||
"label": "char value",
|
||||
"documentation": {
|
||||
"value": "**value**: The value to write.",
|
||||
"isTrusted": false
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Write",
|
||||
"label": "void Console.Write(char[] buffer)",
|
||||
"documentation": {
|
||||
"value": "Writes the specified array of Unicode characters to the standard output stream.",
|
||||
"isTrusted": false
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "buffer",
|
||||
"label": "char[] buffer",
|
||||
"documentation": {
|
||||
"value": "**buffer**: A Unicode character array.",
|
||||
"isTrusted": false
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Write",
|
||||
"label": "void Console.Write(char[] buffer, int index, int count)",
|
||||
"documentation": {
|
||||
"value": "Writes the specified subarray of Unicode characters to the standard output stream.",
|
||||
"isTrusted": false
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "buffer",
|
||||
"label": "char[] buffer",
|
||||
"documentation": {
|
||||
"value": "**buffer**: An array of Unicode characters.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "index",
|
||||
"label": "int index",
|
||||
"documentation": {
|
||||
"value": "**index**: The starting position in buffer .",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "count",
|
||||
"label": "int count",
|
||||
"documentation": {
|
||||
"value": "**count**: The number of characters to write.",
|
||||
"isTrusted": false
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Write",
|
||||
"label": "void Console.Write(decimal value)",
|
||||
"documentation": {
|
||||
"value": "Writes the text representation of the specified System.Decimal value to the standard output stream.",
|
||||
"isTrusted": false
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "value",
|
||||
"label": "decimal value",
|
||||
"documentation": {
|
||||
"value": "**value**: The value to write.",
|
||||
"isTrusted": false
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Write",
|
||||
"label": "void Console.Write(double value)",
|
||||
"documentation": {
|
||||
"value": "Writes the text representation of the specified double-precision floating-point value to the standard output stream.",
|
||||
"isTrusted": false
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "value",
|
||||
"label": "double value",
|
||||
"documentation": {
|
||||
"value": "**value**: The value to write.",
|
||||
"isTrusted": false
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Write",
|
||||
"label": "void Console.Write(int value)",
|
||||
"documentation": {
|
||||
"value": "Writes the text representation of the specified 32-bit signed integer value to the standard output stream.",
|
||||
"isTrusted": false
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "value",
|
||||
"label": "int value",
|
||||
"documentation": {
|
||||
"value": "**value**: The value to write.",
|
||||
"isTrusted": false
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Write",
|
||||
"label": "void Console.Write(long value)",
|
||||
"documentation": {
|
||||
"value": "Writes the text representation of the specified 64-bit signed integer value to the standard output stream.",
|
||||
"isTrusted": false
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "value",
|
||||
"label": "long value",
|
||||
"documentation": {
|
||||
"value": "**value**: The value to write.",
|
||||
"isTrusted": false
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Write",
|
||||
"label": "void Console.Write(object value)",
|
||||
"documentation": {
|
||||
"value": "Writes the text representation of the specified object to the standard output stream.",
|
||||
"isTrusted": false
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "value",
|
||||
"label": "object value",
|
||||
"documentation": {
|
||||
"value": "**value**: The value to write, or null.",
|
||||
"isTrusted": false
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Write",
|
||||
"label": "void Console.Write(float value)",
|
||||
"documentation": {
|
||||
"value": "Writes the text representation of the specified single-precision floating-point value to the standard output stream.",
|
||||
"isTrusted": false
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "value",
|
||||
"label": "float value",
|
||||
"documentation": {
|
||||
"value": "**value**: The value to write.",
|
||||
"isTrusted": false
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Write",
|
||||
"label": "void Console.Write(string value)",
|
||||
"documentation": {
|
||||
"value": "Writes the specified string value to the standard output stream.",
|
||||
"isTrusted": false
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "value",
|
||||
"label": "string value",
|
||||
"documentation": {
|
||||
"value": "**value**: The value to write.",
|
||||
"isTrusted": false
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Write",
|
||||
"label": "void Console.Write(string format, object arg0)",
|
||||
"documentation": {
|
||||
"value": "Writes the text representation of the specified object to the standard output stream using the specified format information.",
|
||||
"isTrusted": false
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "format",
|
||||
"label": "string format",
|
||||
"documentation": {
|
||||
"value": "**format**: A composite format string.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "arg0",
|
||||
"label": "object arg0",
|
||||
"documentation": {
|
||||
"value": "**arg0**: An object to write using format .",
|
||||
"isTrusted": false
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Write",
|
||||
"label": "void Console.Write(string format, object arg0, object arg1)",
|
||||
"documentation": {
|
||||
"value": "Writes the text representation of the specified objects to the standard output stream using the specified format information.",
|
||||
"isTrusted": false
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "format",
|
||||
"label": "string format",
|
||||
"documentation": {
|
||||
"value": "**format**: A composite format string.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "arg0",
|
||||
"label": "object arg0",
|
||||
"documentation": {
|
||||
"value": "**arg0**: The first object to write using format .",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "arg1",
|
||||
"label": "object arg1",
|
||||
"documentation": {
|
||||
"value": "**arg1**: The second object to write using format .",
|
||||
"isTrusted": false
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Write",
|
||||
"label": "void Console.Write(string format, object arg0, object arg1, object arg2)",
|
||||
"documentation": {
|
||||
"value": "Writes the text representation of the specified objects to the standard output stream using the specified format information.",
|
||||
"isTrusted": false
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "format",
|
||||
"label": "string format",
|
||||
"documentation": {
|
||||
"value": "**format**: A composite format string.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "arg0",
|
||||
"label": "object arg0",
|
||||
"documentation": {
|
||||
"value": "**arg0**: The first object to write using format .",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "arg1",
|
||||
"label": "object arg1",
|
||||
"documentation": {
|
||||
"value": "**arg1**: The second object to write using format .",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "arg2",
|
||||
"label": "object arg2",
|
||||
"documentation": {
|
||||
"value": "**arg2**: The third object to write using format .",
|
||||
"isTrusted": false
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Write",
|
||||
"label": "void Console.Write(string format, params object[] arg)",
|
||||
"documentation": {
|
||||
"value": "Writes the text representation of the specified array of objects to the standard output stream using the specified format information.",
|
||||
"isTrusted": false
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "format",
|
||||
"label": "string format",
|
||||
"documentation": {
|
||||
"value": "**format**: A composite format string.",
|
||||
"isTrusted": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "arg",
|
||||
"label": "params object[] arg",
|
||||
"documentation": {
|
||||
"value": "**arg**: An array of objects to write using format .",
|
||||
"isTrusted": false
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Write",
|
||||
"label": "void Console.Write(uint value)",
|
||||
"documentation": {
|
||||
"value": "Writes the text representation of the specified 32-bit unsigned integer value to the standard output stream.",
|
||||
"isTrusted": false
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "value",
|
||||
"label": "uint value",
|
||||
"documentation": {
|
||||
"value": "**value**: The value to write.",
|
||||
"isTrusted": false
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Write",
|
||||
"label": "void Console.Write(ulong value)",
|
||||
"documentation": {
|
||||
"value": "Writes the text representation of the specified 64-bit unsigned integer value to the standard output stream.",
|
||||
"isTrusted": false
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "value",
|
||||
"label": "ulong value",
|
||||
"documentation": {
|
||||
"value": "**value**: The value to write.",
|
||||
"isTrusted": false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"activeSignature": 0,
|
||||
"activeParameter": 0,
|
||||
"requestId": "TestRun",
|
||||
"diagnostics": [
|
||||
{
|
||||
"start": 192,
|
||||
"end": 197,
|
||||
"message": "ViewportCode.cs(12,21): error CS1501: No overload for method 'Write' takes 0 arguments",
|
||||
"severity": 3,
|
||||
"id": "CS1501",
|
||||
"location": "ViewportCode.cs(12,21): error CS1501"
|
||||
},
|
||||
{
|
||||
"start": 136,
|
||||
"end": 142,
|
||||
"message": "ViewportCode.cs(9,30): error CS0161: 'Sample.Method()': not all code paths return a value",
|
||||
"severity": 3,
|
||||
"id": "CS0161",
|
||||
"location": "ViewportCode.cs(9,30): error CS0161"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,283 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Assent;
|
||||
using FluentAssertions;
|
||||
using Microsoft.DotNet.Try.Project;
|
||||
using Microsoft.DotNet.Try.Protocol;
|
||||
using Microsoft.DotNet.Try.Protocol.Tests;
|
||||
using Recipes;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace MLS.Agent.Tests.ApiContracts
|
||||
{
|
||||
public class ApiOutputContractTests : ApiViaHttpTestsBase
|
||||
{
|
||||
private readonly Configuration configuration;
|
||||
|
||||
public ApiOutputContractTests(ITestOutputHelper output) : base(output)
|
||||
{
|
||||
configuration = new Configuration()
|
||||
.UsingExtension("json");
|
||||
|
||||
configuration = configuration.SetInteractive(true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task The_Run_contract_for_compiling_code_has_not_been_broken()
|
||||
{
|
||||
var viewport = ViewportCode();
|
||||
|
||||
var requestJson = new WorkspaceRequest(
|
||||
new Workspace(
|
||||
workspaceType: "console",
|
||||
buffers: new[]
|
||||
{
|
||||
EntrypointCode(),
|
||||
viewport
|
||||
}),
|
||||
activeBufferId: viewport.Id,
|
||||
requestId: "TestRun");
|
||||
|
||||
var response = await CallRun(requestJson.ToJson());
|
||||
|
||||
var result = await response.Content.ReadAsStringAsync();
|
||||
|
||||
this.Assent(result.FormatJson(), configuration);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task The_Run_contract_for_noncompiling_code_has_not_been_broken()
|
||||
{
|
||||
var viewport = ViewportCode("doesn't compile");
|
||||
|
||||
var request = new WorkspaceRequest(
|
||||
new Workspace(
|
||||
workspaceType: "console",
|
||||
buffers: new[]
|
||||
{
|
||||
EntrypointCode(),
|
||||
viewport
|
||||
}),
|
||||
activeBufferId: viewport.Id,
|
||||
requestId: "TestRun");
|
||||
|
||||
var requestBody = request.ToJson();
|
||||
|
||||
var response = await CallRun(requestBody);
|
||||
|
||||
var result = await response.Content.ReadAsStringAsync();
|
||||
|
||||
this.Assent(RemoveMachineSpecificPaths(result).FormatJson(), configuration);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task The_Compile_contract_for_compiling_code_has_not_been_broken()
|
||||
{
|
||||
var viewport = ViewportCode();
|
||||
|
||||
var requestJson = new WorkspaceRequest(
|
||||
new Workspace(
|
||||
workspaceType: "console",
|
||||
buffers: new[]
|
||||
{
|
||||
EntrypointCode(),
|
||||
viewport
|
||||
}),
|
||||
activeBufferId: viewport.Id,
|
||||
requestId: "TestRun");
|
||||
|
||||
var response = await CallCompile(requestJson.ToJson());
|
||||
|
||||
var result = await response.Content.ReadAsStringAsync();
|
||||
|
||||
var compileResult = result.FromJsonTo<CompileResult>();
|
||||
|
||||
compileResult.Base64Assembly.Should().NotBeNullOrWhiteSpace();
|
||||
compileResult = new CompileResult(
|
||||
compileResult.Succeeded,
|
||||
"",
|
||||
compileResult.GetFeature<Diagnostics>(),
|
||||
compileResult.RequestId);
|
||||
|
||||
result = compileResult.ToJson().FormatJson();
|
||||
|
||||
this.Assent(result, configuration);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task The_Compile_contract_for_noncompiling_code_has_not_been_broken()
|
||||
{
|
||||
var viewport = ViewportCode("doesn't compile");
|
||||
|
||||
var request = new WorkspaceRequest(
|
||||
new Workspace(
|
||||
workspaceType: "console",
|
||||
buffers: new[]
|
||||
{
|
||||
EntrypointCode(),
|
||||
viewport
|
||||
}),
|
||||
activeBufferId: viewport.Id,
|
||||
requestId: "TestRun");
|
||||
|
||||
var requestBody = request.ToJson();
|
||||
|
||||
var response = await CallCompile(requestBody);
|
||||
|
||||
var result = await response.Content.ReadAsStringAsync();
|
||||
|
||||
this.Assent(RemoveMachineSpecificPaths(result).FormatJson(), configuration);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task The_Completions_contract_has_not_been_broken()
|
||||
{
|
||||
var viewport = ViewportCode("Console.Ou$$");
|
||||
|
||||
var requestJson = new WorkspaceRequest(
|
||||
new Workspace(
|
||||
workspaceType: "console",
|
||||
buffers: new[]
|
||||
{
|
||||
EntrypointCode(),
|
||||
viewport
|
||||
}),
|
||||
activeBufferId: viewport.Id,
|
||||
requestId: "TestRun").ToJson();
|
||||
|
||||
var response = await CallCompletion(requestJson);
|
||||
|
||||
var result = await response.Content.ReadAsStringAsync();
|
||||
|
||||
this.Assent(result.FormatJson(), configuration);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task The_signature_help_contract_has_not_been_broken()
|
||||
{
|
||||
var viewport = ViewportCode("Console.Write($$);");
|
||||
|
||||
var requestJson = new WorkspaceRequest(
|
||||
new Workspace(
|
||||
workspaceType: "console",
|
||||
buffers: new[]
|
||||
{
|
||||
EntrypointCode(),
|
||||
viewport
|
||||
}),
|
||||
activeBufferId: viewport.Id,
|
||||
requestId: "TestRun").ToJson();
|
||||
|
||||
var response = await CallSignatureHelp(requestJson);
|
||||
|
||||
var result = await response.Content.ReadAsStringAsync();
|
||||
|
||||
this.Assent(result.FormatJson(), configuration);
|
||||
}
|
||||
|
||||
[Fact(Skip = "Needs moved onto Package2")]
|
||||
public async Task The_instrumentation_contract_has_not_been_broken()
|
||||
{
|
||||
var requestJson = new WorkspaceRequest(
|
||||
new Workspace(
|
||||
workspaceType: "console",
|
||||
buffers: new[]
|
||||
{
|
||||
EntrypointCode("int a = 1; int b = 2; a = 3; b = a;")
|
||||
},
|
||||
includeInstrumentation: true),
|
||||
requestId: "TestRun"
|
||||
).ToJson();
|
||||
|
||||
var response = await CallRun(requestJson);
|
||||
|
||||
var result = await response.Content.ReadAsStringAsync();
|
||||
|
||||
this.Assent(RemoveMachineSpecificPaths(result).FormatJson(), configuration);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task The_run_contract_with_no_instrumentation_has_not_been_broken()
|
||||
{
|
||||
var requestJson = new WorkspaceRequest(
|
||||
new Workspace(
|
||||
workspaceType: "console",
|
||||
buffers: new[]
|
||||
{
|
||||
EntrypointCode("int a = 1; int b = 2; a = 3; b = a;")
|
||||
},
|
||||
includeInstrumentation: false),
|
||||
requestId: "TestRun"
|
||||
).ToJson();
|
||||
|
||||
var response = await CallRun(requestJson);
|
||||
|
||||
var result = await response.Content.ReadAsStringAsync();
|
||||
|
||||
this.Assent(RemoveMachineSpecificPaths(result).FormatJson(), configuration);
|
||||
}
|
||||
|
||||
private static Buffer EntrypointCode(string mainContent = @"Console.WriteLine(Sample.Method());$$")
|
||||
{
|
||||
var input = $@"
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Example
|
||||
{{
|
||||
public class Program
|
||||
{{
|
||||
public static void Main()
|
||||
{{
|
||||
{mainContent}
|
||||
}}
|
||||
}}
|
||||
}}".EnforceLF();
|
||||
|
||||
MarkupTestFile.GetPosition(input, out var output, out var position);
|
||||
|
||||
return new Buffer(
|
||||
"Program.cs",
|
||||
output,
|
||||
position ?? 0);
|
||||
}
|
||||
|
||||
private static string RemoveMachineSpecificPaths(string result)
|
||||
{
|
||||
var regex = new Regex($@"(""location"":\s*"")([^""]*[\\/]+)?([^""]*"")");
|
||||
|
||||
return regex.Replace(result, "$1$3");
|
||||
}
|
||||
|
||||
private static Buffer ViewportCode(string methodContent = @"return ""Hello world!"";$$ ")
|
||||
{
|
||||
var input = $@"
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
namespace Example
|
||||
{{
|
||||
public static class Sample
|
||||
{{
|
||||
public static object Method()
|
||||
{{
|
||||
#region viewport
|
||||
{methodContent}
|
||||
#endregion
|
||||
}}
|
||||
}}
|
||||
}}".EnforceLF();
|
||||
|
||||
MarkupTestFile.GetPosition(input, out var output, out var position);
|
||||
|
||||
return new Buffer(
|
||||
"ViewportCode.cs",
|
||||
output,
|
||||
position ?? 0);
|
||||
}
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,95 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Clockwise;
|
||||
using MLS.Agent.CommandLine;
|
||||
using Pocket;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace MLS.Agent.Tests
|
||||
{
|
||||
public abstract class ApiViaHttpTestsBase : IDisposable
|
||||
{
|
||||
private readonly CompositeDisposable _disposables = new CompositeDisposable();
|
||||
|
||||
protected ApiViaHttpTestsBase(ITestOutputHelper output)
|
||||
{
|
||||
_disposables.Add(output.SubscribeToPocketLogger());
|
||||
_disposables.Add(VirtualClock.Start());
|
||||
EnsureConsoleWorkspaceCreated();
|
||||
}
|
||||
|
||||
private void EnsureConsoleWorkspaceCreated()
|
||||
{
|
||||
Task.Run(() => WorkspaceServer.Tests.Default.PackageRegistry.ValueAsync()).Wait();
|
||||
}
|
||||
|
||||
public void Dispose() => _disposables.Dispose();
|
||||
|
||||
protected static Task<HttpResponseMessage> CallCompletion(
|
||||
string requestBody,
|
||||
int? timeoutMs = null) =>
|
||||
Call("/workspace/completion",
|
||||
requestBody,
|
||||
timeoutMs);
|
||||
|
||||
protected static Task<HttpResponseMessage> CallRun(
|
||||
string requestBody,
|
||||
int? timeoutMs = null,
|
||||
StartupOptions options = null) =>
|
||||
Call("/workspace/run",
|
||||
requestBody,
|
||||
timeoutMs,
|
||||
options);
|
||||
|
||||
protected static Task<HttpResponseMessage> CallCompile(
|
||||
string requestBody,
|
||||
int? timeoutMs = null,
|
||||
StartupOptions options = null) =>
|
||||
Call("/workspace/compile",
|
||||
requestBody,
|
||||
timeoutMs,
|
||||
options);
|
||||
|
||||
protected static Task<HttpResponseMessage> CallSignatureHelp(
|
||||
string requestBody,
|
||||
int? timeoutMs = null) =>
|
||||
Call("/workspace/signaturehelp",
|
||||
requestBody,
|
||||
timeoutMs);
|
||||
|
||||
private static async Task<HttpResponseMessage> Call(
|
||||
string relativeUrl,
|
||||
string requestBody,
|
||||
int? timeoutMs = null,
|
||||
StartupOptions options = null)
|
||||
{
|
||||
HttpResponseMessage response;
|
||||
using (var agent = new AgentService(options ?? StartupOptions.FromCommandLine("hosted")))
|
||||
{
|
||||
var request = new HttpRequestMessage(
|
||||
HttpMethod.Post,
|
||||
relativeUrl)
|
||||
{
|
||||
Content = new StringContent(
|
||||
requestBody,
|
||||
Encoding.UTF8,
|
||||
"application/json")
|
||||
};
|
||||
|
||||
if (timeoutMs != null)
|
||||
{
|
||||
request.Headers.Add("Timeout", timeoutMs.Value.ToString("F0"));
|
||||
}
|
||||
|
||||
response = await agent.SendAsync(request);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,435 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.CommandLine.IO;
|
||||
using System.CommandLine.Parsing;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Microsoft.DotNet.Interactive.Telemetry;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using MLS.Agent.CommandLine;
|
||||
using MLS.Agent.Tools;
|
||||
using WorkspaceServer.Packaging;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace MLS.Agent.Tests.CommandLine
|
||||
{
|
||||
public class CommandLineParserTests : IDisposable
|
||||
{
|
||||
private readonly ITestOutputHelper _output;
|
||||
private readonly TestConsole _console = new TestConsole();
|
||||
private StartupOptions _startOptions;
|
||||
private readonly Parser _parser;
|
||||
private TryGitHubOptions _tryGitHubOptions;
|
||||
private PackOptions _packOptions;
|
||||
private InstallOptions _installOptions;
|
||||
private PackageSource _installPackageSource;
|
||||
private VerifyOptions _verifyOptions;
|
||||
private DemoOptions _demoOptions;
|
||||
private PublishOptions _publishOptions;
|
||||
|
||||
public CommandLineParserTests(ITestOutputHelper output)
|
||||
{
|
||||
_output = output;
|
||||
|
||||
_parser = CommandLineParser.Create(
|
||||
new ServiceCollection(),
|
||||
startServer: (options, invocationContext) =>
|
||||
{
|
||||
_startOptions = options;
|
||||
},
|
||||
demo: (options, console, context, startOptions) =>
|
||||
{
|
||||
_demoOptions = options;
|
||||
return Task.CompletedTask;
|
||||
},
|
||||
tryGithub: (options, c) =>
|
||||
{
|
||||
_tryGitHubOptions = options;
|
||||
return Task.CompletedTask;
|
||||
},
|
||||
pack: (options, console) =>
|
||||
{
|
||||
_packOptions = options;
|
||||
return Task.CompletedTask;
|
||||
},
|
||||
install: (options, console) =>
|
||||
{
|
||||
_installOptions = options;
|
||||
_installPackageSource = options.AddSource;
|
||||
return Task.CompletedTask;
|
||||
},
|
||||
verify: (options, console, startupOptions, context) =>
|
||||
{
|
||||
_verifyOptions = options;
|
||||
return Task.FromResult(0);
|
||||
},
|
||||
telemetry: new FakeTelemetry(),
|
||||
publish: (options, console, startupOptions, context) =>
|
||||
{
|
||||
_publishOptions = options;
|
||||
return Task.FromResult(0);
|
||||
},
|
||||
firstTimeUseNoticeSentinel: new NopFirstTimeUseNoticeSentinel());
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_output.WriteLine(_console.Error.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Parse_empty_command_line_has_sane_defaults()
|
||||
{
|
||||
await _parser.InvokeAsync("hosted", _console);
|
||||
|
||||
_startOptions.Production.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Parse_production_mode_flag_switches_option_to_production()
|
||||
{
|
||||
await _parser.InvokeAsync("hosted --production", _console);
|
||||
|
||||
_startOptions.Production.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Parse_root_directory_with_a_valid_path_succeeds()
|
||||
{
|
||||
var path = Directory.GetCurrentDirectory();
|
||||
await _parser.InvokeAsync(new[] { path }, _console);
|
||||
_startOptions.RootDirectory.GetFullyQualifiedRoot().FullName.Should().Be(path + Path.DirectorySeparatorChar);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task It_parses_log_output_directory()
|
||||
{
|
||||
var logPath = new DirectoryInfo(Path.GetTempPath());
|
||||
|
||||
await _parser.InvokeAsync($"--log-path {logPath}", _console);
|
||||
|
||||
_startOptions
|
||||
.LogPath
|
||||
.FullName
|
||||
.Should()
|
||||
.Be(logPath.FullName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task It_parses_verbose_option()
|
||||
{
|
||||
await _parser.InvokeAsync($"--verbose", _console);
|
||||
|
||||
_startOptions
|
||||
.Verbose
|
||||
.Should()
|
||||
.BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task It_parses_the_package_option()
|
||||
{
|
||||
await _parser.InvokeAsync("--package console", _console);
|
||||
|
||||
_startOptions
|
||||
.Package
|
||||
.Should()
|
||||
.Be("console");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task It_parses_the_package_version_option()
|
||||
{
|
||||
await _parser.InvokeAsync("--package-version 1.2.3-beta", _console);
|
||||
|
||||
_startOptions
|
||||
.PackageVersion
|
||||
.Should()
|
||||
.Be("1.2.3-beta");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Parse_empty_command_line_has_current_directory_as_root_directory()
|
||||
{
|
||||
await _parser.InvokeAsync("", _console);
|
||||
_startOptions.RootDirectory.GetFullyQualifiedRoot().FullName.Should().Be(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Parse_root_directory_with_a_non_existing_path_fails()
|
||||
{
|
||||
await _parser.InvokeAsync("INVALIDPATH", _console);
|
||||
_startOptions.Should().BeNull();
|
||||
_console.Error.ToString().Should().Match("*Directory does not exist: INVALIDPATH*");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Parse_uri_workspace()
|
||||
{
|
||||
await _parser.InvokeAsync("--uri https://google.com/foo.md", _console);
|
||||
_startOptions.Uri.Should().Be("https://google.com/foo.md");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Parse_enable_preview_features_flag()
|
||||
{
|
||||
await _parser.InvokeAsync("--enable-preview-features", _console);
|
||||
_startOptions.EnablePreviewFeatures.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Parse_language_service_mode_flag_switches_option_to_language_service()
|
||||
{
|
||||
await _parser.InvokeAsync("hosted --language-service", _console);
|
||||
_startOptions.IsLanguageService.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parse_key_without_parameter_fails_the_parse()
|
||||
{
|
||||
_parser.Parse("hosted -k")
|
||||
.Errors
|
||||
.Should()
|
||||
.Contain(e => e.Message == "Required argument missing for option: -k");
|
||||
|
||||
_parser.Parse("hosted --key")
|
||||
.Errors
|
||||
.Should()
|
||||
.Contain(e => e.Message == "Required argument missing for option: --key");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Parse_key_with_parameter_succeeds()
|
||||
{
|
||||
await _parser.InvokeAsync("hosted -k abc123", _console);
|
||||
_startOptions.Key.Should().Be("abc123");
|
||||
|
||||
await _parser.InvokeAsync("hosted --key abc123", _console);
|
||||
_startOptions.Key.Should().Be("abc123");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AiKey_defaults_to_null()
|
||||
{
|
||||
await _parser.InvokeAsync("hosted", _console);
|
||||
_startOptions.ApplicationInsightsKey.Should().BeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Parses_the_port()
|
||||
{
|
||||
await _parser.InvokeAsync("--port 6000", _console);
|
||||
_startOptions.Port.Should().Be(6000);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Negative_port_fails_the_parse()
|
||||
{
|
||||
_parser.Parse("--port -8090")
|
||||
.Errors
|
||||
.Should()
|
||||
.Contain(e => e.Message == "Invalid argument for --port option");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parse_application_insights_key_without_parameter_fails_the_parse()
|
||||
{
|
||||
var result = _parser.Parse("hosted --ai-key");
|
||||
|
||||
result.Errors.Should().Contain(e => e.Message == "Required argument missing for option: --ai-key");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Parse_aiKey_with_parameter_succeeds()
|
||||
{
|
||||
await _parser.InvokeAsync("hosted --ai-key abc123", _console);
|
||||
_startOptions.ApplicationInsightsKey.Should().Be("abc123");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task When_root_command_is_specified_then_agent_is_in_try_mode()
|
||||
{
|
||||
await _parser.InvokeAsync("", _console);
|
||||
_startOptions.Mode.Should().Be(StartupMode.Try);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task When_hosted_command_is_specified_then_agent_is_in_hosted_mode()
|
||||
{
|
||||
await _parser.InvokeAsync("hosted", _console);
|
||||
_startOptions.Mode.Should().Be(StartupMode.Hosted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GitHub_handler_not_run_if_argument_is_missing()
|
||||
{
|
||||
await _parser.InvokeAsync("github", _console);
|
||||
_tryGitHubOptions.Should().BeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GitHub_handler_run_if_argument_is_present()
|
||||
{
|
||||
await _parser.InvokeAsync("github roslyn", _console);
|
||||
_tryGitHubOptions.Repo.Should().Be("roslyn");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Pack_not_run_if_argument_is_missing()
|
||||
{
|
||||
var console = new TestConsole();
|
||||
await _parser.InvokeAsync("pack", console);
|
||||
console.Out.ToString().Should().Contain("pack [options] <PackTarget>");
|
||||
_packOptions.Should().BeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Pack_parses_directory_info()
|
||||
{
|
||||
var console = new TestConsole();
|
||||
var expected = Path.GetDirectoryName(typeof(PackCommand).Assembly.Location);
|
||||
|
||||
await _parser.InvokeAsync($"pack {expected}", console);
|
||||
_packOptions.PackTarget.FullName.Should().Be(expected);
|
||||
_packOptions.EnableWasm.Should().Be(false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Pack_parses_version()
|
||||
{
|
||||
var console = new TestConsole();
|
||||
var directoryName = Path.GetDirectoryName(typeof(PackCommand).Assembly.Location);
|
||||
var expectedVersion = "2.0.0";
|
||||
|
||||
await _parser.InvokeAsync($"pack {directoryName} --version {expectedVersion}", console);
|
||||
_packOptions.Version.Should().Be(expectedVersion);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Pack_parses_enable_wasm()
|
||||
{
|
||||
var console = new TestConsole();
|
||||
var directoryName = Path.GetDirectoryName(typeof(PackCommand).Assembly.Location);
|
||||
|
||||
await _parser.InvokeAsync($"pack {directoryName} --enable-wasm", console);
|
||||
_packOptions.EnableWasm.Should().Be(true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Install_not_run_if_argument_is_missing()
|
||||
{
|
||||
var console = new TestConsole();
|
||||
await _parser.InvokeAsync("install", console);
|
||||
console.Out.ToString().Should().Contain("install [options] <PackageName>");
|
||||
_installOptions.Should().BeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Install_parses_source_option()
|
||||
{
|
||||
var console = new TestConsole();
|
||||
|
||||
var expectedPackageSource = Path.GetDirectoryName(typeof(PackCommand).Assembly.Location);
|
||||
|
||||
await _parser.InvokeAsync($"install --add-source {expectedPackageSource} the-package", console);
|
||||
|
||||
_installOptions.PackageName.Should().Be("the-package");
|
||||
_installPackageSource.ToString().Should().Be(expectedPackageSource);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Verify_argument_specifies_root_directory()
|
||||
{
|
||||
var directory = Path.GetDirectoryName(typeof(VerifyCommand).Assembly.Location);
|
||||
|
||||
await _parser.InvokeAsync($"verify {directory}", _console);
|
||||
|
||||
_verifyOptions.RootDirectory
|
||||
.GetFullyQualifiedRoot()
|
||||
.FullName
|
||||
.Should()
|
||||
.Be(directory + Path.DirectorySeparatorChar);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Verify_takes_current_directory_as_default_if_none_is_specified()
|
||||
{
|
||||
await _parser.InvokeAsync($"verify", _console);
|
||||
|
||||
_verifyOptions.RootDirectory
|
||||
.GetFullyQualifiedRoot()
|
||||
.FullName
|
||||
.Should()
|
||||
.Be(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Demo_allows_output_path_to_be_specified()
|
||||
{
|
||||
var expected = Path.GetTempPath();
|
||||
|
||||
await _parser.InvokeAsync($"demo --output {expected}", _console);
|
||||
|
||||
_demoOptions
|
||||
.Output
|
||||
.FullName
|
||||
.Should()
|
||||
.Be(expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Publish_root_directory_defaults_to_current_directory()
|
||||
{
|
||||
await _parser.InvokeAsync("publish", _console);
|
||||
|
||||
_publishOptions
|
||||
.RootDirectory
|
||||
.GetFullyQualifiedRoot()
|
||||
.FullName
|
||||
.Should()
|
||||
.Be(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Publish_target_directory_defaults_to_root_directory()
|
||||
{
|
||||
var rootDir = Path.GetTempPath();
|
||||
|
||||
await _parser.InvokeAsync($"publish {rootDir}", _console);
|
||||
|
||||
_publishOptions
|
||||
.TargetDirectory
|
||||
.GetFullyQualifiedRoot()
|
||||
.FullName
|
||||
.Should()
|
||||
.Be(rootDir);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Publish_can_specify_source_and_target_directory()
|
||||
{
|
||||
var rootDir = Path.GetTempPath();
|
||||
var targetDir = Path.Combine(Path.GetTempPath(), "target");
|
||||
|
||||
await _parser.InvokeAsync($"publish --target-directory {targetDir} {rootDir}", _console);
|
||||
|
||||
_publishOptions
|
||||
.RootDirectory
|
||||
.GetFullyQualifiedRoot()
|
||||
.FullName
|
||||
.Should()
|
||||
.Be(rootDir);
|
||||
|
||||
_publishOptions
|
||||
.TargetDirectory
|
||||
.GetFullyQualifiedRoot()
|
||||
.FullName
|
||||
.Should()
|
||||
.Be(targetDir + Path.DirectorySeparatorChar);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,136 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.CommandLine.IO;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using MLS.Agent.CommandLine;
|
||||
using MLS.Agent.Tools;
|
||||
using WorkspaceServer.Tests;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace MLS.Agent.Tests.CommandLine
|
||||
{
|
||||
public class DemoCommandTests
|
||||
{
|
||||
private readonly ITestOutputHelper _output;
|
||||
|
||||
public DemoCommandTests(ITestOutputHelper output)
|
||||
{
|
||||
_output = output;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Demo_project_passes_verification()
|
||||
{
|
||||
var console = new TestConsole();
|
||||
|
||||
var outputDirectory = Create.EmptyWorkspace().Directory;
|
||||
var packageFile = outputDirectory.Subdirectory("Snippets")
|
||||
.File("Snippets.csproj");
|
||||
|
||||
await DemoCommand.Do(new DemoOptions(output: outputDirectory), console);
|
||||
|
||||
var resultCode = await VerifyCommand.Do(
|
||||
new VerifyOptions(new FileSystemDirectoryAccessor(outputDirectory)),
|
||||
console,
|
||||
startupOptions: new StartupOptions(package: packageFile.FullName));
|
||||
|
||||
_output.WriteLine(console.Out.ToString());
|
||||
_output.WriteLine(console.Error.ToString());
|
||||
|
||||
resultCode.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Demo_sources_pass_verification()
|
||||
{
|
||||
var console = new TestConsole();
|
||||
|
||||
var demoSourcesDir = Create.EmptyWorkspace().Directory;
|
||||
var packageFile = demoSourcesDir.Subdirectory("Snippets")
|
||||
.File("Snippets.csproj");
|
||||
|
||||
_output.WriteLine(demoSourcesDir.FullName);
|
||||
_output.WriteLine(packageFile.FullName);
|
||||
|
||||
await DemoCommand.Do(new DemoOptions(output: demoSourcesDir), console);
|
||||
|
||||
var resultCode = await VerifyCommand.Do(
|
||||
new VerifyOptions(new FileSystemDirectoryAccessor(demoSourcesDir)),
|
||||
console);
|
||||
|
||||
_output.WriteLine(console.Out.ToString());
|
||||
_output.WriteLine(console.Error.ToString());
|
||||
|
||||
resultCode.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Demo_creates_the_output_directory_if_it_does_not_exist()
|
||||
{
|
||||
var console = new TestConsole();
|
||||
|
||||
var outputDirectory = new DirectoryInfo(
|
||||
Path.Combine(
|
||||
Create.EmptyWorkspace().Directory.FullName,
|
||||
Guid.NewGuid().ToString("N")));
|
||||
|
||||
await DemoCommand.Do(
|
||||
new DemoOptions(output: outputDirectory),
|
||||
console,
|
||||
startServer: (options, context) => { });
|
||||
|
||||
outputDirectory.Refresh();
|
||||
|
||||
outputDirectory.Exists.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Demo_returns_an_error_if_the_output_directory_is_not_empty()
|
||||
{
|
||||
var console = new TestConsole();
|
||||
|
||||
var outputDirectory = Create.EmptyWorkspace().Directory;
|
||||
|
||||
File.WriteAllText(Path.Combine(outputDirectory.FullName, "a file.txt"), "");
|
||||
|
||||
await DemoCommand.Do(
|
||||
new DemoOptions(output: outputDirectory),
|
||||
console,
|
||||
startServer: (options, context) => { });
|
||||
|
||||
var resultCode = await VerifyCommand.Do(
|
||||
new VerifyOptions(new FileSystemDirectoryAccessor(outputDirectory)),
|
||||
console);
|
||||
|
||||
resultCode.Should().NotBe(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Demo_starts_the_server_if_there_are_no_errors()
|
||||
{
|
||||
var console = new TestConsole();
|
||||
|
||||
var outputDirectory = Create.EmptyWorkspace().Directory;
|
||||
|
||||
StartupOptions startupOptions = null;
|
||||
await DemoCommand.Do(
|
||||
new DemoOptions(output: outputDirectory),
|
||||
console,
|
||||
(options, context) => startupOptions = options);
|
||||
|
||||
await VerifyCommand.Do(
|
||||
new VerifyOptions(new FileSystemDirectoryAccessor(outputDirectory)),
|
||||
console);
|
||||
|
||||
_output.WriteLine(console.Out.ToString());
|
||||
_output.WriteLine(console.Error.ToString());
|
||||
|
||||
startupOptions.Uri.Should().Be(new Uri("QuickStart.md", UriKind.Relative));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.CommandLine.IO;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using MLS.Agent.CommandLine;
|
||||
using MLS.Repositories;
|
||||
using Xunit;
|
||||
|
||||
namespace MLS.Agent.Tests.CommandLine
|
||||
{
|
||||
public class GitHubCommandTests
|
||||
{
|
||||
private readonly IRepoLocator _locator = new RepoLocatorSimulator();
|
||||
|
||||
class TestRepoLocator : IRepoLocator
|
||||
{
|
||||
private readonly IEnumerable<Repo> _repos;
|
||||
|
||||
|
||||
public TestRepoLocator(IEnumerable<Repo> repos)
|
||||
{
|
||||
_repos = repos;
|
||||
}
|
||||
|
||||
public Task<IEnumerable<Repo>> LocateRepo(string repo)
|
||||
{
|
||||
return Task.FromResult(_repos);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task It_reports_no_matches()
|
||||
{
|
||||
var console = new TestConsole();
|
||||
await GitHubHandler.Handler(new TryGitHubOptions("foo"), console, _locator);
|
||||
console.Out.ToString().Replace("\r\n", "\n")
|
||||
.Should().Be("Didn't find any repos called `foo`\n");
|
||||
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task It_finds_the_requested_repo()
|
||||
{
|
||||
var console = new TestConsole();
|
||||
await GitHubHandler.Handler(new TryGitHubOptions("rchande/2660eaec-6af8-452d-b70d-41227d616cd9"), console, _locator);
|
||||
console.Out.ToString().Replace("\r\n", "\n").
|
||||
Should().Be("Found repo `rchande/2660eaec-6af8-452d-b70d-41227d616cd9`\nTo try `rchande/2660eaec-6af8-452d-b70d-41227d616cd9`, cd to your desired directory and run the following command:\n\n\tgit clone https://github.com/rchande/2660eaec-6af8-452d-b70d-41227d616cd9.git && dotnet try .\n");
|
||||
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task It_asks_for_disambiguation()
|
||||
{
|
||||
var console = new TestConsole();
|
||||
await GitHubHandler.Handler(new TryGitHubOptions("rchande/tribble"), console, _locator);
|
||||
console.Out.ToString().Replace("\r\n", "\n").Should()
|
||||
.Be("Which of the following did you mean?\n\trchande/upgraded-octo-tribble.\n\trchande/downgraded-octo-tribble.\n");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.CommandLine.IO;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Microsoft.DotNet.Interactive.Utility;
|
||||
using MLS.Agent.CommandLine;
|
||||
using MLS.Agent.Tools;
|
||||
using WorkspaceServer.Packaging;
|
||||
using WorkspaceServer.Tests;
|
||||
using Xunit;
|
||||
|
||||
namespace MLS.Agent.Tests.CommandLine
|
||||
{
|
||||
public class PackCommandTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task Pack_project_creates_a_nupkg_with_passed_version()
|
||||
{
|
||||
var asset = await Create.NetstandardWorkspaceCopy();
|
||||
|
||||
var console = new TestConsole();
|
||||
|
||||
await PackCommand.Do(new PackOptions(asset.Directory, "3.4.5"), console);
|
||||
|
||||
asset.Directory
|
||||
.GetFiles()
|
||||
.Should()
|
||||
.Contain(f => f.Name.Contains("3.4.5.nupkg"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Pack_project_works_with_blazor()
|
||||
{
|
||||
var asset = await Create.NetstandardWorkspaceCopy();
|
||||
|
||||
var console = new TestConsole();
|
||||
|
||||
await PackCommand.Do(new PackOptions(asset.Directory, enableWasm: true), console);
|
||||
|
||||
asset.Directory
|
||||
.GetFiles()
|
||||
.Should()
|
||||
.Contain(f => f.Name.Contains("nupkg"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Pack_project_blazor_contents()
|
||||
{
|
||||
var asset = await Create.NetstandardWorkspaceCopy();
|
||||
|
||||
var packageName = Path.GetFileNameWithoutExtension(asset.Directory.GetFiles("*.csproj").First().Name);
|
||||
|
||||
var console = new TestConsole();
|
||||
|
||||
await PackCommand.Do(new PackOptions(asset.Directory, enableWasm: true), console);
|
||||
|
||||
asset.Directory
|
||||
.GetFiles()
|
||||
.Should()
|
||||
.Contain(f => f.Name.Contains("nupkg"));
|
||||
|
||||
var dotnet = new Dotnet(asset.Directory);
|
||||
|
||||
var result = await dotnet.ToolInstall(packageName, asset.Directory, new PackageSource(asset.Directory.FullName).ToString());
|
||||
|
||||
var exe = Path.Combine(asset.Directory.FullName, packageName);
|
||||
|
||||
var tool = WorkspaceServer.WorkspaceFeatures.PackageTool.TryCreateFromDirectory(packageName, new FileSystemDirectoryAccessor(asset.Directory));
|
||||
|
||||
await tool.Prepare();
|
||||
|
||||
var wasmDirectory = await tool.LocateWasmAsset();
|
||||
wasmDirectory.Should().NotBeNull();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,246 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.CommandLine.IO;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Microsoft.DotNet.Try.Protocol.Tests;
|
||||
using MLS.Agent.CommandLine;
|
||||
using MLS.Agent.Tools;
|
||||
using MLS.Agent.Tools.Tests;
|
||||
using WorkspaceServer.Tests;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace MLS.Agent.Tests.CommandLine
|
||||
{
|
||||
public class PublishCommandTests
|
||||
{
|
||||
private const string CsprojContents = @"<Project Sdk=""Microsoft.NET.Sdk"">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
";
|
||||
|
||||
private const string CompilingProgramWithRegionCs = @"
|
||||
using System;
|
||||
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
#region null_coalesce
|
||||
var length = (args[0] as string)?.Length ?? 0;
|
||||
#endregion
|
||||
|
||||
#region userCodeRegion
|
||||
#endregion
|
||||
}
|
||||
}";
|
||||
|
||||
public class WithMarkdownOutputFormat : WithPublish
|
||||
{
|
||||
public WithMarkdownOutputFormat(ITestOutputHelper output) : base(output) {}
|
||||
|
||||
[Theory]
|
||||
[InlineData("##Title")]
|
||||
[InlineData("markdown with line\r\nbreak")]
|
||||
[InlineData("markdown with linux line\nbreak")]
|
||||
[InlineData("[link](https://try.dot.net/)")]
|
||||
public async Task When_there_are_no_code_fence_annotations_markdown_is_unchanged(string markdown)
|
||||
{
|
||||
var files = PrepareFiles(
|
||||
("doc.md", markdown)
|
||||
);
|
||||
|
||||
var (publishOutput, resultCode) = await DoPublish(files);
|
||||
|
||||
resultCode.Should().Be(0);
|
||||
publishOutput.OutputFiles.Single().Content.Should().Be(markdown.EnforceLF());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"
|
||||
## C# null coalesce example
|
||||
``` cs --source-file ./project/Program.cs --region null_coalesce --project ./project/some.csproj
|
||||
```
|
||||
")] [InlineData(@"
|
||||
## C# null coalesce example
|
||||
``` cs --source-file ./project/Program.cs --region null_coalesce --project ./project/some.csproj
|
||||
var length = some buggy c# example code
|
||||
|
||||
```
|
||||
")]
|
||||
public async Task When_there_are_code_fence_annotations_in_markdown_content_of_the_fenced_section_is_replaced_with_the_project_code(string markdown)
|
||||
{
|
||||
var files = PrepareFiles(
|
||||
("./folder/project/some.csproj", CsprojContents),
|
||||
("./folder/project/Program.cs", CompilingProgramWithRegionCs),
|
||||
("./folder/doc.md", markdown)
|
||||
);
|
||||
|
||||
var (publishOutput, resultCode) = await DoPublish(files);
|
||||
|
||||
resultCode.Should().Be(0);
|
||||
MarkdownShouldBeEquivalent(publishOutput.OutputFiles.Single().Content, @"
|
||||
## C# null coalesce example
|
||||
``` cs --source-file ./project/Program.cs --region null_coalesce --project ./project/some.csproj
|
||||
var length = (args[0] as string)?.Length ?? 0;
|
||||
```
|
||||
");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"
|
||||
``` cs --source-file ./project/Program.cs --region the_region --project ./project/some.csproj --session one
|
||||
Console.WriteLine(""hello!"");
|
||||
```
|
||||
``` console --session one
|
||||
```
|
||||
")]
|
||||
[InlineData(@"
|
||||
``` cs --source-file ./project/Program.cs --region the_region --project ./project/some.csproj --session one
|
||||
Console.WriteLine(""hello!"");
|
||||
```
|
||||
``` console --session one
|
||||
pre-existing text
|
||||
```
|
||||
")]
|
||||
public async Task Content_of_console_annotated_blocks_is_replaced_by_code_output(string markdown)
|
||||
{
|
||||
var files = PrepareFiles(
|
||||
("./project/some.csproj", CsprojContents),
|
||||
("./project/Program.cs", @"
|
||||
using System;
|
||||
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
#region the_region
|
||||
Console.WriteLine(""hello!"");
|
||||
#endregion
|
||||
}
|
||||
}"),
|
||||
("./doc.md", markdown));
|
||||
|
||||
var (publishOutput, resultCode) = await DoPublish(files);
|
||||
|
||||
resultCode.Should().Be(0);
|
||||
|
||||
publishOutput.OutputFiles
|
||||
.Single()
|
||||
.Content
|
||||
.Should()
|
||||
.Contain(@"
|
||||
``` console --session one
|
||||
hello!
|
||||
|
||||
```".EnforceLF());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task When_target_directory_is_sub_directory_of_source_then_markdown_files_in_target_dir_are_not_used_as_sources()
|
||||
{
|
||||
const string markdown = @"
|
||||
## C# null coalesce example
|
||||
``` cs --source-file ./../../project/Program.cs --region null_coalesce --project ./../../project/some.csproj
|
||||
```
|
||||
";
|
||||
var files = PrepareFiles(
|
||||
("./project/some.csproj", CsprojContents),
|
||||
("./project/Program.cs", CompilingProgramWithRegionCs),
|
||||
("./documentation/details/doc.md", markdown),
|
||||
("./doc_output/details/doc.md", "result file of previous publish run, will be overridden when publishing docs and should be ignored as source")
|
||||
);
|
||||
|
||||
var targetDir = (DirectoryInfo)files.GetFullyQualifiedPath(new RelativeDirectoryPath("doc_output"));
|
||||
var target = new InMemoryDirectoryAccessor(targetDir);
|
||||
|
||||
var (publishOutput, resultCode) = await DoPublish(files, target);
|
||||
|
||||
resultCode.Should().Be(0);
|
||||
publishOutput.OutputFiles.Count().Should().Be(1, "Expected existing file in doc_output to be ignored");
|
||||
var outputFilePath = new FileInfo(publishOutput.OutputFiles.Single().Path);
|
||||
var expectedFilePath = target.GetFullyQualifiedPath(new RelativeFilePath("documentation/details/doc.md"));
|
||||
|
||||
outputFilePath.FullName.Should().Be(expectedFilePath.FullName);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class WithPublish
|
||||
{
|
||||
private static PublishOptions Options(IDirectoryAccessor source, IDirectoryAccessor target = null) => new PublishOptions(source, target ?? source, PublishFormat.Markdown);
|
||||
|
||||
private readonly ITestOutputHelper _output;
|
||||
|
||||
protected WithPublish(ITestOutputHelper output) => _output = output;
|
||||
|
||||
protected static IDirectoryAccessor PrepareFiles(
|
||||
params (string path, string content)[] files)
|
||||
{
|
||||
var rootDirectory = Create.EmptyWorkspace(isRebuildablePackage: true).Directory;
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(rootDirectory);
|
||||
foreach (var file in files)
|
||||
{
|
||||
directoryAccessor.Add(file);
|
||||
}
|
||||
directoryAccessor.CreateFiles();
|
||||
|
||||
return directoryAccessor;
|
||||
}
|
||||
|
||||
protected async Task<(PublishOutput publishOutput, int resultCode)> DoPublish(
|
||||
IDirectoryAccessor rootDirectory,
|
||||
IDirectoryAccessor targetDirectory = null)
|
||||
{
|
||||
var console = new TestConsole();
|
||||
|
||||
var output = new PublishOutput();
|
||||
|
||||
var resultCode = await PublishCommand.Do(
|
||||
Options(rootDirectory, targetDirectory),
|
||||
console,
|
||||
context: new MarkdownProcessingContext(rootDirectory,
|
||||
writeFile: output.Add));
|
||||
|
||||
_output.WriteLine(console.Out.ToString());
|
||||
|
||||
return (output, resultCode);
|
||||
}
|
||||
}
|
||||
|
||||
private static void MarkdownShouldBeEquivalent(string expected, string actual)
|
||||
{
|
||||
static string Normalize(string input) => Regex.Replace(input.Trim(), @"[\s]+", " ");
|
||||
|
||||
var expectedNormalized = Normalize(expected);
|
||||
var actualNormalized = Normalize(actual);
|
||||
|
||||
actualNormalized.Should().Be(expectedNormalized);
|
||||
}
|
||||
|
||||
public class PublishOutput
|
||||
{
|
||||
private readonly List<OutputFile> _outputFiles = new List<OutputFile>();
|
||||
public IEnumerable<OutputFile> OutputFiles => _outputFiles;
|
||||
|
||||
public void Add(string path, string content) => _outputFiles.Add(new OutputFile(path, content));
|
||||
}
|
||||
|
||||
public class OutputFile
|
||||
{
|
||||
public string Path { get; }
|
||||
public string Content { get; }
|
||||
|
||||
public OutputFile(string path, string content)
|
||||
{
|
||||
Path = path;
|
||||
Content = content;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using MLS.Agent.CommandLine;
|
||||
using MLS.Agent.Tools;
|
||||
using WorkspaceServer;
|
||||
using Xunit;
|
||||
|
||||
namespace MLS.Agent.Tests.CommandLine
|
||||
{
|
||||
public class StartupOptionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void When_Production_is_true_and_in_hosted_mode_then_EnvironmentName_is_production()
|
||||
{
|
||||
var options = new StartupOptions(production: true, rootDirectory: null);
|
||||
|
||||
options.EnvironmentName.Should().Be(Environments.Production);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void When_Production_is_true_and_not_in_hosted_mode_then_EnvironmentName_is_production()
|
||||
{
|
||||
var options = new StartupOptions(production: true, rootDirectory: new FileSystemDirectoryAccessor(Directory.GetCurrentDirectory()));
|
||||
|
||||
options.EnvironmentName.Should().Be(Environments.Production);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void When_not_in_hosted_mode_then_EnvironmentName_is_production()
|
||||
{
|
||||
var options = new StartupOptions(rootDirectory: new FileSystemDirectoryAccessor(Directory.GetCurrentDirectory()));
|
||||
|
||||
options.EnvironmentName.Should().Be(Environments.Production);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.CommandLine.Parsing;
|
||||
using System.CommandLine.IO;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Microsoft.DotNet.Interactive.Telemetry;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using MLS.Agent.CommandLine;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace MLS.Agent.Tests.CommandLine
|
||||
{
|
||||
public class TelemetryTests : IDisposable
|
||||
{
|
||||
private readonly FakeTelemetry _fakeTelemetry;
|
||||
private readonly ITestOutputHelper _output;
|
||||
private readonly TestConsole _console = new TestConsole();
|
||||
private readonly Parser _parser;
|
||||
|
||||
public TelemetryTests(ITestOutputHelper output)
|
||||
{
|
||||
_fakeTelemetry = new FakeTelemetry();
|
||||
|
||||
_output = output;
|
||||
|
||||
_parser = CommandLineParser.Create(
|
||||
new ServiceCollection(),
|
||||
startServer: (options, invocationContext) => { },
|
||||
demo: (options, console, context, startOptions) => Task.CompletedTask,
|
||||
tryGithub: (options, c) => Task.CompletedTask,
|
||||
pack: (options, console) => Task.CompletedTask,
|
||||
verify: (options, console, startupOptions, context) => Task.FromResult(1),
|
||||
telemetry: _fakeTelemetry,
|
||||
firstTimeUseNoticeSentinel: new NopFirstTimeUseNoticeSentinel());
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_output.WriteLine(_console.Error.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Hosted_is_does_not_send_any_telemetry()
|
||||
{
|
||||
await _parser.InvokeAsync("hosted", _console);
|
||||
_fakeTelemetry.LogEntries.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Invalid_command_is_does_not_send_any_telemetry()
|
||||
{
|
||||
await _parser.InvokeAsync("invalidcommand", _console);
|
||||
_fakeTelemetry.LogEntries.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Show_first_time_message_if_environment_variable_is_not_set()
|
||||
{
|
||||
var environmentVariableName = FirstTimeUseNoticeSentinel.SkipFirstTimeExperienceEnvironmentVariableName;
|
||||
var currentState = Environment.GetEnvironmentVariable(environmentVariableName);
|
||||
Environment.SetEnvironmentVariable(environmentVariableName, null);
|
||||
try
|
||||
{
|
||||
await _parser.InvokeAsync(string.Empty, _console);
|
||||
_console.Out.ToString().Should().Contain(Telemetry.WelcomeMessage);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Environment.SetEnvironmentVariable(environmentVariableName, currentState);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Do_not_show_first_time_message_if_environment_variable_is_set()
|
||||
{
|
||||
var environmentVariableName = FirstTimeUseNoticeSentinel.SkipFirstTimeExperienceEnvironmentVariableName;
|
||||
var currentState = Environment.GetEnvironmentVariable(environmentVariableName);
|
||||
Environment.SetEnvironmentVariable(environmentVariableName, null);
|
||||
Environment.SetEnvironmentVariable(environmentVariableName, "1");
|
||||
try
|
||||
{
|
||||
await _parser.InvokeAsync(string.Empty, _console);
|
||||
_console.Out.ToString().Should().NotContain(Telemetry.WelcomeMessage);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Environment.SetEnvironmentVariable(environmentVariableName, currentState);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,794 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.CommandLine.IO;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Microsoft.DotNet.Try.Protocol.Tests;
|
||||
using MLS.Agent.CommandLine;
|
||||
using MLS.Agent.Tools;
|
||||
using MLS.Agent.Tools.Tests;
|
||||
using WorkspaceServer.Tests;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace MLS.Agent.Tests.CommandLine
|
||||
{
|
||||
public class VerifyCommandTests
|
||||
{
|
||||
private const string CompilingProgramCs = @"
|
||||
using System;
|
||||
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
Console.WriteLine();
|
||||
}
|
||||
}";
|
||||
|
||||
private const string CompilingProgramWithRegionCs = @"
|
||||
using System;
|
||||
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
#region targetRegion
|
||||
#endregion
|
||||
|
||||
#region userCodeRegion
|
||||
#endregion
|
||||
}
|
||||
}";
|
||||
|
||||
private const string NonCompilingProgramWithRegionCs = @"
|
||||
using System;
|
||||
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
#region targetRegion
|
||||
DOES NOT COMPILE
|
||||
#endregion
|
||||
}
|
||||
}";
|
||||
|
||||
private const string CsprojContents = @"<Project Sdk=""Microsoft.NET.Sdk"">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
";
|
||||
|
||||
public class WithBackingProject
|
||||
{
|
||||
private readonly ITestOutputHelper _output;
|
||||
|
||||
public WithBackingProject(ITestOutputHelper output)
|
||||
{
|
||||
_output = output;
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task Errors_are_written_to_std_out()
|
||||
{
|
||||
var root = new DirectoryInfo(Directory.GetDirectoryRoot(Directory.GetCurrentDirectory()));
|
||||
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(root, root)
|
||||
{
|
||||
("doc.md", @"
|
||||
This is some sample code:
|
||||
```cs --source-file Program.cs
|
||||
```
|
||||
")
|
||||
};
|
||||
|
||||
var console = new TestConsole();
|
||||
|
||||
await VerifyCommand.Do(new VerifyOptions(directoryAccessor), console);
|
||||
|
||||
console.Out
|
||||
.ToString()
|
||||
.Should()
|
||||
.Match($"*{root}doc.md*Line 3:*{root}Program.cs (in project UNKNOWN)*File not found: ./Program.cs*No project file or package specified*");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Files_are_listed()
|
||||
{
|
||||
var root = Create.EmptyWorkspace(isRebuildablePackage: true).Directory;
|
||||
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(root, root)
|
||||
{
|
||||
("some.csproj", CsprojContents),
|
||||
("Program.cs", CompilingProgramCs),
|
||||
("doc.md", @"
|
||||
```cs --source-file Program.cs
|
||||
```
|
||||
")
|
||||
}.CreateFiles();
|
||||
|
||||
var console = new TestConsole();
|
||||
|
||||
await VerifyCommand.Do(new VerifyOptions(directoryAccessor), console);
|
||||
|
||||
_output.WriteLine(console.Out.ToString());
|
||||
|
||||
console.Out
|
||||
.ToString()
|
||||
.EnforceLF()
|
||||
.Trim()
|
||||
.Should()
|
||||
.Match(
|
||||
$"*{root}{Path.DirectorySeparatorChar}doc.md*Line 2:*{root}{Path.DirectorySeparatorChar}Program.cs (in project {root}{Path.DirectorySeparatorChar}some.csproj)*".EnforceLF());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Fails_if_language_is_not_compatible_with_backing_project()
|
||||
{
|
||||
var root = Create.EmptyWorkspace(isRebuildablePackage: true).Directory;
|
||||
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(root, root)
|
||||
{
|
||||
("some.csproj", CsprojContents),
|
||||
("Program.cs", CompilingProgramCs),
|
||||
("support.fs", "let a = 0"),
|
||||
("doc.md", @"
|
||||
```fs --source-file support.fs --project some.csproj
|
||||
```
|
||||
")
|
||||
}.CreateFiles();
|
||||
|
||||
var console = new TestConsole();
|
||||
|
||||
await VerifyCommand.Do(new VerifyOptions(directoryAccessor), console);
|
||||
|
||||
_output.WriteLine(console.Out.ToString());
|
||||
|
||||
console.Out
|
||||
.ToString()
|
||||
.EnforceLF()
|
||||
.Trim()
|
||||
.Should()
|
||||
.Match(
|
||||
$"*Build failed as project {root}{Path.DirectorySeparatorChar}some.csproj is not compatible with language fsharp*".EnforceLF());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task When_non_editable_code_blocks_do_not_contain_errors_then_validation_succeeds()
|
||||
{
|
||||
var root = Create.EmptyWorkspace(isRebuildablePackage: true).Directory;
|
||||
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(root, root)
|
||||
{
|
||||
("some.csproj", CsprojContents),
|
||||
("Program.cs", CompilingProgramCs),
|
||||
("doc.md", @"
|
||||
```cs --source-file Program.cs
|
||||
```
|
||||
```cs --editable false
|
||||
// global include
|
||||
public class EmptyClass {}
|
||||
```
|
||||
```cs --editable false
|
||||
// global include
|
||||
public class EmptyClassTwo {}
|
||||
```
|
||||
")
|
||||
}.CreateFiles();
|
||||
|
||||
var console = new TestConsole();
|
||||
|
||||
var resultCode = await VerifyCommand.Do(new VerifyOptions(directoryAccessor), console);
|
||||
|
||||
_output.WriteLine(console.Out.ToString());
|
||||
|
||||
resultCode.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task When_projects_are_deeper_than_root_it_succeeds()
|
||||
{
|
||||
var root = Create.EmptyWorkspace(isRebuildablePackage: true).Directory;
|
||||
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(root, root)
|
||||
{
|
||||
("./folder/project/some.csproj", CsprojContents),
|
||||
("./folder/project/Program.cs", CompilingProgramWithRegionCs),
|
||||
("./folder/doc2.md", @"
|
||||
```cs --source-file ./project/Program.cs --region targetRegion --project ./project/some.csproj
|
||||
```
|
||||
|
||||
```cs --source-file ./project/Program.cs --region userCodeRegion --project ./project/some.csproj
|
||||
```
|
||||
|
||||
")
|
||||
}.CreateFiles();
|
||||
|
||||
var console = new TestConsole();
|
||||
|
||||
var resultCode = await VerifyCommand.Do(new VerifyOptions(directoryAccessor), console);
|
||||
|
||||
_output.WriteLine(console.Out.ToString());
|
||||
|
||||
resultCode.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task With_non_editable_code_block_targeting_regions_with_non_compiling_code_then_validation_fails()
|
||||
{
|
||||
var root = Create.EmptyWorkspace(isRebuildablePackage: true).Directory;
|
||||
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(root, root)
|
||||
{
|
||||
("some.csproj", CsprojContents),
|
||||
("Program.cs", NonCompilingProgramWithRegionCs),
|
||||
("doc.md", @"
|
||||
```cs --editable false --source-file Program.cs --region targetRegion
|
||||
```
|
||||
")
|
||||
}.CreateFiles();
|
||||
|
||||
var console = new TestConsole();
|
||||
|
||||
var resultCode = await VerifyCommand.Do(new VerifyOptions(directoryAccessor), console);
|
||||
|
||||
_output.WriteLine(console.Out.ToString());
|
||||
|
||||
resultCode.Should().NotBe(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task When_there_are_no_markdown_errors_then_return_code_is_0()
|
||||
{
|
||||
var rootDirectory = Create.EmptyWorkspace(isRebuildablePackage: true).Directory;
|
||||
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(rootDirectory, rootDirectory)
|
||||
{
|
||||
("some.csproj", CsprojContents),
|
||||
("Program.cs", CompilingProgramCs),
|
||||
("doc.md", @"
|
||||
```cs --source-file Program.cs
|
||||
```
|
||||
")
|
||||
}.CreateFiles();
|
||||
|
||||
var console = new TestConsole();
|
||||
|
||||
var resultCode = await VerifyCommand.Do(new VerifyOptions(directoryAccessor), console);
|
||||
|
||||
_output.WriteLine(console.Out.ToString());
|
||||
|
||||
resultCode.Should().Be(0);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("invalid")]
|
||||
[InlineData("--source-file ./NONEXISTENT.CS")]
|
||||
[InlineData("--source-file ./Program.cs --region NONEXISTENT")]
|
||||
public async Task When_there_are_code_fence_annotation_errors_then_return_code_is_nonzero(string args)
|
||||
{
|
||||
var rootDirectory = Create.EmptyWorkspace(isRebuildablePackage: true).Directory;
|
||||
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(rootDirectory)
|
||||
{
|
||||
("doc.md", $@"
|
||||
```cs {args}
|
||||
```
|
||||
"),
|
||||
("Program.cs", $@"
|
||||
using System;
|
||||
|
||||
public class Program
|
||||
{{
|
||||
public static void Main(string[] args)
|
||||
{{
|
||||
#region main
|
||||
Console.WriteLine(""Hello World!"");
|
||||
#endregion
|
||||
}}
|
||||
}}"),
|
||||
("default.csproj", CsprojContents)
|
||||
}.CreateFiles();
|
||||
|
||||
var console = new TestConsole();
|
||||
|
||||
var resultCode = await VerifyCommand.Do(new VerifyOptions(directoryAccessor), console);
|
||||
|
||||
_output.WriteLine(console.Out.ToString());
|
||||
|
||||
resultCode.Should().NotBe(0);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task When_there_are_no_files_found_then_return_code_is_nonzero()
|
||||
{
|
||||
var rootDirectory = Create.EmptyWorkspace(isRebuildablePackage: true).Directory;
|
||||
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(rootDirectory);
|
||||
|
||||
var console = new TestConsole();
|
||||
|
||||
var resultCode = await VerifyCommand.Do(new VerifyOptions(directoryAccessor), console);
|
||||
|
||||
console.Error.ToString().Should().Contain("No markdown files found");
|
||||
resultCode.Should().NotBe(0);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"
|
||||
```cs --source-file Program.cs --session one --project a.csproj
|
||||
```
|
||||
```cs --source-file Program.cs --session one --project b.csproj
|
||||
```")]
|
||||
[InlineData(@"
|
||||
```cs --source-file Program.cs --session one --package console
|
||||
```
|
||||
```cs --source-file Program.cs --session one --project b.csproj
|
||||
```")]
|
||||
public async Task Returns_an_error_when_a_session_has_more_than_one_package_or_project(string mdFileContents)
|
||||
{
|
||||
var rootDirectory = new DirectoryInfo(".");
|
||||
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(rootDirectory)
|
||||
{
|
||||
("doc.md", mdFileContents),
|
||||
("a.csproj", ""),
|
||||
("b.csproj", ""),
|
||||
};
|
||||
|
||||
var console = new TestConsole();
|
||||
|
||||
var resultCode = await VerifyCommand.Do(new VerifyOptions(directoryAccessor), console);
|
||||
|
||||
console.Out.ToString().Should().Contain("Session cannot span projects or packages: --session one");
|
||||
|
||||
resultCode.Should().NotBe(0);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("")]
|
||||
[InlineData("--region mask")]
|
||||
public async Task Verify_shows_diagnostics_for_compilation_failures(string args)
|
||||
{
|
||||
var directory = Create.EmptyWorkspace(isRebuildablePackage: true).Directory;
|
||||
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(directory, directory)
|
||||
{
|
||||
|
||||
("Program.cs", $@"
|
||||
public class Program
|
||||
{{
|
||||
public static void Main(string[] args)
|
||||
{{
|
||||
#region mask
|
||||
Console.WriteLine()
|
||||
#endregion
|
||||
}}
|
||||
}}"),
|
||||
("sample.md", $@"
|
||||
```cs {args} --source-file Program.cs
|
||||
```"),
|
||||
("sample.csproj",
|
||||
@"<Project Sdk=""Microsoft.NET.Sdk"">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
")
|
||||
}.CreateFiles();
|
||||
|
||||
var console = new TestConsole();
|
||||
|
||||
var resultCode = await VerifyCommand.Do(new VerifyOptions(directoryAccessor), console);
|
||||
|
||||
_output.WriteLine(console.Out.ToString());
|
||||
|
||||
console.Out.ToString().Should().Contain($"Build failed for project {directoryAccessor.GetFullyQualifiedPath(new RelativeFilePath("sample.csproj"))}");
|
||||
|
||||
resultCode.Should().NotBe(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task When_there_are_compilation_errors_outside_the_mask_then_they_are_displayed()
|
||||
{
|
||||
var rootDirectory = Create.EmptyWorkspace(isRebuildablePackage: true).Directory;
|
||||
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(rootDirectory, rootDirectory)
|
||||
{
|
||||
("Program.cs", $@"
|
||||
using System;
|
||||
|
||||
public class Program
|
||||
{{
|
||||
public static void Main(string[] args) DOES NOT COMPILE
|
||||
{{
|
||||
#region mask
|
||||
Console.WriteLine();
|
||||
#endregion
|
||||
}}
|
||||
}}"),
|
||||
("sample.md", $@"
|
||||
```cs --source-file Program.cs --region mask
|
||||
```"),
|
||||
("sample.csproj",
|
||||
CsprojContents)
|
||||
}.CreateFiles();
|
||||
|
||||
var console = new TestConsole();
|
||||
|
||||
var resultCode = await VerifyCommand.Do(new VerifyOptions(directoryAccessor), console);
|
||||
|
||||
_output.WriteLine(console.Out.ToString());
|
||||
|
||||
console.Out.ToString()
|
||||
.Should().Contain("Build failed")
|
||||
.And.Contain("Program.cs(6,72): error CS1002: ; expected");
|
||||
|
||||
resultCode.Should().NotBe(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task When_there_are_compilation_errors_in_non_editable_blocks_then_they_are_displayed()
|
||||
{
|
||||
var rootDirectory = Create.EmptyWorkspace(isRebuildablePackage: true).Directory;
|
||||
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(rootDirectory, rootDirectory)
|
||||
{
|
||||
("Program.cs", @"
|
||||
using System;
|
||||
|
||||
public class Program
|
||||
{{
|
||||
public static void Main(string[] args)
|
||||
{{
|
||||
#region mask
|
||||
Console.WriteLine();
|
||||
#endregion
|
||||
}}
|
||||
}}"),
|
||||
("sample.md", @"
|
||||
```cs --source-file Program.cs --region mask
|
||||
```
|
||||
```cs --editable false
|
||||
DOES NOT COMPILE
|
||||
DOES NOT COMPILE
|
||||
DOES NOT COMPILE
|
||||
```
|
||||
"),
|
||||
("sample.csproj",
|
||||
CsprojContents)
|
||||
}.CreateFiles();
|
||||
|
||||
var console = new TestConsole();
|
||||
|
||||
var resultCode = await VerifyCommand.Do(new VerifyOptions(directoryAccessor), console);
|
||||
|
||||
_output.WriteLine(console.Out.ToString());
|
||||
|
||||
console.Out.ToString()
|
||||
.Should().Contain("Build failed")
|
||||
.And.Contain("generated_include_file_global.cs(3,53): error CS1002: ; expected");
|
||||
|
||||
resultCode.Should().NotBe(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Console_output_blocks_do_not_cause_verification_to_fail()
|
||||
{
|
||||
var rootDirectory = Create.EmptyWorkspace(isRebuildablePackage: true).Directory;
|
||||
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(rootDirectory, rootDirectory)
|
||||
{
|
||||
("Program.cs", @"
|
||||
using System;
|
||||
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
#region mask
|
||||
Console.WriteLine(""hello!"");
|
||||
#endregion
|
||||
}
|
||||
}"),
|
||||
("sample.md", @"
|
||||
```cs --source-file Program.cs --region mask --session one
|
||||
```
|
||||
```console --session one
|
||||
hello!
|
||||
```
|
||||
"),
|
||||
("sample.csproj", CsprojContents)
|
||||
}.CreateFiles();
|
||||
|
||||
var console = new TestConsole();
|
||||
|
||||
var resultCode = await VerifyCommand.Do(
|
||||
new VerifyOptions(directoryAccessor),
|
||||
console);
|
||||
|
||||
_output.WriteLine(console.Out.ToString());
|
||||
|
||||
resultCode.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task When_there_are_code_fence_options_errors_then_compilation_is_not_attempted()
|
||||
{
|
||||
var root = new DirectoryInfo(Directory.GetDirectoryRoot(Directory.GetCurrentDirectory()));
|
||||
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(root, root)
|
||||
{
|
||||
("doc.md", @"
|
||||
This is some sample code:
|
||||
```cs --source-file Program.cs
|
||||
```
|
||||
")
|
||||
};
|
||||
|
||||
var console = new TestConsole();
|
||||
|
||||
await VerifyCommand.Do(new VerifyOptions(directoryAccessor), console);
|
||||
|
||||
_output.WriteLine(console.Out.ToString());
|
||||
|
||||
console.Out
|
||||
.ToString()
|
||||
.Should()
|
||||
.NotContain("Compiling samples for session");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task If_a_new_file_is_added_and_verify_is_called_the_compile_errors_in_it_are_emitted()
|
||||
{
|
||||
var rootDirectory = Create.EmptyWorkspace(isRebuildablePackage: true).Directory;
|
||||
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(rootDirectory, rootDirectory)
|
||||
{
|
||||
("Program.cs", $@"
|
||||
using System;
|
||||
|
||||
public class Program
|
||||
{{
|
||||
public static void Main(string[] args)
|
||||
{{
|
||||
#region mask
|
||||
Console.WriteLine();
|
||||
#endregion
|
||||
}}
|
||||
}}"),
|
||||
("sample.md", $@"
|
||||
```cs --source-file Program.cs --region mask
|
||||
```"),
|
||||
("sample.csproj",
|
||||
CsprojContents)
|
||||
}.CreateFiles();
|
||||
|
||||
var console = new TestConsole();
|
||||
|
||||
var resultCode = await VerifyCommand.Do(new VerifyOptions(directoryAccessor), console);
|
||||
|
||||
_output.WriteLine(console.Out.ToString());
|
||||
resultCode.Should().Be(0);
|
||||
|
||||
File.WriteAllText(directoryAccessor.GetFullyQualifiedPath(new RelativeFilePath("Sample.cs")).FullName, "DOES NOT COMPILE");
|
||||
|
||||
resultCode = await VerifyCommand.Do(new VerifyOptions(directoryAccessor), console);
|
||||
|
||||
_output.WriteLine(console.Out.ToString());
|
||||
|
||||
console.Out.ToString()
|
||||
.Should().Contain("Build failed")
|
||||
.And.Contain("Sample.cs(1,17): error CS1002: ; expected");
|
||||
|
||||
resultCode.Should().NotBe(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task When_the_file_is_modified_and_errors_are_added_verify_command_shows_the_errors()
|
||||
{
|
||||
var rootDirectory = Create.EmptyWorkspace(isRebuildablePackage: true).Directory;
|
||||
|
||||
var validCode = $@"
|
||||
using System;
|
||||
|
||||
public class Program
|
||||
{{
|
||||
public static void Main(string[] args)
|
||||
{{
|
||||
#region mask
|
||||
Console.WriteLine();
|
||||
#endregion
|
||||
}}
|
||||
}}";
|
||||
|
||||
var invalidCode = $@"
|
||||
using System;
|
||||
|
||||
public class Program
|
||||
{{
|
||||
public static void Main(string[] args) DOES NOT COMPILE
|
||||
{{
|
||||
#region mask
|
||||
Console.WriteLine();
|
||||
#endregion
|
||||
}}
|
||||
}}";
|
||||
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(rootDirectory, rootDirectory)
|
||||
{
|
||||
("Program.cs", validCode),
|
||||
("sample.md", $@"
|
||||
```cs --source-file Program.cs --region mask
|
||||
```"),
|
||||
("sample.csproj",
|
||||
CsprojContents)
|
||||
}.CreateFiles();
|
||||
|
||||
var console = new TestConsole();
|
||||
|
||||
|
||||
var resultCode = await VerifyCommand.Do(new VerifyOptions(directoryAccessor), console);
|
||||
|
||||
resultCode.Should().Be(0);
|
||||
|
||||
File.WriteAllText(directoryAccessor.GetFullyQualifiedPath(new RelativeFilePath("Program.cs")).FullName, invalidCode);
|
||||
|
||||
resultCode = await VerifyCommand.Do(new VerifyOptions(directoryAccessor), console);
|
||||
|
||||
_output.WriteLine(console.Out.ToString());
|
||||
|
||||
console.Out.ToString()
|
||||
.Should().Contain("Build failed")
|
||||
.And.Contain("Program.cs(6,72): error CS1002: ; expected");
|
||||
|
||||
resultCode.Should().NotBe(0);
|
||||
}
|
||||
}
|
||||
|
||||
public class WithStandaloneMarkdown
|
||||
{
|
||||
private readonly ITestOutputHelper _output;
|
||||
|
||||
public WithStandaloneMarkdown(ITestOutputHelper output)
|
||||
{
|
||||
_output = output;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task When_standalone_markdown_with_non_editable_code_block_targeting_regions_has_compiling_code_then_validation_succeeds()
|
||||
{
|
||||
var root = Create.EmptyWorkspace(isRebuildablePackage: true).Directory;
|
||||
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(root, root)
|
||||
{
|
||||
("./subFolder/some.csproj", CsprojContents),
|
||||
("doc.md", $@"
|
||||
```cs --editable false --destination-file Program.cs
|
||||
{CompilingProgramWithRegionCs}
|
||||
```
|
||||
```cs --editable false --destination-file Program.cs --region targetRegion
|
||||
Console.WriteLine(""code"");
|
||||
```
|
||||
")
|
||||
}.CreateFiles();
|
||||
|
||||
var console = new TestConsole();
|
||||
var project = directoryAccessor.GetFullyQualifiedPath(new RelativeFilePath("./subFolder/some.csproj"));
|
||||
|
||||
var resultCode = await VerifyCommand.Do(new VerifyOptions(directoryAccessor), console,
|
||||
new StartupOptions(package: project.FullName));
|
||||
|
||||
_output.WriteLine(console.Out.ToString());
|
||||
|
||||
resultCode.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task When_standalone_markdown_has_compiling_code_then_validation_succeeds()
|
||||
{
|
||||
var root = Create.EmptyWorkspace(isRebuildablePackage: true).Directory;
|
||||
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(root, root)
|
||||
{
|
||||
("./subFolder/some.csproj", CsprojContents),
|
||||
("doc.md", $@"
|
||||
```cs --editable false --destination-file Program.cs
|
||||
{CompilingProgramWithRegionCs}
|
||||
```
|
||||
```cs --editable false
|
||||
// global include
|
||||
public class EmptyClass {{}}
|
||||
```
|
||||
```cs --editable false
|
||||
// global include
|
||||
public class EmptyClassTwo {{}}
|
||||
```
|
||||
")
|
||||
}.CreateFiles();
|
||||
|
||||
var console = new TestConsole();
|
||||
var project = directoryAccessor.GetFullyQualifiedPath(new RelativeFilePath("./subFolder/some.csproj"));
|
||||
|
||||
var resultCode = await VerifyCommand.Do(new VerifyOptions(directoryAccessor), console,
|
||||
new StartupOptions(package: project.FullName)
|
||||
);
|
||||
|
||||
_output.WriteLine(console.Out.ToString());
|
||||
|
||||
resultCode.Should().Be(0);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task When_standalone_markdown_contains_non_compiling_code_then_validation_fails()
|
||||
{
|
||||
var root = Create.EmptyWorkspace(isRebuildablePackage: true).Directory;
|
||||
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(root, root)
|
||||
{
|
||||
("./subFolder/some.csproj", CsprojContents),
|
||||
("doc.md", $@"
|
||||
```cs --editable false --destination-file Program.cs
|
||||
{CompilingProgramWithRegionCs}
|
||||
```
|
||||
```cs --editable false
|
||||
// global include
|
||||
public class EmptyClassTwo {{
|
||||
DOES NOT COMPILE
|
||||
}}
|
||||
```
|
||||
")
|
||||
}.CreateFiles();
|
||||
|
||||
var console = new TestConsole();
|
||||
var project = directoryAccessor.GetFullyQualifiedPath(new RelativeFilePath("./subFolder/some.csproj"));
|
||||
|
||||
var resultCode = await VerifyCommand.Do(new VerifyOptions(directoryAccessor), console,
|
||||
new StartupOptions(package: project.FullName));
|
||||
|
||||
_output.WriteLine(console.Out.ToString());
|
||||
console.Out.ToString()
|
||||
.Should().Contain("Build failed");
|
||||
|
||||
resultCode.Should().NotBe(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task When_markdown_has_Program_with_a_region_and_markdown_has_destination_file_then_validation_succeeds()
|
||||
{
|
||||
var root = Create.EmptyWorkspace(isRebuildablePackage: true).Directory;
|
||||
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(root, root)
|
||||
{
|
||||
("./subFolder/some.csproj", CsprojContents),
|
||||
("Program.cs", CompilingProgramWithRegionCs),
|
||||
("doc.md", $@"
|
||||
```cs --destination-file Program.cs --region targetRegion
|
||||
Console.WriteLine(""This code should be compiled with the targetRegion in Program.cs"");
|
||||
```
|
||||
")
|
||||
}.CreateFiles();
|
||||
|
||||
var console = new TestConsole();
|
||||
var project = directoryAccessor.GetFullyQualifiedPath(new RelativeFilePath("./subFolder/some.csproj"));
|
||||
|
||||
var resultCode = await VerifyCommand.Do(new VerifyOptions(directoryAccessor), console,
|
||||
new StartupOptions(package: project.FullName)
|
||||
);
|
||||
|
||||
_output.WriteLine(console.Out.ToString());
|
||||
|
||||
resultCode.Should().Be(0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,263 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Threading.Tasks;
|
||||
using Clockwise;
|
||||
using FluentAssertions;
|
||||
using FluentAssertions.Extensions;
|
||||
using HtmlAgilityPack;
|
||||
using Microsoft.DotNet.Interactive.Utility;
|
||||
using Microsoft.DotNet.Try.Markdown;
|
||||
using MLS.Agent.CommandLine;
|
||||
using MLS.Agent.Tools;
|
||||
using MLS.Agent.Tools.Tests;
|
||||
using Recipes;
|
||||
using WorkspaceServer;
|
||||
using WorkspaceServer.Packaging;
|
||||
using WorkspaceServer.Tests;
|
||||
using Xunit;
|
||||
|
||||
namespace MLS.Agent.Tests
|
||||
{
|
||||
public class DocumentationAPITests
|
||||
{
|
||||
[Fact]
|
||||
public async Task Request_for_non_existent_markdown_file_returns_404()
|
||||
{
|
||||
using (var agent = new AgentService(new StartupOptions(rootDirectory: new FileSystemDirectoryAccessor(TestAssets.SampleConsole))))
|
||||
{
|
||||
var response = await agent.GetAsync(@"/DOESNOTEXIST");
|
||||
|
||||
response.Should().BeNotFound();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Return_html_for_an_existing_markdown_file()
|
||||
{
|
||||
using (var agent = new AgentService(new StartupOptions(rootDirectory: new FileSystemDirectoryAccessor(TestAssets.SampleConsole))))
|
||||
{
|
||||
var response = await agent.GetAsync(@"Readme.md");
|
||||
|
||||
response.Should().BeSuccessful();
|
||||
|
||||
var result = await response.Content.ReadAsStringAsync();
|
||||
response.Content.Headers.ContentType.MediaType.Should().Be("text/html");
|
||||
result.Should().Contain("<em>markdown file</em>");
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Return_html_for_existing_markdown_files_in_a_subdirectory()
|
||||
{
|
||||
using (var agent = new AgentService(new StartupOptions(rootDirectory: new FileSystemDirectoryAccessor(TestAssets.SampleConsole))))
|
||||
{
|
||||
var response = await agent.GetAsync(@"Subdirectory/Tutorial.md");
|
||||
|
||||
response.Should().BeSuccessful();
|
||||
|
||||
var result = await response.Content.ReadAsStringAsync();
|
||||
result.Should().Contain("<em>tutorial file</em>");
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Lists_markdown_files_when_a_folder_is_requested()
|
||||
{
|
||||
using (var agent = new AgentService(new StartupOptions(rootDirectory: new FileSystemDirectoryAccessor(TestAssets.SampleConsole))))
|
||||
{
|
||||
var response = await agent.GetAsync(@"/");
|
||||
|
||||
response.Should().BeSuccessful();
|
||||
|
||||
var html = await response.Content.ReadAsStringAsync();
|
||||
|
||||
var htmlDoc = new HtmlDocument();
|
||||
htmlDoc.LoadHtml(html);
|
||||
|
||||
var links = htmlDoc.DocumentNode
|
||||
.SelectNodes("//a")
|
||||
.Select(a => a.Attributes["href"].Value)
|
||||
.ToArray();
|
||||
|
||||
links.Should().Contain("./Readme.md");
|
||||
links.Should().Contain("./Subdirectory/Tutorial.md");
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Scaffolding_HTML_includes_trydotnet_js_script_link()
|
||||
{
|
||||
using (var agent = new AgentService(new StartupOptions(rootDirectory: new FileSystemDirectoryAccessor(TestAssets.SampleConsole))))
|
||||
{
|
||||
var response = await agent.GetAsync(@"Subdirectory/Tutorial.md");
|
||||
|
||||
response.Should().BeSuccessful();
|
||||
|
||||
var html = await response.Content.ReadAsStringAsync();
|
||||
|
||||
var document = new HtmlDocument();
|
||||
document.LoadHtml(html);
|
||||
|
||||
var script = document.DocumentNode
|
||||
.Descendants("head")
|
||||
.Single()
|
||||
.Descendants("script")
|
||||
.FirstOrDefault();
|
||||
|
||||
script.Attributes["src"].Value.Should().StartWith("/api/trydotnet.min.js?v=");
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Scaffolding_HTML_includes_trydotnet_js_autoEnable_invocation_with_useBlazor_defaulting_to_false()
|
||||
{
|
||||
using (var agent = new AgentService(new StartupOptions(rootDirectory: new FileSystemDirectoryAccessor(TestAssets.SampleConsole))))
|
||||
{
|
||||
var response = await agent.GetAsync(@"Subdirectory/Tutorial.md");
|
||||
|
||||
response.Should().BeSuccessful();
|
||||
|
||||
var html = await response.Content.ReadAsStringAsync();
|
||||
|
||||
var document = new HtmlDocument();
|
||||
document.LoadHtml(html);
|
||||
|
||||
var scripts = document.DocumentNode
|
||||
.Descendants("body")
|
||||
.Single()
|
||||
.Descendants("script")
|
||||
.Select(s => s.InnerHtml);
|
||||
|
||||
scripts.Should()
|
||||
.Contain(s => s.Contains(@"trydotnet.autoEnable({ apiBaseAddress: new URL(""http://localhost""), useWasmRunner: false });"));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Scaffolding_HTML_trydotnet_js_autoEnable_useBlazor_is_true_when_package_is_specified_and_supports_wasmrunner()
|
||||
{
|
||||
var (name, addSource) = await Create.NupkgWithBlazorEnabled("packageName");
|
||||
|
||||
var startupOptions = new StartupOptions(
|
||||
rootDirectory: new FileSystemDirectoryAccessor(TestAssets.SampleConsole),
|
||||
addPackageSource: new PackageSource(addSource.FullName),
|
||||
package: name);
|
||||
|
||||
using (var agent = new AgentService(startupOptions))
|
||||
{
|
||||
var response = await agent.GetAsync(@"Subdirectory/Tutorial.md");
|
||||
|
||||
response.Should().BeSuccessful();
|
||||
|
||||
var html = await response.Content.ReadAsStringAsync();
|
||||
|
||||
var document = new HtmlDocument();
|
||||
document.LoadHtml(html);
|
||||
|
||||
var scripts = document.DocumentNode
|
||||
.Descendants("body")
|
||||
.Single()
|
||||
.Descendants("script")
|
||||
.Select(s => s.InnerHtml);
|
||||
|
||||
scripts.Should()
|
||||
.Contain(s => s.Contains(@"trydotnet.autoEnable({ apiBaseAddress: new URL(""http://localhost""), useWasmRunner: true });"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task Scaffolding_HTML_trydotnet_js_autoEnable_useBlazor_is_true_when_package_is_not_specified_and_supports_wasmrunner()
|
||||
{
|
||||
var (name, addSource) = await Create.NupkgWithBlazorEnabled("packageName");
|
||||
|
||||
using (var dir = DisposableDirectory.Create())
|
||||
{
|
||||
var text = $@"
|
||||
```cs --package {name}
|
||||
```";
|
||||
|
||||
var path = Path.Combine(dir.Directory.FullName, "BlazorTutorial.md");
|
||||
File.WriteAllText(path, text);
|
||||
|
||||
var startupOptions = new StartupOptions(
|
||||
rootDirectory: new FileSystemDirectoryAccessor(dir.Directory),
|
||||
addPackageSource: new PackageSource(addSource.FullName));
|
||||
|
||||
using (var agent = new AgentService(startupOptions))
|
||||
{
|
||||
var response = await agent.GetAsync(@"/BlazorTutorial.md");
|
||||
|
||||
response.Should().BeSuccessful();
|
||||
|
||||
var html = await response.Content.ReadAsStringAsync();
|
||||
|
||||
var document = new HtmlDocument();
|
||||
document.LoadHtml(html);
|
||||
|
||||
var scripts = document.DocumentNode
|
||||
.Descendants("body")
|
||||
.Single()
|
||||
.Descendants("script")
|
||||
.Select(s => s.InnerHtml);
|
||||
|
||||
scripts.Should()
|
||||
.Contain(s => s.Contains(@"trydotnet.autoEnable({ apiBaseAddress: new URL(""http://localhost""), useWasmRunner: true });"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task When_relative_uri_is_specified_then_it_opens_to_that_page()
|
||||
{
|
||||
var launchUri = new Uri("something.md", UriKind.Relative);
|
||||
|
||||
using (var clock = VirtualClock.Start())
|
||||
using (var agent = new AgentService(new StartupOptions(
|
||||
rootDirectory: new FileSystemDirectoryAccessor(TestAssets.SampleConsole),
|
||||
uri: launchUri)))
|
||||
{
|
||||
await clock.Wait(5.Seconds());
|
||||
|
||||
agent.BrowserLauncher
|
||||
.LaunchedUri
|
||||
.ToString()
|
||||
.Should()
|
||||
.Match("https://localhost:*/something.md");
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task When_readme_file_is_on_root_browser_opens_there()
|
||||
{
|
||||
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor
|
||||
{
|
||||
("./readme.md", ""),
|
||||
("./subfolder/part1.md", ""),
|
||||
("./subfolder/part2.md", "")
|
||||
};
|
||||
|
||||
var root = directoryAccessor.GetFullyQualifiedPath(new RelativeDirectoryPath(".")) as DirectoryInfo;
|
||||
|
||||
var options = new StartupOptions(rootDirectory: new FileSystemDirectoryAccessor(root));
|
||||
|
||||
using (var clock = VirtualClock.Start())
|
||||
using (var agent = new AgentService(options: options, directoryAccessor: directoryAccessor))
|
||||
{
|
||||
await clock.Wait(5.Seconds());
|
||||
|
||||
agent.BrowserLauncher
|
||||
.LaunchedUri
|
||||
.ToString()
|
||||
.Should()
|
||||
.Match("https://localhost:*/readme.md");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Clockwise;
|
||||
using FluentAssertions;
|
||||
using FluentAssertions.Extensions;
|
||||
using WorkspaceServer.Servers.Scripting;
|
||||
using Xunit;
|
||||
|
||||
namespace MLS.Agent.Tests
|
||||
{
|
||||
public class ExceptionExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task When_time_budget_expires_in_user_code_then_a_417_is_returned()
|
||||
{
|
||||
using (VirtualClock.Start())
|
||||
{
|
||||
var budget = new TimeBudget(10.Seconds());
|
||||
|
||||
await Clock.Current.Wait(11.Seconds());
|
||||
|
||||
budget.RecordEntry(ScriptingWorkspaceServer.UserCodeCompletedBudgetEntryName);
|
||||
|
||||
var exception = new BudgetExceededException(budget);
|
||||
|
||||
exception.ToHttpStatusCode().Should().Be(417);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task When_time_budget_expires_prior_to_user_code_then_a_504_is_returned()
|
||||
{
|
||||
using (VirtualClock.Start())
|
||||
{
|
||||
var budget = new TimeBudget(10.Seconds());
|
||||
|
||||
await Clock.Current.Wait(11.Seconds());
|
||||
|
||||
var exception = new BudgetExceededException(budget);
|
||||
|
||||
exception.ToHttpStatusCode().Should().Be(504);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace MLS.Agent.Tests
|
||||
{
|
||||
public class FakeBrowserLauncher : IBrowserLauncher
|
||||
{
|
||||
public void LaunchBrowser(Uri uri)
|
||||
{
|
||||
LaunchedUri = uri;
|
||||
}
|
||||
|
||||
public Uri LaunchedUri { get; private set; }
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.DotNet.Interactive.Telemetry;
|
||||
|
||||
namespace MLS.Agent.Tests
|
||||
{
|
||||
public sealed class FakeTelemetry : ITelemetry
|
||||
{
|
||||
public FakeTelemetry()
|
||||
{
|
||||
Enabled = true;
|
||||
}
|
||||
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
public void TrackEvent(string eventName,
|
||||
IDictionary<string, string> properties,
|
||||
IDictionary<string, double> measurements)
|
||||
{
|
||||
LogEntries.Add(
|
||||
new LogEntry
|
||||
{
|
||||
EventName = eventName,
|
||||
Measurement = measurements,
|
||||
Properties = properties
|
||||
});
|
||||
}
|
||||
|
||||
public ConcurrentBag<LogEntry> LogEntries { get; set; } = new ConcurrentBag<LogEntry>();
|
||||
|
||||
public class LogEntry
|
||||
{
|
||||
public string EventName { get; set; }
|
||||
public IDictionary<string, string> Properties { get; set; }
|
||||
public IDictionary<string, double> Measurement { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.CommandLine.IO;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using MLS.Agent.CommandLine;
|
||||
using WorkspaceServer.Tests;
|
||||
|
||||
namespace MLS.Agent.Tests
|
||||
{
|
||||
public static class LocalToolHelpers
|
||||
{
|
||||
public static async Task<(DirectoryInfo, string)> CreateTool(TestConsole console)
|
||||
{
|
||||
var asset = await Create.NetstandardWorkspaceCopy();
|
||||
await PackCommand.Do(new PackOptions(asset.Directory), console);
|
||||
return (asset.Directory, asset.Name);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using FluentAssertions;
|
||||
using System.CommandLine.IO;
|
||||
using System.Threading.Tasks;
|
||||
using MLS.Agent.CommandLine;
|
||||
using WorkspaceServer.Packaging;
|
||||
using WorkspaceServer.Tests;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using WorkspaceServer;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.DotNet.Interactive.Utility;
|
||||
using MLS.Agent.Tools;
|
||||
|
||||
namespace MLS.Agent.Tests
|
||||
{
|
||||
public class LocalToolPackageDiscoveryStrategyTests
|
||||
{
|
||||
private readonly ITestOutputHelper output;
|
||||
|
||||
public LocalToolPackageDiscoveryStrategyTests(ITestOutputHelper _output)
|
||||
{
|
||||
output = _output;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Discover_tool_from_directory()
|
||||
{
|
||||
using (var directory = DisposableDirectory.Create())
|
||||
{
|
||||
var console = new TestConsole();
|
||||
var temp = directory.Directory;
|
||||
var package = await Create.ConsoleWorkspaceCopy();
|
||||
File.Move(package.Directory.GetFiles("*.csproj").First().FullName, Path.Combine(package.Directory.FullName, "not-console.csproj"));
|
||||
await PackCommand.Do(new PackOptions(package.Directory, outputDirectory: temp, enableWasm: false), console);
|
||||
var result = await Microsoft.DotNet.Interactive.Utility.CommandLine.Execute("dotnet", $"tool install --add-source {temp.FullName} not-console --tool-path {temp.FullName}");
|
||||
output.WriteLine(string.Join("\n", result.Error));
|
||||
result.ExitCode.Should().Be(0);
|
||||
|
||||
var strategy = new LocalToolInstallingPackageDiscoveryStrategy(temp);
|
||||
var tool = await strategy.Locate(new PackageDescriptor("not-console"));
|
||||
tool.Should().NotBeNull();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Does_not_throw_for_missing_tool()
|
||||
{
|
||||
using (var directory = DisposableDirectory.Create())
|
||||
{
|
||||
var temp = directory.Directory;
|
||||
var strategy = new LocalToolInstallingPackageDiscoveryStrategy(temp);
|
||||
|
||||
strategy.Invoking(s => s.Locate(new PackageDescriptor("not-a-workspace")).Wait()).Should().NotThrow();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Installs_tool_from_package_source_when_requested()
|
||||
{
|
||||
var console = new TestConsole();
|
||||
var (asset, name) = await LocalToolHelpers.CreateTool(console);
|
||||
|
||||
var strategy = new LocalToolInstallingPackageDiscoveryStrategy(asset, new PackageSource(asset.FullName));
|
||||
var package = await strategy.Locate(new PackageDescriptor("blazor-console"));
|
||||
package.Should().NotBeNull();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,209 +0,0 @@
|
|||
{
|
||||
"name": "wwwroot",
|
||||
"content": [
|
||||
{
|
||||
"name": "/0.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/10.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/11.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/12.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/13.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/14.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/15.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/16.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/17.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/18.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/19.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/2.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/20.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/21.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/22.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/23.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/24.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/25.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/26.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/27.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/28.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/29.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/3.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/30.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/31.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/32.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/33.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/34.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/35.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/36.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/37.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/38.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/39.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/4.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/40.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/41.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/42.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/43.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/44.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/45.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/46.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/47.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/48.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/49.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/5.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/50.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/51.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/52.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/53.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/6.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/7.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/8.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/9.bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/api/trydotnet.min.js"
|
||||
},
|
||||
{
|
||||
"name": "/api/trydotnet.min.js.map"
|
||||
},
|
||||
{
|
||||
"name": "/bundle.css"
|
||||
},
|
||||
{
|
||||
"name": "/bundle.js"
|
||||
},
|
||||
{
|
||||
"name": "/bundle.js.map"
|
||||
},
|
||||
{
|
||||
"name": "/css/trydotnet.css"
|
||||
},
|
||||
{
|
||||
"name": "/editor.worker.js"
|
||||
},
|
||||
{
|
||||
"name": "/favicon.ico"
|
||||
},
|
||||
{
|
||||
"name": "/fonts/materialdesignicons-webfont.eot"
|
||||
},
|
||||
{
|
||||
"name": "/fonts/materialdesignicons-webfont.svg"
|
||||
},
|
||||
{
|
||||
"name": "/fonts/materialdesignicons-webfont.ttf"
|
||||
},
|
||||
{
|
||||
"name": "/fonts/materialdesignicons-webfont.woff"
|
||||
},
|
||||
{
|
||||
"name": "/fonts/materialdesignicons-webfont.woff2"
|
||||
},
|
||||
{
|
||||
"name": "/static/docs.css"
|
||||
},
|
||||
{
|
||||
"name": "/static/vs_windows.png"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<LangVersion>Latest</LangVersion>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<AssetTargetFallback>$(AssetTargetFallback);dotnet5.4;portable-net45+win8</AssetTargetFallback>
|
||||
<NoWarn>$(NoWarn);8002</NoWarn><!-- Assent, Clockwise, and Markdig aren't strongly signed -->
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="TestResults\**" />
|
||||
<EmbeddedResource Remove="TestResults\**" />
|
||||
<None Remove="TestResults\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\WorkspaceServer\(Recipes)\JsonSerializationExtensions.cs" />
|
||||
<Compile Include="..\WorkspaceServer.Tests\(Recipes)\HttpResponseMessageAssertions.cs" />
|
||||
<Compile Include="..\WorkspaceServer.Tests\(Recipes)\HttpResponseMessageExtensions.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="5.10.3" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.30" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="6.0.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="Pocket.Disposable" Version="1.1.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Pocket.TypeDiscovery" Version="0.5.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="PocketLogger" Version="0.4.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<!-- <PackageReference Include="PocketLogger.For.Xunit" Version="0.1.4">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference> -->
|
||||
<PackageReference Include="PocketLogger.Subscribe" Version="0.7.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.DotNet.Interactive.Telemetry\Microsoft.DotNet.Interactive.Telemetry.csproj" />
|
||||
<ProjectReference Include="..\Microsoft.DotNet.Try.Project\Microsoft.DotNet.Try.Project.csproj" />
|
||||
<ProjectReference Include="..\MLS.Agent\MLS.Agent.csproj" />
|
||||
<ProjectReference Include="..\MLS.Agent.Tools\MLS.Agent.Tools.csproj" />
|
||||
<ProjectReference Include="..\WorkspaceServer.Tests\WorkspaceServer.Tests.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Update="xunit.runner.visualstudio" Version="2.4.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -1,555 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.CommandLine.IO;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using HtmlAgilityPack;
|
||||
using Markdig;
|
||||
using Microsoft.DotNet.Interactive.Utility;
|
||||
using Microsoft.DotNet.Try.Markdown;
|
||||
using Microsoft.DotNet.Try.Protocol.Tests;
|
||||
using MLS.Agent.Controllers;
|
||||
using MLS.Agent.Markdown;
|
||||
using MLS.Agent.Tools;
|
||||
using MLS.Agent.Tools.Tests;
|
||||
using WorkspaceServer;
|
||||
using WorkspaceServer.Packaging;
|
||||
using WorkspaceServer.Tests;
|
||||
using Xunit;
|
||||
|
||||
namespace MLS.Agent.Tests.Markdown
|
||||
{
|
||||
public class CodeBlockAnnotationExtensionTests
|
||||
{
|
||||
private readonly AsyncLazy<(PackageRegistry, string)> _package;
|
||||
|
||||
public CodeBlockAnnotationExtensionTests()
|
||||
{
|
||||
var console = new TestConsole();
|
||||
_package = new AsyncLazy<(PackageRegistry, string)>( async () => {
|
||||
var (dir, name) = await LocalToolHelpers.CreateTool(console);
|
||||
var strategy = new LocalToolInstallingPackageDiscoveryStrategy(dir, new PackageSource(dir.FullName));
|
||||
return (new PackageRegistry(true, null, additionalStrategies: strategy), name);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("cs")]
|
||||
[InlineData("csharp")]
|
||||
[InlineData("c#")]
|
||||
[InlineData("CS")]
|
||||
[InlineData("CSHARP")]
|
||||
[InlineData("C#")]
|
||||
public async Task Inserts_code_when_an_existing_file_is_specified_using_source_file_option(string language)
|
||||
{
|
||||
var fileContent = @"using System;
|
||||
|
||||
namespace BasicConsoleApp
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void MyProgram(string[] args)
|
||||
{
|
||||
Console.WriteLine(""Hello World!"");
|
||||
}
|
||||
}
|
||||
}".EnforceLF();
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor()
|
||||
{
|
||||
("Program.cs", fileContent),
|
||||
("sample.csproj", "")
|
||||
};
|
||||
var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor,await Default.PackageRegistry.ValueAsync()).Build();
|
||||
var document =
|
||||
$@"```{language} --source-file Program.cs
|
||||
```";
|
||||
var html = (await pipeline.RenderHtmlAsync(document)).EnforceLF();
|
||||
html.Should().Contain(fileContent.HtmlEncode().ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Does_not_insert_code_when_specified_language_is_not_supported()
|
||||
{
|
||||
var expectedValue =
|
||||
@"<pre><code class=""language-js"">console.log("Hello World");
|
||||
</code></pre>
|
||||
".EnforceLF();
|
||||
|
||||
var testDir = TestAssets.SampleConsole;
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(testDir);
|
||||
var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor,await Default.PackageRegistry.ValueAsync()).Build();
|
||||
var document = @"
|
||||
```js --source-file Program.cs
|
||||
console.log(""Hello World"");
|
||||
```";
|
||||
var html = (await pipeline.RenderHtmlAsync(document)).EnforceLF();
|
||||
html.Should().Contain(expectedValue);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("cs", "language-cs")]
|
||||
[InlineData("csharp", "language-csharp")]
|
||||
[InlineData("c#", "language-c#")]
|
||||
[InlineData("fs", "language-fs")]
|
||||
[InlineData("fsharp", "language-fsharp")]
|
||||
[InlineData("f#", "language-f#")]
|
||||
public async Task Does_not_insert_code_when_supported_language_is_specified_but_no_additional_options(string fenceLanguage, string expectedClass)
|
||||
{
|
||||
var expectedValue =
|
||||
$@"<pre><code class=""{expectedClass}"">Console.WriteLine("Hello World");
|
||||
</code></pre>
|
||||
".EnforceLF();
|
||||
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor();
|
||||
var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor,await Default.PackageRegistry.ValueAsync()).Build();
|
||||
var document = $@"
|
||||
```{fenceLanguage}
|
||||
Console.WriteLine(""Hello World"");
|
||||
```";
|
||||
var html = (await pipeline.RenderHtmlAsync(document)).EnforceLF();
|
||||
html.Should().Contain(expectedValue);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task Error_message_is_displayed_when_the_linked_file_does_not_exist()
|
||||
{
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor()
|
||||
{
|
||||
("sample.csproj", "")
|
||||
};
|
||||
var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor,await Default.PackageRegistry.ValueAsync()).Build();
|
||||
var document =
|
||||
@"```cs --source-file DOESNOTEXIST
|
||||
```";
|
||||
var html = (await pipeline.RenderHtmlAsync(document)).EnforceLF();
|
||||
html.Should().Contain("File not found: ./DOESNOTEXIST");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Error_message_is_displayed_when_no_project_is_specified_and_no_project_file_is_found()
|
||||
{
|
||||
var testDir = TestAssets.SampleConsole;
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(testDir)
|
||||
{
|
||||
("Program.cs", "")
|
||||
};
|
||||
var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor,await Default.PackageRegistry.ValueAsync()).Build();
|
||||
var document =
|
||||
@"```cs --source-file Program.cs
|
||||
```";
|
||||
var html = (await pipeline.RenderHtmlAsync(document)).EnforceLF();
|
||||
|
||||
html.Should().Contain($"No project file or package specified");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Error_message_is_displayed_when_a_project_is_specified_but_the_file_does_not_exist()
|
||||
{
|
||||
var testDir = TestAssets.SampleConsole;
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(testDir)
|
||||
{
|
||||
("Program.cs", "")
|
||||
};
|
||||
var projectPath = "sample.csproj";
|
||||
|
||||
var document =
|
||||
$@"```cs --project {projectPath} --source-file Program.cs
|
||||
```";
|
||||
var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor,await Default.PackageRegistry.ValueAsync()).Build();
|
||||
var html = (await pipeline.RenderHtmlAsync(document)).EnforceLF();
|
||||
|
||||
html.Should().Contain($"Project not found: ./{projectPath}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Sets_the_trydotnet_package_attribute_using_the_passed_project_path()
|
||||
{
|
||||
var rootDirectory = TestAssets.SampleConsole;
|
||||
var currentDir = new DirectoryInfo(Path.Combine(rootDirectory.FullName, "docs"));
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(currentDir, rootDirectory)
|
||||
{
|
||||
("src/sample/Program.cs", ""),
|
||||
("src/sample/sample.csproj", "")
|
||||
};
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor,await Default.PackageRegistry.ValueAsync()).Build();
|
||||
|
||||
var package = "../src/sample/sample.csproj";
|
||||
var document =
|
||||
$@"```cs --project {package} --source-file ../src/sample/Program.cs
|
||||
```";
|
||||
|
||||
var html = (await pipeline.RenderHtmlAsync(document)).EnforceLF();
|
||||
var node = GetSingleHtmlNode(html, "//pre/code");
|
||||
var output = node.Attributes["data-trydotnet-package"];
|
||||
|
||||
var fullProjectPath = directoryAccessor.GetFullyQualifiedPath(new RelativeFilePath(package));
|
||||
output.Value.Should().Be(fullProjectPath.FullName);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("cs", "Program.cs", "sample.csproj", "csharp")]
|
||||
[InlineData("c#", "Program.cs", "sample.csproj", "csharp")]
|
||||
[InlineData("fs", "Program.fs", "sample.fsproj", "fsharp")]
|
||||
[InlineData("f#", "Program.fs", "sample.fsproj", "fsharp")]
|
||||
public async Task Sets_the_trydotnet_language_attribute_using_the_fence_command(string fenceLanguage, string fileName, string projectName, string expectedLanguage)
|
||||
{
|
||||
var rootDirectory = TestAssets.SampleConsole;
|
||||
var currentDir = new DirectoryInfo(Path.Combine(rootDirectory.FullName, "docs"));
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(currentDir, rootDirectory)
|
||||
{
|
||||
($"src/sample/{fileName}", ""),
|
||||
($"src/sample/{projectName}", "")
|
||||
};
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor,await Default.PackageRegistry.ValueAsync()).Build();
|
||||
|
||||
var package = $"../src/sample/{projectName}";
|
||||
var document =
|
||||
$@"```{fenceLanguage} --project {package} --source-file ../src/sample/{fileName}
|
||||
```";
|
||||
|
||||
var html = (await pipeline.RenderHtmlAsync(document)).EnforceLF();
|
||||
|
||||
var htmlDocument = new HtmlDocument();
|
||||
htmlDocument.LoadHtml(html);
|
||||
var trydotnetLanguage = htmlDocument.DocumentNode
|
||||
.SelectSingleNode("//pre/code").Attributes["data-trydotnet-language"];
|
||||
|
||||
trydotnetLanguage.Value.Should().Be(expectedLanguage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Sets_the_trydotnet_package_attribute_using_the_passed_package_option()
|
||||
{
|
||||
var rootDirectoryToAddFiles = TestAssets.SampleConsole;
|
||||
var workingDirectory = new DirectoryInfo(Path.Combine(rootDirectoryToAddFiles.FullName, "docs"));
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(workingDirectory, rootDirectoryToAddFiles)
|
||||
{
|
||||
("src/sample/Program.cs", ""),
|
||||
("src/sample/sample.csproj", "")
|
||||
};
|
||||
|
||||
var (registry, package) = await _package.ValueAsync();
|
||||
var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor, registry).Build();
|
||||
|
||||
var document =
|
||||
$@"```cs --package {package} --source-file ../src/sample/Program.cs
|
||||
```";
|
||||
|
||||
var html = (await pipeline.RenderHtmlAsync(document)).EnforceLF();
|
||||
var node = GetSingleHtmlNode(html, "//pre/code");
|
||||
var output = node.Attributes["data-trydotnet-package"];
|
||||
|
||||
output.Value.Should().Be(package);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task When_both_package_and_project_are_specified_then_package_wins()
|
||||
{
|
||||
var rootDirectory = TestAssets.SampleConsole;
|
||||
var currentDir = new DirectoryInfo(Path.Combine(rootDirectory.FullName, "docs"));
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(currentDir, rootDirectory)
|
||||
{
|
||||
("src/sample/Program.cs", ""),
|
||||
("src/sample/sample.csproj", "")
|
||||
};
|
||||
|
||||
var (registry, package) = await _package.ValueAsync();
|
||||
var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor, registry).Build();
|
||||
|
||||
var project = "../src/sample/sample.csproj";
|
||||
var document =
|
||||
$@"```cs --package {package} --project {project} --source-file ../src/sample/Program.cs
|
||||
```";
|
||||
|
||||
var html = (await pipeline.RenderHtmlAsync(document)).EnforceLF();
|
||||
var node = GetSingleHtmlNode(html, "//pre/code");
|
||||
var output = node.Attributes["data-trydotnet-package"];
|
||||
|
||||
output.Value.Should().Be(package);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Sets_the_code_in_the_pre_tag_using_the_region_specified_in_markdown()
|
||||
{
|
||||
var regionCode = @"Console.WriteLine(""Hello World!"");";
|
||||
var fileContent = $@"using System;
|
||||
|
||||
namespace BasicConsoleApp
|
||||
{{
|
||||
class Program
|
||||
{{
|
||||
static void MyProgram(string[] args)
|
||||
{{
|
||||
#region codeRegion
|
||||
{regionCode}
|
||||
#endregion
|
||||
}}
|
||||
}}
|
||||
}}".EnforceLF();
|
||||
|
||||
|
||||
var rootDirectory = TestAssets.SampleConsole;
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(rootDirectory)
|
||||
{
|
||||
("Program.cs", fileContent),
|
||||
("sample.csproj", "")
|
||||
};
|
||||
|
||||
var document =
|
||||
@"```cs --source-file Program.cs --region codeRegion
|
||||
```";
|
||||
var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor,await Default.PackageRegistry.ValueAsync()).Build();
|
||||
var html = (await pipeline.RenderHtmlAsync(document)).EnforceLF();
|
||||
var node = GetSingleHtmlNode(html, "//pre/code");
|
||||
var output = node.InnerText.Trim();
|
||||
|
||||
output.Should().BeEquivalentTo($"{regionCode.HtmlEncode()}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Sets_the_trydotnet_filename_using_the_filename_specified_in_the_markdown_via_source_file()
|
||||
{
|
||||
var rootDirectory = TestAssets.SampleConsole;
|
||||
var filename = "Program.cs";
|
||||
var codeContent = @"
|
||||
#region codeRegion
|
||||
Console.WriteLine(""Hello World"");
|
||||
#endregion";
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(rootDirectory)
|
||||
{
|
||||
(filename, codeContent),
|
||||
("sample.csproj", "")
|
||||
};
|
||||
|
||||
var document =
|
||||
$@"```cs --source-file {filename} --region codeRegion
|
||||
```";
|
||||
var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor,await Default.PackageRegistry.ValueAsync()).Build();
|
||||
var html = (await pipeline.RenderHtmlAsync(document)).EnforceLF();
|
||||
var node = GetSingleHtmlNode(html, "//pre/code");
|
||||
var output = node.Attributes["data-trydotnet-file-name"];
|
||||
|
||||
output.Value.Should().Be(directoryAccessor.GetFullyQualifiedPath(new RelativeFilePath(filename)).FullName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Sets_the_trydotnet_filename_using_the_filename_specified_in_the_markdown_via_destination_file()
|
||||
{
|
||||
var rootDirectory = TestAssets.SampleConsole;
|
||||
var sourceFile = "Program.cs";
|
||||
var destinationFile = "EntryPoint.cs";
|
||||
var codeContent = @"
|
||||
#region codeRegion
|
||||
Console.WriteLine(""Hello World"");
|
||||
#endregion";
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(rootDirectory)
|
||||
{
|
||||
(sourceFile, codeContent),
|
||||
("sample.csproj", "")
|
||||
};
|
||||
|
||||
var document =
|
||||
$@"```cs --source-file {sourceFile} --destination-file {destinationFile} --region codeRegion
|
||||
```";
|
||||
var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor,await Default.PackageRegistry.ValueAsync()).Build();
|
||||
var html = (await pipeline.RenderHtmlAsync(document)).EnforceLF();
|
||||
var node = GetSingleHtmlNode(html, "//pre/code");
|
||||
var output = node.Attributes["data-trydotnet-file-name"];
|
||||
|
||||
output.Value.Should().Be(directoryAccessor.GetFullyQualifiedPath(new RelativeFilePath(destinationFile)).FullName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Sets_the_trydotnet_region_using_the_region_passed_in_the_markdown()
|
||||
{
|
||||
var rootDirectory = TestAssets.SampleConsole;
|
||||
var region = "codeRegion";
|
||||
var codeContent = $@"
|
||||
#region {region}
|
||||
Console.WriteLine(""Hello World"");
|
||||
#endregion";
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(rootDirectory)
|
||||
{
|
||||
("Program.cs", codeContent),
|
||||
("sample.csproj", "")
|
||||
};
|
||||
|
||||
var document =
|
||||
$@"```cs --source-file Program.cs --region {region}
|
||||
```";
|
||||
var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor,await Default.PackageRegistry.ValueAsync()).Build();
|
||||
var html = (await pipeline.RenderHtmlAsync(document)).EnforceLF();
|
||||
var node = GetSingleHtmlNode(html, "//pre/code");
|
||||
var output = node.Attributes["data-trydotnet-region"];
|
||||
|
||||
output.Value.Should().Be(region);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task If_the_specified_region_does_not_exist_then_an_error_message_is_shown()
|
||||
{
|
||||
var rootDirectory = TestAssets.SampleConsole;
|
||||
var region = "noRegion";
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(rootDirectory)
|
||||
{
|
||||
("Program.cs", ""),
|
||||
("sample.csproj", "")
|
||||
};
|
||||
|
||||
var document =
|
||||
$@"```cs --source-file Program.cs --region {region}
|
||||
```";
|
||||
var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor,await Default.PackageRegistry.ValueAsync()).Build();
|
||||
var html = (await pipeline.RenderHtmlAsync(document)).EnforceLF();
|
||||
var node = GetSingleHtmlNode(html, "//div[@class='notification is-danger']");
|
||||
|
||||
var expected = $"Region \"{region}\" not found in file {directoryAccessor.GetFullyQualifiedPath(new RelativeFilePath("./Program.cs"))}".HtmlEncode().ToString();
|
||||
|
||||
node.InnerHtml.Should().Contain(expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task If_the_specified_region_exists_more_than_once_then_an_error_is_displayed()
|
||||
{
|
||||
var rootDirectory = TestAssets.SampleConsole;
|
||||
var codeContent = @"
|
||||
#region codeRegion
|
||||
#endregion
|
||||
#region codeRegion
|
||||
#endregion";
|
||||
var region = "codeRegion";
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(rootDirectory)
|
||||
{
|
||||
("Program.cs", codeContent),
|
||||
("sample.csproj", "")
|
||||
};
|
||||
|
||||
var document =
|
||||
$@"```cs --source-file Program.cs --region {region}
|
||||
```";
|
||||
var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor,await Default.PackageRegistry.ValueAsync()).Build();
|
||||
var html = (await pipeline.RenderHtmlAsync(document)).EnforceLF();
|
||||
var pre = GetSingleHtmlNode(html, "//div[@class='notification is-danger']");
|
||||
|
||||
pre.InnerHtml.Should().Contain($"Multiple regions found: {region}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Sets_the_trydotnet_session_using_the_session_passed_in_the_markdown()
|
||||
{
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(TestAssets.SampleConsole)
|
||||
{
|
||||
("Program.cs", ""),
|
||||
("sample.csproj", "")
|
||||
};
|
||||
|
||||
var session = "the-session-name";
|
||||
var document =
|
||||
$@"```cs --source-file Program.cs --session {session}
|
||||
```";
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseCodeBlockAnnotations(directoryAccessor,await Default.PackageRegistry.ValueAsync())
|
||||
.Build();
|
||||
|
||||
var html = (await pipeline.RenderHtmlAsync(document)).EnforceLF();
|
||||
var node = GetSingleHtmlNode(html, "//pre/code");
|
||||
var output = node.Attributes["data-trydotnet-session-id"];
|
||||
|
||||
output.Value.Should().Be(session);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Sets_the_trydotnet_session_to_a_default_value_when_a_session_is_not_passed_in_the_markdown()
|
||||
{
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(TestAssets.SampleConsole)
|
||||
{
|
||||
("Program.cs", ""),
|
||||
("sample.csproj", "")
|
||||
};
|
||||
|
||||
var document =
|
||||
@"```cs --source-file Program.cs
|
||||
```";
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseCodeBlockAnnotations(directoryAccessor,await Default.PackageRegistry.ValueAsync())
|
||||
.Build();
|
||||
|
||||
var html = (await pipeline.RenderHtmlAsync(document)).EnforceLF();
|
||||
var node = GetSingleHtmlNode(html, "//pre/code");
|
||||
var output = node.Attributes["data-trydotnet-session-id"];
|
||||
|
||||
output.Value.Should().StartWith("Run");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Sets_a_diagnostic_if_the_package_cannot_be_found()
|
||||
{
|
||||
var rootDirectory = TestAssets.SampleConsole;
|
||||
var currentDir = new DirectoryInfo(Path.Combine(rootDirectory.FullName, "docs"));
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(currentDir, rootDirectory)
|
||||
{
|
||||
("src/sample/Program.cs", ""),
|
||||
("src/sample/sample.csproj", "")
|
||||
};
|
||||
|
||||
var (registry, package) = await _package.ValueAsync();
|
||||
var pipeline = new MarkdownPipelineBuilder().UseCodeBlockAnnotations(directoryAccessor, registry).Build();
|
||||
|
||||
package = "not-the-package";
|
||||
|
||||
var document =
|
||||
$@"```cs --package {package}
|
||||
```";
|
||||
|
||||
var html = (await pipeline.RenderHtmlAsync(document)).EnforceLF();
|
||||
var node = GetSingleHtmlNode(html, "//div[@class='notification is-danger']");
|
||||
|
||||
node.InnerHtml.Should().Contain($"Package named "{package}" not found");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Arguments_are_forwarded_to_the_users_program_entry_point()
|
||||
{
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(TestAssets.SampleConsole)
|
||||
{
|
||||
("Program.cs", ""),
|
||||
("sample.csproj", ""),
|
||||
("sample.md",
|
||||
@"```cs --region the-region --source-file Program.cs -- one two ""and three""
|
||||
```")
|
||||
};
|
||||
|
||||
var project = new MarkdownProject(directoryAccessor, new PackageRegistry());
|
||||
|
||||
var markdownFile = project.GetAllMarkdownFiles().Single();
|
||||
|
||||
var html = await DocumentationController.SessionControlsHtml(markdownFile);
|
||||
|
||||
var htmlDocument = new HtmlDocument();
|
||||
htmlDocument.LoadHtml(html.ToString());
|
||||
|
||||
var value = htmlDocument.DocumentNode
|
||||
.SelectSingleNode("//button")
|
||||
.Attributes["data-trydotnet-run-args"]
|
||||
.Value;
|
||||
|
||||
value.Should().Be("--region the-region --source-file Program.cs -- one two \"and three\"".HtmlAttributeEncode().ToString());
|
||||
}
|
||||
|
||||
private static HtmlNode GetSingleHtmlNode(string html, string nodeName)
|
||||
{
|
||||
var htmlDocument = new HtmlDocument();
|
||||
htmlDocument.LoadHtml(html);
|
||||
var node = htmlDocument?.DocumentNode?.SelectSingleNode(nodeName);
|
||||
Assert.True(node != null, $"Unexpected value for `{nameof(node)}`. Html was:\r\n{html}");
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,596 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using FluentAssertions;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using HtmlAgilityPack;
|
||||
using Microsoft.DotNet.Try.Protocol.Tests;
|
||||
using MLS.Agent.CommandLine;
|
||||
using MLS.Agent.Markdown;
|
||||
using WorkspaceServer.Tests;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using MLS.Agent.Tools;
|
||||
using MLS.Agent.Tools.Tests;
|
||||
|
||||
namespace MLS.Agent.Tests.Markdown
|
||||
{
|
||||
public class MarkdownFileTests
|
||||
{
|
||||
public class ToHtmlContent
|
||||
{
|
||||
private readonly ITestOutputHelper _output;
|
||||
|
||||
public ToHtmlContent(ITestOutputHelper output)
|
||||
{
|
||||
_output = output;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Renders_html_content_for_the_files_in_the_root_path()
|
||||
{
|
||||
var html = await RenderHtml(("Readme.md", "This is a sample *markdown file*"));
|
||||
|
||||
html.Should().Contain("<em>markdown file</em>");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Renders_html_content_for_files_in_subdirectories()
|
||||
{
|
||||
var html = await RenderHtml(("SubDirectory/Tutorial.md", "This is a sample *tutorial file*"));
|
||||
|
||||
html.Should().Contain("<em>tutorial file</em>");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task When_file_argument_is_specified_then_it_inserts_code_present_in_csharp_file()
|
||||
{
|
||||
var codeContent = @"using System;
|
||||
|
||||
namespace BasicConsoleApp
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void MyProgram(string[] args)
|
||||
{
|
||||
Console.WriteLine(""Hello World!"");
|
||||
}
|
||||
}
|
||||
}".EnforceLF();
|
||||
|
||||
var html = await RenderHtml(("Program.cs", codeContent),
|
||||
("Readme.md",
|
||||
@"This is a sample *markdown file*
|
||||
|
||||
```cs --source-file Program.cs
|
||||
```"),
|
||||
("sample.csproj", "")
|
||||
);
|
||||
|
||||
html.EnforceLF().Should().Contain(codeContent.HtmlEncode().ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task When_no_source_file_argument_is_specified_then_it_does_not_replace_fenced_csharp_code()
|
||||
{
|
||||
var fencedCode = @"// this is the actual embedded code";
|
||||
|
||||
var html = await RenderHtml(
|
||||
("Readme.md",
|
||||
$@"This is a sample *markdown file*
|
||||
|
||||
```cs
|
||||
{fencedCode}
|
||||
```"));
|
||||
|
||||
var htmlDocument = new HtmlDocument();
|
||||
htmlDocument.LoadHtml(html);
|
||||
var output = htmlDocument.DocumentNode
|
||||
.SelectSingleNode("//pre/code").InnerHtml.EnforceLF();
|
||||
output.Should().Be($"{fencedCode}\n");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_parse_markdown_file_and_insert_code_from_paths_relative_to_the_markdown_file()
|
||||
{
|
||||
var codeContent = @"using System;
|
||||
|
||||
namespace BasicConsoleApp
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void MyProgram(string[] args)
|
||||
{
|
||||
Console.WriteLine(""Hello World!"");
|
||||
}
|
||||
}
|
||||
}".EnforceLF();
|
||||
|
||||
var package = "../src/sample/sample.csproj";
|
||||
|
||||
var html = await RenderHtml(("src/sample/Program.cs", codeContent),
|
||||
("src/sample/sample.csproj", ""),
|
||||
("docs/Readme.md",
|
||||
$@"
|
||||
```cs --project {package} --source-file ../src/sample/Program.cs
|
||||
```"));
|
||||
|
||||
html.EnforceLF().Should().Contain(codeContent.HtmlEncode().ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_parse_markdown_file_and_set_package_with_fully_resolved_path()
|
||||
{
|
||||
var packagePathRelativeToBaseDir = "src/sample/sample.csproj";
|
||||
|
||||
var dirAccessor = new InMemoryDirectoryAccessor()
|
||||
{
|
||||
("src/sample/Program.cs", ""),
|
||||
(packagePathRelativeToBaseDir, ""),
|
||||
("docs/Readme.md",
|
||||
$@"```cs --project ../{packagePathRelativeToBaseDir} --source-file ../src/sample/Program.cs
|
||||
```")
|
||||
};
|
||||
|
||||
var project = new MarkdownProject(dirAccessor, await Default.PackageRegistry.ValueAsync());
|
||||
project.TryGetMarkdownFile(new RelativeFilePath("docs/Readme.md"), out var markdownFile).Should().BeTrue();
|
||||
var htmlDocument = new HtmlDocument();
|
||||
htmlDocument.LoadHtml((await markdownFile.ToHtmlContentAsync()).ToString());
|
||||
var output = htmlDocument.DocumentNode
|
||||
.SelectSingleNode("//pre/code").Attributes["data-trydotnet-package"];
|
||||
|
||||
var fullProjectPath = dirAccessor.GetFullyQualifiedPath(new RelativeFilePath(packagePathRelativeToBaseDir));
|
||||
output.Value.Should().Be(fullProjectPath.FullName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_include_the_code_from_source_file_and_not_the_fenced_code()
|
||||
{
|
||||
var codeContent = @"using System;
|
||||
|
||||
namespace BasicConsoleApp
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void MyProgram(string[] args)
|
||||
{
|
||||
Console.WriteLine(""Hello World!"");
|
||||
}
|
||||
}
|
||||
}".EnforceLF();
|
||||
|
||||
var html = await RenderHtml(
|
||||
("sample.csproj", ""),
|
||||
("Program.cs", codeContent),
|
||||
("Readme.md",
|
||||
@"```cs --source-file Program.cs
|
||||
Console.WriteLine(""This code should not appear"");
|
||||
```"));
|
||||
|
||||
var htmlDocument = new HtmlDocument();
|
||||
htmlDocument.LoadHtml(html);
|
||||
var output = htmlDocument.DocumentNode
|
||||
.SelectSingleNode("//pre/code").InnerHtml.EnforceLF();
|
||||
|
||||
output.Should().Contain($"{codeContent.HtmlEncode()}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_emit_include_mode_for_non_editable_blocks()
|
||||
{
|
||||
var codeContent = @"using System;
|
||||
|
||||
namespace BasicConsoleApp
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void MyProgram(string[] args)
|
||||
{
|
||||
Console.WriteLine(""Hello World!"");
|
||||
}
|
||||
}
|
||||
}".EnforceLF();
|
||||
|
||||
var html = await RenderHtml(
|
||||
("sample.csproj", ""),
|
||||
("Program.cs", codeContent),
|
||||
("Readme.md",
|
||||
@"```cs --source-file Program.cs --editable false
|
||||
using System;
|
||||
```"));
|
||||
|
||||
var htmlDocument = new HtmlDocument();
|
||||
htmlDocument.LoadHtml(html);
|
||||
var code = htmlDocument.DocumentNode
|
||||
.SelectSingleNode("//pre/code");
|
||||
|
||||
var output = code.InnerHtml.EnforceLF();
|
||||
|
||||
code.Attributes["data-trydotnet-mode"].Value.Should().Be("include");
|
||||
code.ParentNode.Attributes["style"].Should().BeNull();
|
||||
|
||||
output.Should().Contain($"{codeContent.HtmlEncode()}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_emit_replace_injection_point_for_readonly_regions_from_source_file()
|
||||
{
|
||||
var expectedCode = @"Console.WriteLine(""Hello World!"");";
|
||||
|
||||
var codeContent = @"using System;
|
||||
|
||||
namespace BasicConsoleApp
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void MyProgram(string[] args)
|
||||
{
|
||||
#region code
|
||||
Console.WriteLine(""Hello World!"");
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}".EnforceLF();
|
||||
|
||||
var html = await RenderHtml(
|
||||
("sample.csproj", ""),
|
||||
("Program.cs", codeContent),
|
||||
("Readme.md",
|
||||
@"```cs --source-file Program.cs --region code --editable false
|
||||
using System;
|
||||
```"));
|
||||
|
||||
var htmlDocument = new HtmlDocument();
|
||||
htmlDocument.LoadHtml(html);
|
||||
var code = htmlDocument.DocumentNode
|
||||
.SelectSingleNode("//pre/code");
|
||||
|
||||
var output = code.InnerHtml.EnforceLF();
|
||||
|
||||
code.Attributes["data-trydotnet-mode"].Value.Should().Be("include");
|
||||
code.Attributes["data-trydotnet-injection-point"].Value.Should().Be("replace");
|
||||
code.ParentNode.Attributes["style"].Should().BeNull();
|
||||
|
||||
output.Should().Contain($"{expectedCode.HtmlEncode()}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_emit_run_buttons_for_editable_blocks()
|
||||
{
|
||||
var codeContent = @"using System;
|
||||
|
||||
namespace BasicConsoleApp
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void MyProgram(string[] args)
|
||||
{
|
||||
Console.WriteLine(""Hello World!"");
|
||||
}
|
||||
}
|
||||
}".EnforceLF();
|
||||
|
||||
var html = await RenderHtml(
|
||||
("sample.csproj", ""),
|
||||
("Program.cs", codeContent),
|
||||
("Readme.md",
|
||||
@"```cs --source-file Program.cs
|
||||
Console.WriteLine(""Hello world"");
|
||||
```"));
|
||||
|
||||
var htmlDocument = new HtmlDocument();
|
||||
htmlDocument.LoadHtml(html);
|
||||
var buttons = htmlDocument.DocumentNode
|
||||
.SelectSingleNode("//button");
|
||||
|
||||
buttons.Attributes["data-trydotnet-mode"].Value.Should().Be("run");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_not_emit_run_button_for_non_editable_blocks()
|
||||
{
|
||||
var codeContent = @"using System;
|
||||
|
||||
namespace BasicConsoleApp
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void MyProgram(string[] args)
|
||||
{
|
||||
Console.WriteLine(""Hello World!"");
|
||||
}
|
||||
}
|
||||
}".EnforceLF();
|
||||
|
||||
var html = await RenderHtml(
|
||||
("sample.csproj", ""),
|
||||
("Program.cs", codeContent),
|
||||
("Readme.md",
|
||||
@"```cs --source-file Program.cs --editable false
|
||||
using System;
|
||||
```"));
|
||||
|
||||
var htmlDocument = new HtmlDocument();
|
||||
htmlDocument.LoadHtml(html);
|
||||
var buttons = htmlDocument.DocumentNode
|
||||
.SelectNodes("//button");
|
||||
|
||||
buttons.Should().BeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_emit_math_inline_block_rendering()
|
||||
{
|
||||
var html = await RenderHtml(
|
||||
("Readme.md", @"this is math inline $$\sum ^{n}_{i=0}\left(x_{i}+a_{i}y_{i}\right)$$"));
|
||||
|
||||
var htmlDocument = new HtmlDocument();
|
||||
htmlDocument.LoadHtml(html);
|
||||
|
||||
var math = htmlDocument.DocumentNode
|
||||
.SelectSingleNode("//span");
|
||||
|
||||
math.HasClass("math").Should().BeTrue();
|
||||
math.InnerText.Should().Match(@"\(\sum ^{n}_{i=0}\left(x_{i}+a_{i}y_{i}\right)\)");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_emit_math_block_rendering()
|
||||
{
|
||||
var html = await RenderHtml(
|
||||
("Readme.md", @"$$
|
||||
\begin{equation}
|
||||
\int_0^\infty \frac{x^3}{e^x-1}\,dx = \frac{\pi^4}{15}
|
||||
\label{eq:sample}
|
||||
\end{equation}
|
||||
$$"));
|
||||
|
||||
var htmlDocument = new HtmlDocument();
|
||||
htmlDocument.LoadHtml(html);
|
||||
|
||||
var math = htmlDocument.DocumentNode
|
||||
.SelectSingleNode("//div");
|
||||
|
||||
math.HasClass("math").Should().BeTrue();
|
||||
math.InnerText.EnforceLF().Should().Match(@"
|
||||
\[
|
||||
\begin{equation}
|
||||
\int_0^\infty \frac{x^3}{e^x-1}\,dx = \frac{\pi^4}{15}
|
||||
\label{eq:sample}
|
||||
\end{equation}
|
||||
\]".EnforceLF());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_emit_pre_style_for_hidden_blocks()
|
||||
{
|
||||
var codeContent = @"using System;
|
||||
|
||||
namespace BasicConsoleApp
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void MyProgram(string[] args)
|
||||
{
|
||||
Console.WriteLine(""Hello World!"");
|
||||
}
|
||||
}
|
||||
}".EnforceLF();
|
||||
|
||||
var html = await RenderHtml(
|
||||
("sample.csproj", ""),
|
||||
("Program.cs", codeContent),
|
||||
("Readme.md",
|
||||
@"```cs --source-file Program.cs --editable false --hidden
|
||||
Console.WriteLine(""This code should not appear"");
|
||||
```"));
|
||||
|
||||
var htmlDocument = new HtmlDocument();
|
||||
htmlDocument.LoadHtml(html);
|
||||
|
||||
var code = htmlDocument.DocumentNode
|
||||
.SelectSingleNode("//pre/code");
|
||||
|
||||
var output = code.InnerHtml.EnforceLF();
|
||||
|
||||
code.Attributes["data-trydotnet-mode"].Value.Should().Match("include");
|
||||
|
||||
code.ParentNode.Attributes["style"].Value.Should().Match("border:none; margin:0px; padding:0px; visibility:hidden; display: none;");
|
||||
|
||||
output.Should().Contain($"{codeContent.HtmlEncode()}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_enforce_editable_false_for_hidden_blocks()
|
||||
{
|
||||
var codeContent = @"using System;
|
||||
|
||||
namespace BasicConsoleApp
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void MyProgram(string[] args)
|
||||
{
|
||||
Console.WriteLine(""Hello World!"");
|
||||
}
|
||||
}
|
||||
}".EnforceLF();
|
||||
|
||||
var html = await RenderHtml(
|
||||
("sample.csproj", ""),
|
||||
("Program.cs", codeContent),
|
||||
("Readme.md",
|
||||
@"```cs --source-file Program.cs --hidden
|
||||
Console.WriteLine(""This code should not appear"");
|
||||
```"));
|
||||
|
||||
var htmlDocument = new HtmlDocument();
|
||||
htmlDocument.LoadHtml(html);
|
||||
|
||||
var code = htmlDocument.DocumentNode
|
||||
.SelectSingleNode("//pre/code");
|
||||
|
||||
var output = code.InnerHtml.EnforceLF();
|
||||
|
||||
code.Attributes["data-trydotnet-mode"].Value.Should().Match("include");
|
||||
|
||||
code.ParentNode.Attributes["style"].Value.Should().Match("border:none; margin:0px; padding:0px; visibility:hidden; display: none;");
|
||||
|
||||
output.Should().Contain($"{codeContent.HtmlEncode()}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Multiple_fenced_code_blocks_are_correctly_rendered()
|
||||
{
|
||||
var region1Code = @"Console.WriteLine(""I am region one code"");";
|
||||
var region2Code = @"Console.WriteLine(""I am region two code"");";
|
||||
var codeContent = $@"using System;
|
||||
|
||||
namespace BasicConsoleApp
|
||||
{{
|
||||
class Program
|
||||
{{
|
||||
static void MyProgram(string[] args)
|
||||
{{
|
||||
#region region1
|
||||
{region1Code}
|
||||
#endregion
|
||||
|
||||
#region region2
|
||||
{region2Code}
|
||||
#endregion
|
||||
}}
|
||||
}}
|
||||
}}".EnforceLF();
|
||||
|
||||
var html = await RenderHtml(("sample.csproj", ""),
|
||||
("Program.cs", codeContent),
|
||||
("Readme.md",
|
||||
@"This is a markdown file with two regions
|
||||
This is region 1
|
||||
```cs --source-file Program.cs --region region1
|
||||
//This part should not be included
|
||||
```
|
||||
This is region 2
|
||||
```cs --source-file Program.cs --region region2
|
||||
//This part should not be included as well
|
||||
```
|
||||
This is the end of the file"));
|
||||
var htmlDocument = new HtmlDocument();
|
||||
htmlDocument.LoadHtml(html);
|
||||
var codeNodes = htmlDocument.DocumentNode.SelectNodes("//pre/code");
|
||||
|
||||
codeNodes.Should().HaveCount(2);
|
||||
codeNodes[0].InnerHtml.Should().Contain($"{region1Code.HtmlEncode()}");
|
||||
codeNodes[1].InnerHtml.Should().Contain($"{region2Code.HtmlEncode()}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Non_editable_code_inserts_code_present_in_markdown()
|
||||
{
|
||||
var html = await RenderHtml(("readme.md", @"
|
||||
```cs --editable false --package console
|
||||
//some code to include
|
||||
```
|
||||
"));
|
||||
|
||||
var htmlDocument = new HtmlDocument();
|
||||
htmlDocument.LoadHtml(html);
|
||||
|
||||
var codeNodes = htmlDocument.DocumentNode.SelectNodes("//pre/code[@data-trydotnet-mode='include']");
|
||||
codeNodes.Should().HaveCount(1);
|
||||
|
||||
codeNodes.Single().InnerText.Should().Match(@"*//some code to include*");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Annotated_console_blocks_do_not_produce_HTML_elements()
|
||||
{
|
||||
var codeContent = $@"using System;
|
||||
|
||||
namespace BasicConsoleApp
|
||||
{{
|
||||
class Program
|
||||
{{
|
||||
static void MyProgram(string[] args)
|
||||
{{
|
||||
#region abc
|
||||
Console.WriteLine(""conca"" + ""tenated"");
|
||||
#endregion
|
||||
|
||||
}}
|
||||
}}
|
||||
}}".EnforceLF();
|
||||
|
||||
var html = await RenderHtml(
|
||||
("readme.md", @"
|
||||
```cs --source-file Program.cs --region abc --session 123
|
||||
```
|
||||
```console --session 123
|
||||
concatenated
|
||||
```
|
||||
"),
|
||||
("Program.cs", codeContent),
|
||||
("sample.csproj", ""));
|
||||
|
||||
html.Should().NotContain("<div class=\"notification is-danger\">");
|
||||
html.Should().NotContain("concatenated");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Package_option_defaults_to_startup_options()
|
||||
{
|
||||
const string expectedPackage = "console";
|
||||
const string expectedPackageVersion = "1.2.3";
|
||||
|
||||
var defaultCodeBlockAnnotations = new StartupOptions(
|
||||
package: expectedPackage,
|
||||
packageVersion: expectedPackageVersion);
|
||||
var project = new MarkdownProject(
|
||||
new InMemoryDirectoryAccessor(new DirectoryInfo(Directory.GetCurrentDirectory()))
|
||||
{
|
||||
("readme.md", @"
|
||||
```cs --source-file Program.cs
|
||||
```
|
||||
"),
|
||||
("Program.cs", "")
|
||||
},
|
||||
await Default.PackageRegistry.ValueAsync(),
|
||||
defaultCodeBlockAnnotations
|
||||
);
|
||||
|
||||
var html = (await project.GetAllMarkdownFiles()
|
||||
.Single()
|
||||
.ToHtmlContentAsync())
|
||||
.ToString();
|
||||
|
||||
html.Should()
|
||||
.Contain($"data-trydotnet-package=\"{expectedPackage}\" data-trydotnet-package-version=\"{expectedPackageVersion}\"");
|
||||
}
|
||||
|
||||
protected async Task<string> RenderHtml(params (string, string)[] project)
|
||||
{
|
||||
var directoryAccessor = new InMemoryDirectoryAccessor(new DirectoryInfo(Directory.GetCurrentDirectory()));
|
||||
|
||||
foreach (var valueTuple in project)
|
||||
{
|
||||
directoryAccessor.Add(valueTuple);
|
||||
}
|
||||
|
||||
var markdownProject = new MarkdownProject(
|
||||
directoryAccessor,
|
||||
await Default.PackageRegistry.ValueAsync());
|
||||
|
||||
var markdownFile = markdownProject.GetAllMarkdownFiles().Single();
|
||||
var html = (await markdownFile.ToHtmlContentAsync()).ToString();
|
||||
|
||||
_output.WriteLine(html);
|
||||
|
||||
return html;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.IO;
|
||||
using FluentAssertions;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
using WorkspaceServer;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.DotNet.Try.Markdown;
|
||||
using MLS.Agent.Markdown;
|
||||
using WorkspaceServer.Tests;
|
||||
using MLS.Agent.Tools;
|
||||
using MLS.Agent.Tools.Tests;
|
||||
|
||||
namespace MLS.Agent.Tests
|
||||
{
|
||||
public class MarkdownProjectTests
|
||||
{
|
||||
public class GetAllMarkdownFiles
|
||||
{
|
||||
[Fact]
|
||||
public async Task Returns_list_of_all_relative_paths_to_all_markdown_files()
|
||||
{
|
||||
var dirAccessor = new InMemoryDirectoryAccessor()
|
||||
{
|
||||
("Readme.md", ""),
|
||||
("Subdirectory/Tutorial.md", ""),
|
||||
("Program.cs", "")
|
||||
};
|
||||
|
||||
var project = new MarkdownProject(dirAccessor, await Default.PackageRegistry.ValueAsync());
|
||||
|
||||
var files = project.GetAllMarkdownFiles();
|
||||
|
||||
files.Should().HaveCount(2);
|
||||
files.Should().Contain(f => f.Path.Value.Equals("./Readme.md"));
|
||||
files.Should().Contain(f => f.Path.Value.Equals("./Subdirectory/Tutorial.md"));
|
||||
}
|
||||
}
|
||||
|
||||
public class TryGetMarkdownFile
|
||||
{
|
||||
[Fact]
|
||||
public async Task Returns_false_for_nonexistent_file()
|
||||
{
|
||||
var workingDir = TestAssets.SampleConsole;
|
||||
var dirAccessor = new InMemoryDirectoryAccessor(workingDir);
|
||||
var project = new MarkdownProject(dirAccessor, await Default.PackageRegistry.ValueAsync());
|
||||
var path = new RelativeFilePath("DOESNOTEXIST");
|
||||
|
||||
project.TryGetMarkdownFile(path, out _).Should().BeFalse();
|
||||
}
|
||||
}
|
||||
|
||||
public class GetAllProjects
|
||||
{
|
||||
[Fact]
|
||||
public async Task Returns_all_projects_referenced_from_all_markdown_files()
|
||||
{
|
||||
var project = new MarkdownProject(
|
||||
new InMemoryDirectoryAccessor(new DirectoryInfo(Directory.GetCurrentDirectory()))
|
||||
{
|
||||
("readme.md", @"
|
||||
```cs --project ../Project1/Console1.csproj
|
||||
```
|
||||
```cs --project ../Project2/Console2.csproj
|
||||
```
|
||||
"),
|
||||
("../Project1/Console1.csproj", @""),
|
||||
("../Project2/Console2.csproj", @"")
|
||||
},
|
||||
await Default.PackageRegistry.ValueAsync());
|
||||
|
||||
var markdownFiles = project.GetAllMarkdownFiles();
|
||||
|
||||
var annotatedCodeBlocks = await Task.WhenAll(markdownFiles.Select(f => f.GetAnnotatedCodeBlocks()));
|
||||
|
||||
annotatedCodeBlocks
|
||||
.SelectMany(f => f)
|
||||
.Select(block => block.Annotations)
|
||||
.OfType<LocalCodeBlockAnnotations>()
|
||||
.Select(b => b.Project)
|
||||
.Should()
|
||||
.Contain(p => p.Directory.Name == "Project1")
|
||||
.And
|
||||
.Contain(p => p.Directory.Name == "Project2");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using FluentAssertions;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using WorkspaceServer.Packaging;
|
||||
using WorkspaceServer.Tests;
|
||||
using Xunit;
|
||||
|
||||
namespace MLS.Agent.Tests
|
||||
{
|
||||
public class ProjectFilePackageDiscoveryStrategyTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task Discover_package_from_project_file()
|
||||
{
|
||||
var strategy = new ProjectFilePackageDiscoveryStrategy(false);
|
||||
var sampleProject = (await Create.ConsoleWorkspaceCopy()).Directory;
|
||||
var projectFile = sampleProject.GetFiles("*.csproj").Single();
|
||||
var packageBuilder = await strategy.Locate(new PackageDescriptor(projectFile.FullName));
|
||||
|
||||
packageBuilder.PackageName.Should().Be(projectFile.FullName);
|
||||
packageBuilder.Directory.FullName.Should().Be(sampleProject.FullName);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using Xunit;
|
||||
|
||||
[assembly: CollectionBehavior(DisableTestParallelization = true)]
|
|
@ -1,47 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace MLS.Agent.Tests
|
||||
{
|
||||
public class ProxyTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task XForwardedPathBase_is_prepended_to_request_url()
|
||||
{
|
||||
using (var service = new AgentService())
|
||||
{
|
||||
var message = new HttpRequestMessage(HttpMethod.Get, "/");
|
||||
message.Headers.Add("X-Forwarded-PathBase", "/LocalCodeRunner/blazor-console");
|
||||
|
||||
var expected = @"<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset=""utf-8"" />
|
||||
<meta name=""viewport"" content=""width=device-width"">
|
||||
<base href=""/LocalCodeRunner/blazor-console/"" />
|
||||
</head>
|
||||
<body>
|
||||
<app>Loading...</app>
|
||||
|
||||
<script src=""interop.js""></script>
|
||||
<script src=""_framework/blazor.webassembly.js""></script>
|
||||
</body>
|
||||
</html>
|
||||
";
|
||||
|
||||
var result = await service.SendAsync(message);
|
||||
var content = await result.Content.ReadAsStringAsync();
|
||||
content.Should().Be(expected);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using Microsoft.DotNet.Try.Protocol.Tests;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace MLS.Agent.Tests
|
||||
{
|
||||
internal static class StringExtensions
|
||||
{
|
||||
public static string FormatJson(this string value)
|
||||
{
|
||||
var s = JToken.Parse(value).ToString(Formatting.Indented);
|
||||
return s.EnforceLF();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using FluentAssertions;
|
||||
using MLS.Agent.CommandLine;
|
||||
using System.Linq;
|
||||
using System.Net.NetworkInformation;
|
||||
using Xunit;
|
||||
namespace MLS.Agent.Tests
|
||||
{
|
||||
public class WebHostBuilderExtensionTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(StartupMode.Try)]
|
||||
[InlineData(StartupMode.Hosted)]
|
||||
public void If_port_is_not_specified_a_free_port_is_returned(StartupMode mode)
|
||||
{
|
||||
var uri = WebHostBuilderExtensions.GetBrowserLaunchUri(mode, null);
|
||||
CheckIfPortIsAvailable(uri.Port).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(StartupMode.Try)]
|
||||
[InlineData(StartupMode.Hosted)]
|
||||
public void If_a_port_it_specified_it_is_used(StartupMode mode)
|
||||
{
|
||||
var uri = WebHostBuilderExtensions.GetBrowserLaunchUri(mode, 6000);
|
||||
uri.Port.Should().Be(6000);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void In_try_mode_host_should_be_localhost()
|
||||
{
|
||||
var uri = WebHostBuilderExtensions.GetBrowserLaunchUri(StartupMode.Try, 6000);
|
||||
uri.Host.Should().Be("localhost");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void In_try_mode_scheme_should_be_https()
|
||||
{
|
||||
var uri = WebHostBuilderExtensions.GetBrowserLaunchUri(StartupMode.Try, 6000);
|
||||
uri.Scheme.Should().Be("https");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void In_hosted_mode_host_should_be_star()
|
||||
{
|
||||
var uri = WebHostBuilderExtensions.GetBrowserLaunchUri(StartupMode.Hosted, 6000);
|
||||
uri.Host.Should().Be("*");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void In_hosted_mode_scheme_should_be_http()
|
||||
{
|
||||
var uri = WebHostBuilderExtensions.GetBrowserLaunchUri(StartupMode.Hosted, 6000);
|
||||
uri.Scheme.Should().Be("http");
|
||||
}
|
||||
|
||||
private static bool CheckIfPortIsAvailable(ushort port)
|
||||
{
|
||||
// Evaluate current system tcp connections. This is the same information provided
|
||||
// by the netstat command line application, just in .Net strongly-typed object
|
||||
// form. We will look through the list, and if our port we would like to use
|
||||
// in our TcpClient is occupied, we will set isAvailable to false.
|
||||
IPGlobalProperties ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();
|
||||
TcpConnectionInformation[] tcpConnInfoArray = ipGlobalProperties.GetActiveTcpConnections();
|
||||
|
||||
return tcpConnInfoArray.FirstOrDefault(tcpi => tcpi.LocalEndPoint.Port == port) == null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using Recipes;
|
||||
using System;
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.IO;
|
||||
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;
|
||||
using WorkspaceServer.Packaging;
|
||||
using WorkspaceServer.Tests;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using Buffer = Microsoft.DotNet.Try.Protocol.Buffer;
|
||||
using File = Microsoft.DotNet.Try.Protocol.File;
|
||||
using MLS.Agent.Tools;
|
||||
|
||||
namespace MLS.Agent.Tests
|
||||
{
|
||||
public class WorkspaceDiscoveryTests : ApiViaHttpTestsBase
|
||||
{
|
||||
public WorkspaceDiscoveryTests(ITestOutputHelper output) : base(output)
|
||||
{
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Local_tool_workspace_can_be_discovered()
|
||||
{
|
||||
var console = new TestConsole();
|
||||
var (packageName, packageLocation) = await CreateLocalTool(console);
|
||||
|
||||
var output = Guid.NewGuid().ToString();
|
||||
var requestJson = Create.SimpleWorkspaceRequestAsJson(output, packageName);
|
||||
|
||||
var response = await CallRun(requestJson, options: new StartupOptions(addPackageSource: new PackageSource(packageLocation.FullName), rootDirectory: new FileSystemDirectoryAccessor(Directory.GetCurrentDirectory())));
|
||||
var result = await response
|
||||
.EnsureSuccess()
|
||||
.DeserializeAs<RunResult>();
|
||||
|
||||
result.ShouldSucceedWithOutput(output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Project_file_path_workspace_can_be_discovered_and_run_with_buffer_inlining()
|
||||
{
|
||||
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) },
|
||||
buffers: new[] { new Buffer(new BufferId(programCs.FullName, "alpha"), $"Console.WriteLine(\"{output}\");") },
|
||||
workspaceType: csproj.FullName);
|
||||
|
||||
var requestJson = new WorkspaceRequest(ws, requestId: "TestRun").ToJson();
|
||||
|
||||
var response = await CallRun(requestJson);
|
||||
var result = await response
|
||||
.EnsureSuccess()
|
||||
.DeserializeAs<RunResult>();
|
||||
|
||||
result.ShouldSucceedWithOutput(output);
|
||||
}
|
||||
|
||||
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 ws = await ((ICreateWorkspaceForRun)build).CreateRoslynWorkspaceForRunAsync(new TimeBudget(30.Seconds()));
|
||||
var packageLocation = new DirectoryInfo(
|
||||
Path.Combine(build.Directory.FullName, "pack-output"));
|
||||
|
||||
var packageName = await PackCommand.Do(
|
||||
new PackOptions(
|
||||
build.Directory,
|
||||
outputDirectory: packageLocation,
|
||||
enableWasm: false),
|
||||
console);
|
||||
|
||||
return (packageName, packageLocation);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using FluentAssertions;
|
||||
using Microsoft.DotNet.Try.Protocol;
|
||||
using Recipes;
|
||||
using WorkspaceServer.Tests;
|
||||
using Xunit;
|
||||
using Buffer = Microsoft.DotNet.Try.Protocol.Buffer;
|
||||
|
||||
namespace MLS.Agent.Tests
|
||||
{
|
||||
public class WorkspaceRequestTests
|
||||
{
|
||||
[Fact]
|
||||
public void webrequest_must_have_verb()
|
||||
{
|
||||
var action = new Action(() =>
|
||||
{
|
||||
var wr = new HttpRequest(@"/handler", string.Empty);
|
||||
});
|
||||
action.Should().Throw<ArgumentException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void webrequest_must_have_relative_url()
|
||||
{
|
||||
var action = new Action(() =>
|
||||
{
|
||||
var wr = new HttpRequest(@"http://www.microsoft.com", "post");
|
||||
});
|
||||
action.Should().Throw<ArgumentException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void When_ActiveBufferId_is_not_specified_and_there_is_only_one_buffer_then_it_returns_that_buffers_id()
|
||||
{
|
||||
var request = new WorkspaceRequest(
|
||||
new Workspace(
|
||||
buffers: new[]
|
||||
{
|
||||
new Buffer("the.only.buffer.cs", "its content", 123)
|
||||
}),
|
||||
requestId: "TestRun");
|
||||
|
||||
request.ActiveBufferId.Should().Be(BufferId.Parse("the.only.buffer.cs"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WorkspaceRequest_deserializes_from_JSON()
|
||||
{
|
||||
var (processed, position) = CodeManipulation.ProcessMarkup("Console.WriteLine($$)");
|
||||
|
||||
var original = new WorkspaceRequest(
|
||||
activeBufferId: BufferId.Parse("default.cs"),
|
||||
workspace: Workspace.FromSource(
|
||||
processed,
|
||||
"script",
|
||||
id: "default.cs",
|
||||
position: position), requestId: "TestRun");
|
||||
|
||||
var json = original.ToJson();
|
||||
|
||||
var deserialized = json.FromJsonTo<WorkspaceRequest>();
|
||||
|
||||
deserialized.Should().BeEquivalentTo(original);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
import * as path from "path";
|
||||
import * as fs from "fs";
|
||||
|
||||
//Read the expected approval file as json and return the result
|
||||
export function GetExpectedResultASJSON(filename: string) {
|
||||
let testDir = path.resolve(process.cwd(),"node_modules", "mls-agent-results", filename);
|
||||
return JSON.parse(fs.readFileSync(testDir).toString());
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"name": "mls-agent-results",
|
||||
"requires": true,
|
||||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "10.12.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.12.tgz",
|
||||
"integrity": "sha512-Pr+6JRiKkfsFvmU/LK68oBRCQeEg36TyAbPhc2xpez24OOZZCuoIhWGTd39VZy6nGafSbxzGouFPTFD/rR1A0A=="
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
{
|
||||
"name": "mls-agent-results",
|
||||
"description": "Package to verify if the agent and the simulator in the client are in sync",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": [
|
||||
"dist",
|
||||
"*.approved.json"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsc"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://msazure.visualstudio.com/DefaultCollection/One/_git/MLS-Agent"
|
||||
},
|
||||
"author": "MLS-Dev",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@types/node": "^10.12.12"
|
||||
}
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
/* Basic Options */
|
||||
"target": "es2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
|
||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
||||
// "lib": [], /* Specify library files to be included in the compilation. */
|
||||
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||
// "checkJs": true, /* Report errors in .js files. */
|
||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
||||
"declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
||||
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||
"outDir": "dist", /* Redirect output structure to the directory. */
|
||||
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||
// "removeComments": true, /* Do not emit comments to output. */
|
||||
// "noEmit": true, /* Do not emit outputs. */
|
||||
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||
|
||||
/* Strict Type-Checking Options */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* Enable strict null checks. */
|
||||
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
|
||||
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||
|
||||
/* Additional Checks */
|
||||
// "noUnusedLocals": true, /* Report errors on unused locals. */
|
||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
|
||||
/* Module Resolution Options */
|
||||
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
"types": ["node"], /* Type declaration files to be included in compilation. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
|
||||
/* Source Map Options */
|
||||
// "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||
// "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
|
||||
|
||||
/* Experimental Options */
|
||||
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||
}
|
||||
}
|
|
@ -1,350 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using FluentAssertions;
|
||||
using Microsoft.DotNet.PlatformAbstractions;
|
||||
using MLS.Agent.Tools;
|
||||
using MLS.Agent.Tools.Tests;
|
||||
using Xunit;
|
||||
using static Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment;
|
||||
|
||||
namespace MLS.Agent.Tests.Markdown
|
||||
{
|
||||
public abstract class DirectoryAccessorTests
|
||||
{
|
||||
public abstract IDirectoryAccessor GetDirectory(DirectoryInfo dirInfo, DirectoryInfo rootDirectoryToAddFiles = null);
|
||||
|
||||
public abstract IDirectoryAccessor CreateDirectory([CallerMemberName]string testName = null);
|
||||
|
||||
[Fact]
|
||||
public void It_can_retrieve_all_files_recursively()
|
||||
{
|
||||
var directory = GetDirectory(TestAssets.SampleConsole);
|
||||
|
||||
var files = directory.GetAllFilesRecursively();
|
||||
|
||||
files.Should()
|
||||
.Contain(new RelativeFilePath("BasicConsoleApp.csproj"))
|
||||
.And
|
||||
.Contain(new RelativeFilePath("Program.cs"))
|
||||
.And
|
||||
.Contain(new RelativeFilePath("Readme.md"))
|
||||
.And
|
||||
.Contain(new RelativeFilePath("Subdirectory/AnotherProgram.cs"))
|
||||
.And
|
||||
.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()
|
||||
{
|
||||
var directory = GetDirectory(TestAssets.SampleConsole);
|
||||
|
||||
var files = directory.GetAllFilesRecursively();
|
||||
|
||||
files.Should().NotContain(f => f.Value.EndsWith("Subdirectory"));
|
||||
files.Should().NotContain(f => f.Value.EndsWith("Subdirectory/"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void It_can_retrieve_all_directories_recursively()
|
||||
{
|
||||
var directory = GetDirectory(TestAssets.SampleConsole);
|
||||
|
||||
var directories = directory.GetAllDirectoriesRecursively();
|
||||
|
||||
directories.Should()
|
||||
.Contain(new RelativeDirectoryPath("Subdirectory"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAllDirectoriesRecursively_does_not_return_files()
|
||||
{
|
||||
var directory = GetDirectory(TestAssets.SampleConsole);
|
||||
|
||||
var directories = directory.GetAllDirectoriesRecursively();
|
||||
|
||||
directories.Should()
|
||||
.NotContain(d => d.Value.EndsWith("BasicConsoleApp.csproj"))
|
||||
.And
|
||||
.NotContain(d => d.Value.EndsWith("Program.cs"))
|
||||
.And
|
||||
.NotContain(d => d.Value.EndsWith("Readme.md"))
|
||||
.And
|
||||
.NotContain(d => d.Value.EndsWith("Subdirectory/AnotherProgram.cs"))
|
||||
.And
|
||||
.NotContain(d => d.Value.EndsWith("Subdirectory/Tutorial.md"));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(".")]
|
||||
[InlineData("./Subdirectory")]
|
||||
public void When_the_directory_exists_DirectoryExists_returns_true(string path)
|
||||
{
|
||||
var directoryAccessor = GetDirectory(TestAssets.SampleConsole);
|
||||
|
||||
directoryAccessor.DirectoryExists(path).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(".")]
|
||||
[InlineData("Subdirectory")]
|
||||
public void It_can_ensure_a_directory_exists(string path)
|
||||
{
|
||||
var directoryAccessor = CreateDirectory();
|
||||
|
||||
directoryAccessor.EnsureDirectoryExists(path);
|
||||
|
||||
directoryAccessor.DirectoryExists(path).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnsureDirectoryExists_is_idempotent()
|
||||
{
|
||||
var directoryAccessor = CreateDirectory();
|
||||
|
||||
var subdirectory = "./a-subdirectory";
|
||||
|
||||
directoryAccessor.EnsureDirectoryExists(subdirectory);
|
||||
|
||||
directoryAccessor
|
||||
.Invoking(d => d.EnsureDirectoryExists(subdirectory))
|
||||
.Should()
|
||||
.NotThrow();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("./some-file.txt", "hello!")]
|
||||
public void It_can_write_text_to_a_file(string path, string text)
|
||||
{
|
||||
var directory = CreateDirectory();
|
||||
|
||||
directory.WriteAllText(path, text);
|
||||
|
||||
directory.ReadAllText(path).Should().Be(text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void It_can_overwrite_an_existing_file()
|
||||
{
|
||||
var directory = CreateDirectory();
|
||||
|
||||
directory.WriteAllText("./some-file.txt", "original text");
|
||||
directory.WriteAllText("./some-file.txt", "updated text");
|
||||
|
||||
directory.ReadAllText("./some-file.txt").Should().Be("updated text");
|
||||
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void When_the_file_exists_FileExists_returns_true()
|
||||
{
|
||||
var testDir = TestAssets.SampleConsole;
|
||||
GetDirectory(testDir).FileExists(new RelativeFilePath("Program.cs")).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void When_the_filepath_is_null_FileExists_returns_false()
|
||||
{
|
||||
var testDir = TestAssets.SampleConsole;
|
||||
GetDirectory(testDir).Invoking(d => d.FileExists(null)).Should().Throw<ArgumentNullException>();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"Subdirectory/AnotherProgram.cs")]
|
||||
[InlineData(@"Subdirectory\AnotherProgram.cs")]
|
||||
public void When_the_filepath_contains_subdirectory_paths_FileExists_returns_true(string filepath)
|
||||
{
|
||||
var testDir = TestAssets.SampleConsole;
|
||||
GetDirectory(testDir).FileExists(new RelativeFilePath(filepath)).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"../Program.cs")]
|
||||
[InlineData(@"..\Program.cs")]
|
||||
public void When_the_filepath_contains_a_path_that_looks_upward_in_tree_then_FileExists_returns_the_text(string filePath)
|
||||
{
|
||||
var rootDirectoryToAddFiles = TestAssets.SampleConsole;
|
||||
var testDir = new DirectoryInfo(Path.Combine(rootDirectoryToAddFiles.FullName, "Subdirectory"));
|
||||
GetDirectory(testDir, rootDirectoryToAddFiles).FileExists(new RelativeFilePath(filePath)).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void When_the_filepath_contains_an_existing_file_ReadAllText_returns_the_text()
|
||||
{
|
||||
var testDir = TestAssets.SampleConsole;
|
||||
GetDirectory(testDir).ReadAllText(new RelativeFilePath("Program.cs")).Should().Contain("Hello World!");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"Subdirectory/AnotherProgram.cs")]
|
||||
[InlineData(@"Subdirectory\AnotherProgram.cs")]
|
||||
public void When_the_filepath_contains_an_existing_file_from_subdirectory_then_ReadAllText_returns_the_text(string filePath)
|
||||
{
|
||||
var testDir = TestAssets.SampleConsole;
|
||||
GetDirectory(testDir).ReadAllText(new RelativeFilePath(filePath)).Should().Contain("Hello from Another Program!");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"../Program.cs")]
|
||||
[InlineData(@"..\Program.cs")]
|
||||
public void When_the_filepath_contains_a_path_that_looks_upward_in_tree_then_ReadAllText_returns_the_text(string filePath)
|
||||
{
|
||||
var rootDirectoryToAddFiles = TestAssets.SampleConsole;
|
||||
var testDir = new DirectoryInfo(Path.Combine(rootDirectoryToAddFiles.FullName, "Subdirectory"));
|
||||
var value = GetDirectory(testDir, rootDirectoryToAddFiles).ReadAllText(new RelativeFilePath(filePath));
|
||||
value.Should().Contain("Hello World!");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_return_a_directory_accessor_for_a_relative_path()
|
||||
{
|
||||
var rootDir = TestAssets.SampleConsole;
|
||||
var outerDirAccessor = GetDirectory(rootDir);
|
||||
var inner = outerDirAccessor.GetDirectoryAccessorForRelativePath(new RelativeDirectoryPath("Subdirectory"));
|
||||
inner.FileExists(new RelativeFilePath("AnotherProgram.cs")).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Path_separators_are_uniform()
|
||||
{
|
||||
var directory = GetDirectory(TestAssets.SampleConsole);
|
||||
var unexpectedPathSeparator = OperatingSystemPlatform == Platform.Windows
|
||||
? "/"
|
||||
: "\\";
|
||||
|
||||
foreach (var relativePath in directory.GetAllFilesRecursively())
|
||||
{
|
||||
var fullyQualifiedPath = directory.GetFullyQualifiedPath(relativePath).FullName;
|
||||
fullyQualifiedPath.Should().NotContain(unexpectedPathSeparator);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void It_can_make_a_directory_accessor_from_an_absolute_DirectoryInfo()
|
||||
{
|
||||
|
||||
var directory = GetDirectory(TestAssets.SampleConsole);
|
||||
|
||||
var fullyQualifiedSubdirectory = new DirectoryInfo(directory.GetFullyQualifiedFilePath("./Subdirectory/").FullName);
|
||||
|
||||
var subdirectory = directory.GetDirectoryAccessorFor(fullyQualifiedSubdirectory);
|
||||
|
||||
subdirectory.FileExists("Tutorial.md").Should().BeTrue();
|
||||
}
|
||||
}
|
||||
|
||||
public class FileSystemDirectoryAccessorTests : DirectoryAccessorTests
|
||||
{
|
||||
public override IDirectoryAccessor CreateDirectory([CallerMemberName]string testName = null)
|
||||
{
|
||||
var directory = PackageUtilities.CreateDirectory(testName);
|
||||
|
||||
return new FileSystemDirectoryAccessor(directory);
|
||||
}
|
||||
|
||||
public override IDirectoryAccessor GetDirectory(DirectoryInfo directoryInfo, DirectoryInfo rootDirectoryToAddFiles = null)
|
||||
{
|
||||
return new FileSystemDirectoryAccessor(directoryInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public class InMemoryDirectoryAccessorTests : DirectoryAccessorTests
|
||||
{
|
||||
public override IDirectoryAccessor CreateDirectory([CallerMemberName]string testName = null)
|
||||
{
|
||||
return new InMemoryDirectoryAccessor();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("one")]
|
||||
[InlineData("./one")]
|
||||
[InlineData("./one/two")]
|
||||
[InlineData("./one/two/three")]
|
||||
public void DirectoryExists_returns_true_for_parent_directories_of_explicitly_added_relative_file_paths(string relativeDirectoryPath)
|
||||
{
|
||||
var directory = new InMemoryDirectoryAccessor
|
||||
{
|
||||
("./one/two/three/file.txt", "")
|
||||
};
|
||||
|
||||
directory.DirectoryExists(relativeDirectoryPath).Should().BeTrue();
|
||||
}
|
||||
|
||||
public override IDirectoryAccessor GetDirectory(DirectoryInfo rootDirectory, DirectoryInfo rootDirectoryToAddFiles = null)
|
||||
{
|
||||
return new InMemoryDirectoryAccessor(rootDirectory, rootDirectoryToAddFiles)
|
||||
{
|
||||
("BasicConsoleApp.csproj",
|
||||
@"<Project Sdk=""Microsoft.NET.Sdk"">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
"),
|
||||
("Program.cs",
|
||||
@"using System;
|
||||
|
||||
namespace BasicConsoleApp
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void MyProgram(string[] args)
|
||||
{
|
||||
Console.WriteLine(""Hello World!"");
|
||||
}
|
||||
}
|
||||
}"),
|
||||
("Readme.md",
|
||||
@"This is a sample *markdown file*
|
||||
|
||||
```cs Program.cs
|
||||
```"),
|
||||
("./Subdirectory/Tutorial.md", "This is a sample *tutorial file*"),
|
||||
("./Subdirectory/AnotherProgram.cs",
|
||||
@"using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace MLS.Agent.Tests.TestProjects.BasicConsoleApp.Subdirectory
|
||||
{
|
||||
class AnotherPorgram
|
||||
{
|
||||
static void MyAnotherProgram(string[] args)
|
||||
{
|
||||
Console.WriteLine(""Hello from Another Program!"");
|
||||
}
|
||||
}
|
||||
}
|
||||
")
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,209 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace MLS.Agent.Tools.Tests
|
||||
{
|
||||
public class InMemoryDirectoryAccessor : IDirectoryAccessor, IEnumerable
|
||||
{
|
||||
private readonly DirectoryInfo _rootDirToAddFiles;
|
||||
|
||||
private Dictionary<FileSystemInfo, string> _files = new Dictionary<FileSystemInfo, string>(
|
||||
new Dictionary<FileSystemInfo, string>(),
|
||||
new FileSystemInfoComparer());
|
||||
|
||||
public InMemoryDirectoryAccessor(
|
||||
DirectoryInfo workingDirectory = null,
|
||||
DirectoryInfo rootDirectoryToAddFiles = null)
|
||||
{
|
||||
WorkingDirectory = workingDirectory ??
|
||||
new DirectoryInfo(Path.Combine("some", "fake", "path"));
|
||||
|
||||
_rootDirToAddFiles = rootDirectoryToAddFiles ??
|
||||
WorkingDirectory;
|
||||
}
|
||||
|
||||
internal DirectoryInfo WorkingDirectory { get; }
|
||||
|
||||
public void Add((string path, string content) file)
|
||||
{
|
||||
var fileInfo = new FileInfo(Path.Combine(_rootDirToAddFiles.FullName, file.path));
|
||||
|
||||
_files.Add(fileInfo, file.content);
|
||||
|
||||
var directory = fileInfo.Directory;
|
||||
|
||||
while (directory != null &&
|
||||
!FileSystemInfoComparer.Instance.Equals(directory, WorkingDirectory))
|
||||
{
|
||||
_files.TryAdd(directory, null);
|
||||
|
||||
directory = directory.Parent;
|
||||
}
|
||||
}
|
||||
|
||||
public FileSystemDirectoryAccessor CreateFiles()
|
||||
{
|
||||
foreach (var filePath in GetAllFilesRecursively())
|
||||
{
|
||||
var absolutePath = GetFullyQualifiedPath(filePath);
|
||||
|
||||
var text = ReadAllText(filePath);
|
||||
|
||||
if (absolutePath is FileInfo file)
|
||||
{
|
||||
if (!file.Directory.Exists)
|
||||
{
|
||||
file.Directory.Create();
|
||||
}
|
||||
|
||||
File.WriteAllText(absolutePath.FullName, text);
|
||||
}
|
||||
}
|
||||
|
||||
return new FileSystemDirectoryAccessor(WorkingDirectory);
|
||||
}
|
||||
|
||||
public bool DirectoryExists(RelativeDirectoryPath path)
|
||||
{
|
||||
var fullyQualifiedDirPath = GetFullyQualifiedPath(path);
|
||||
|
||||
return _files
|
||||
.Keys
|
||||
.Any(f =>
|
||||
{
|
||||
switch (f)
|
||||
{
|
||||
case FileInfo file:
|
||||
return FileSystemInfoComparer.Instance.Equals(
|
||||
file.Directory,
|
||||
fullyQualifiedDirPath);
|
||||
|
||||
case DirectoryInfo dir:
|
||||
return FileSystemInfoComparer.Instance.Equals(
|
||||
dir,
|
||||
fullyQualifiedDirPath);
|
||||
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void EnsureDirectoryExists(RelativeDirectoryPath path)
|
||||
{
|
||||
_files[GetFullyQualifiedPath(path)] = null;
|
||||
}
|
||||
|
||||
public bool FileExists(RelativeFilePath path)
|
||||
{
|
||||
return _files.ContainsKey(GetFullyQualifiedPath(path));
|
||||
}
|
||||
|
||||
public string ReadAllText(RelativeFilePath path)
|
||||
{
|
||||
_files.TryGetValue(GetFullyQualifiedPath(path), out var value);
|
||||
return value;
|
||||
}
|
||||
|
||||
public void WriteAllText(RelativeFilePath path, string text)
|
||||
{
|
||||
_files[GetFullyQualifiedPath(path)] = text;
|
||||
}
|
||||
|
||||
public FileSystemInfo GetFullyQualifiedPath(RelativePath path)
|
||||
{
|
||||
if (path == null)
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
|
||||
switch (path)
|
||||
{
|
||||
case RelativeFilePath rfp:
|
||||
return WorkingDirectory.Combine(rfp);
|
||||
case RelativeDirectoryPath rdp:
|
||||
return WorkingDirectory.Combine(rdp);
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator GetEnumerator()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IDirectoryAccessor GetDirectoryAccessorForRelativePath(RelativeDirectoryPath relativePath)
|
||||
{
|
||||
var newPath = WorkingDirectory.Combine(relativePath);
|
||||
return new InMemoryDirectoryAccessor(newPath)
|
||||
{
|
||||
_files = _files
|
||||
};
|
||||
}
|
||||
|
||||
public IEnumerable<RelativeDirectoryPath> GetAllDirectoriesRecursively()
|
||||
{
|
||||
return _files.Keys
|
||||
.OfType<DirectoryInfo>()
|
||||
.Select(key => new RelativeDirectoryPath(
|
||||
Path.GetRelativePath(WorkingDirectory.FullName, key.FullName)));
|
||||
}
|
||||
|
||||
public IEnumerable<RelativeFilePath> GetAllFiles()
|
||||
{
|
||||
return _files.Keys
|
||||
.OfType<FileInfo>()
|
||||
.Where(key => FileSystemInfoComparer.Instance.Equals(key.Directory, WorkingDirectory))
|
||||
.Select(key => new RelativeFilePath(
|
||||
Path.GetRelativePath(WorkingDirectory.FullName, key.FullName)));
|
||||
}
|
||||
|
||||
public IEnumerable<RelativeFilePath> GetAllFilesRecursively()
|
||||
{
|
||||
return _files.Keys
|
||||
.OfType<FileInfo>()
|
||||
.Select(key => new RelativeFilePath(
|
||||
Path.GetRelativePath(WorkingDirectory.FullName, key.FullName)));
|
||||
}
|
||||
|
||||
public override string ToString() => this.GetFullyQualifiedRoot().FullName;
|
||||
|
||||
private class FileSystemInfoComparer : IEqualityComparer<FileSystemInfo>
|
||||
{
|
||||
public static FileSystemInfoComparer Instance { get; } = new FileSystemInfoComparer();
|
||||
|
||||
public bool Equals(FileSystemInfo x, FileSystemInfo y)
|
||||
{
|
||||
if (x?.GetType() == y?.GetType() && x != null)
|
||||
{
|
||||
return x is DirectoryInfo
|
||||
? RelativePath.NormalizeDirectory(x.FullName) == RelativePath.NormalizeDirectory(y.FullName)
|
||||
: x.FullName == y.FullName;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public int GetHashCode(FileSystemInfo obj)
|
||||
{
|
||||
var fullName = obj.FullName;
|
||||
|
||||
if (obj is DirectoryInfo)
|
||||
{
|
||||
fullName = RelativePath.NormalizeDirectory(fullName);
|
||||
}
|
||||
|
||||
var hashCode = $"{obj.GetType().GetHashCode()}:{fullName}".GetHashCode();
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<LangVersion>Latest</LangVersion>
|
||||
<AssetTargetFallback>portable-net45+win8+wp8+wpa81</AssetTargetFallback>
|
||||
<NoWarn>$(NoWarn);8002</NoWarn><!-- Assent isn't strongly signed -->
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="TestResults\**" />
|
||||
<Compile Remove="TestProjects\**" />
|
||||
<EmbeddedResource Remove="TestResults\**" />
|
||||
<None Remove="TestResults\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="TestProjects\**">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="5.10.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MLS.Agent.Tools\MLS.Agent.Tools.csproj" />
|
||||
<ProjectReference Include="..\WorkspaceServer\WorkspaceServer.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FSharp.Core" Version="6.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Update="xunit.runner.visualstudio" Version="2.4.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
</Project>
|
|
@ -1,90 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reactive.Concurrency;
|
||||
using System.Threading.Tasks;
|
||||
using Clockwise;
|
||||
using WorkspaceServer.Packaging;
|
||||
|
||||
namespace MLS.Agent.Tools.Tests
|
||||
{
|
||||
public static class PackageUtilities
|
||||
{
|
||||
private static readonly object CreateDirectoryLock = new object();
|
||||
|
||||
public static async Task<Package> 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, info =>
|
||||
{
|
||||
switch (info)
|
||||
{
|
||||
case FileInfo fileInfo:
|
||||
return FileLock.IsLockFile(fileInfo) || fileInfo.Extension.EndsWith("binlog");
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
namespace MLS.Agent.Tools.Tests
|
||||
{
|
||||
public class RelativeDirectoryPathTests
|
||||
{
|
||||
[Fact]
|
||||
public void Can_create_directory_paths_from_string_with_directory()
|
||||
{
|
||||
var path = new RelativeDirectoryPath("../src");
|
||||
path.Value.Should().Be("../src/");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Normalises_the_passed_path()
|
||||
{
|
||||
var path = new RelativeDirectoryPath(@"..\src");
|
||||
path.Value.Should().Be("../src/");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Throws_exception_if_the_path_contains_invalid_path_characters()
|
||||
{
|
||||
Action action = () => new RelativeDirectoryPath(@"abc|def");
|
||||
action.Should().Throw<ArgumentException>();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("/")]
|
||||
[InlineData("/some/path")]
|
||||
[InlineData(@"c:\some\path")]
|
||||
[InlineData(@"\\some\path")]
|
||||
public void Throws_if_path_is_absolute(string value)
|
||||
{
|
||||
Action action = () => new RelativeDirectoryPath(value);
|
||||
action.Should().Throw<ArgumentException>();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(".", ".")]
|
||||
[InlineData(".", "./")]
|
||||
[InlineData(".", @".\")]
|
||||
[InlineData("./", @".\")]
|
||||
[InlineData("..", "..")]
|
||||
[InlineData(@"../", @"..\")]
|
||||
[InlineData("../a/", "../a")]
|
||||
[InlineData("a", "./a")]
|
||||
public void Equality_is_based_on_same_resolved_directory_path(
|
||||
string value1,
|
||||
string value2)
|
||||
{
|
||||
var path1 = new RelativeDirectoryPath(value1);
|
||||
var path2 = new RelativeDirectoryPath(value2);
|
||||
|
||||
path1.GetHashCode().Should().Be(path2.GetHashCode());
|
||||
path1.Equals(path2).Should().BeTrue();
|
||||
path2.Equals(path1).Should().BeTrue();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using FluentAssertions;
|
||||
using MLS.Agent.Tools;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace MLS.Agent.Tools.Tests
|
||||
{
|
||||
public class RelativeFilePathTests
|
||||
{
|
||||
private readonly ITestOutputHelper _output;
|
||||
|
||||
public RelativeFilePathTests(ITestOutputHelper output)
|
||||
{
|
||||
_output = output;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Can_create_file_paths_from_string_with_directory()
|
||||
{
|
||||
var path = new RelativeFilePath("../readme.md");
|
||||
path.Value.Should().Be("../readme.md");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Can_create_file_paths_from_string_without_directory()
|
||||
{
|
||||
var path = new RelativeFilePath("readme.md");
|
||||
path.Value.Should().Be("./readme.md");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Normalises_the_passed_path()
|
||||
{
|
||||
var path = new RelativeFilePath(@"..\readme.md");
|
||||
_output.WriteLine(path.Value);
|
||||
_output.WriteLine(path.Directory.Value);
|
||||
path.Value.Should().Be("../readme.md");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Throws_exception_if_the_path_contains_invalid_filename_characters()
|
||||
{
|
||||
Action action = () => new RelativeFilePath(@"abc*def");
|
||||
action.Should().Throw<ArgumentException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Throws_exception_if_the_path_contains_invalid_path_characters()
|
||||
{
|
||||
Action action = () => new RelativeFilePath(@"abc|def");
|
||||
action.Should().Throw<ArgumentException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Throws_exception_if_the_path_is_empty()
|
||||
{
|
||||
Action action = () => new RelativeFilePath("");
|
||||
action.Should().Throw<ArgumentException>();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("../src/Program.cs", "../src/")]
|
||||
[InlineData("src/Program.cs", "./src/")]
|
||||
[InlineData("Readme.md", "./")]
|
||||
public void Returns_the_directory_path(string path, string directory)
|
||||
{
|
||||
var relativePath = new RelativeFilePath(path);
|
||||
relativePath.Directory.Value.Should().Be(directory);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Extension_returns_file_extension_with_a_dot()
|
||||
{
|
||||
new RelativeFilePath("../Program.cs").Extension.Should().Be(".cs");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Program.cs", "Program.cs")]
|
||||
[InlineData("./Program.cs", "Program.cs")]
|
||||
[InlineData("./Program.cs", @".\Program.cs")]
|
||||
[InlineData("./a/Program.cs", @".\a/Program.cs")]
|
||||
public void Equality_is_based_on_same_resolved_file_path(
|
||||
string value1,
|
||||
string value2)
|
||||
{
|
||||
var path1 = new RelativeFilePath(value1);
|
||||
var path2 = new RelativeFilePath(value2);
|
||||
|
||||
_output.WriteLine($"path1.Value: {path1.Value}");
|
||||
_output.WriteLine($"path1.Directory.Value: {path1.Directory.Value}");
|
||||
_output.WriteLine($"path2.Value: {path2.Value}");
|
||||
_output.WriteLine($"path2.Directory.Value: {path2.Directory.Value}");
|
||||
|
||||
path1.GetHashCode().Should().Be(path2.GetHashCode());
|
||||
path1.Equals(path2).Should().BeTrue();
|
||||
path2.Equals(path1).Should().BeTrue();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
|
@ -1,17 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace BasicConsoleApp
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main()
|
||||
{
|
||||
#region theregion
|
||||
Console.WriteLine("Hello World!");
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
This is a sample *markdown file*
|
||||
|
||||
```cs --source-file Program.cs
|
||||
```
|
|
@ -1,17 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace MLS.Agent.Tests.TestProjects.BasicConsoleApp.Subdirectory
|
||||
{
|
||||
class AnotherPorgram
|
||||
{
|
||||
static void MyAnotherProgram(string[] args)
|
||||
{
|
||||
Console.WriteLine("Hello from Another Program!");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
This is a sample *tutorial file*
|
|
@ -1,22 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace MLS.Agent.Tools.Tests
|
||||
{
|
||||
public static class TestAssets
|
||||
{
|
||||
public static DirectoryInfo SampleConsole =>
|
||||
new DirectoryInfo(Path.Combine(GetTestProjectsFolder(), "SampleConsole"));
|
||||
|
||||
public static DirectoryInfo KernelExtension =>
|
||||
new DirectoryInfo(Path.Combine(GetTestProjectsFolder(), "KernelExtension"));
|
||||
|
||||
private static string GetTestProjectsFolder()
|
||||
{
|
||||
var current = Directory.GetCurrentDirectory();
|
||||
return Path.Combine(current, "TestProjects");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MLS.Agent.Tools
|
||||
{
|
||||
public class AsyncLazy<T>
|
||||
{
|
||||
private readonly Lazy<Task<T>> _lazy;
|
||||
|
||||
public AsyncLazy(Func<Task<T>> initialize)
|
||||
{
|
||||
if (initialize == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(initialize));
|
||||
}
|
||||
|
||||
_lazy = new Lazy<Task<T>>(initialize);
|
||||
}
|
||||
|
||||
public Task<T> ValueAsync() => _lazy.Value;
|
||||
}
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.IO;
|
||||
using Microsoft.DotNet.Interactive.Utility;
|
||||
|
||||
namespace MLS.Agent.Tools
|
||||
{
|
||||
public static class DirectoryAccessor
|
||||
{
|
||||
public static bool DirectoryExists(
|
||||
this IDirectoryAccessor directoryAccessor,
|
||||
string relativePath) =>
|
||||
directoryAccessor.DirectoryExists(new RelativeDirectoryPath(relativePath));
|
||||
|
||||
public static bool RootDirectoryExists(
|
||||
this IDirectoryAccessor directoryAccessor) =>
|
||||
directoryAccessor.DirectoryExists(new RelativeDirectoryPath("."));
|
||||
|
||||
public static void EnsureDirectoryExists(
|
||||
this IDirectoryAccessor directoryAccessor,
|
||||
string relativePath) =>
|
||||
directoryAccessor.EnsureDirectoryExists(new RelativeDirectoryPath(relativePath));
|
||||
|
||||
public static void EnsureRootDirectoryExists(
|
||||
this IDirectoryAccessor directoryAccessor) =>
|
||||
directoryAccessor.EnsureDirectoryExists(new RelativeDirectoryPath("."));
|
||||
|
||||
public static bool FileExists(
|
||||
this IDirectoryAccessor directoryAccessor,
|
||||
string relativePath) =>
|
||||
directoryAccessor.FileExists(new RelativeFilePath(relativePath));
|
||||
|
||||
public static string ReadAllText(
|
||||
this IDirectoryAccessor directoryAccessor,
|
||||
string relativePath) =>
|
||||
directoryAccessor.ReadAllText(new RelativeFilePath(relativePath));
|
||||
|
||||
public static void WriteAllText(
|
||||
this IDirectoryAccessor directoryAccessor,
|
||||
string relativePath,
|
||||
string text) =>
|
||||
directoryAccessor.WriteAllText(
|
||||
new RelativeFilePath(relativePath),
|
||||
text);
|
||||
|
||||
public static IDirectoryAccessor GetDirectoryAccessorForRelativePath(
|
||||
this IDirectoryAccessor directoryAccessor,
|
||||
string relativePath) =>
|
||||
directoryAccessor.GetDirectoryAccessorForRelativePath(new RelativeDirectoryPath(relativePath));
|
||||
|
||||
public static IDirectoryAccessor GetDirectoryAccessorFor(this IDirectoryAccessor directory, DirectoryInfo directoryInfo)
|
||||
{
|
||||
var relative = PathUtilities.GetRelativePath(
|
||||
directory.GetFullyQualifiedRoot().FullName,
|
||||
directoryInfo.FullName);
|
||||
return directory.GetDirectoryAccessorForRelativePath(new RelativeDirectoryPath(relative));
|
||||
}
|
||||
|
||||
public static DirectoryInfo GetFullyQualifiedRoot(this IDirectoryAccessor directoryAccessor) =>
|
||||
(DirectoryInfo) directoryAccessor.GetFullyQualifiedPath(new RelativeDirectoryPath("."));
|
||||
|
||||
public static FileInfo GetFullyQualifiedFilePath(this IDirectoryAccessor directoryAccessor, string relativeFilePath) =>
|
||||
GetFullyQualifiedFilePath(directoryAccessor, new RelativeFilePath(relativeFilePath));
|
||||
|
||||
public static FileInfo GetFullyQualifiedFilePath(this IDirectoryAccessor directoryAccessor, RelativeFilePath relativePath) =>
|
||||
(FileInfo) directoryAccessor.GetFullyQualifiedPath(relativePath);
|
||||
|
||||
public static DirectoryInfo GetFullyQualifiedDirectoryPath(this IDirectoryAccessor directoryAccessor, RelativeDirectoryPath relativePath) =>
|
||||
(DirectoryInfo)directoryAccessor.GetFullyQualifiedPath(relativePath);
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace MLS.Agent.Tools
|
||||
{
|
||||
public static class DirectoryInfoExtensions
|
||||
{
|
||||
public static void CopyTo(
|
||||
this DirectoryInfo source,
|
||||
DirectoryInfo destination,
|
||||
Func<FileSystemInfo,bool> skipWhen = null)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
}
|
||||
|
||||
if (!source.Exists)
|
||||
{
|
||||
throw new DirectoryNotFoundException(source.FullName);
|
||||
}
|
||||
|
||||
if (!destination.Exists)
|
||||
{
|
||||
destination.Create();
|
||||
}
|
||||
|
||||
foreach (var file in source.GetFiles())
|
||||
{
|
||||
if (skipWhen?.Invoke(file) == true)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
file.CopyTo(
|
||||
Path.Combine(
|
||||
destination.FullName, file.Name), false);
|
||||
}
|
||||
|
||||
foreach (var subdirectory in source.GetDirectories())
|
||||
{
|
||||
if (skipWhen?.Invoke(subdirectory) == true)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
subdirectory.CopyTo(
|
||||
new DirectoryInfo(
|
||||
Path.Combine(
|
||||
destination.FullName, subdirectory.Name)));
|
||||
}
|
||||
}
|
||||
|
||||
public static DirectoryInfo Subdirectory(this DirectoryInfo directoryInfo, string path)
|
||||
{
|
||||
return new DirectoryInfo(Path.Combine(directoryInfo.FullName, path));
|
||||
}
|
||||
|
||||
public static FileInfo File(this DirectoryInfo directoryInfo, string name)
|
||||
{
|
||||
return new FileInfo(Path.Combine(directoryInfo.FullName, name));
|
||||
}
|
||||
|
||||
public static DirectoryInfo NormalizeEnding(this DirectoryInfo directoryInfo)
|
||||
{
|
||||
if (!directoryInfo.FullName.EndsWith(Path.DirectorySeparatorChar.ToString()))
|
||||
{
|
||||
return new DirectoryInfo(Path.Combine(directoryInfo.FullName, Path.DirectorySeparatorChar.ToString()));
|
||||
}
|
||||
|
||||
return directoryInfo;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MLS.Agent.Tools
|
||||
{
|
||||
public static class FileInfoExtensions
|
||||
{
|
||||
public static string Read(this FileInfo file)
|
||||
{
|
||||
using (var reader = file.OpenText())
|
||||
{
|
||||
return reader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<string> ReadAsync(this FileInfo file)
|
||||
{
|
||||
using (var reader = file.OpenText())
|
||||
{
|
||||
return await reader.ReadToEndAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsBuildOutput(this FileInfo fileInfo)
|
||||
{
|
||||
var directory = fileInfo.Directory;
|
||||
|
||||
while (directory != null)
|
||||
{
|
||||
if (directory.Name == "obj" || directory.Name == "bin")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
directory = directory.Parent;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.DotNet.Interactive.Utility;
|
||||
|
||||
namespace MLS.Agent.Tools
|
||||
{
|
||||
public class FileSystemDirectoryAccessor : IDirectoryAccessor
|
||||
{
|
||||
private readonly DirectoryInfo _rootDirectory;
|
||||
|
||||
public FileSystemDirectoryAccessor(string directory): this(new DirectoryInfo(directory))
|
||||
{
|
||||
}
|
||||
|
||||
public FileSystemDirectoryAccessor(DirectoryInfo rootDir)
|
||||
{
|
||||
_rootDirectory = rootDir ?? throw new ArgumentNullException(nameof(rootDir));
|
||||
}
|
||||
|
||||
public bool DirectoryExists(RelativeDirectoryPath path)
|
||||
{
|
||||
return GetFullyQualifiedPath(path).Exists;
|
||||
}
|
||||
|
||||
public bool FileExists(RelativeFilePath filePath)
|
||||
{
|
||||
return GetFullyQualifiedPath(filePath).Exists;
|
||||
}
|
||||
|
||||
public void EnsureDirectoryExists(RelativeDirectoryPath path)
|
||||
{
|
||||
var fullyQualifiedPath = GetFullyQualifiedPath(path);
|
||||
|
||||
if (!Directory.Exists(fullyQualifiedPath.FullName))
|
||||
{
|
||||
Directory.CreateDirectory(fullyQualifiedPath.FullName);
|
||||
}
|
||||
}
|
||||
|
||||
public string ReadAllText(RelativeFilePath filePath)
|
||||
{
|
||||
return File.ReadAllText(GetFullyQualifiedPath(filePath).FullName);
|
||||
}
|
||||
|
||||
public void WriteAllText(RelativeFilePath path, string text)
|
||||
{
|
||||
File.WriteAllText(GetFullyQualifiedPath(path).FullName, text);
|
||||
}
|
||||
|
||||
public FileSystemInfo GetFullyQualifiedPath(RelativePath path)
|
||||
{
|
||||
if (path == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(path));
|
||||
}
|
||||
|
||||
return path.Match<FileSystemInfo>(
|
||||
directory => new DirectoryInfo(_rootDirectory.Combine(directory).FullName),
|
||||
file => new FileInfo(_rootDirectory.Combine(file).FullName)
|
||||
);
|
||||
}
|
||||
|
||||
public IDirectoryAccessor GetDirectoryAccessorForRelativePath(RelativeDirectoryPath relativePath)
|
||||
{
|
||||
var absolutePath = _rootDirectory.Combine(relativePath).FullName;
|
||||
return new FileSystemDirectoryAccessor(new DirectoryInfo(absolutePath));
|
||||
}
|
||||
|
||||
|
||||
public IEnumerable<RelativeDirectoryPath> GetAllDirectoriesRecursively()
|
||||
{
|
||||
var directories = _rootDirectory.GetDirectories("*", SearchOption.AllDirectories);
|
||||
|
||||
return directories.Select(f =>
|
||||
new RelativeDirectoryPath(PathUtilities.GetRelativePath(_rootDirectory.FullName, f.FullName)));
|
||||
}
|
||||
|
||||
public IEnumerable<RelativeFilePath> GetAllFilesRecursively()
|
||||
{
|
||||
var files = _rootDirectory.GetFiles("*", SearchOption.AllDirectories);
|
||||
|
||||
return files.Select(f =>
|
||||
new RelativeFilePath(PathUtilities.GetRelativePath(_rootDirectory.FullName, f.FullName)));
|
||||
}
|
||||
|
||||
public IEnumerable<RelativeFilePath> 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;
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace MLS.Agent.Tools
|
||||
{
|
||||
public interface IDirectoryAccessor
|
||||
{
|
||||
bool FileExists(RelativeFilePath path);
|
||||
|
||||
bool DirectoryExists(RelativeDirectoryPath path);
|
||||
|
||||
void EnsureDirectoryExists(RelativeDirectoryPath path);
|
||||
|
||||
string ReadAllText(RelativeFilePath path);
|
||||
|
||||
void WriteAllText(RelativeFilePath path, string text);
|
||||
|
||||
IEnumerable<RelativeFilePath> GetAllFilesRecursively();
|
||||
|
||||
IEnumerable<RelativeFilePath> GetAllFiles();
|
||||
|
||||
IEnumerable<RelativeDirectoryPath> GetAllDirectoriesRecursively();
|
||||
|
||||
FileSystemInfo GetFullyQualifiedPath(RelativePath path);
|
||||
|
||||
IDirectoryAccessor GetDirectoryAccessorForRelativePath(RelativeDirectoryPath path);
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<LangVersion>Latest</LangVersion>
|
||||
<NoWarn>$(NoWarn);8002</NoWarn><!-- Clockwise isn't strongly signed -->
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="TestResults\**" />
|
||||
<EmbeddedResource Remove="TestResults\**" />
|
||||
<None Remove="TestResults\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Microsoft.DotNet.Interactive\Utility\FileNameUtilities.cs" Link="%28Recipes%29\FileNameUtilities.cs" />
|
||||
<Compile Include="..\Microsoft.DotNet.Interactive\Utility\PathUtilities.cs" Link="%28Recipes%29\PathUtilities.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- Any new dependency added here must also be included in the Microsoft.DotNet.Try.Markdown nuspec file -->
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="PocketLogger" Version="0.4.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Diagnostics.Process" Version="$(SystemDiagnosticsProcessVersion)" />
|
||||
<PackageReference Include="System.Reactive" Version="5.0.0" />
|
||||
<PackageReference Include="System.Runtime.Extensions" Version="4.3.1" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.0.1" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.0.1" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,66 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace MLS.Agent.Tools
|
||||
{
|
||||
public class RelativeDirectoryPath :
|
||||
RelativePath,
|
||||
IEquatable<RelativeDirectoryPath>
|
||||
{
|
||||
public static RelativeDirectoryPath Root { get; } = new RelativeDirectoryPath("./");
|
||||
|
||||
public RelativeDirectoryPath(string value) : base(value)
|
||||
{
|
||||
Value = NormalizeDirectory(value);
|
||||
}
|
||||
|
||||
public bool Equals(RelativeDirectoryPath other)
|
||||
{
|
||||
if (ReferenceEquals(null, other))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(this, other))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return Equals(Value, other.Value);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(this, obj))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj.GetType() != GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Equals((RelativeDirectoryPath) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode() => Value.GetHashCode();
|
||||
|
||||
public static bool operator ==(RelativeDirectoryPath left, RelativeDirectoryPath right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(RelativeDirectoryPath left, RelativeDirectoryPath right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace MLS.Agent.Tools
|
||||
{
|
||||
public class RelativeFilePath :
|
||||
RelativePath,
|
||||
IEquatable<RelativeFilePath>
|
||||
{
|
||||
public RelativeFilePath(string value) : base(value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
throw new ArgumentException("File path cannot be null or consist entirely of whitespace", nameof(value));
|
||||
}
|
||||
|
||||
var (directoryPath, fileName) = GetFileAndDirectoryNames(value);
|
||||
|
||||
FileName = fileName;
|
||||
|
||||
ThrowIfContainsDisallowedFilePathChars(FileName);
|
||||
|
||||
Directory = new RelativeDirectoryPath(directoryPath);
|
||||
|
||||
Value = Directory.Value + FileName;
|
||||
}
|
||||
|
||||
private static (string directoryPath, string fileName) GetFileAndDirectoryNames(string filePath)
|
||||
{
|
||||
var lastDirectorySeparatorPos = filePath.LastIndexOfAny(new[] { '\\', '/' });
|
||||
|
||||
if (lastDirectorySeparatorPos == -1)
|
||||
{
|
||||
return ("./", filePath);
|
||||
}
|
||||
|
||||
var fileName = filePath.Substring(lastDirectorySeparatorPos + 1);
|
||||
|
||||
var directoryPath = filePath.Substring(0, lastDirectorySeparatorPos);
|
||||
|
||||
directoryPath = NormalizeDirectory(directoryPath);
|
||||
|
||||
return (directoryPath, fileName);
|
||||
}
|
||||
|
||||
public string FileName { get; }
|
||||
|
||||
public RelativeDirectoryPath Directory { get; }
|
||||
|
||||
public string Extension =>
|
||||
Path.GetExtension(Value);
|
||||
|
||||
public static bool TryParse(string path, out RelativeFilePath relativeFilePath)
|
||||
{
|
||||
relativeFilePath = null;
|
||||
try
|
||||
{
|
||||
relativeFilePath = new RelativeFilePath(path);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Equals(RelativeFilePath other)
|
||||
{
|
||||
if (ReferenceEquals(null, other))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(this, other))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return string.Equals(FileName, other.FileName) &&
|
||||
Equals(Directory, other.Directory);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(this, obj))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj.GetType() != GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Equals((RelativeFilePath) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode() => Value.GetHashCode();
|
||||
}
|
||||
}
|
|
@ -1,192 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace MLS.Agent.Tools
|
||||
{
|
||||
public abstract class RelativePath
|
||||
{
|
||||
private string _value;
|
||||
|
||||
protected RelativePath(string value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
ThrowIfPathIsRooted(value);
|
||||
}
|
||||
|
||||
private void ThrowIfPathIsRooted(string path)
|
||||
{
|
||||
if (IsPathRootedRegardlessOfOS(path))
|
||||
{
|
||||
throw new ArgumentException($"Path cannot be absolute: {path}");
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsPathRootedRegardlessOfOS(string path)
|
||||
{
|
||||
return Path.IsPathRooted(path) ||
|
||||
path.StartsWith(@"/") ||
|
||||
path.StartsWith(@"\\") ||
|
||||
path.Substring(1).StartsWith(@":\");
|
||||
}
|
||||
|
||||
public string Value
|
||||
{
|
||||
get => _value;
|
||||
protected set => _value = value ?? throw new ArgumentNullException(nameof(value)) ;
|
||||
}
|
||||
|
||||
public override string ToString() => Value;
|
||||
|
||||
private static readonly HashSet<char> DisallowedPathChars = new HashSet<char>(
|
||||
new char[]
|
||||
{
|
||||
'|',
|
||||
'\0',
|
||||
'\u0001',
|
||||
'\u0002',
|
||||
'\u0003',
|
||||
'\u0004',
|
||||
'\u0005',
|
||||
'\u0006',
|
||||
'\a',
|
||||
'\b',
|
||||
'\t',
|
||||
'\n',
|
||||
'\v',
|
||||
'\f',
|
||||
'\r',
|
||||
'\u000e',
|
||||
'\u000f',
|
||||
'\u0010',
|
||||
'\u0011',
|
||||
'\u0012',
|
||||
'\u0013',
|
||||
'\u0014',
|
||||
'\u0015',
|
||||
'\u0016',
|
||||
'\u0017',
|
||||
'\u0018',
|
||||
'\u0019',
|
||||
'\u001a',
|
||||
'\u001b',
|
||||
'\u001c',
|
||||
'\u001d',
|
||||
'\u001e',
|
||||
'\u001f'
|
||||
});
|
||||
|
||||
private static readonly HashSet<char> DisallowedFileNameChars = new HashSet<char>(
|
||||
new char[]
|
||||
{
|
||||
'"',
|
||||
'<',
|
||||
'>',
|
||||
'|',
|
||||
'\0',
|
||||
'\u0001',
|
||||
'\u0002',
|
||||
'\u0003',
|
||||
'\u0004',
|
||||
'\u0005',
|
||||
'\u0006',
|
||||
'\a',
|
||||
'\b',
|
||||
'\t',
|
||||
'\n',
|
||||
'\v',
|
||||
'\f',
|
||||
'\r',
|
||||
'\u000e',
|
||||
'\u000f',
|
||||
'\u0010',
|
||||
'\u0011',
|
||||
'\u0012',
|
||||
'\u0013',
|
||||
'\u0014',
|
||||
'\u0015',
|
||||
'\u0016',
|
||||
'\u0016',
|
||||
'\u0017',
|
||||
'\u0018',
|
||||
'\u0019',
|
||||
'\u001a',
|
||||
'\u001b',
|
||||
'\u001c',
|
||||
'\u001d',
|
||||
'\u001e',
|
||||
'\u001f',
|
||||
':',
|
||||
'*',
|
||||
'?',
|
||||
'\\'
|
||||
});
|
||||
|
||||
public static string NormalizeDirectory(string directoryPath)
|
||||
{
|
||||
directoryPath = directoryPath.Replace('\\', '/');
|
||||
|
||||
if (string.IsNullOrWhiteSpace(directoryPath))
|
||||
{
|
||||
directoryPath = "./";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!IsPathRootedRegardlessOfOS(directoryPath) &&
|
||||
!directoryPath.StartsWith(".") &&
|
||||
!directoryPath.StartsWith("..") &&
|
||||
!directoryPath.StartsWith("/"))
|
||||
{
|
||||
directoryPath = $"./{directoryPath}";
|
||||
}
|
||||
|
||||
directoryPath = directoryPath.TrimEnd('\\', '/') + '/';
|
||||
}
|
||||
|
||||
ThrowIfContainsDisallowedDirectoryPathChars(directoryPath);
|
||||
|
||||
return directoryPath;
|
||||
}
|
||||
|
||||
protected static void ThrowIfContainsDisallowedDirectoryPathChars(string path)
|
||||
{
|
||||
for (var index = 0; index < path.Length; index++)
|
||||
{
|
||||
var ch = path[index];
|
||||
if (DisallowedPathChars.Contains(ch))
|
||||
{
|
||||
throw new ArgumentException($"The character {ch} is not allowed in the path");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static void ThrowIfContainsDisallowedFilePathChars(string filename)
|
||||
{
|
||||
for (var index = 0; index < filename.Length; index++)
|
||||
{
|
||||
var ch = filename[index];
|
||||
if (DisallowedFileNameChars.Contains(ch))
|
||||
{
|
||||
throw new ArgumentException($"The character {ch} is not allowed in the filename");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// public static bool operator ==(RelativePath left, RelativePath right)
|
||||
// {
|
||||
// return Equals(left, right);
|
||||
// }
|
||||
//
|
||||
// public static bool operator !=(RelativePath left, RelativePath right)
|
||||
// {
|
||||
// return !Equals(left, right);
|
||||
// }
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace MLS.Agent.Tools
|
||||
{
|
||||
public static class RelativePathExtensions
|
||||
{
|
||||
public static FileInfo Combine(
|
||||
this DirectoryInfo directory,
|
||||
RelativeFilePath filePath)
|
||||
{
|
||||
var filePart = filePath.Value;
|
||||
|
||||
if (filePart.StartsWith("./"))
|
||||
{
|
||||
filePart = filePart.Substring(2);
|
||||
}
|
||||
|
||||
return new FileInfo(
|
||||
Path.Combine(
|
||||
directory.FullName,
|
||||
filePart.Replace('/', Path.DirectorySeparatorChar)));
|
||||
}
|
||||
|
||||
public static DirectoryInfo Combine(
|
||||
this DirectoryInfo directory,
|
||||
RelativeDirectoryPath directoryPath)
|
||||
{
|
||||
return new DirectoryInfo(
|
||||
Path.Combine(
|
||||
RelativePath.NormalizeDirectory(directory.FullName),
|
||||
directoryPath.Value.Replace('/', Path.DirectorySeparatorChar)));
|
||||
}
|
||||
|
||||
public static T Match<T>(this RelativePath path, Func<RelativeDirectoryPath, T> directory, Func<RelativeFilePath, T> file)
|
||||
{
|
||||
switch (path)
|
||||
{
|
||||
case RelativeDirectoryPath relativeDirectoryPath:
|
||||
return directory(relativeDirectoryPath);
|
||||
case RelativeFilePath relativeFilePath:
|
||||
return file(relativeFilePath);
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException($"Unexpected type derived from {nameof(RelativePath)}: {path.GetType().Name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace MLS.Agent.Tools
|
||||
{
|
||||
public static class TypeExtensions
|
||||
{
|
||||
public static string ReadManifestResource(this Type type, string resourceName)
|
||||
{
|
||||
if (type == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(type));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(resourceName))
|
||||
{
|
||||
throw new ArgumentException("Value cannot be null or whitespace.", nameof(resourceName));
|
||||
}
|
||||
|
||||
var assembly = type.Assembly;
|
||||
|
||||
var assemblyResourceName = assembly.GetManifestResourceNames().First(s => s.Contains(resourceName));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(assemblyResourceName))
|
||||
{
|
||||
throw new InvalidOperationException($"Cannot locate resource {resourceName} in {assembly}");
|
||||
}
|
||||
|
||||
using (var reader = new StreamReader(assembly.GetManifestResourceStream(assemblyResourceName)))
|
||||
{
|
||||
return reader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
|
||||
namespace Recipes
|
||||
{
|
||||
internal class BadRequestOnInvalidModelFilter : IActionFilter
|
||||
{
|
||||
public void OnActionExecuting(ActionExecutingContext context)
|
||||
{
|
||||
if (!context.ModelState.IsValid)
|
||||
{
|
||||
context.Result = new BadRequestObjectResult(
|
||||
context.ModelState
|
||||
.Values
|
||||
.SelectMany(e => e.Errors
|
||||
.Select(ee => ee.ErrorMessage)));
|
||||
}
|
||||
}
|
||||
|
||||
public void OnActionExecuted(ActionExecutedContext context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,329 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
using Microsoft.DotNet.Try.Protocol;
|
||||
using Microsoft.DotNet.Try.Protocol.ClientApi;
|
||||
using Microsoft.DotNet.Try.Protocol.ClientApi.GitHub;
|
||||
|
||||
namespace Recipes
|
||||
{
|
||||
internal static class ClientConfigurationExtensions
|
||||
{
|
||||
private static readonly Regex OptionalRouteFilter = new Regex(@"/\{.+\?\}", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
|
||||
|
||||
private static string ToSha256(string value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
var inputBytes = Encoding.UTF8.GetBytes(value);
|
||||
|
||||
byte[] hash;
|
||||
using (var sha256 = SHA256.Create())
|
||||
{
|
||||
hash = sha256.ComputeHash(inputBytes);
|
||||
}
|
||||
|
||||
return Convert.ToBase64String(hash);
|
||||
}
|
||||
public static string ComputeHash(this RequestDescriptors links)
|
||||
{
|
||||
return ToSha256(links.ToJson());
|
||||
}
|
||||
|
||||
public static string BuildUrl(this RequestDescriptor requestDescriptor, Dictionary<string, object> context = null)
|
||||
{
|
||||
var url = requestDescriptor.Href;
|
||||
if (requestDescriptor.Templated && context?.Count > 0)
|
||||
{
|
||||
foreach (var entry in context)
|
||||
{
|
||||
var filter = new Regex(@"\{" + entry.Key + @"\??\}", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
|
||||
url = filter.Replace(url, $"{UrlEncode(entry.Value.ToString())}");
|
||||
}
|
||||
}
|
||||
|
||||
return OptionalRouteFilter.Replace(url, string.Empty);
|
||||
}
|
||||
|
||||
public static string BuildQueryString(this RequestDescriptor requestDescriptor, Dictionary<string, object> context = null)
|
||||
{
|
||||
var parts = new List<string>();
|
||||
if (context?.Count > 0)
|
||||
{
|
||||
if (context.TryGetValue("hostOrigin", out var hostOrigin))
|
||||
{
|
||||
parts.Add($"hostOrigin={UrlEncode(hostOrigin.ToString())}");
|
||||
}
|
||||
|
||||
foreach (var property in requestDescriptor.Properties ?? Enumerable.Empty<RequestDescriptorProperty>())
|
||||
{
|
||||
if (context.TryGetValue(property.Name, out var propertyValue))
|
||||
{
|
||||
parts.Add($"{property.Name}={UrlEncode(propertyValue.ToString())}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return string.Join("&", parts);
|
||||
}
|
||||
|
||||
public static HttpRequestMessage BuildRequest(this RequestDescriptor requestDescriptor, Dictionary<string, object> context = null)
|
||||
{
|
||||
var fullUrl = requestDescriptor.BuildFullUri(context);
|
||||
|
||||
var request = new HttpRequestMessage
|
||||
{
|
||||
RequestUri = new Uri(fullUrl, UriKind.RelativeOrAbsolute)
|
||||
};
|
||||
|
||||
switch (requestDescriptor.Method)
|
||||
{
|
||||
case "POST":
|
||||
request.Method = HttpMethod.Post;
|
||||
break;
|
||||
case "DELETE":
|
||||
request.Method = HttpMethod.Delete;
|
||||
break;
|
||||
case "PUT":
|
||||
request.Method = HttpMethod.Put;
|
||||
break;
|
||||
case "HEAD":
|
||||
request.Method = HttpMethod.Head;
|
||||
break;
|
||||
case "OPTIONS":
|
||||
request.Method = HttpMethod.Options;
|
||||
break;
|
||||
case "TRACE":
|
||||
request.Method = HttpMethod.Trace;
|
||||
break;
|
||||
default:
|
||||
request.Method = HttpMethod.Get;
|
||||
break;
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
public static string BuildFullUri(this RequestDescriptor requestDescriptor, Dictionary<string, object> context = null)
|
||||
{
|
||||
var url = requestDescriptor.BuildUrl(context);
|
||||
var queryString = requestDescriptor.BuildQueryString(context);
|
||||
|
||||
var fullUrl = url;
|
||||
if (!string.IsNullOrWhiteSpace(queryString))
|
||||
{
|
||||
var joinSymbol = "?";
|
||||
if (url.IndexOf("?", StringComparison.InvariantCultureIgnoreCase) >= 0)
|
||||
{
|
||||
joinSymbol = "&";
|
||||
}
|
||||
|
||||
fullUrl = $"{url}{joinSymbol}{queryString}";
|
||||
}
|
||||
|
||||
return fullUrl;
|
||||
}
|
||||
|
||||
public static HttpRequestMessage BuildLoadFromRequest(this ClientConfiguration configuration, string codeUrl, string hostOrigin)
|
||||
{
|
||||
var api = configuration.Links.Snippet;
|
||||
var context = new Dictionary<string, object>
|
||||
{
|
||||
{ "from", codeUrl }
|
||||
};
|
||||
|
||||
var request = BuildRequestWithHeaders(configuration, api, hostOrigin, context);
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
private static HttpRequestMessage BuildRequestWithHeaders(this ClientConfiguration configuration, RequestDescriptor requestDescriptor, string hostOrigin, Dictionary<string, object> context = null)
|
||||
{
|
||||
var safeContext = context ?? new Dictionary<string, object>();
|
||||
|
||||
if (hostOrigin != null)
|
||||
{
|
||||
safeContext["hostOrigin"] = hostOrigin;
|
||||
}
|
||||
|
||||
var request = requestDescriptor.BuildRequest(safeContext);
|
||||
|
||||
configuration.AddConfigurationVersionIdHeader(request);
|
||||
configuration.AddTimeoutHeader(request, requestDescriptor);
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
public static HttpRequestMessage BuildLoadFromGistRequest(this ClientConfiguration configuration, string gist, string hash = null, string workspaceType = null,
|
||||
bool? extractBuffers = null, string hostOrigin = null)
|
||||
{
|
||||
var api = configuration.Links.LoadFromGist;
|
||||
var context = new Dictionary<string, object>
|
||||
{
|
||||
{ "gistId", gist }
|
||||
};
|
||||
|
||||
if (hash != null)
|
||||
{
|
||||
context["commitHash"] = hash;
|
||||
}
|
||||
|
||||
if (workspaceType != null)
|
||||
{
|
||||
context["workspaceType"] = workspaceType;
|
||||
}
|
||||
|
||||
if (extractBuffers != null)
|
||||
{
|
||||
context["extractBuffers"] = extractBuffers;
|
||||
}
|
||||
|
||||
return BuildRequestWithHeaders(configuration, api, hostOrigin, context);
|
||||
}
|
||||
|
||||
public static HttpRequestMessage BuildRegionFromFilesRequest(this ClientConfiguration configuration, IEnumerable<SourceFile> files, string hostOrigin, string requestId = null)
|
||||
{
|
||||
var api = configuration.Links.RegionsFromFiles;
|
||||
|
||||
var request = BuildRequestWithHeaders(configuration, api, hostOrigin);
|
||||
var payload = new CreateRegionsFromFilesRequest(requestId ?? (Guid.NewGuid().ToString()), files?.ToArray());
|
||||
SetRequestContent(payload, request);
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
public static HttpRequestMessage BuildProjectFromGistRequest(this ClientConfiguration configuration, string gistId, string projectTemplate, string hostOrigin, string hash = null, string requestId = null)
|
||||
{
|
||||
var api = configuration.Links.ProjectFromGist;
|
||||
|
||||
var request = BuildRequestWithHeaders(configuration, api, hostOrigin);
|
||||
var payload = new CreateProjectFromGistRequest(requestId ?? (Guid.NewGuid().ToString()), gistId, projectTemplate, hash);
|
||||
SetRequestContent(payload, request);
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
public static HttpRequestMessage BuildCompletionRequest(this ClientConfiguration configuration, object workspaceRequest, string hostOrigin)
|
||||
{
|
||||
var api = configuration.Links.Completion;
|
||||
|
||||
var request = BuildRequestWithHeaders(configuration, api, hostOrigin);
|
||||
|
||||
SetRequestContent(workspaceRequest, request);
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
public static HttpRequestMessage BuildCompileRequest(this ClientConfiguration configuration, object workspaceRequest, string hostOrigin)
|
||||
{
|
||||
var api = configuration.Links.Compile;
|
||||
|
||||
var request = BuildRequestWithHeaders(configuration, api, hostOrigin);
|
||||
|
||||
SetRequestContent(workspaceRequest, request);
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
public static HttpRequestMessage BuildSignatureHelpRequest(this ClientConfiguration configuration, object workspaceRequest, string hostOrigin)
|
||||
{
|
||||
var api = configuration.Links.SignatureHelp;
|
||||
|
||||
var request = BuildRequestWithHeaders(configuration, api, hostOrigin);
|
||||
|
||||
SetRequestContent(workspaceRequest, request);
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
public static HttpRequestMessage BuildDiagnosticsRequest(this ClientConfiguration configuration, object workspace, string hostOrigin)
|
||||
{
|
||||
var api = configuration.Links.Diagnostics;
|
||||
|
||||
var request = BuildRequestWithHeaders(configuration, api, hostOrigin);
|
||||
|
||||
SetRequestContent(workspace, request);
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
public static HttpRequestMessage BuildRunRequest(this ClientConfiguration configuration, object workspaceRequest, string hostOrigin)
|
||||
{
|
||||
var api = configuration.Links.Run;
|
||||
|
||||
var request = BuildRequestWithHeaders(configuration, api, hostOrigin);
|
||||
|
||||
SetRequestContent(workspaceRequest, request);
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
public static HttpRequestMessage BuildVersionRequest(this ClientConfiguration configuration, string hostOrigin)
|
||||
{
|
||||
var api = configuration.Links.Version;
|
||||
|
||||
return BuildRequestWithHeaders(configuration, api, hostOrigin);
|
||||
}
|
||||
|
||||
public static HttpRequestMessage BuildGetPackagesRequest(this ClientConfiguration configuration, string packageName, string packageVersion, string hostOrigin)
|
||||
{
|
||||
var api = configuration.Links.GetPackage;
|
||||
var context = new Dictionary<string, object>
|
||||
{
|
||||
{ "name", packageName },
|
||||
{ "version", packageVersion }
|
||||
};
|
||||
|
||||
var request = BuildRequestWithHeaders(configuration, api, hostOrigin, context);
|
||||
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
private static string UrlEncode(string source)
|
||||
{
|
||||
return HttpUtility.UrlEncode(HttpUtility.UrlDecode(source));
|
||||
}
|
||||
|
||||
private static void AddConfigurationVersionIdHeader(this ClientConfiguration configuration, HttpRequestMessage request)
|
||||
{
|
||||
request.Headers.Remove("ClientConfigurationVersionId");
|
||||
request.Headers.Add("ClientConfigurationVersionId", configuration.VersionId);
|
||||
}
|
||||
|
||||
private static void AddTimeoutHeader(this ClientConfiguration configuration, HttpRequestMessage request, RequestDescriptor requestDescriptor = null)
|
||||
{
|
||||
request.Headers.Remove("Timeout");
|
||||
var timeoutMs = configuration.DefaultTimeoutMs.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (requestDescriptor != null && requestDescriptor.TimeoutMs > 0)
|
||||
{
|
||||
timeoutMs = requestDescriptor.TimeoutMs.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
request.Headers.Add("Timeout", timeoutMs);
|
||||
}
|
||||
|
||||
private static void SetRequestContent(object content, HttpRequestMessage request)
|
||||
{
|
||||
switch (content)
|
||||
{
|
||||
case string text:
|
||||
request.Content = new JsonContent(text);
|
||||
break;
|
||||
default:
|
||||
request.Content = new JsonContent(content);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Recipes
|
||||
{
|
||||
internal static class DictionaryExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds a key/value pair to the dictionary if the key does not already exist.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">The type of the key.</typeparam>
|
||||
/// <typeparam name="TValue">The type of the value.</typeparam>
|
||||
/// <param name="dictionary">The dictionary.</param>
|
||||
/// <param name="key">The key of the element to add.</param>
|
||||
/// <param name="valueFactory">The function used to generate a value for the key.</param>
|
||||
/// <returns>
|
||||
/// The value for the key. This will be either the existing value for the key if the key is already in the dictionary, or the new value for the key as returned by valueFactory if the key was not in the dictionary.
|
||||
/// </returns>
|
||||
/// <exception cref="System.ArgumentNullException">dictionary</exception>
|
||||
public static TValue GetOrAdd<TKey, TValue>(
|
||||
this IDictionary<TKey, TValue> dictionary,
|
||||
TKey key,
|
||||
Func<TKey, TValue> valueFactory)
|
||||
{
|
||||
if (dictionary == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(dictionary));
|
||||
}
|
||||
if (valueFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(valueFactory));
|
||||
}
|
||||
|
||||
TValue value;
|
||||
if (dictionary.TryGetValue(key, out value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
value = valueFactory(key);
|
||||
dictionary.Add(key, value);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
using System.Net.Http;
|
||||
using System.Text;
|
||||
|
||||
namespace Recipes
|
||||
{
|
||||
internal class JsonContent : StringContent
|
||||
{
|
||||
public JsonContent(object content)
|
||||
: base(content.ToJson(),
|
||||
Encoding.UTF8,
|
||||
"application/json")
|
||||
{
|
||||
}
|
||||
|
||||
public JsonContent(string content)
|
||||
: base(content,
|
||||
Encoding.UTF8,
|
||||
"application/json")
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Recipes
|
||||
{
|
||||
#if !RecipesProject
|
||||
[DebuggerStepThrough]
|
||||
#endif
|
||||
internal partial class VersionSensor
|
||||
{
|
||||
private static readonly Lazy<BuildInfo> buildInfo = new Lazy<BuildInfo>(() =>
|
||||
{
|
||||
var assembly = typeof(VersionSensor).GetTypeInfo().Assembly;
|
||||
|
||||
var info = new BuildInfo
|
||||
{
|
||||
AssemblyName = assembly.GetName().Name,
|
||||
AssemblyInformationalVersion = assembly
|
||||
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
|
||||
.InformationalVersion,
|
||||
AssemblyVersion = assembly.GetName().Version.ToString(),
|
||||
BuildDate = new FileInfo(assembly.Location).CreationTimeUtc.ToString("o")
|
||||
};
|
||||
|
||||
AssignServiceVersionTo(info);
|
||||
|
||||
return info;
|
||||
});
|
||||
|
||||
public static BuildInfo Version()
|
||||
{
|
||||
return buildInfo.Value;
|
||||
}
|
||||
|
||||
public class BuildInfo
|
||||
{
|
||||
public string AssemblyVersion { get; set; }
|
||||
public string BuildDate { get; set; }
|
||||
public string AssemblyInformationalVersion { get; set; }
|
||||
public string AssemblyName { get; set; }
|
||||
public string ServiceVersion { get; set; }
|
||||
}
|
||||
|
||||
static partial void AssignServiceVersionTo(BuildInfo buildInfo);
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.StaticFiles.Infrastructure;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Recipes;
|
||||
|
||||
namespace MLS.Agent
|
||||
{
|
||||
internal static class ApplicationBuilderExtensions
|
||||
{
|
||||
public static IApplicationBuilder EnableCachingBlazorContent(this IApplicationBuilder app)
|
||||
{
|
||||
return app.Use((context, next) =>
|
||||
{
|
||||
if (HttpMethods.IsGet(context.Request.Method))
|
||||
{
|
||||
context.Response.Headers[HeaderNames.CacheControl] = "public, max-age=604800";
|
||||
}
|
||||
|
||||
return next();
|
||||
});
|
||||
}
|
||||
|
||||
public static IApplicationBuilder UseStaticFilesFromToolLocationAndRootDirectory(this IApplicationBuilder app, DirectoryInfo rootDirectory)
|
||||
{
|
||||
var options = GetStaticFilesOptions(rootDirectory);
|
||||
|
||||
app.UseStaticFiles();
|
||||
if (options != null)
|
||||
{
|
||||
app.UseStaticFiles(options);
|
||||
}
|
||||
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
private static StaticFileOptions GetStaticFilesOptions(DirectoryInfo rootDirectory)
|
||||
{
|
||||
var paths = new List<string>
|
||||
{
|
||||
Path.Combine(Path.GetDirectoryName(typeof(Startup).Assembly.Location), "wwwroot"),
|
||||
rootDirectory.FullName
|
||||
};
|
||||
|
||||
var providers = paths.Where(Directory.Exists).Select(p => new PhysicalFileProvider(p)).ToArray();
|
||||
|
||||
StaticFileOptions options = null;
|
||||
|
||||
if (providers.Length > 0)
|
||||
{
|
||||
var combinedProvider = new CompositeFileProvider(providers);
|
||||
|
||||
var sharedOptions = new SharedOptions { FileProvider = combinedProvider };
|
||||
options = new StaticFileOptions(sharedOptions);
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Clockwise;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using WorkspaceServer;
|
||||
using WorkspaceServer.Packaging;
|
||||
|
||||
namespace MLS.Agent.Blazor
|
||||
{
|
||||
internal sealed class BlazorPackageConfiguration
|
||||
{
|
||||
public static void Configure(
|
||||
IApplicationBuilder app,
|
||||
IServiceProvider serviceProvider,
|
||||
PackageRegistry registry,
|
||||
Budget budget,
|
||||
bool prepareIfNeeded)
|
||||
{
|
||||
List<Task> prepareTasks = new List<Task>();
|
||||
|
||||
foreach (var builderFactory in registry)
|
||||
{
|
||||
var builder = builderFactory.Result;
|
||||
if (builder.BlazorSupported)
|
||||
{
|
||||
var package = (BlazorPackage) builder.GetPackage();
|
||||
if (PackageHasBeenBuiltAndHasBlazorStuff(package))
|
||||
{
|
||||
SetupMappingsForBlazorContentsOfPackage(package, app);
|
||||
}
|
||||
else if(prepareIfNeeded)
|
||||
{
|
||||
prepareTasks.Add(Task.Run(() => package.EnsureReady(budget)).ContinueWith(t => {
|
||||
if (t.IsCompletedSuccessfully)
|
||||
{
|
||||
SetupMappingsForBlazorContentsOfPackage(package, app);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Task.WaitAll(prepareTasks.ToArray());
|
||||
}
|
||||
|
||||
private static void SetupMappingsForBlazorContentsOfPackage(BlazorPackage package, IApplicationBuilder builder)
|
||||
{
|
||||
builder.Map(package.CodeRunnerPath, appBuilder =>
|
||||
{
|
||||
var blazorEntryPoint = package.BlazorEntryPointAssemblyPath;
|
||||
appBuilder.UsePathBase(package.CodeRunnerPathBase);
|
||||
appBuilder.UseClientSideBlazorFiles(blazorEntryPoint.FullName);
|
||||
});
|
||||
}
|
||||
|
||||
private static bool PackageHasBeenBuiltAndHasBlazorStuff(BlazorPackage package)
|
||||
{
|
||||
return package.BlazorEntryPointAssemblyPath.Exists;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace MLS.Agent
|
||||
{
|
||||
public class BrowserLaunchUri
|
||||
{
|
||||
public BrowserLaunchUri(string scheme, string host, ushort port)
|
||||
{
|
||||
Scheme = scheme ?? throw new ArgumentNullException(nameof(scheme));
|
||||
Host = host ?? throw new ArgumentNullException(nameof(host));
|
||||
Port = port;
|
||||
}
|
||||
|
||||
public string Scheme { get; }
|
||||
public string Host { get; }
|
||||
public ushort Port { get; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Scheme}://{Host}:{Port}";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MLS.Agent
|
||||
{
|
||||
internal class BrowserLauncher : IBrowserLauncher
|
||||
{
|
||||
public void LaunchBrowser(Uri uri)
|
||||
{
|
||||
if (uri == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(uri));
|
||||
}
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
Process.Start(new ProcessStartInfo("cmd", $"/c start {uri}"));
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
Process.Start("xdg-open", uri.ToString());
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
Process.Start("open", uri.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,430 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.Builder;
|
||||
using System.CommandLine.Invocation;
|
||||
using System.CommandLine.IO;
|
||||
using System.CommandLine.Parsing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Clockwise;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.DotNet.Interactive.Telemetry;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using MLS.Agent.Markdown;
|
||||
using MLS.Agent.Tools;
|
||||
using MLS.Repositories;
|
||||
using Recipes;
|
||||
using WorkspaceServer;
|
||||
using WorkspaceServer.Packaging;
|
||||
using CommandHandler = System.CommandLine.Invocation.CommandHandler;
|
||||
|
||||
namespace MLS.Agent.CommandLine
|
||||
{
|
||||
public static class CommandLineParser
|
||||
{
|
||||
public delegate void StartServer(
|
||||
StartupOptions options,
|
||||
InvocationContext context);
|
||||
|
||||
public delegate Task Install(
|
||||
InstallOptions options,
|
||||
IConsole console);
|
||||
|
||||
public delegate Task Demo(
|
||||
DemoOptions options,
|
||||
IConsole console,
|
||||
StartServer startServer = null,
|
||||
InvocationContext invocationContext = null);
|
||||
|
||||
public delegate Task TryGitHub(
|
||||
TryGitHubOptions options,
|
||||
IConsole console);
|
||||
|
||||
public delegate Task Pack(
|
||||
PackOptions options,
|
||||
IConsole console);
|
||||
|
||||
public delegate Task<int> Verify(
|
||||
VerifyOptions options,
|
||||
IConsole console,
|
||||
StartupOptions startupOptions,
|
||||
MarkdownProcessingContext context);
|
||||
|
||||
public delegate Task<int> Publish(
|
||||
PublishOptions options,
|
||||
IConsole console,
|
||||
StartupOptions startupOptions,
|
||||
MarkdownProcessingContext context);
|
||||
|
||||
public static Parser Create(
|
||||
IServiceCollection services,
|
||||
StartServer startServer = null,
|
||||
Install install = null,
|
||||
Demo demo = null,
|
||||
TryGitHub tryGithub = null,
|
||||
Pack pack = null,
|
||||
Verify verify = null,
|
||||
Publish publish = null,
|
||||
ITelemetry telemetry = null,
|
||||
IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel = null)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
startServer ??= (startupOptions, invocationContext) =>
|
||||
Program.ConstructWebHost(startupOptions).Run();
|
||||
|
||||
demo ??= DemoCommand.Do;
|
||||
|
||||
tryGithub ??= (repo, console) =>
|
||||
GitHubHandler.Handler(repo,
|
||||
console,
|
||||
new GitHubRepoLocator());
|
||||
|
||||
verify ??= VerifyCommand.Do;
|
||||
|
||||
publish ??= PublishCommand.Do;
|
||||
|
||||
pack ??= PackCommand.Do;
|
||||
|
||||
install ??= InstallCommand.Do;
|
||||
|
||||
// Setup first time use notice sentinel.
|
||||
firstTimeUseNoticeSentinel ??=
|
||||
new FirstTimeUseNoticeSentinel(VersionSensor.Version().AssemblyInformationalVersion);
|
||||
|
||||
// Setup telemetry.
|
||||
telemetry ??= new Telemetry(
|
||||
VersionSensor.Version().AssemblyInformationalVersion,
|
||||
firstTimeUseNoticeSentinel);
|
||||
var filter = new TelemetryFilter(Sha256Hasher.HashWithNormalizedCasing);
|
||||
|
||||
var dirArgument = new Argument<FileSystemDirectoryAccessor>(result =>
|
||||
{
|
||||
var directory = result.Tokens
|
||||
.Select(t => t.Value)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (!string.IsNullOrEmpty(directory) &&
|
||||
!Directory.Exists(directory))
|
||||
{
|
||||
result.ErrorMessage = $"Directory does not exist: {directory}";
|
||||
return null;
|
||||
}
|
||||
|
||||
return new FileSystemDirectoryAccessor(
|
||||
directory ??
|
||||
Directory.GetCurrentDirectory());
|
||||
}, isDefault: true)
|
||||
{
|
||||
Name = "root-directory",
|
||||
Arity = ArgumentArity.ZeroOrOne,
|
||||
Description = "The root directory for your documentation"
|
||||
};
|
||||
|
||||
var rootCommand = StartInTryMode();
|
||||
|
||||
rootCommand.AddCommand(StartInHostedMode());
|
||||
rootCommand.AddCommand(Demo());
|
||||
rootCommand.AddCommand(GitHub());
|
||||
rootCommand.AddCommand(Install());
|
||||
rootCommand.AddCommand(Pack());
|
||||
rootCommand.AddCommand(Verify());
|
||||
rootCommand.AddCommand(Publish());
|
||||
|
||||
return new CommandLineBuilder(rootCommand)
|
||||
.UseDefaults()
|
||||
.UseMiddleware(async (context, next) =>
|
||||
{
|
||||
if (context.ParseResult.Errors.Count == 0)
|
||||
{
|
||||
telemetry.SendFiltered(filter, context.ParseResult);
|
||||
}
|
||||
|
||||
// If sentinel does not exist, print the welcome message showing the telemetry notification.
|
||||
if (!firstTimeUseNoticeSentinel.Exists() && !Telemetry.SkipFirstTimeExperience)
|
||||
{
|
||||
context.Console.Out.WriteLine();
|
||||
context.Console.Out.WriteLine(Telemetry.WelcomeMessage);
|
||||
|
||||
firstTimeUseNoticeSentinel.CreateIfNotExists();
|
||||
}
|
||||
|
||||
if (context.ParseResult.Directives.Contains("debug") &&
|
||||
!(Clock.Current is VirtualClock))
|
||||
{
|
||||
VirtualClock.Start();
|
||||
}
|
||||
|
||||
await next(context);
|
||||
})
|
||||
.Build();
|
||||
|
||||
RootCommand StartInTryMode()
|
||||
{
|
||||
var command = new RootCommand
|
||||
{
|
||||
Name = "dotnet-try",
|
||||
Description = "Interactive documentation in your browser"
|
||||
};
|
||||
|
||||
command.AddArgument(dirArgument);
|
||||
|
||||
command.AddOption(new Option(
|
||||
"--add-package-source",
|
||||
"Specify an additional NuGet package source")
|
||||
{
|
||||
Argument = new Argument<PackageSource>(() => new PackageSource(Directory.GetCurrentDirectory()))
|
||||
{
|
||||
Name = "NuGet source"
|
||||
}
|
||||
});
|
||||
|
||||
command.AddOption(new Option(
|
||||
"--package",
|
||||
"Specify a Try .NET package or path to a .csproj to run code samples with")
|
||||
{
|
||||
Argument = new Argument<string>
|
||||
{
|
||||
Name = "name or .csproj"
|
||||
}
|
||||
});
|
||||
|
||||
command.AddOption(new Option(
|
||||
"--package-version",
|
||||
"Specify a Try .NET package version to use with the --package option")
|
||||
{
|
||||
Argument = new Argument<string>
|
||||
{
|
||||
Name = "version"
|
||||
}
|
||||
});
|
||||
|
||||
command.AddOption(new Option<Uri>(
|
||||
"--uri",
|
||||
"Specify a URL or a relative path to a Markdown file"));
|
||||
|
||||
command.AddOption(new Option<bool>(
|
||||
"--enable-preview-features",
|
||||
"Enable preview features"));
|
||||
|
||||
command.AddOption(new Option(
|
||||
"--log-path",
|
||||
"Enable file logging to the specified directory")
|
||||
{
|
||||
Argument = new Argument<DirectoryInfo>
|
||||
{
|
||||
Name = "dir"
|
||||
}
|
||||
});
|
||||
|
||||
command.AddOption(new Option<bool>(
|
||||
"--verbose",
|
||||
"Enable verbose logging to the console"));
|
||||
|
||||
var portArgument = new Argument<ushort>();
|
||||
|
||||
portArgument.AddValidator(symbolResult =>
|
||||
{
|
||||
if (symbolResult.Tokens
|
||||
.Select(t => t.Value)
|
||||
.Count(value => !ushort.TryParse(value, out _)) > 0)
|
||||
{
|
||||
return "Invalid argument for --port option";
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
command.AddOption(new Option(
|
||||
"--port",
|
||||
"Specify the port for dotnet try to listen on")
|
||||
{
|
||||
Argument = portArgument
|
||||
});
|
||||
|
||||
command.Handler = CommandHandler.Create<InvocationContext, StartupOptions>((context, options) =>
|
||||
{
|
||||
services.AddSingleton(_ => PackageRegistry.CreateForTryMode(
|
||||
options.RootDirectory,
|
||||
options.AddPackageSource));
|
||||
|
||||
startServer(options, context);
|
||||
});
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
Command StartInHostedMode()
|
||||
{
|
||||
var command = new Command("hosted")
|
||||
{
|
||||
new Option<string>(
|
||||
"--id",
|
||||
description: "A unique id for the agent instance (e.g. its development environment id).",
|
||||
getDefaultValue: () => Environment.MachineName),
|
||||
new Option<bool>(
|
||||
"--production",
|
||||
"Specifies whether the agent is being run using production resources"),
|
||||
new Option<bool>(
|
||||
"--language-service",
|
||||
"Specifies whether the agent is being run in language service-only mode"),
|
||||
new Option<string>(
|
||||
new[]
|
||||
{
|
||||
"-k",
|
||||
"--key"
|
||||
},
|
||||
"The encryption key"),
|
||||
new Option<string>(
|
||||
new[]
|
||||
{
|
||||
"--ai-key",
|
||||
"--application-insights-key"
|
||||
},
|
||||
"Application Insights key."),
|
||||
new Option<string>(
|
||||
"--region-id",
|
||||
"A unique id for the agent region"),
|
||||
new Option<bool>(
|
||||
"--log-to-file",
|
||||
"Writes a log file")
|
||||
};
|
||||
|
||||
command.Description = "Starts the Try .NET agent";
|
||||
|
||||
command.IsHidden = true;
|
||||
|
||||
command.Handler = CommandHandler.Create<InvocationContext, StartupOptions>((context, options) =>
|
||||
{
|
||||
services.AddSingleton(_ => PackageRegistry.CreateForHostedMode());
|
||||
services.AddSingleton(c => new MarkdownProject(c.GetRequiredService<PackageRegistry>()));
|
||||
startServer(options, context);
|
||||
});
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
Command Demo()
|
||||
{
|
||||
var demoCommand = new Command(
|
||||
"demo",
|
||||
"Learn how to create Try .NET content with an interactive demo")
|
||||
{
|
||||
new Option<DirectoryInfo>(
|
||||
"--output",
|
||||
description: "Where should the demo project be written to?",
|
||||
getDefaultValue: () => new DirectoryInfo(Directory.GetCurrentDirectory()))
|
||||
};
|
||||
|
||||
demoCommand.Handler = CommandHandler.Create<DemoOptions, InvocationContext>((options, context) => { demo(options, context.Console, startServer, context); });
|
||||
|
||||
return demoCommand;
|
||||
}
|
||||
|
||||
Command GitHub()
|
||||
{
|
||||
var argument = new Argument<string>
|
||||
{
|
||||
// System.CommandLine parameter binding does lookup by name,
|
||||
// so name the argument after the github command's string param
|
||||
Name = nameof(TryGitHubOptions.Repo)
|
||||
};
|
||||
|
||||
var github = new Command("github", "Try a GitHub repo")
|
||||
{
|
||||
argument
|
||||
};
|
||||
|
||||
github.IsHidden = true;
|
||||
|
||||
github.Handler = CommandHandler.Create(tryGithub);
|
||||
|
||||
return github;
|
||||
}
|
||||
|
||||
Command Install()
|
||||
{
|
||||
var installCommand = new Command("install", "Install a Try .NET package")
|
||||
{
|
||||
new Argument<string>
|
||||
{
|
||||
Name = nameof(InstallOptions.PackageName),
|
||||
Arity = ArgumentArity.ExactlyOne
|
||||
},
|
||||
new Option<PackageSource>("--add-source")
|
||||
};
|
||||
|
||||
installCommand.IsHidden = true;
|
||||
|
||||
installCommand.Handler = CommandHandler.Create(install);
|
||||
|
||||
return installCommand;
|
||||
}
|
||||
|
||||
Command Pack()
|
||||
{
|
||||
var packCommand = new Command("pack", "Create a Try .NET package")
|
||||
{
|
||||
new Argument<DirectoryInfo>
|
||||
{
|
||||
Name = nameof(PackOptions.PackTarget)
|
||||
},
|
||||
new Option<string>("--version", "The version of the Try .NET package"),
|
||||
new Option<bool>("--enable-wasm", "Enables web assembly code execution")
|
||||
};
|
||||
|
||||
packCommand.IsHidden = true;
|
||||
|
||||
packCommand.Handler = CommandHandler.Create(pack);
|
||||
|
||||
return packCommand;
|
||||
}
|
||||
|
||||
Command Verify()
|
||||
{
|
||||
var verifyCommand = new Command("verify", "Verify Markdown files found under the root directory.")
|
||||
{
|
||||
dirArgument
|
||||
};
|
||||
|
||||
verifyCommand.Handler = CommandHandler.Create(verify);
|
||||
|
||||
return verifyCommand;
|
||||
}
|
||||
|
||||
Command Publish()
|
||||
{
|
||||
var publishCommand = new Command("publish", "Publish code from sample projects found under the root directory into Markdown files in the target directory")
|
||||
{
|
||||
new Option<PublishFormat>(
|
||||
"--format",
|
||||
description: "Format of the files to publish",
|
||||
getDefaultValue: () => PublishFormat.Markdown),
|
||||
new Option<IDirectoryAccessor>(
|
||||
"--target-directory",
|
||||
description: "The path where the output files should go. This can be the same as the root directory, which will overwrite files in place.",
|
||||
parseArgument: result =>
|
||||
{
|
||||
var directory = result.Tokens
|
||||
.Select(t => t.Value)
|
||||
.Single();
|
||||
|
||||
return new FileSystemDirectoryAccessor(directory);
|
||||
}
|
||||
),
|
||||
dirArgument
|
||||
};
|
||||
publishCommand.Handler = CommandHandler.Create(publish);
|
||||
|
||||
return publishCommand;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using MLS.Agent.Tools;
|
||||
using System;
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.Invocation;
|
||||
using System.CommandLine.IO;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.DotNet.Interactive.Utility;
|
||||
|
||||
namespace MLS.Agent.CommandLine
|
||||
{
|
||||
public static class DemoCommand
|
||||
{
|
||||
public static Task<int> Do(
|
||||
DemoOptions options,
|
||||
IConsole console,
|
||||
CommandLineParser.StartServer startServer = null,
|
||||
InvocationContext context = null)
|
||||
{
|
||||
var extractDemoFiles = true;
|
||||
|
||||
if (!options.Output.Exists)
|
||||
{
|
||||
options.Output.Create();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (options.Output.GetFiles().Any())
|
||||
{
|
||||
if (options.Output.GetFiles().All(f => f.Name != "QuickStart.md"))
|
||||
{
|
||||
console.Out.WriteLine($"Directory {options.Output} must be empty. To specify a directory to create the demo sample in, use: dotnet try demo --output <dir>");
|
||||
return Task.FromResult(-1);
|
||||
}
|
||||
|
||||
extractDemoFiles = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (extractDemoFiles)
|
||||
{
|
||||
using (var disposableDirectory = DisposableDirectory.Create())
|
||||
{
|
||||
var assembly = typeof(Program).Assembly;
|
||||
|
||||
using (var resourceStream = assembly.GetManifestResourceStream("demo.zip"))
|
||||
{
|
||||
var zipPath = Path.Combine(disposableDirectory.Directory.FullName, "demo.zip");
|
||||
|
||||
using (var fileStream = new FileStream(zipPath, FileMode.Create, FileAccess.Write))
|
||||
{
|
||||
resourceStream.CopyTo(fileStream);
|
||||
}
|
||||
|
||||
ZipFile.ExtractToDirectory(zipPath, options.Output.FullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
startServer?.Invoke(new StartupOptions(
|
||||
uri: new Uri("QuickStart.md", UriKind.Relative),
|
||||
rootDirectory: new FileSystemDirectoryAccessor(options.Output)),
|
||||
context);
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace MLS.Agent.CommandLine
|
||||
{
|
||||
public class DemoOptions
|
||||
{
|
||||
public DemoOptions(DirectoryInfo output)
|
||||
{
|
||||
Output = output;
|
||||
}
|
||||
|
||||
public DirectoryInfo Output { get; }
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using MLS.Agent.Tools;
|
||||
|
||||
namespace MLS.Agent.CommandLine
|
||||
{
|
||||
internal static class DirectoryExtensions
|
||||
{
|
||||
private static readonly RelativeDirectoryPath _here = new RelativeDirectoryPath("./");
|
||||
|
||||
public static bool IsChildOf(this FileSystemInfo file, IDirectoryAccessor directory)
|
||||
{
|
||||
var parent = directory.GetFullyQualifiedPath(_here).FullName;
|
||||
var child = Path.GetDirectoryName(file.FullName);
|
||||
|
||||
child = child.EndsWith('/') || child.EndsWith('\\') ? child : child + "/";
|
||||
return IsBaseOf(parent, child, selfIsChild: true);
|
||||
}
|
||||
|
||||
public static bool IsSubDirectoryOf(this IDirectoryAccessor potentialChild, IDirectoryAccessor directory)
|
||||
{
|
||||
var child = potentialChild.GetFullyQualifiedPath(_here).FullName;
|
||||
var parent = directory.GetFullyQualifiedPath(_here).FullName;
|
||||
return IsBaseOf(parent, child, selfIsChild: false);
|
||||
}
|
||||
|
||||
private static bool IsBaseOf(string parent, string child, bool selfIsChild)
|
||||
{
|
||||
var parentUri = new Uri(parent);
|
||||
var childUri = new Uri(child);
|
||||
return (selfIsChild || parentUri != childUri) && parentUri.IsBaseOf(childUri);
|
||||
}
|
||||
|
||||
public static void EnsureDirectoryExists(this IDirectoryAccessor directoryAccessor, RelativePath path)
|
||||
{
|
||||
var relativeDirectoryPath = path.Match(
|
||||
directory => directory,
|
||||
file => file.Directory
|
||||
);
|
||||
directoryAccessor.EnsureDirectoryExists(relativeDirectoryPath);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using MLS.Repositories;
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MLS.Agent.CommandLine
|
||||
{
|
||||
public static class GitHubHandler
|
||||
{
|
||||
public static async Task Handler(TryGitHubOptions options, IConsole console, IRepoLocator locator)
|
||||
{
|
||||
var repos = (await locator.LocateRepo(options.Repo)).ToArray();
|
||||
|
||||
if (repos.Length == 0)
|
||||
{
|
||||
console.Out.WriteLine($"Didn't find any repos called `{options.Repo}`");
|
||||
}
|
||||
else if (repos[0].Name == options.Repo)
|
||||
{
|
||||
console.Out.WriteLine(GenerateCommandExample(repos[0].Name, repos[0].CloneUrl));
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
console.Out.WriteLine("Which of the following did you mean?");
|
||||
foreach (var instance in repos)
|
||||
{
|
||||
console.Out.WriteLine($"\t{instance.Name}");
|
||||
}
|
||||
}
|
||||
|
||||
string GenerateCommandExample(string name, string cloneUrl)
|
||||
{
|
||||
var text = $"Found repo `{name}`\n";
|
||||
text += $"To try `{name}`, cd to your desired directory and run the following command:\n\n";
|
||||
text += $"\tgit clone {cloneUrl} && dotnet try .";
|
||||
|
||||
return text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.CommandLine;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.DotNet.Interactive.Utility;
|
||||
using MLS.Agent.Tools;
|
||||
|
||||
namespace MLS.Agent.CommandLine
|
||||
{
|
||||
public static class InstallCommand
|
||||
{
|
||||
public static async Task Do(InstallOptions options, IConsole console)
|
||||
{
|
||||
var dotnet = new Dotnet();
|
||||
(await dotnet.ToolInstall(
|
||||
options.PackageName,
|
||||
options.Location,
|
||||
options.AddSource.ToString())).ThrowOnFailure();
|
||||
|
||||
var tool = WorkspaceServer.WorkspaceFeatures.PackageTool.TryCreateFromDirectory(options.PackageName, new FileSystemDirectoryAccessor(options.Location));
|
||||
await tool.Prepare();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.DotNet.Interactive.Utility;
|
||||
using WorkspaceServer;
|
||||
using WorkspaceServer.Packaging;
|
||||
|
||||
namespace MLS.Agent.CommandLine
|
||||
{
|
||||
public class InstallOptions
|
||||
{
|
||||
public InstallOptions(string packageName, PackageSource addSource = null, DirectoryInfo location = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(packageName))
|
||||
{
|
||||
throw new ArgumentException("Value cannot be null or whitespace.", nameof(packageName));
|
||||
}
|
||||
AddSource = addSource;
|
||||
PackageName = packageName;
|
||||
Location = location ?? Package.DefaultPackagesDirectory;
|
||||
}
|
||||
|
||||
public PackageSource AddSource { get; }
|
||||
|
||||
public string PackageName { get; }
|
||||
public DirectoryInfo Location { get; }
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.IO;
|
||||
using System.IO;
|
||||
using Microsoft.DotNet.Try.Markdown;
|
||||
using MLS.Agent.Markdown;
|
||||
using MLS.Agent.Tools;
|
||||
using WorkspaceServer;
|
||||
using WorkspaceServer.Servers;
|
||||
|
||||
namespace MLS.Agent.CommandLine
|
||||
{
|
||||
public class MarkdownProcessingContext
|
||||
{
|
||||
private readonly Lazy<IWorkspaceServer> _lazyWorkspaceServer;
|
||||
|
||||
|
||||
public MarkdownProcessingContext(
|
||||
IDirectoryAccessor rootDirectory,
|
||||
IDefaultCodeBlockAnnotations defaultAnnotations = null,
|
||||
WriteFile writeFile = null,
|
||||
IConsole console = null)
|
||||
{
|
||||
RootDirectory = rootDirectory;
|
||||
Console = console ?? new SystemConsole();
|
||||
|
||||
var packageRegistry = PackageRegistry.CreateForTryMode(rootDirectory);
|
||||
|
||||
Project = new MarkdownProject(
|
||||
rootDirectory,
|
||||
packageRegistry,
|
||||
defaultAnnotations ?? new DefaultCodeBlockAnnotations());
|
||||
|
||||
_lazyWorkspaceServer = new Lazy<IWorkspaceServer>(() => new WorkspaceServerMultiplexer(packageRegistry));
|
||||
|
||||
WriteFile = writeFile ?? File.WriteAllText;
|
||||
}
|
||||
|
||||
public IConsole Console { get; }
|
||||
|
||||
public IDirectoryAccessor RootDirectory { get; }
|
||||
|
||||
public WriteFile WriteFile { get; }
|
||||
|
||||
public MarkdownProject Project { get; }
|
||||
|
||||
public IWorkspaceServer WorkspaceServer => _lazyWorkspaceServer.Value;
|
||||
|
||||
public IList<string> Errors { get; } = new List<string>();
|
||||
}
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.IO;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.DotNet.Interactive.Utility;
|
||||
using MLS.Agent.Tools;
|
||||
using WorkspaceServer.Packaging;
|
||||
|
||||
namespace MLS.Agent.CommandLine
|
||||
{
|
||||
public static class PackCommand
|
||||
{
|
||||
public static async Task<string> Do(PackOptions options, IConsole console)
|
||||
{
|
||||
console.Out.WriteLine($"Creating package-tool from {options.PackTarget.FullName}");
|
||||
|
||||
using (var disposableDirectory = DisposableDirectory.Create())
|
||||
{
|
||||
var temp = disposableDirectory.Directory;
|
||||
var temp_projects = temp.CreateSubdirectory("projects");
|
||||
|
||||
var name = options.PackageName;
|
||||
|
||||
var temp_projects_build = temp_projects.CreateSubdirectory("build");
|
||||
options.PackTarget.CopyTo(temp_projects_build);
|
||||
|
||||
if (options.EnableWasm)
|
||||
{
|
||||
string runnerDirectoryName = "wasm";
|
||||
var temp_projects_wasm = temp_projects.CreateSubdirectory(runnerDirectoryName);
|
||||
var temp_mlsblazor = temp.CreateSubdirectory("MLS.Blazor");
|
||||
await AddBlazorProject(temp_mlsblazor, GetProjectFile(temp_projects_build), name, temp_projects_wasm);
|
||||
}
|
||||
|
||||
var temp_toolproject = temp.CreateSubdirectory("project");
|
||||
var archivePath = Path.Combine(temp_toolproject.FullName, "package.zip");
|
||||
ZipFile.CreateFromDirectory(temp_projects.FullName, archivePath, CompressionLevel.Fastest, includeBaseDirectory: false);
|
||||
|
||||
console.Out.WriteLine(archivePath);
|
||||
|
||||
var projectFilePath = Path.Combine(temp_toolproject.FullName, "package-tool.csproj");
|
||||
var contentFilePath = Path.Combine(temp_toolproject.FullName, "program.cs");
|
||||
|
||||
await File.WriteAllTextAsync(
|
||||
projectFilePath,
|
||||
typeof(Program).ReadManifestResource("MLS.Agent.MLS.PackageTool.csproj"));
|
||||
|
||||
await File.WriteAllTextAsync(contentFilePath, typeof(Program).ReadManifestResource("MLS.Agent.Program.cs"));
|
||||
|
||||
var dotnet = new Dotnet(temp_toolproject);
|
||||
var result = await dotnet.Build();
|
||||
|
||||
result.ThrowOnFailure("Failed to build intermediate project.");
|
||||
var versionArg = "";
|
||||
|
||||
if(!string.IsNullOrEmpty(options.Version))
|
||||
{
|
||||
versionArg = $"/p:PackageVersion={options.Version}";
|
||||
}
|
||||
|
||||
result = await dotnet.Pack($@"/p:PackageId=""{name}"" /p:ToolCommandName=""{name}"" {versionArg} ""{projectFilePath}"" -o ""{options.OutputDirectory.FullName}""");
|
||||
|
||||
result.ThrowOnFailure("Package build failed.");
|
||||
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task AddBlazorProject(DirectoryInfo blazorTargetDirectory, FileInfo projectToReference, string name, DirectoryInfo wasmLocation)
|
||||
{
|
||||
var initializer = new BlazorPackageInitializer(name, new System.Collections.Generic.List<(string,string,string)>());
|
||||
await initializer.Initialize(blazorTargetDirectory);
|
||||
|
||||
await AddReference(blazorTargetDirectory, projectToReference);
|
||||
var dotnet = new Dotnet(blazorTargetDirectory);
|
||||
var result = await dotnet.Publish($"-o {wasmLocation.FullName}");
|
||||
result.ThrowOnFailure();
|
||||
}
|
||||
|
||||
private static async Task AddReference(DirectoryInfo blazorTargetDirectory, FileInfo projectToReference)
|
||||
{
|
||||
var dotnet = new Dotnet(blazorTargetDirectory);
|
||||
(await dotnet.AddReference(projectToReference)).ThrowOnFailure();
|
||||
}
|
||||
|
||||
private static FileInfo GetProjectFile(DirectoryInfo directory)
|
||||
{
|
||||
return directory.GetFiles("*.csproj").Single();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace MLS.Agent.CommandLine
|
||||
{
|
||||
public class PackOptions
|
||||
{
|
||||
private string _packageName;
|
||||
|
||||
public PackOptions(
|
||||
DirectoryInfo packTarget,
|
||||
string version = null,
|
||||
DirectoryInfo outputDirectory = null,
|
||||
bool enableWasm = false,
|
||||
string packageName = null)
|
||||
{
|
||||
PackTarget = packTarget ?? throw new ArgumentNullException(nameof(packTarget));
|
||||
OutputDirectory = outputDirectory ?? packTarget;
|
||||
EnableWasm = enableWasm;
|
||||
Version = version;
|
||||
_packageName = packageName;
|
||||
}
|
||||
|
||||
public DirectoryInfo PackTarget { get; }
|
||||
public DirectoryInfo OutputDirectory { get; }
|
||||
public bool EnableWasm { get; }
|
||||
public string Version { get; }
|
||||
public string PackageName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_packageName))
|
||||
{
|
||||
return _packageName;
|
||||
}
|
||||
|
||||
var csproj = PackTarget.GetFiles("*.csproj").Single();
|
||||
_packageName = Path.GetFileNameWithoutExtension(csproj.Name);
|
||||
return _packageName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,204 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.IO;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Markdig;
|
||||
using Markdig.Renderers;
|
||||
using Markdig.Renderers.Normalize;
|
||||
using Markdig.Syntax;
|
||||
using Microsoft.DotNet.Try.Markdown;
|
||||
using Microsoft.DotNet.Try.Protocol;
|
||||
using MLS.Agent.Markdown;
|
||||
using MLS.Agent.Tools;
|
||||
|
||||
namespace MLS.Agent.CommandLine
|
||||
{
|
||||
public delegate void WriteFile(string path, string content);
|
||||
|
||||
|
||||
public static class PublishCommand
|
||||
{
|
||||
public static async Task<int> Do(
|
||||
PublishOptions publishOptions,
|
||||
IConsole console,
|
||||
StartupOptions startupOptions = null,
|
||||
MarkdownProcessingContext context = null)
|
||||
{
|
||||
context ??= new MarkdownProcessingContext(
|
||||
publishOptions.RootDirectory,
|
||||
startupOptions,
|
||||
console: console);
|
||||
|
||||
var verifyResult = await VerifyCommand.Do(
|
||||
publishOptions,
|
||||
console,
|
||||
startupOptions,
|
||||
context);
|
||||
|
||||
if (verifyResult != 0)
|
||||
{
|
||||
return verifyResult;
|
||||
}
|
||||
|
||||
var targetIsSubDirectoryOfSource =
|
||||
publishOptions.TargetDirectory
|
||||
.IsSubDirectoryOf(publishOptions.RootDirectory);
|
||||
|
||||
foreach (var markdownFile in context.Project.GetAllMarkdownFiles())
|
||||
{
|
||||
var fullSourcePath = publishOptions.RootDirectory.GetFullyQualifiedPath(markdownFile.Path);
|
||||
|
||||
if (targetIsSubDirectoryOfSource &&
|
||||
fullSourcePath.IsChildOf(publishOptions.TargetDirectory))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var sessions = await markdownFile.GetSessions();
|
||||
|
||||
var outputsBySessionName = new Dictionary<string,string>();
|
||||
|
||||
foreach (var session in sessions)
|
||||
{
|
||||
if (session.CodeBlocks.Any(b => b.Annotations is OutputBlockAnnotations))
|
||||
{
|
||||
var workspace = await session.GetWorkspaceAsync();
|
||||
|
||||
var runArgs =
|
||||
session.CodeBlocks
|
||||
.Select(c => c.Annotations)
|
||||
.OfType<CodeBlockAnnotations>()
|
||||
.Select(a => a.RunArgs)
|
||||
.FirstOrDefault();
|
||||
|
||||
var request = new WorkspaceRequest(
|
||||
workspace,
|
||||
runArgs: runArgs);
|
||||
|
||||
var result = await context.WorkspaceServer.Run(request);
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
var output = result.Output.Count > 0
|
||||
? string.Join("\n", result.Output)
|
||||
: result.Exception;
|
||||
|
||||
outputsBySessionName.Add(
|
||||
session.Name,
|
||||
output);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Errors.Add(
|
||||
$"Running session {session.Name} failed:\n" + result.Exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var document = ParseMarkdownDocument(markdownFile);
|
||||
|
||||
var rendered = await Render(
|
||||
publishOptions.Format,
|
||||
document,
|
||||
outputsBySessionName);
|
||||
|
||||
var targetPath = WriteTargetFile(
|
||||
rendered,
|
||||
markdownFile.Path,
|
||||
publishOptions,
|
||||
context,
|
||||
publishOptions.Format);
|
||||
|
||||
console.Out.WriteLine($"Published '{fullSourcePath}' to {targetPath}");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static string WriteTargetFile(
|
||||
string content,
|
||||
RelativeFilePath relativePath,
|
||||
PublishOptions publishOptions,
|
||||
MarkdownProcessingContext context,
|
||||
PublishFormat format)
|
||||
{
|
||||
context.Project
|
||||
.DirectoryAccessor
|
||||
.EnsureDirectoryExists(relativePath);
|
||||
|
||||
var targetPath = publishOptions
|
||||
.TargetDirectory
|
||||
.GetFullyQualifiedPath(relativePath).FullName;
|
||||
|
||||
if (format == PublishFormat.HTML)
|
||||
{
|
||||
targetPath = Path.ChangeExtension(targetPath, ".html");
|
||||
}
|
||||
|
||||
context.WriteFile(targetPath, content);
|
||||
|
||||
return targetPath;
|
||||
}
|
||||
|
||||
private static async Task<string> Render(
|
||||
PublishFormat format,
|
||||
MarkdownDocument document,
|
||||
Dictionary<string, string> outputsBySessionName)
|
||||
{
|
||||
MarkdownPipeline pipeline;
|
||||
IMarkdownRenderer renderer;
|
||||
var writer = new StringWriter();
|
||||
switch (format)
|
||||
{
|
||||
case PublishFormat.Markdown:
|
||||
pipeline = new MarkdownPipelineBuilder()
|
||||
.UseNormalizeCodeBlockAnnotations(outputsBySessionName)
|
||||
.Build();
|
||||
var normalizeRenderer = new NormalizeRenderer(writer);
|
||||
normalizeRenderer.Writer.NewLine = "\n";
|
||||
renderer = normalizeRenderer;
|
||||
break;
|
||||
case PublishFormat.HTML:
|
||||
pipeline = new MarkdownPipelineBuilder()
|
||||
.UseCodeBlockAnnotations(inlineControls: false)
|
||||
.Build();
|
||||
renderer = new HtmlRenderer(writer);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(format), format, null);
|
||||
}
|
||||
|
||||
pipeline.Setup(renderer);
|
||||
|
||||
var blocks = document
|
||||
.OfType<AnnotatedCodeBlock>()
|
||||
.OrderBy(c => c.Order)
|
||||
.ToList();
|
||||
|
||||
await Task.WhenAll(blocks.Select(b => b.InitializeAsync()));
|
||||
|
||||
renderer.Render(document);
|
||||
writer.Flush();
|
||||
|
||||
var rendered = writer.ToString();
|
||||
return rendered;
|
||||
}
|
||||
|
||||
private static MarkdownDocument ParseMarkdownDocument(MarkdownFile markdownFile)
|
||||
{
|
||||
var pipeline = markdownFile.Project.GetMarkdownPipelineFor(markdownFile.Path);
|
||||
|
||||
var markdown = markdownFile.ReadAllText();
|
||||
|
||||
return Markdig.Markdown.Parse(
|
||||
markdown,
|
||||
pipeline);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
namespace MLS.Agent.CommandLine
|
||||
{
|
||||
public enum PublishFormat
|
||||
{
|
||||
Markdown,
|
||||
HTML
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using MLS.Agent.Tools;
|
||||
|
||||
namespace MLS.Agent.CommandLine
|
||||
{
|
||||
public class PublishOptions : VerifyOptions
|
||||
{
|
||||
public PublishOptions(
|
||||
IDirectoryAccessor rootDirectory,
|
||||
IDirectoryAccessor targetDirectory = null,
|
||||
PublishFormat format = PublishFormat.Markdown) : base(rootDirectory)
|
||||
{
|
||||
Format = format;
|
||||
TargetDirectory = targetDirectory ?? rootDirectory;
|
||||
}
|
||||
|
||||
public IDirectoryAccessor TargetDirectory { get; }
|
||||
|
||||
public PublishFormat Format { get; }
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
namespace MLS.Agent.CommandLine
|
||||
{
|
||||
public enum StartupMode
|
||||
{
|
||||
Hosted,
|
||||
Try
|
||||
}
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.CommandLine.Parsing;
|
||||
using System.IO;
|
||||
using Microsoft.DotNet.Try.Markdown;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using MLS.Agent.Tools;
|
||||
using WorkspaceServer.Packaging;
|
||||
|
||||
namespace MLS.Agent.CommandLine
|
||||
{
|
||||
public class StartupOptions : IDefaultCodeBlockAnnotations
|
||||
{
|
||||
private readonly ParseResult _parseResult;
|
||||
|
||||
public static StartupOptions FromCommandLine(string commandLine)
|
||||
{
|
||||
StartupOptions startupOptions = null;
|
||||
|
||||
CommandLineParser.Create(new ServiceCollection(), startServer: (options, context) =>
|
||||
{
|
||||
startupOptions = options;
|
||||
})
|
||||
.InvokeAsync(commandLine);
|
||||
|
||||
return startupOptions;
|
||||
}
|
||||
|
||||
public StartupOptions(
|
||||
bool production = false,
|
||||
bool languageService = false,
|
||||
string key = null,
|
||||
string applicationInsightsKey = null,
|
||||
string id = null,
|
||||
string regionId = null,
|
||||
PackageSource addPackageSource = null,
|
||||
Uri uri = null,
|
||||
DirectoryInfo logPath = null,
|
||||
bool verbose = false,
|
||||
bool enablePreviewFeatures = false,
|
||||
string package = null,
|
||||
string packageVersion = null,
|
||||
ParseResult parseResult = null,
|
||||
ushort? port = null,
|
||||
IDirectoryAccessor rootDirectory = null)
|
||||
{
|
||||
_parseResult = parseResult;
|
||||
LogPath = logPath;
|
||||
Verbose = verbose;
|
||||
Id = id;
|
||||
Production = production;
|
||||
IsLanguageService = languageService;
|
||||
Key = key;
|
||||
ApplicationInsightsKey = applicationInsightsKey;
|
||||
RegionId = regionId;
|
||||
RootDirectory = rootDirectory?? new FileSystemDirectoryAccessor(new DirectoryInfo(Directory.GetCurrentDirectory()));
|
||||
AddPackageSource = addPackageSource;
|
||||
Uri = uri;
|
||||
EnablePreviewFeatures = enablePreviewFeatures;
|
||||
Package = package;
|
||||
PackageVersion = packageVersion;
|
||||
Port = port;
|
||||
}
|
||||
|
||||
|
||||
public bool EnablePreviewFeatures { get; }
|
||||
public string Id { get; }
|
||||
public string RegionId { get; }
|
||||
public IDirectoryAccessor RootDirectory { get; }
|
||||
public PackageSource AddPackageSource { get; }
|
||||
public Uri Uri { get; set; }
|
||||
public bool Production { get; }
|
||||
public bool IsLanguageService { get; set; }
|
||||
public string Key { get; }
|
||||
public string ApplicationInsightsKey { get; }
|
||||
|
||||
public StartupMode Mode
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (_parseResult?.CommandResult?.Command?.Name)
|
||||
{
|
||||
case "hosted":
|
||||
return StartupMode.Hosted;
|
||||
default:
|
||||
return StartupMode.Try;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string EnvironmentName =>
|
||||
Production || Mode != StartupMode.Hosted
|
||||
? Environments.Production
|
||||
: Environments.Development;
|
||||
|
||||
public DirectoryInfo LogPath { get; }
|
||||
|
||||
public bool Verbose { get; }
|
||||
|
||||
public string Package { get; }
|
||||
|
||||
public string PackageVersion { get; }
|
||||
public ushort? Port { get; }
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
namespace MLS.Agent.CommandLine
|
||||
{
|
||||
public class TryGitHubOptions
|
||||
{
|
||||
public TryGitHubOptions(string repo)
|
||||
{
|
||||
Repo = repo;
|
||||
}
|
||||
|
||||
public string Repo { get; }
|
||||
}
|
||||
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче