diff --git a/TestAssets/TestProjects/PortableTests/PortableApp/project.json b/TestAssets/TestProjects/PortableTests/PortableApp/project.json index 02df06c0b..07faeb1ac 100644 --- a/TestAssets/TestProjects/PortableTests/PortableApp/project.json +++ b/TestAssets/TestProjects/PortableTests/PortableApp/project.json @@ -16,5 +16,14 @@ } } } + }, + + "runtimeOptions": { + "somethingString": "anything", + "somethingBoolean": true, + "someArray": ["one", "two"], + "someObject": { + "someProperty": "someValue" + } } } diff --git a/TestAssets/TestProjects/PortableTests/StandaloneApp/project.json b/TestAssets/TestProjects/PortableTests/StandaloneApp/project.json index edacc3ddb..2c21f3257 100644 --- a/TestAssets/TestProjects/PortableTests/StandaloneApp/project.json +++ b/TestAssets/TestProjects/PortableTests/StandaloneApp/project.json @@ -22,5 +22,14 @@ "centos.7-x64": {}, "rhel.7.2-x64": {}, "debian.8.2-x64": {} + }, + + "runtimeOptions": { + "somethingString": "anything", + "somethingBoolean": true, + "someArray": ["one", "two"], + "someObject": { + "someProperty": "someValue" + } } } diff --git a/src/Microsoft.DotNet.Compiler.Common/Executable.cs b/src/Microsoft.DotNet.Compiler.Common/Executable.cs index ac922459d..5582e7d57 100644 --- a/src/Microsoft.DotNet.Compiler.Common/Executable.cs +++ b/src/Microsoft.DotNet.Compiler.Common/Executable.cs @@ -131,31 +131,8 @@ namespace Microsoft.Dotnet.Cli.Compiler.Common var runtimeOptions = new JObject(); json.Add("runtimeOptions", runtimeOptions); - var redistPackage = _context.RootProject.Dependencies - .Where(r => r.Type.Equals(LibraryDependencyType.Platform)) - .ToList(); - if(redistPackage.Count > 0) - { - if(redistPackage.Count > 1) - { - throw new InvalidOperationException("Multiple packages with type: \"platform\" were specified!"); - } - var packageName = redistPackage.Single().Name; - - var redistExport = exporter.GetAllExports() - .FirstOrDefault(e => e.Library.Identity.Name.Equals(packageName)); - if (redistExport == null) - { - throw new InvalidOperationException($"Platform package '{packageName}' was not present in the graph."); - } - else - { - var framework = new JObject( - new JProperty("name", redistExport.Library.Identity.Name), - new JProperty("version", redistExport.Library.Identity.Version.ToNormalizedString())); - runtimeOptions.Add("framework", framework); - } - } + WriteFramework(runtimeOptions, exporter); + WriteRuntimeOptions(runtimeOptions); var runtimeConfigJsonFile = Path.Combine(_runtimeOutputPath, _compilerOptions.OutputName + FileNameSuffixes.RuntimeConfigJson); @@ -168,6 +145,49 @@ namespace Microsoft.Dotnet.Cli.Compiler.Common } } + private void WriteFramework(JObject runtimeOptions, LibraryExporter exporter) + { + var redistPackage = _context.RootProject.Dependencies + .Where(r => r.Type.Equals(LibraryDependencyType.Platform)) + .ToList(); + if (redistPackage.Count > 0) + { + if (redistPackage.Count > 1) + { + throw new InvalidOperationException("Multiple packages with type: \"platform\" were specified!"); + } + var packageName = redistPackage.Single().Name; + + var redistExport = exporter.GetAllExports() + .FirstOrDefault(e => e.Library.Identity.Name.Equals(packageName)); + if (redistExport == null) + { + throw new InvalidOperationException($"Platform package '{packageName}' was not present in the graph."); + } + else + { + var framework = new JObject( + new JProperty("name", redistExport.Library.Identity.Name), + new JProperty("version", redistExport.Library.Identity.Version.ToNormalizedString())); + runtimeOptions.Add("framework", framework); + } + } + } + + private void WriteRuntimeOptions(JObject runtimeOptions) + { + if (string.IsNullOrEmpty(_context.ProjectFile.RawRuntimeOptions)) + { + return; + } + + var runtimeOptionsFromProjectJson = JObject.Parse(_context.ProjectFile.RawRuntimeOptions); + foreach (var runtimeOption in runtimeOptionsFromProjectJson) + { + runtimeOptions.Add(runtimeOption.Key, runtimeOption.Value); + } + } + private void WriteDevRuntimeConfig(LibraryExporter exporter) { if (_context.TargetFramework.IsDesktop()) diff --git a/src/Microsoft.DotNet.ProjectModel/FileFormatException.cs b/src/Microsoft.DotNet.ProjectModel/FileFormatException.cs index b65ed5ea9..f60d97d22 100644 --- a/src/Microsoft.DotNet.ProjectModel/FileFormatException.cs +++ b/src/Microsoft.DotNet.ProjectModel/FileFormatException.cs @@ -2,6 +2,8 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using Microsoft.Extensions.JsonParser.Sources; namespace Microsoft.DotNet.ProjectModel @@ -21,7 +23,7 @@ namespace Microsoft.DotNet.ProjectModel public string Path { get; private set; } public int Line { get; private set; } public int Column { get; private set; } - + public override string ToString() { return $"{Path}({Line},{Column}): Error: {base.ToString()}"; @@ -29,20 +31,12 @@ namespace Microsoft.DotNet.ProjectModel internal static FileFormatException Create(Exception exception, string filePath) { - if (exception is JsonDeserializerException) - { - return new FileFormatException(exception.Message, exception) - .WithFilePath(filePath) - .WithLineInfo((JsonDeserializerException)exception); - } - else - { - return new FileFormatException(exception.Message, exception) - .WithFilePath(filePath); - } + return new FileFormatException(exception.Message, exception) + .WithFilePath(filePath) + .WithLineInfo(exception); } - internal static FileFormatException Create(Exception exception, JsonValue jsonValue, string filePath) + internal static FileFormatException Create(Exception exception, JToken jsonValue, string filePath) { var result = Create(exception, jsonValue) .WithFilePath(filePath); @@ -50,7 +44,7 @@ namespace Microsoft.DotNet.ProjectModel return result; } - internal static FileFormatException Create(Exception exception, JsonValue jsonValue) + internal static FileFormatException Create(Exception exception, JToken jsonValue) { var result = new FileFormatException(exception.Message, exception) .WithLineInfo(jsonValue); @@ -58,7 +52,7 @@ namespace Microsoft.DotNet.ProjectModel return result; } - internal static FileFormatException Create(string message, JsonValue jsonValue, string filePath) + internal static FileFormatException Create(string message, JToken jsonValue, string filePath) { var result = Create(message, jsonValue) .WithFilePath(filePath); @@ -74,6 +68,14 @@ namespace Microsoft.DotNet.ProjectModel return result; } + internal static FileFormatException Create(string message, JToken jsonValue) + { + var result = new FileFormatException(message) + .WithLineInfo(jsonValue); + + return result; + } + internal static FileFormatException Create(string message, JsonValue jsonValue) { var result = new FileFormatException(message) @@ -82,6 +84,14 @@ namespace Microsoft.DotNet.ProjectModel return result; } + internal static FileFormatException Create(Exception exception, JsonValue jsonValue) + { + var result = new FileFormatException(exception.Message, exception) + .WithLineInfo(jsonValue); + + return result; + } + internal FileFormatException WithFilePath(string path) { if (path == null) @@ -94,15 +104,17 @@ namespace Microsoft.DotNet.ProjectModel return this; } - private FileFormatException WithLineInfo(JsonValue value) + private FileFormatException WithLineInfo(Exception exception) { - if (value == null) + if (exception is JsonDeserializerException) { - throw new ArgumentNullException(nameof(value)); + WithLineInfo((JsonDeserializerException) exception); } - Line = value.Line; - Column = value.Column; + if (exception is JsonReaderException) + { + WithLineInfo((JsonReaderException) exception); + } return this; } @@ -119,5 +131,45 @@ namespace Microsoft.DotNet.ProjectModel return this; } + + private FileFormatException WithLineInfo(JsonReaderException exception) + { + if (exception == null) + { + throw new ArgumentNullException(nameof(exception)); + } + + Line = exception.LineNumber; + Column = exception.LinePosition; + + return this; + } + + private FileFormatException WithLineInfo(JsonValue value) + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + Line = value.Line; + Column = value.Column; + + return this; + } + + private FileFormatException WithLineInfo(JToken value) + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + var lineInfo = (IJsonLineInfo)value; + Line = lineInfo.LineNumber; + Column = lineInfo.LinePosition; + + return this; + } } } diff --git a/src/Microsoft.DotNet.ProjectModel/Files/NamedResourceReader.cs b/src/Microsoft.DotNet.ProjectModel/Files/NamedResourceReader.cs index dcd87c7eb..6721a9fa6 100644 --- a/src/Microsoft.DotNet.ProjectModel/Files/NamedResourceReader.cs +++ b/src/Microsoft.DotNet.ProjectModel/Files/NamedResourceReader.cs @@ -3,53 +3,53 @@ using System.Collections.Generic; using System.IO; -using Microsoft.Extensions.JsonParser.Sources; +using Newtonsoft.Json.Linq; namespace Microsoft.DotNet.ProjectModel.Files { internal static class NamedResourceReader { - public static IDictionary ReadNamedResources(JsonObject rawProject, string projectFilePath) + public static IDictionary ReadNamedResources(JObject rawProject, string projectFilePath) { - if (!rawProject.Keys.Contains("namedResource")) + JToken namedResourceToken; + if (!rawProject.TryGetValue("namedResource", out namedResourceToken)) { return new Dictionary(); } - var namedResourceToken = rawProject.ValueAsJsonObject("namedResource"); - if (namedResourceToken == null) + if (namedResourceToken.Type != JTokenType.Object) { - throw FileFormatException.Create("Value must be object.", rawProject.Value("namedResource"), projectFilePath); + throw FileFormatException.Create( + "Value must be object.", + rawProject.Value("namedResource"), projectFilePath); } var namedResources = new Dictionary(); - - foreach (var namedResourceKey in namedResourceToken.Keys) + foreach (var namedResource in (JObject)namedResourceToken) { - var resourcePath = namedResourceToken.ValueAsString(namedResourceKey); - if (resourcePath == null) + if (namedResource.Value.Type != JTokenType.String) { - throw FileFormatException.Create("Value must be string.", namedResourceToken.Value(namedResourceKey), projectFilePath); + throw FileFormatException.Create("Value must be string.", namedResource.Key, projectFilePath); } - if (resourcePath.Value.Contains("*")) + var resourcePath = namedResource.Value.ToString(); + if (resourcePath.Contains("*")) { throw FileFormatException.Create("Value cannot contain wildcards.", resourcePath, projectFilePath); } - var resourceFileFullPath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(projectFilePath), resourcePath)); + var resourceFileFullPath = + Path.GetFullPath(Path.Combine(Path.GetDirectoryName(projectFilePath), resourcePath)); - if (namedResources.ContainsKey(namedResourceKey)) + if (namedResources.ContainsKey(namedResource.Key)) { throw FileFormatException.Create( - string.Format("The named resource {0} already exists.", namedResourceKey), + string.Format("The named resource {0} already exists.", namedResource.Key), resourcePath, projectFilePath); } - namedResources.Add( - namedResourceKey, - resourceFileFullPath); + namedResources.Add(namedResource.Key, resourceFileFullPath); } return namedResources; diff --git a/src/Microsoft.DotNet.ProjectModel/Files/PackIncludeEntry.cs b/src/Microsoft.DotNet.ProjectModel/Files/PackIncludeEntry.cs index 49445b86a..f5f4963cb 100644 --- a/src/Microsoft.DotNet.ProjectModel/Files/PackIncludeEntry.cs +++ b/src/Microsoft.DotNet.ProjectModel/Files/PackIncludeEntry.cs @@ -2,7 +2,8 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Linq; -using Microsoft.Extensions.JsonParser.Sources; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; namespace Microsoft.DotNet.ProjectModel.Files { @@ -13,9 +14,14 @@ namespace Microsoft.DotNet.ProjectModel.Files public int Line { get; } public int Column { get; } - internal PackIncludeEntry(string target, JsonValue json) - : this(target, ExtractValues(json), json.Line, json.Column) + internal PackIncludeEntry(string target, JToken json) { + Target = target; + SourceGlobs = ExtractValues(json); + + var lineInfo = (IJsonLineInfo)json; + Line = lineInfo.LineNumber; + Column = lineInfo.LinePosition; } public PackIncludeEntry(string target, string[] sourceGlobs, int line, int column) @@ -26,18 +32,16 @@ namespace Microsoft.DotNet.ProjectModel.Files Column = column; } - private static string[] ExtractValues(JsonValue json) + private static string[] ExtractValues(JToken json) { - var valueAsString = json as JsonString; - if (valueAsString != null) + if (json.Type == JTokenType.String) { - return new string[] { valueAsString.Value }; + return new string[] { json.Value() }; } - var valueAsArray = json as JsonArray; - if(valueAsArray != null) + if(json.Type == JTokenType.Array) { - return valueAsArray.Values.Select(v => v.ToString()).ToArray(); + return json.Select(v => v.ToString()).ToArray(); } return new string[0]; } diff --git a/src/Microsoft.DotNet.ProjectModel/Files/PatternGroup.cs b/src/Microsoft.DotNet.ProjectModel/Files/PatternGroup.cs index 7091a8f02..627966e74 100644 --- a/src/Microsoft.DotNet.ProjectModel/Files/PatternGroup.cs +++ b/src/Microsoft.DotNet.ProjectModel/Files/PatternGroup.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using Microsoft.Extensions.FileSystemGlobbing; -using Microsoft.Extensions.JsonParser.Sources; +using Newtonsoft.Json.Linq; namespace Microsoft.DotNet.ProjectModel.Files { @@ -33,7 +33,7 @@ namespace Microsoft.DotNet.ProjectModel.Files _matcher.AddExcludePatterns(ExcludePatterns); } - internal static PatternGroup Build(JsonObject rawProject, + internal static PatternGroup Build(JObject rawProject, string projectDirectory, string projectFilePath, string name, diff --git a/src/Microsoft.DotNet.ProjectModel/Files/PatternsCollectionHelper.cs b/src/Microsoft.DotNet.ProjectModel/Files/PatternsCollectionHelper.cs index 96e3037b3..c457388ee 100644 --- a/src/Microsoft.DotNet.ProjectModel/Files/PatternsCollectionHelper.cs +++ b/src/Microsoft.DotNet.ProjectModel/Files/PatternsCollectionHelper.cs @@ -1,11 +1,11 @@ // 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 Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.IO; using System.Linq; -using Microsoft.Extensions.JsonParser.Sources; namespace Microsoft.DotNet.ProjectModel.Files { @@ -13,7 +13,7 @@ namespace Microsoft.DotNet.ProjectModel.Files { private static readonly char[] PatternSeparator = new[] { ';' }; - public static IEnumerable GetPatternsCollection(JsonObject rawProject, + public static IEnumerable GetPatternsCollection(JObject rawProject, string projectDirectory, string projectFilePath, string propertyName, @@ -24,29 +24,29 @@ namespace Microsoft.DotNet.ProjectModel.Files try { - if (!rawProject.Keys.Contains(propertyName)) + JToken propertyNameToken; + if (!rawProject.TryGetValue(propertyName, out propertyNameToken)) { return CreateCollection(projectDirectory, propertyName, defaultPatterns, literalPath); } - var valueInString = rawProject.ValueAsString(propertyName); - if (valueInString != null) + if (propertyNameToken.Type == JTokenType.String) { - return CreateCollection(projectDirectory, propertyName, new string[] { valueInString }, literalPath); + return CreateCollection(projectDirectory, propertyName, new string[] { propertyNameToken.Value() }, literalPath); } - var valuesInArray = rawProject.ValueAsStringArray(propertyName); - if (valuesInArray != null) + if (propertyNameToken.Type == JTokenType.Array) { + var valuesInArray = propertyNameToken.Values(); return CreateCollection(projectDirectory, propertyName, valuesInArray.Select(s => s.ToString()), literalPath); } } catch (Exception ex) { - throw FileFormatException.Create(ex, rawProject.Value(propertyName), projectFilePath); + throw FileFormatException.Create(ex, rawProject.Value(propertyName), projectFilePath); } - throw FileFormatException.Create("Value must be either string or array.", rawProject.Value(propertyName), projectFilePath); + throw FileFormatException.Create("Value must be either string or array.", rawProject.Value(propertyName), projectFilePath); } private static IEnumerable CreateCollection(string projectDirectory, string propertyName, IEnumerable patternsStrings, bool literalPath) diff --git a/src/Microsoft.DotNet.ProjectModel/Files/ProjectFilesCollection.cs b/src/Microsoft.DotNet.ProjectModel/Files/ProjectFilesCollection.cs index 931e21cf5..a6ddc1acb 100644 --- a/src/Microsoft.DotNet.ProjectModel/Files/ProjectFilesCollection.cs +++ b/src/Microsoft.DotNet.ProjectModel/Files/ProjectFilesCollection.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading; -using Microsoft.Extensions.JsonParser.Sources; +using Newtonsoft.Json.Linq; namespace Microsoft.DotNet.ProjectModel.Files { @@ -35,10 +35,10 @@ namespace Microsoft.DotNet.ProjectModel.Files private readonly string _projectDirectory; private readonly string _projectFilePath; - private JsonObject _rawProject; + private JObject _rawProject; private bool _initialized; - internal ProjectFilesCollection(JsonObject rawProject, string projectDirectory, string projectFilePath) + internal ProjectFilesCollection(JObject rawProject, string projectDirectory, string projectFilePath) { _projectDirectory = projectDirectory; _projectFilePath = projectFilePath; @@ -79,13 +79,16 @@ namespace Microsoft.DotNet.ProjectModel.Files _namedResources = NamedResourceReader.ReadNamedResources(_rawProject, _projectFilePath); // Files to be packed along with the project - var packIncludeJson = _rawProject.ValueAsJsonObject(PackIncludePropertyName); + var packIncludeJson = _rawProject.Value(PackIncludePropertyName) as JObject; if (packIncludeJson != null) { - _packInclude = packIncludeJson - .Keys - .Select(k => new PackIncludeEntry(k, packIncludeJson.Value(k))) - .ToList(); + var packIncludeEntries = new List(); + foreach (var token in packIncludeJson) + { + packIncludeEntries.Add(new PackIncludeEntry(token.Key, token.Value)); + } + + _packInclude = packIncludeEntries; } else { diff --git a/src/Microsoft.DotNet.ProjectModel/GlobalSettings.cs b/src/Microsoft.DotNet.ProjectModel/GlobalSettings.cs index 2357cf96c..4f144f093 100644 --- a/src/Microsoft.DotNet.ProjectModel/GlobalSettings.cs +++ b/src/Microsoft.DotNet.ProjectModel/GlobalSettings.cs @@ -1,10 +1,12 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.IO; -using Microsoft.Extensions.JsonParser.Sources; +using Newtonsoft.Json; +using System.Linq; namespace Microsoft.DotNet.ProjectModel { @@ -42,27 +44,11 @@ namespace Microsoft.DotNet.ProjectModel globalJsonPath = Path.Combine(path, FileName); } - globalSettings = new GlobalSettings(); - try { using (var fs = File.OpenRead(globalJsonPath)) { - var reader = new StreamReader(fs); - var jobject = JsonDeserializer.Deserialize(reader) as JsonObject; - - if (jobject == null) - { - throw new InvalidOperationException("The JSON file can't be deserialized to a JSON object."); - } - - var projectSearchPaths = jobject.ValueAsStringArray("projects") ?? - jobject.ValueAsStringArray("sources") ?? - new string[] { }; - - globalSettings.ProjectSearchPaths = new List(projectSearchPaths); - globalSettings.PackagesPath = jobject.ValueAsString("packages"); - globalSettings.FilePath = globalJsonPath; + globalSettings = GetGlobalSettings(fs, globalJsonPath); } } catch (Exception ex) @@ -73,6 +59,41 @@ namespace Microsoft.DotNet.ProjectModel return true; } + public static GlobalSettings GetGlobalSettings(Stream fs, string globalJsonPath) + { + var globalSettings = new GlobalSettings(); + + var reader = new StreamReader(fs); + JObject jobject; + try + { + jobject = JObject.Parse(reader.ReadToEnd()); + } + catch (JsonReaderException) + { + throw new InvalidOperationException("The JSON file can't be deserialized to a JSON object."); + } + + IEnumerable projectSearchPaths = Enumerable.Empty(); + JToken projectSearchPathsToken; + if (jobject.TryGetValue("projects", out projectSearchPathsToken) && + projectSearchPathsToken.Type == JTokenType.Array) + { + projectSearchPaths = projectSearchPathsToken.Values(); + } + else if (jobject.TryGetValue("sources", out projectSearchPathsToken) && + projectSearchPathsToken.Type == JTokenType.Array) + { + projectSearchPaths = projectSearchPathsToken.Values(); + } + + globalSettings.ProjectSearchPaths = new List(projectSearchPaths); + globalSettings.PackagesPath = jobject.Value("packages"); + globalSettings.FilePath = globalJsonPath; + + return globalSettings; + } + public static bool HasGlobalFile(string path) { string projectPath = Path.Combine(path, FileName); diff --git a/src/Microsoft.DotNet.ProjectModel/Project.cs b/src/Microsoft.DotNet.ProjectModel/Project.cs index aba323351..2df027c5b 100644 --- a/src/Microsoft.DotNet.ProjectModel/Project.cs +++ b/src/Microsoft.DotNet.ProjectModel/Project.cs @@ -88,6 +88,8 @@ namespace Microsoft.DotNet.ProjectModel public IDictionary> Scripts { get; } = new Dictionary>(StringComparer.OrdinalIgnoreCase); + public string RawRuntimeOptions { get; set; } + public bool IsTestProject => !string.IsNullOrEmpty(TestRunner); public IEnumerable GetTargetFrameworks() diff --git a/src/Microsoft.DotNet.ProjectModel/ProjectReader.cs b/src/Microsoft.DotNet.ProjectModel/ProjectReader.cs index 75e218936..694944884 100644 --- a/src/Microsoft.DotNet.ProjectModel/ProjectReader.cs +++ b/src/Microsoft.DotNet.ProjectModel/ProjectReader.cs @@ -8,7 +8,8 @@ using System.Linq; using Microsoft.DotNet.ProjectModel.Files; using Microsoft.DotNet.ProjectModel.Graph; using Microsoft.DotNet.ProjectModel.Utilities; -using Microsoft.Extensions.JsonParser.Sources; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using NuGet.Frameworks; using NuGet.Versioning; @@ -84,7 +85,8 @@ namespace Microsoft.DotNet.ProjectModel var project = new Project(); var reader = new StreamReader(stream); - var rawProject = JsonDeserializer.Deserialize(reader) as JsonObject; + + var rawProject = JObject.Parse(reader.ReadToEnd()); if (rawProject == null) { throw FileFormatException.Create( @@ -93,10 +95,10 @@ namespace Microsoft.DotNet.ProjectModel } // Meta-data properties - project.Name = rawProject.ValueAsString("name") ?? projectName; + project.Name = rawProject.Value("name") ?? projectName; project.ProjectFilePath = Path.GetFullPath(projectPath); - var version = rawProject.Value("version") as JsonString; + var version = rawProject.Value("version"); if (version == null) { project.Version = new NuGetVersion("1.0.0"); @@ -135,28 +137,29 @@ namespace Microsoft.DotNet.ProjectModel } } - project.Description = rawProject.ValueAsString("description"); - project.Summary = rawProject.ValueAsString("summary"); - project.Copyright = rawProject.ValueAsString("copyright"); - project.Title = rawProject.ValueAsString("title"); - project.EntryPoint = rawProject.ValueAsString("entryPoint"); - project.ProjectUrl = rawProject.ValueAsString("projectUrl"); - project.LicenseUrl = rawProject.ValueAsString("licenseUrl"); - project.IconUrl = rawProject.ValueAsString("iconUrl"); - project.CompilerName = rawProject.ValueAsString("compilerName") ?? "csc"; - project.TestRunner = rawProject.ValueAsString("testRunner"); + project.Description = rawProject.Value("description"); + project.Summary = rawProject.Value("summary"); + project.Copyright = rawProject.Value("copyright"); + project.Title = rawProject.Value("title"); + project.EntryPoint = rawProject.Value("entryPoint"); + project.ProjectUrl = rawProject.Value("projectUrl"); + project.LicenseUrl = rawProject.Value("licenseUrl"); + project.IconUrl = rawProject.Value("iconUrl"); + project.CompilerName = rawProject.Value("compilerName") ?? "csc"; + project.TestRunner = rawProject.Value("testRunner"); - project.Authors = rawProject.ValueAsStringArray("authors") ?? EmptyArray.Value; - project.Owners = rawProject.ValueAsStringArray("owners") ?? EmptyArray.Value; - project.Tags = rawProject.ValueAsStringArray("tags") ?? EmptyArray.Value; + project.Authors = + rawProject.Value("authors")?.Values().ToArray() ?? EmptyArray.Value; + project.Owners = rawProject.Value("owners")?.Values().ToArray() ?? EmptyArray.Value; + project.Tags = rawProject.Value("tags")?.Values().ToArray() ?? EmptyArray.Value; - project.Language = rawProject.ValueAsString("language"); - project.ReleaseNotes = rawProject.ValueAsString("releaseNotes"); + project.Language = rawProject.Value("language"); + project.ReleaseNotes = rawProject.Value("releaseNotes"); - project.RequireLicenseAcceptance = rawProject.ValueAsBoolean("requireLicenseAcceptance", defaultValue: false); + project.RequireLicenseAcceptance = rawProject.Value("requireLicenseAcceptance"); // REVIEW: Move this to the dependencies node? - project.EmbedInteropTypes = rawProject.ValueAsBoolean("embedInteropTypes", defaultValue: false); + project.EmbedInteropTypes = rawProject.Value("embedInteropTypes"); project.Dependencies = new List(); project.Tools = new List(); @@ -164,41 +167,42 @@ namespace Microsoft.DotNet.ProjectModel // Project files project.Files = new ProjectFilesCollection(rawProject, project.ProjectDirectory, project.ProjectFilePath); - var commands = rawProject.Value("commands") as JsonObject; + var commands = rawProject.Value("commands") as JObject; if (commands != null) { - foreach (var key in commands.Keys) + foreach (var command in commands) { - var value = commands.ValueAsString(key); - if (value != null) + var commandValue = command.Value.Type == JTokenType.String ? command.Value.Value() : null; + if (commandValue != null) { - project.Commands[key] = value; + project.Commands[command.Key] = commandValue; } } } - var scripts = rawProject.Value("scripts") as JsonObject; + var scripts = rawProject.Value("scripts") as JObject; if (scripts != null) { - foreach (var key in scripts.Keys) + foreach (var script in scripts) { - var stringValue = scripts.ValueAsString(key); + var stringValue = script.Value.Type == JTokenType.String ? script.Value.Value() : null; if (stringValue != null) { - project.Scripts[key] = new string[] { stringValue }; + project.Scripts[script.Key] = new string[] { stringValue }; continue; } - var arrayValue = scripts.ValueAsStringArray(key); + var arrayValue = + script.Value.Type == JTokenType.Array ? script.Value.Values().ToArray() : null; if (arrayValue != null) { - project.Scripts[key] = arrayValue; + project.Scripts[script.Key] = arrayValue; continue; } throw FileFormatException.Create( string.Format("The value of a script in {0} can only be a string or an array of strings", Project.FileName), - scripts.Value(key), + script.Value, project.ProjectFilePath); } } @@ -219,6 +223,18 @@ namespace Microsoft.DotNet.ProjectModel "tools", isGacOrFrameworkReference: false); + JToken runtimeOptionsToken; + if (rawProject.TryGetValue("runtimeOptions", out runtimeOptionsToken)) + { + var runtimeOptions = runtimeOptionsToken as JObject; + if (runtimeOptions == null) + { + throw FileFormatException.Create("The runtimeOptions must be an object", runtimeOptionsToken); + } + + project.RawRuntimeOptions = runtimeOptions.ToString(); + } + return project; } @@ -242,124 +258,96 @@ namespace Microsoft.DotNet.ProjectModel private static void PopulateDependencies( string projectPath, IList results, - JsonObject settings, + JObject settings, string propertyName, bool isGacOrFrameworkReference) { - var dependencies = settings.ValueAsJsonObject(propertyName); + var dependencies = settings.Value(propertyName) as JObject; if (dependencies != null) { - foreach (var dependencyKey in dependencies.Keys) + foreach (var dependency in dependencies) { - if (string.IsNullOrEmpty(dependencyKey)) + if (string.IsNullOrEmpty(dependency.Key)) { throw FileFormatException.Create( "Unable to resolve dependency ''.", - dependencies.Value(dependencyKey), + dependency.Key, projectPath); } - var dependencyValue = dependencies.Value(dependencyKey); + var dependencyValue = dependency.Value; var dependencyTypeValue = LibraryDependencyType.Default; - JsonString dependencyVersionAsString = null; + string dependencyVersionAsString = null; LibraryType target = isGacOrFrameworkReference ? LibraryType.ReferenceAssembly : LibraryType.Unspecified; - if (dependencyValue is JsonObject) + if (dependencyValue.Type == JTokenType.Object) { // "dependencies" : { "Name" : { "version": "1.0", "type": "build", "target": "project" } } - var dependencyValueAsObject = (JsonObject)dependencyValue; - dependencyVersionAsString = dependencyValueAsObject.ValueAsString("version"); + dependencyVersionAsString = dependencyValue.Value("version"); - var type = dependencyValueAsObject.ValueAsString("type"); + var type = dependencyValue.Value("type"); if (type != null) { - dependencyTypeValue = LibraryDependencyType.Parse(type.Value); + dependencyTypeValue = LibraryDependencyType.Parse(type); } // Read the target if specified if (!isGacOrFrameworkReference) { LibraryType parsedTarget; - var targetStr = dependencyValueAsObject.ValueAsString("target"); + var targetStr = dependencyValue.Value("target"); if (!string.IsNullOrEmpty(targetStr) && LibraryType.TryParse(targetStr, out parsedTarget)) { target = parsedTarget; } } } - else if (dependencyValue is JsonString) + else if (dependencyValue.Type == JTokenType.String) { // "dependencies" : { "Name" : "1.0" } - dependencyVersionAsString = (JsonString)dependencyValue; + dependencyVersionAsString = dependencyValue.Value(); } else { throw FileFormatException.Create( - string.Format("Invalid dependency version: {0}. The format is not recognizable.", dependencyKey), + string.Format( + "Invalid dependency version: {0}. The format is not recognizable.", + dependency.Key), dependencyValue, projectPath); } VersionRange dependencyVersionRange = null; - if (!string.IsNullOrEmpty(dependencyVersionAsString?.Value)) + if (!string.IsNullOrEmpty(dependencyVersionAsString)) { try { - dependencyVersionRange = VersionRange.Parse(dependencyVersionAsString.Value); + dependencyVersionRange = VersionRange.Parse(dependencyVersionAsString); } catch (Exception ex) { - throw FileFormatException.Create( - ex, - dependencyValue, - projectPath); + throw FileFormatException.Create(ex, dependencyValue, projectPath); } } + var lineInfo = (IJsonLineInfo)dependencyValue; results.Add(new LibraryRange( - dependencyKey, + dependency.Key, dependencyVersionRange, target, dependencyTypeValue, projectPath, - dependencies.Value(dependencyKey).Line, - dependencies.Value(dependencyKey).Column)); + lineInfo.LineNumber, + lineInfo.LinePosition)); } } } - private static bool TryGetStringEnumerable(JsonObject parent, string property, out IEnumerable result) - { - var collection = new List(); - var valueInString = parent.ValueAsString(property); - if (valueInString != null) - { - collection.Add(valueInString); - } - else - { - var valueInArray = parent.ValueAsStringArray(property); - if (valueInArray != null) - { - collection.AddRange(valueInArray); - } - else - { - result = null; - return false; - } - } - - result = collection.SelectMany(value => value.Split(new[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries)); - return true; - } - - private void BuildTargetFrameworksAndConfigurations(Project project, JsonObject projectJsonObject, ICollection diagnostics) + private void BuildTargetFrameworksAndConfigurations(Project project, JObject projectJsonObject, ICollection diagnostics) { // Get the shared compilationOptions - project._defaultCompilerOptions = GetCompilationOptions(projectJsonObject, - project) - ?? new CommonCompilerOptions(); + project._defaultCompilerOptions = + GetCompilationOptions(projectJsonObject, project) ?? new CommonCompilerOptions(); project._defaultTargetFrameworkConfiguration = new TargetFrameworkInformation { @@ -391,16 +379,15 @@ namespace Microsoft.DotNet.ProjectModel } */ - var configurationsSection = projectJsonObject.ValueAsJsonObject("configurations"); + var configurationsSection = projectJsonObject.Value("configurations") as JObject; if (configurationsSection != null) { - foreach (var configKey in configurationsSection.Keys) + foreach (var configKey in configurationsSection) { - var compilerOptions = GetCompilationOptions(configurationsSection.ValueAsJsonObject(configKey), - project); + var compilerOptions = GetCompilationOptions(configKey.Value as JObject, project); // Only use this as a configuration if it's not a target framework - project._compilerOptionsByConfiguration[configKey] = compilerOptions; + project._compilerOptionsByConfiguration[configKey.Key] = compilerOptions; } } @@ -416,30 +403,31 @@ namespace Microsoft.DotNet.ProjectModel } */ - var frameworks = projectJsonObject.ValueAsJsonObject("frameworks"); + var frameworks = projectJsonObject.Value("frameworks") as JObject; if (frameworks != null) { - foreach (var frameworkKey in frameworks.Keys) + foreach (var framework in frameworks) { try { - var frameworkToken = frameworks.ValueAsJsonObject(frameworkKey); - var success = BuildTargetFrameworkNode(project, frameworkKey, frameworkToken); + var frameworkToken = framework.Value as JObject; + var success = BuildTargetFrameworkNode(project, framework.Key, frameworkToken); if (!success) { + var lineInfo = (IJsonLineInfo)framework.Value; diagnostics?.Add( new DiagnosticMessage( ErrorCodes.NU1008, - $"\"{frameworkKey}\" is an unsupported framework.", + $"\"{framework.Key}\" is an unsupported framework.", project.ProjectFilePath, DiagnosticMessageSeverity.Error, - frameworkToken.Line, - frameworkToken.Column)); + lineInfo.LineNumber, + lineInfo.LinePosition)); } } catch (Exception ex) { - throw FileFormatException.Create(ex, frameworks.Value(frameworkKey), project.ProjectFilePath); + throw FileFormatException.Create(ex, framework.Value, project.ProjectFilePath); } } } @@ -451,7 +439,7 @@ namespace Microsoft.DotNet.ProjectModel /// The name of the framework /// The Json object represent the settings /// Returns true if it successes. - private bool BuildTargetFrameworkNode(Project project, string frameworkKey, JsonObject frameworkValue) + private bool BuildTargetFrameworkNode(Project project, string frameworkKey, JObject frameworkValue) { // If no compilation options are provided then figure them out from the node var compilerOptions = GetCompilationOptions(frameworkValue, project) ?? @@ -477,13 +465,14 @@ namespace Microsoft.DotNet.ProjectModel compilerOptions.Defines = defines; + var lineInfo = (IJsonLineInfo)frameworkValue; var targetFrameworkInformation = new TargetFrameworkInformation { FrameworkName = frameworkName, Dependencies = new List(), CompilerOptions = compilerOptions, - Line = frameworkValue.Line, - Column = frameworkValue.Column + Line = lineInfo.LineNumber, + Column = lineInfo.LinePosition }; var frameworkDependencies = new List(); @@ -506,12 +495,12 @@ namespace Microsoft.DotNet.ProjectModel frameworkDependencies.AddRange(frameworkAssemblies); targetFrameworkInformation.Dependencies = frameworkDependencies; - targetFrameworkInformation.WrappedProject = frameworkValue.ValueAsString("wrappedProject"); + targetFrameworkInformation.WrappedProject = frameworkValue.Value("wrappedProject"); - var binNode = frameworkValue.ValueAsJsonObject("bin"); + var binNode = frameworkValue.Value("bin") as JObject; if (binNode != null) { - targetFrameworkInformation.AssemblyPath = binNode.ValueAsString("assembly"); + targetFrameworkInformation.AssemblyPath = binNode.Value("assembly"); } project._targetFrameworks[frameworkName] = targetFrameworkInformation; @@ -519,39 +508,37 @@ namespace Microsoft.DotNet.ProjectModel return true; } - private static CommonCompilerOptions GetCompilationOptions(JsonObject rawObject, Project project) + private static CommonCompilerOptions GetCompilationOptions(JObject rawObject, Project project) { - var rawOptions = rawObject.ValueAsJsonObject("compilationOptions"); + var rawOptions = rawObject.Value("compilationOptions") as JObject; if (rawOptions == null) { return null; } - var analyzerOptionsJson = rawOptions.Value("analyzerOptions") as JsonObject; + var analyzerOptionsJson = rawOptions.Value("analyzerOptions") as JObject; if (analyzerOptionsJson != null) { var analyzerOptions = new AnalyzerOptions(); - foreach (var key in analyzerOptionsJson.Keys) + foreach (var analyzerOption in analyzerOptionsJson) { - switch (key) + switch (analyzerOption.Key) { case "languageId": - var languageId = analyzerOptionsJson.ValueAsString(key); - if (languageId == null) + if (analyzerOption.Value.Type != JTokenType.String) { throw FileFormatException.Create( "The analyzer languageId must be a string", - analyzerOptionsJson.Value(key), + analyzerOption.Value.ToString(), project.ProjectFilePath); } - analyzerOptions.LanguageId = languageId; + analyzerOptions.LanguageId = analyzerOption.Value.ToString(); break; default: - ; throw FileFormatException.Create( - $"Unrecognized analyzerOption key: {key}", + $"Unrecognized analyzerOption key: {analyzerOption.Key}", project.ProjectFilePath); } } @@ -561,22 +548,22 @@ namespace Microsoft.DotNet.ProjectModel return new CommonCompilerOptions { - Defines = rawOptions.ValueAsStringArray("define"), - SuppressWarnings = rawOptions.ValueAsStringArray("nowarn"), - AdditionalArguments = rawOptions.ValueAsStringArray("additionalArguments"), - LanguageVersion = rawOptions.ValueAsString("languageVersion"), - AllowUnsafe = rawOptions.ValueAsNullableBoolean("allowUnsafe"), - Platform = rawOptions.ValueAsString("platform"), - WarningsAsErrors = rawOptions.ValueAsNullableBoolean("warningsAsErrors"), - Optimize = rawOptions.ValueAsNullableBoolean("optimize"), - KeyFile = rawOptions.ValueAsString("keyFile"), - DelaySign = rawOptions.ValueAsNullableBoolean("delaySign"), - PublicSign = rawOptions.ValueAsNullableBoolean("publicSign"), - DebugType = rawOptions.ValueAsString("debugType"), - EmitEntryPoint = rawOptions.ValueAsNullableBoolean("emitEntryPoint"), - GenerateXmlDocumentation = rawOptions.ValueAsNullableBoolean("xmlDoc"), - PreserveCompilationContext = rawOptions.ValueAsNullableBoolean("preserveCompilationContext"), - OutputName = rawOptions.ValueAsString("outputName") + Defines = rawOptions.Value("define")?.Values().ToArray(), + SuppressWarnings = rawOptions.Value("nowarn")?.Values().ToArray(), + AdditionalArguments = rawOptions.Value("additionalArguments")?.Values().ToArray(), + LanguageVersion = rawOptions.Value("languageVersion"), + AllowUnsafe = rawOptions.Value("allowUnsafe"), + Platform = rawOptions.Value("platform"), + WarningsAsErrors = rawOptions.Value("warningsAsErrors"), + Optimize = rawOptions.Value("optimize"), + KeyFile = rawOptions.Value("keyFile"), + DelaySign = rawOptions.Value("delaySign"), + PublicSign = rawOptions.Value("publicSign"), + DebugType = rawOptions.Value("debugType"), + EmitEntryPoint = rawOptions.Value("emitEntryPoint"), + GenerateXmlDocumentation = rawOptions.Value("xmlDoc"), + PreserveCompilationContext = rawOptions.Value("preserveCompilationContext"), + OutputName = rawOptions.Value("outputName") }; } diff --git a/src/Microsoft.DotNet.ProjectModel/Properties/AssemblyInfo.cs b/src/Microsoft.DotNet.ProjectModel/Properties/AssemblyInfo.cs index 866d0a566..c058faec1 100644 --- a/src/Microsoft.DotNet.ProjectModel/Properties/AssemblyInfo.cs +++ b/src/Microsoft.DotNet.ProjectModel/Properties/AssemblyInfo.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Reflection; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following @@ -23,3 +24,5 @@ using System.Runtime.InteropServices; // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("303677d5-7312-4c3f-baee-beb1a9bd9fe6")] + +[assembly: InternalsVisibleTo("Microsoft.DotNet.ProjectModel.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100039ac461fa5c82c7dd2557400c4fd4e9dcdf7ac47e3d572548c04cd4673e004916610f4ea5cbf86f2b1ca1cb824f2a7b3976afecfcf4eb72d9a899aa6786effa10c30399e6580ed848231fec48374e41b3acf8811931343fc2f73acf72dae745adbcb7063cc4b50550618383202875223fc75401351cd89c44bf9b50e7fa3796")] diff --git a/src/Microsoft.DotNet.ProjectModel/project.json b/src/Microsoft.DotNet.ProjectModel/project.json index bb4601866..8bcb9120e 100644 --- a/src/Microsoft.DotNet.ProjectModel/project.json +++ b/src/Microsoft.DotNet.ProjectModel/project.json @@ -12,6 +12,8 @@ "type": "build", "version": "1.0.0-rc2-16453" }, + "Newtonsoft.Json": "7.0.1", + "System.Runtime.Serialization.Primitives": "4.1.1-rc2-23931", "Microsoft.Extensions.HashCodeCombiner.Sources": { "type": "build", "version": "1.0.0-rc2-16054" @@ -30,6 +32,9 @@ "System.IO": { "type": "build" } + }, + "dependencies": { + "System.Linq": "4.0.1-rc3-23727" } }, "netstandard1.5": { diff --git a/test/Microsoft.DotNet.ProjectModel.Tests/GivenThatIWantToCreateFileCollectionsFromJson.cs b/test/Microsoft.DotNet.ProjectModel.Tests/GivenThatIWantToCreateFileCollectionsFromJson.cs new file mode 100644 index 000000000..692d8fa4a --- /dev/null +++ b/test/Microsoft.DotNet.ProjectModel.Tests/GivenThatIWantToCreateFileCollectionsFromJson.cs @@ -0,0 +1,92 @@ +// 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.Text; +using Microsoft.DotNet.ProjectModel; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Xunit; +using System.Linq; +using FluentAssertions; + +namespace Microsoft.DotNet.ProjectModel.Tests +{ + public class GivenThatIWantToCreateFileCollectionsFromJson + { + private const string ProjectName = "some project name"; + private readonly string ProjectFilePath = AppContext.BaseDirectory; + + [Fact] + public void PackInclude_is_empty_when_it_is_not_set_in_the_ProjectJson() + { + var json = new JObject(); + var project = GetProject(json); + + project.Files.PackInclude.Should().BeEmpty(); + } + + [Fact] + public void It_sets_PackInclude_when_packInclude_is_set_in_the_ProjectJson() + { + const string somePackTarget = "some pack target"; + const string somePackValue = "some pack value"; + + var json = new JObject(); + var packIncludeJson = new JObject(); + json.Add("packInclude", packIncludeJson); + + packIncludeJson.Add(somePackTarget, somePackValue); + + var project = GetProject(json); + + var packInclude = project.Files.PackInclude.FirstOrDefault(); + + packInclude.Target.Should().Be(somePackTarget); + packInclude.SourceGlobs.Should().Contain(somePackValue); + } + + [Fact] + public void It_parses_namedResources_successfully() + { + const string someString = "some string"; + + var json = new JObject(); + var namedResources= new JObject(); + json.Add("namedResource", namedResources); + + namedResources.Add(someString, "Some/Resource.resx"); + + var project = GetProject(json); + + var key = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(ProjectFilePath), "Some", "Resource.resx")); + project.Files.ResourceFiles[key].Should().Be(someString); + } + + private Project GetProject(JObject json, ProjectReaderSettings settings = null) + { + using (var stream = new MemoryStream()) + { + using (var sw = new StreamWriter(stream, Encoding.UTF8, 256, true)) + { + using (var writer = new JsonTextWriter(sw)) + { + writer.Formatting = Formatting.Indented; + json.WriteTo(writer); + } + + stream.Position = 0; + var projectReader = new ProjectReader(); + return projectReader.ReadProject( + stream, + ProjectName, + ProjectFilePath, + new List(), + settings); + } + } + } + } +} diff --git a/test/Microsoft.DotNet.ProjectModel.Tests/GivenThatIWantToLoadAProjectJsonFile.cs b/test/Microsoft.DotNet.ProjectModel.Tests/GivenThatIWantToLoadAProjectJsonFile.cs new file mode 100644 index 000000000..4d57970fb --- /dev/null +++ b/test/Microsoft.DotNet.ProjectModel.Tests/GivenThatIWantToLoadAProjectJsonFile.cs @@ -0,0 +1,883 @@ +// 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; +using System.Text; +using Xunit; +using Microsoft.DotNet.ProjectModel; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using FluentAssertions; +using NuGet.Versioning; +using System.Linq; +using NuGet.ProjectModel; +using Microsoft.DotNet.ProjectModel.Graph; + +namespace Microsoft.DotNet.ProjectModel.Tests +{ + public class GivenThatIWantToLoadAProjectJsonFile + { + private const string ProjectName = "some project name"; + private const string SomeLanguageVersion = "some language version"; + private const string SomeOutputName = "some output name"; + private const string SomePlatform = "some platform"; + private const string SomeKeyFile = "some key file"; + private const string SomeDebugType = "some debug type"; + private const string DependencyName = "some dependency"; + private const string ToolName = "some tool"; + private const string Version = "1.0.0"; + private readonly string ProjectFilePath = AppContext.BaseDirectory; + + private Project _emptyProject; + private readonly string[] _someDefines = new[] {"DEFINE1", "DEFINE2"}; + private readonly string[] _noWarnings = new[] {"warn1", "warn2"}; + private readonly string[] _someAdditionalArguments = new[] {"additional argument 1", "additional argument 2"}; + private readonly VersionRange _versionRange = VersionRange.Parse(Version); + private readonly JObject _jsonCompilationOptions; + private CommonCompilerOptions _commonCompilerOptions; + + public GivenThatIWantToLoadAProjectJsonFile() + { + var json = new JObject(); + _emptyProject = GetProject(json); + _jsonCompilationOptions = new JObject(); + + _jsonCompilationOptions.Add("define", new JArray(_someDefines)); + _jsonCompilationOptions.Add("nowarn", new JArray(_noWarnings)); + _jsonCompilationOptions.Add("additionalArguments", new JArray(_someAdditionalArguments)); + _jsonCompilationOptions.Add("languageVersion", SomeLanguageVersion); + _jsonCompilationOptions.Add("outputName", SomeOutputName); + _jsonCompilationOptions.Add("platform", SomePlatform); + _jsonCompilationOptions.Add("keyFile", SomeKeyFile); + _jsonCompilationOptions.Add("debugType", SomeDebugType); + _jsonCompilationOptions.Add("allowUnsafe", true); + _jsonCompilationOptions.Add("warningsAsErrors", true); + _jsonCompilationOptions.Add("optimize", true); + _jsonCompilationOptions.Add("delaySign", true); + _jsonCompilationOptions.Add("publicSign", true); + _jsonCompilationOptions.Add("emitEntryPoint", true); + _jsonCompilationOptions.Add("xmlDoc", true); + _jsonCompilationOptions.Add("preserveCompilationContext", true); + + _commonCompilerOptions = new CommonCompilerOptions + { + Defines = _someDefines, + SuppressWarnings = _noWarnings, + AdditionalArguments = _someAdditionalArguments, + LanguageVersion = SomeLanguageVersion, + OutputName = SomeOutputName, + Platform = SomePlatform, + KeyFile = SomeKeyFile, + DebugType = SomeDebugType, + AllowUnsafe = true, + WarningsAsErrors = true, + Optimize = true, + DelaySign = true, + PublicSign = true, + EmitEntryPoint = true, + GenerateXmlDocumentation = true, + PreserveCompilationContext = true + }; + } + + [Fact] + public void It_does_not_throw_when_the_project_json_is_empty() + { + var json = new JObject(); + Action action = () => GetProject(json); + + action.ShouldNotThrow(); + } + + [Fact] + public void It_sets_Name_to_the_passed_ProjectName_if_one_is_not_set_in_the_ProjectJson() + { + _emptyProject.Name.Should().Be(ProjectName); + } + + [Fact] + public void It_sets_Name_to_the_Name_in_the_ProjectJson_when_one_is_set() + { + const string nameInProjectJson = "some name in the project.json"; + var json = new JObject(); + json.Add("name", nameInProjectJson); + var project = GetProject(json); + + project.Name.Should().Be(nameInProjectJson); + } + + [Fact] + public void It_sets_the_project_file_path() + { + _emptyProject.ProjectFilePath.Should().Be(ProjectFilePath); + } + + [Fact] + public void It_sets_the_version_to_one_when_it_is_not_set() + { + _emptyProject.Version.Should().Be(new NuGetVersion("1.0.0")); + } + + [Fact] + public void It_sets_the_version_to_the_one_in_the_ProjectJson_when_one_is_set() + { + var json = new JObject(); + json.Add("version", "1.1"); + var project = GetProject(json); + + project.Version.Should().Be(new NuGetVersion("1.1")); + } + + [Fact] + public void It_sets_AssemblyFileVersion_to_the_ProjectJson_version_when_AssemblyFileVersion_is_not_passed_in_the_settings() + { + var json = new JObject(); + json.Add("version", "1.1"); + var project = GetProject(json); + + project.AssemblyFileVersion.Should().Be(new NuGetVersion("1.1").Version); + } + + [Fact] + public void It_sets_AssemblyFileVersion_Revision_to_the_AssemblyFileVersion_passed_in_the_settings_and_everything_else_to_the_projectJson_Version() + { + const int revision = 1; + var json = new JObject(); + json.Add("version", "1.1"); + var project = GetProject(json, new ProjectReaderSettings { AssemblyFileVersion = revision.ToString() }); + + var version = new NuGetVersion("1.1").Version; + project.AssemblyFileVersion.Should().Be( + new Version(version.Major, version.Minor, version.Build, revision)); + } + + [Fact] + public void It_throws_a_FormatException_when_AssemblyFileVersion_passed_in_the_settings_is_invalid() + { + var json = new JObject(); + json.Add("version", "1.1"); + Action action = () => + GetProject(json, new ProjectReaderSettings { AssemblyFileVersion = "not a revision" }); + + action.ShouldThrow().WithMessage("The assembly file version is invalid: not a revision"); + } + + [Fact] + public void It_leaves_marketing_information_empty_when_it_is_not_set_in_the_ProjectJson() + { + _emptyProject.Description.Should().BeNull(); + _emptyProject.Summary.Should().BeNull(); + _emptyProject.Copyright.Should().BeNull(); + _emptyProject.Title.Should().BeNull(); + _emptyProject.EntryPoint.Should().BeNull(); + _emptyProject.ProjectUrl.Should().BeNull(); + _emptyProject.LicenseUrl.Should().BeNull(); + _emptyProject.IconUrl.Should().BeNull(); + _emptyProject.Authors.Should().BeEmpty(); + _emptyProject.Owners.Should().BeEmpty(); + _emptyProject.Tags.Should().BeEmpty(); + _emptyProject.Language.Should().BeNull(); + _emptyProject.ReleaseNotes.Should().BeNull(); + } + + [Fact] + public void It_sets_the_marketing_information_when_it_is_set_in_the_ProjectJson() + { + const string someDescription = "some description"; + const string someSummary = "some summary"; + const string someCopyright = "some copyright"; + const string someTitle = "some title"; + const string someEntryPoint = "some entry point"; + const string someProjectUrl = "some project url"; + const string someLicenseUrl = "some license url"; + const string someIconUrl = "some icon url"; + const string someLanguage = "some language"; + const string someReleaseNotes = "someReleaseNotes"; + var authors = new [] {"some author", "and another author"}; + var owners = new[] {"some owner", "a second owner"}; + var tags = new[] {"tag1", "tag2"}; + + var json = new JObject(); + json.Add("description", someDescription); + json.Add("summary", someSummary); + json.Add("copyright", someCopyright); + json.Add("title", someTitle); + json.Add("entryPoint", someEntryPoint); + json.Add("projectUrl", someProjectUrl); + json.Add("licenseUrl", someLicenseUrl); + json.Add("iconUrl", someIconUrl); + json.Add("authors", new JArray(authors)); + json.Add("owners", new JArray(owners)); + json.Add("tags", new JArray(tags)); + json.Add("language", someLanguage); + json.Add("releaseNotes", someReleaseNotes); + var project = GetProject(json); + + project.Description.Should().Be(someDescription); + project.Summary.Should().Be(someSummary); + project.Copyright.Should().Be(someCopyright); + project.Title.Should().Be(someTitle); + project.EntryPoint.Should().Be(someEntryPoint); + project.ProjectUrl.Should().Be(someProjectUrl); + project.LicenseUrl.Should().Be(someLicenseUrl); + project.IconUrl.Should().Be(someIconUrl); + project.Authors.Should().Contain(authors); + project.Owners.Should().Contain(owners); + project.Tags.Should().Contain(tags); + project.Language.Should().Be(someLanguage); + project.ReleaseNotes.Should().Be(someReleaseNotes); + } + + [Fact] + public void It_sets_the_compilerName_to_csc_when_one_is_not_set_in_the_ProjectJson() + { + _emptyProject.CompilerName.Should().Be("csc"); + } + + [Fact] + public void It_sets_the_compilerName_to_the_one_in_the_ProjectJson() + { + const string compilerName = "a compiler different from csc"; + var json = new JObject(); + json.Add("compilerName", compilerName); + var project = GetProject(json); + + project.CompilerName.Should().Be(compilerName); + } + + [Fact] + public void It_leaves_testRunner_null_when_one_is_not_set_in_the_ProjectJson() + { + _emptyProject.TestRunner.Should().BeNull(); + } + + [Fact] + public void It_sets_testRunner_to_the_one_in_the_ProjectJson() + { + const string someTestRunner = "some test runner"; + var json = new JObject(); + json.Add("testRunner", someTestRunner); + var project = GetProject(json); + + project.TestRunner.Should().Be(someTestRunner); + } + + [Fact] + public void It_sets_requireLicenseAcceptance_to_false_when_one_is_not_set_in_the_ProjectJson() + { + _emptyProject.RequireLicenseAcceptance.Should().BeFalse(); + } + + [Fact] + public void It_sets_requireLicenseAcceptance_to_true_when_it_is_true_in_the_ProjectJson() + { + var json = new JObject(); + json.Add("requireLicenseAcceptance", true); + var project = GetProject(json); + + project.RequireLicenseAcceptance.Should().BeTrue(); + } + + [Fact] + public void It_sets_requireLicenseAcceptance_to_false_when_it_is_false_in_the_ProjectJson() + { + var json = new JObject(); + json.Add("requireLicenseAcceptance", false); + var project = GetProject(json); + + project.RequireLicenseAcceptance.Should().BeFalse(); + } + + [Fact] + public void It_sets_embedInteropTypes_to_false_when_one_is_not_set_in_the_ProjectJson() + { + _emptyProject.EmbedInteropTypes.Should().BeFalse(); + } + + [Fact] + public void It_sets_embedInteropTypes_to_true_when_it_is_true_in_the_ProjectJson() + { + var json = new JObject(); + json.Add("embedInteropTypes", true); + var project = GetProject(json); + + project.EmbedInteropTypes.Should().BeTrue(); + } + + [Fact] + public void It_sets_embedInteropTypes_to_false_when_it_is_false_in_the_ProjectJson() + { + var json = new JObject(); + json.Add("embedInteropTypes", false); + var project = GetProject(json); + + project.EmbedInteropTypes.Should().BeFalse(); + } + + [Fact] + public void It_does_not_add_commands_when_commands_is_not_set_in_the_ProjectJson() + { + _emptyProject.Commands.Should().BeEmpty(); + } + + [Fact] + public void It_does_not_add_commands_when_commands_is_not_a_JsonObject() + { + var json = new JObject(); + json.Add("commands", true); + var project = GetProject(json); + + project.Commands.Should().BeEmpty(); + } + + [Fact] + public void It_does_not_add_the_commands_when_its_value_is_not_a_string() + { + var json = new JObject(); + var commands = new JObject(); + json.Add("commands", commands); + + commands.Add("commandKey1", "commandValue1"); + commands.Add("commandKey2", true); + + var project = GetProject(json); + + project.Commands.Count.Should().Be(1); + project.Commands.First().Key.Should().Be("commandKey1"); + project.Commands.First().Value.Should().Be("commandValue1"); + } + + [Fact] + public void It_does_not_add_scripts_when_scripts_is_not_set_in_the_ProjectJson() + { + _emptyProject.Scripts.Should().BeEmpty(); + } + + [Fact] + public void It_does_not_add_scripts_when_scripts_is_not_a_JsonObject() + { + var json = new JObject(); + json.Add("scripts", true); + var project = GetProject(json); + + project.Scripts.Should().BeEmpty(); + } + + [Fact] + public void It_adds_the_scripts_when_its_value_is_either_a_string_or_an_array_of_strings() + { + var scriptArrayValues = new [] {"scriptValue2", "scriptValue3"}; + + var json = new JObject(); + var scripts = new JObject(); + json.Add("scripts", scripts); + + scripts.Add("scriptKey1", "scriptValue1"); + scripts.Add("scriptKey3", new JArray(scriptArrayValues)); + + var project = GetProject(json); + + project.Scripts.Count.Should().Be(2); + project.Scripts.First().Key.Should().Be("scriptKey1"); + project.Scripts.First().Value.Should().Contain("scriptValue1"); + project.Scripts["scriptKey3"].Should().Contain(scriptArrayValues); + } + + [Fact] + public void It_throws_when_the_value_of_a_script_is_neither_a_string_nor_array_of_strings() + { + var json = new JObject(); + var scripts = new JObject(); + json.Add("scripts", scripts); + + scripts.Add("scriptKey2", true); + + Action action = () => GetProject(json); + + action.ShouldThrow() + .WithMessage("The value of a script in project.json can only be a string or an array of strings"); + } + + [Fact] + public void It_uses_an_empty_compiler_options_when_one_is_not_set_in_the_ProjectJson() + { + _emptyProject.GetCompilerOptions(null, null).Should().Be(new CommonCompilerOptions + { + OutputName = ProjectName + }); + } + + [Fact] + public void It_sets_analyzerOptions_when_it_is_set_in_the_compilationOptions_in_the_ProjectJson() + { + var json = new JObject(); + var compilationOptions = new JObject(); + json.Add("compilationOptions", compilationOptions); + + var analyzerOptions = new JObject(); + compilationOptions.Add("analyzerOptions", analyzerOptions); + + analyzerOptions.Add("languageId", "C#"); + + var project = GetProject(json); + project.AnalyzerOptions.LanguageId.Should().Be("C#"); + } + + [Fact] + public void It_throws_when_the_analyzerOptions_languageId_is_not_a_string() + { + var json = new JObject(); + var compilationOptions = new JObject(); + json.Add("compilationOptions", compilationOptions); + + var analyzerOptions = new JObject(); + compilationOptions.Add("analyzerOptions", analyzerOptions); + + analyzerOptions.Add("languageId", true); + + Action action = () => GetProject(json); + + action.ShouldThrow().WithMessage("The analyzer languageId must be a string"); + } + + [Fact] + public void It_throws_when_the_analyzerOptions_has_no_languageId() + { + var json = new JObject(); + var compilationOptions = new JObject(); + json.Add("compilationOptions", compilationOptions); + + var analyzerOptions = new JObject(); + compilationOptions.Add("analyzerOptions", analyzerOptions); + + analyzerOptions.Add("differentFromLanguageId", "C#"); + + Action action = () => GetProject(json); + + action.ShouldThrow() + .WithMessage("Unrecognized analyzerOption key: differentFromLanguageId"); + } + + [Fact] + public void It_sets_compilationOptions_when_it_is_set_in_the_compilationOptions_in_the_ProjectJson() + { + var json = new JObject(); + json.Add("compilationOptions", _jsonCompilationOptions); + + var project = GetProject(json); + + project.GetCompilerOptions(null, null).Should().Be(_commonCompilerOptions); + } + + [Fact] + public void It_merges_configuration_sections_set_in_the_ProjectJson() + { + var json = new JObject(); + var configurations = new JObject(); + json.Add("compilationOptions", _jsonCompilationOptions); + json.Add("configurations", configurations); + + _jsonCompilationOptions["allowUnsafe"] = null; + + var someConfiguration = new JObject(); + configurations.Add("some configuration", someConfiguration); + var someConfigurationCompilationOptions = new JObject(); + someConfiguration.Add("compilationOptions", someConfigurationCompilationOptions); + someConfigurationCompilationOptions.Add("allowUnsafe", false); + + var project = GetProject(json); + + _commonCompilerOptions.AllowUnsafe = false; + + project.GetCompilerOptions(null, "some configuration").Should().Be(_commonCompilerOptions); + } + + [Fact] + public void It_does_not_set_rawRuntimeOptions_when_it_is_not_set_in_the_ProjectJson() + { + _emptyProject.RawRuntimeOptions.Should().BeNull(); + } + + [Fact] + public void It_throws_when_runtimeOptions_is_not_a_Json_object() + { + var json = new JObject(); + json.Add("runtimeOptions", "not a json object"); + + Action action = () => GetProject(json); + + action.ShouldThrow().WithMessage("The runtimeOptions must be an object"); + } + + [Fact] + public void It_sets_the_rawRuntimeOptions_serialized_when_it_is_set_in_the_ProjectJson() + { + var configProperties = new JObject(); + configProperties.Add("System.GC.Server", true); + var runtimeOptions = new JObject(); + runtimeOptions.Add("configProperties", configProperties); + var json = new JObject(); + json.Add("runtimeOptions", runtimeOptions); + + var project = GetProject(json); + + project.RawRuntimeOptions.Should().Be(runtimeOptions.ToString()); + } + + [Fact] + public void Dependencies_is_empty_when_no_dependencies_and_no_tools_are_set_in_the_ProjectJson() + { + _emptyProject.Dependencies.Should().BeEmpty(); + } + + [Fact] + public void It_throws_when_the_dependency_has_no_name_set() + { + var dependencies = new JObject(); + dependencies.Add("", "1.0.0"); + var json = new JObject(); + json.Add("dependencies", dependencies); + + Action action = () => GetProject(json); + + action.ShouldThrow().WithMessage("Unable to resolve dependency ''."); + } + + [Fact] + public void It_throws_when_the_dependency_value_is_not_an_object_not_a_string() + { + var dependencies = new JObject(); + dependencies.Add(DependencyName, true); + var json = new JObject(); + json.Add("dependencies", dependencies); + + Action action = () => GetProject(json); + + action.ShouldThrow() + .WithMessage($"Invalid dependency version: {DependencyName}. The format is not recognizable."); + } + + [Fact] + public void It_throws_when_the_dependency_version_is_not_valid_when_set_directly() + { + var dependencies = new JObject(); + dependencies.Add(DependencyName, "some invalid version"); + var json = new JObject(); + json.Add("dependencies", dependencies); + + Action action = () => GetProject(json); + + action.ShouldThrow() + .WithMessage("'some invalid version' is not a valid version string."); + } + + [Fact] + public void It_throws_when_the_dependency_version_is_not_valid_when_set_in_an_object() + { + var dependency = new JObject(); + dependency.Add("version", "some invalid version"); + var dependencies = new JObject(); + dependencies.Add(DependencyName, dependency); + var json = new JObject(); + json.Add("dependencies", dependencies); + + Action action = () => GetProject(json); + + action.ShouldThrow() + .WithMessage("'some invalid version' is not a valid version string."); + } + + [Fact] + public void It_leaves_version_null_when_it_is_set_to_empty_string() + { + var dependencies = new JObject(); + dependencies.Add(DependencyName, string.Empty); + var json = new JObject(); + json.Add("dependencies", dependencies); + + var project = GetProject(json); + + var dependency = project.Dependencies.First(); + + dependency.VersionRange.Should().BeNull(); + } + + [Fact] + public void It_adds_the_dependency_when_the_version_is_set_directly() + { + var dependencies = new JObject(); + dependencies.Add(DependencyName, Version); + var json = new JObject(); + json.Add("dependencies", dependencies); + + var project = GetProject(json); + + var dependency = project.Dependencies.First(); + dependency.Name.Should().Be(DependencyName); + dependency.VersionRange.Should().Be(_versionRange); + dependency.Target.Should().Be(LibraryType.Unspecified); + dependency.Type.Should().Be(LibraryDependencyType.Default); + dependency.SourceFilePath.Should().Be(ProjectFilePath); + dependency.SourceLine.Should().Be(3); + dependency.SourceColumn.Should().Be(31); + } + + [Fact] + public void It_adds_the_dependency_when_the_version_is_set_in_an_object() + { + var dependencyJson = new JObject(); + dependencyJson.Add("version", Version); + var dependencies = new JObject(); + dependencies.Add(DependencyName, dependencyJson); + var json = new JObject(); + json.Add("dependencies", dependencies); + + var project = GetProject(json); + + var dependency = project.Dependencies.First(); + dependency.Name.Should().Be(DependencyName); + dependency.VersionRange.Should().Be(_versionRange); + dependency.Target.Should().Be(LibraryType.Unspecified); + dependency.Type.Should().Be(LibraryDependencyType.Default); + dependency.SourceFilePath.Should().Be(ProjectFilePath); + dependency.SourceLine.Should().Be(3); + dependency.SourceColumn.Should().Be(25); + } + + [Fact] + public void It_sets_the_dependency_type_when_it_is_set_in_the_dependency_in_the_ProjectJson() + { + var dependencyJson = new JObject(); + dependencyJson.Add("type", "build"); + var dependencies = new JObject(); + dependencies.Add(DependencyName, dependencyJson); + var json = new JObject(); + json.Add("dependencies", dependencies); + + var project = GetProject(json); + + var dependency = project.Dependencies.First(); + dependency.Type.Should().Be(LibraryDependencyType.Build); + } + + [Fact] + public void It_leaves_the_dependency_target_Unspecified_when_it_fails_to_parse_the_set_target_in_the_ProjectJson() + { + var dependencyJson = new JObject(); + dependencyJson.Add("target", "not a valid target"); + var dependencies = new JObject(); + dependencies.Add(DependencyName, dependencyJson); + var json = new JObject(); + json.Add("dependencies", dependencies); + + var project = GetProject(json); + + var dependency = project.Dependencies.First(); + dependency.Target.Should().Be(LibraryType.Unspecified); + } + + [Fact] + public void It_sets_the_dependency_target_when_it_is_set_in_the_ProjectJson() + { + var dependencyJson = new JObject(); + dependencyJson.Add("target", "Project"); + var dependencies = new JObject(); + dependencies.Add(DependencyName, dependencyJson); + var json = new JObject(); + json.Add("dependencies", dependencies); + + var project = GetProject(json); + + var dependency = project.Dependencies.First(); + dependency.Target.Should().Be(LibraryType.Project); + } + + [Fact] + public void It_throws_when_the_tool_has_no_name_set() + { + var tools = new JObject(); + tools.Add("", "1.0.0"); + var json = new JObject(); + json.Add("tools", tools); + + Action action = () => GetProject(json); + + action.ShouldThrow().WithMessage("Unable to resolve dependency ''."); + } + + [Fact] + public void It_throws_when_the_tool_value_is_not_an_object_not_a_string() + { + var tools = new JObject(); + tools.Add(ToolName, true); + var json = new JObject(); + json.Add("tools", tools); + + Action action = () => GetProject(json); + + action.ShouldThrow() + .WithMessage($"Invalid dependency version: {ToolName}. The format is not recognizable."); + } + + [Fact] + public void It_throws_when_the_tool_version_is_not_valid_when_set_directly() + { + var tools = new JObject(); + tools.Add(ToolName, "some invalid version"); + var json = new JObject(); + json.Add("tools", tools); + + Action action = () => GetProject(json); + + action.ShouldThrow() + .WithMessage("'some invalid version' is not a valid version string."); + } + + [Fact] + public void It_throws_when_the_tool_version_is_not_valid_when_set_in_an_object() + { + var tool = new JObject(); + tool.Add("version", "some invalid version"); + var tools = new JObject(); + tools.Add(ToolName, tool); + var json = new JObject(); + json.Add("tools", tools); + + Action action = () => GetProject(json); + + action.ShouldThrow() + .WithMessage("'some invalid version' is not a valid version string."); + } + + [Fact] + public void It_leaves_the_tools_version_null_when_it_is_set_to_empty_string() + { + var tools = new JObject(); + tools.Add(ToolName, string.Empty); + var json = new JObject(); + json.Add("tools", tools); + + var project = GetProject(json); + + var tool = project.Tools.First(); + + tool.VersionRange.Should().BeNull(); + } + + [Fact] + public void It_adds_the_tool_when_the_version_is_set_directly() + { + var tools = new JObject(); + tools.Add(ToolName, Version); + var json = new JObject(); + json.Add("tools", tools); + + var project = GetProject(json); + + var tool = project.Tools.First(); + tool.Name.Should().Be(ToolName); + tool.VersionRange.Should().Be(_versionRange); + tool.Target.Should().Be(LibraryType.Unspecified); + tool.Type.Should().Be(LibraryDependencyType.Default); + tool.SourceFilePath.Should().Be(ProjectFilePath); + tool.SourceLine.Should().Be(3); + tool.SourceColumn.Should().Be(25); + } + + [Fact] + public void It_adds_the_tool_when_the_version_is_set_in_an_object() + { + var toolJson = new JObject(); + toolJson.Add("version", Version); + var tools = new JObject(); + tools.Add(ToolName, toolJson); + var json = new JObject(); + json.Add("tools", tools); + + var project = GetProject(json); + + var tool = project.Tools.First(); + tool.Name.Should().Be(ToolName); + tool.VersionRange.Should().Be(_versionRange); + tool.Target.Should().Be(LibraryType.Unspecified); + tool.Type.Should().Be(LibraryDependencyType.Default); + tool.SourceFilePath.Should().Be(ProjectFilePath); + tool.SourceLine.Should().Be(3); + tool.SourceColumn.Should().Be(19); + } + + [Fact] + public void It_sets_the_tool_type_when_it_is_set_in_the_tool_in_the_ProjectJson() + { + var toolJson = new JObject(); + toolJson.Add("type", "build"); + var tools = new JObject(); + tools.Add(ToolName, toolJson); + var json = new JObject(); + json.Add("tools", tools); + + var project = GetProject(json); + + var tool = project.Tools.First(); + tool.Type.Should().Be(LibraryDependencyType.Build); + } + + [Fact] + public void It_leaves_the_tool_target_Unspecified_when_it_fails_to_parse_the_set_target_in_the_ProjectJson() + { + var toolJson = new JObject(); + toolJson.Add("target", "not a valid target"); + var tools = new JObject(); + tools.Add(ToolName, toolJson); + var json = new JObject(); + json.Add("tools", tools); + + var project = GetProject(json); + + var tool = project.Tools.First(); + tool.Target.Should().Be(LibraryType.Unspecified); + } + + [Fact] + public void It_sets_the_tool_target_when_it_is_set_in_the_ProjectJson() + { + var toolJson = new JObject(); + toolJson.Add("target", "Project"); + var tools = new JObject(); + tools.Add(ToolName, toolJson); + var json = new JObject(); + json.Add("tools", tools); + + var project = GetProject(json); + + var tool = project.Tools.First(); + tool.Target.Should().Be(LibraryType.Project); + } + + public Project GetProject(JObject json, ProjectReaderSettings settings = null) + { + using (var stream = new MemoryStream()) + { + using (var sw = new StreamWriter(stream, Encoding.UTF8, 256, true)) + { + using (var writer = new JsonTextWriter(sw)) + { + writer.Formatting = Formatting.Indented; + json.WriteTo(writer); + } + + stream.Position = 0; + var projectReader = new ProjectReader(); + return projectReader.ReadProject( + stream, + ProjectName, + ProjectFilePath, + new List(), + settings); + } + } + } + } +} diff --git a/test/Microsoft.DotNet.ProjectModel.Tests/GivenThatIWantToReadFilePatternsFromJson.cs b/test/Microsoft.DotNet.ProjectModel.Tests/GivenThatIWantToReadFilePatternsFromJson.cs new file mode 100644 index 000000000..2892adfef --- /dev/null +++ b/test/Microsoft.DotNet.ProjectModel.Tests/GivenThatIWantToReadFilePatternsFromJson.cs @@ -0,0 +1,125 @@ +// 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.ProjectModel.Files; +using Newtonsoft.Json.Linq; +using Xunit; +using FluentAssertions; + +namespace Microsoft.DotNet.ProjectModel.Tests +{ + public class GivenThatIWantToReadFilePatternsFromJson + { + private const string SomeProperty = "some property"; + private static readonly string[] SomeDefaultValues = { $"**{Path.DirectorySeparatorChar}*.cs" }; + + [Fact] + public void It_returns_empty_when_there_is_no_property_and_no_default_pattern() + { + var json = new JObject(); + var patternsCollection = PatternsCollectionHelper.GetPatternsCollection( + json, + AppContext.BaseDirectory, + string.Empty, + "some non-existing property"); + + patternsCollection.Should().BeEmpty(); + } + + [Fact] + public void It_uses_the_passed_in_default_collection_when_the_property_is_not_in_the_json() + { + var json = new JObject(); + var patternsCollection = PatternsCollectionHelper.GetPatternsCollection( + json, + AppContext.BaseDirectory, + string.Empty, + "some non-existing property", + SomeDefaultValues); + + patternsCollection.Should().Contain(SomeDefaultValues); + } + + [Fact] + public void It_uses_the_value_in_the_property_when_it_is_a_string() + { + var json = new JObject(); + json.Add(SomeProperty, "*"); + var patternsCollection = PatternsCollectionHelper.GetPatternsCollection( + json, + AppContext.BaseDirectory, + string.Empty, + SomeProperty, + SomeDefaultValues); + + patternsCollection.Should().Contain("*"); + } + + [Fact] + public void It_uses_the_values_in_the_property_when_it_is_a_string_array() + { + var patterns = new[] {"*", $"**{Path.DirectorySeparatorChar}*.fs"}; + var json = new JObject(); + json.Add(SomeProperty, new JArray(patterns)); + var patternsCollection = PatternsCollectionHelper.GetPatternsCollection( + json, + AppContext.BaseDirectory, + string.Empty, + SomeProperty, + SomeDefaultValues); + + patternsCollection.Should().Contain(patterns); + } + + [Fact] + public void It_throws_when_the_property_value_is_neither_a_string_nor_an_array() + { + var json = new JObject(); + json.Add(SomeProperty, new JObject()); + Action action = () => PatternsCollectionHelper.GetPatternsCollection( + json, + AppContext.BaseDirectory, + string.Empty, + SomeProperty, + SomeDefaultValues); + + action.ShouldThrow().WithMessage("Value must be either string or array."); + } + + [Fact] + public void It_throws_when_we_ask_for_a_literal_and_specify_a_pattern() + { + var json = new JObject(); + json.Add(SomeProperty, "*"); + Action action = () => PatternsCollectionHelper.GetPatternsCollection( + json, + AppContext.BaseDirectory, + string.Empty, + SomeProperty, + SomeDefaultValues, + true); + + action.ShouldThrow() + .WithMessage($"The '{SomeProperty}' property cannot contain wildcard characters."); + } + + [Fact] + public void It_throws_when_the_property_value_is_a_rooted_path() + { + var json = new JObject(); + json.Add(SomeProperty, AppContext.BaseDirectory); + Action action = () => PatternsCollectionHelper.GetPatternsCollection( + json, + AppContext.BaseDirectory, + string.Empty, + SomeProperty, + SomeDefaultValues, + true); + + action.ShouldThrow() + .WithMessage($"The '{SomeProperty}' property cannot be a rooted path."); + } + } +} diff --git a/test/Microsoft.DotNet.ProjectModel.Tests/GivenThatIWantToReadGlobalSettingsFromJson.cs b/test/Microsoft.DotNet.ProjectModel.Tests/GivenThatIWantToReadGlobalSettingsFromJson.cs new file mode 100644 index 000000000..b62d42b6b --- /dev/null +++ b/test/Microsoft.DotNet.ProjectModel.Tests/GivenThatIWantToReadGlobalSettingsFromJson.cs @@ -0,0 +1,159 @@ +// 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.Text; +using FluentAssertions; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using NuGet.ProjectModel; +using Xunit; + +namespace Microsoft.DotNet.ProjectModel.Tests +{ + public class GivenThatIWantToReadGlobalSettingsFromJson + { + private const string SomePath = "some path"; + + [Fact] + public void It_throws_if_the_stream_is_not_valid_JSON() + { + Action action = () => + { + using (var stream = new MemoryStream()) + { + using (var sw = new StreamWriter(stream, Encoding.UTF8, 256, true)) + { + using (var writer = new JsonTextWriter(sw)) + { + writer.Formatting = Formatting.Indented; + new JValue("not an object").WriteTo(writer); + } + + stream.Position = 0; + GlobalSettings.GetGlobalSettings(stream, string.Empty); + } + } + }; + + action.ShouldThrow(); + } + + [Fact] + public void It_leaves_the_searchPaths_empty_when_no_project_and_sources_are_set_in_the_GlobalJson() + { + var json = new JObject(); + var globalSettings = GetGlobalSettings(json); + + globalSettings.ProjectSearchPaths.Should().BeEmpty(); + } + + [Fact] + public void It_leaves_the_searchPaths_empty_when_projects_is_not_an_array_in_the_GlobalJson() + { + var json = new JObject(); + json.Add("projects", "not an array"); + var globalSettings = GetGlobalSettings(json); + + globalSettings.ProjectSearchPaths.Should().BeEmpty(); + } + + [Fact] + public void It_leaves_the_searchPaths_empty_when_sources_is_not_an_array_in_the_GlobalJson() + { + var json = new JObject(); + json.Add("sources", "not an array"); + var globalSettings = GetGlobalSettings(json); + + globalSettings.ProjectSearchPaths.Should().BeEmpty(); + } + + [Fact] + public void It_sets_searchPaths_to_projects_when_projects_is_an_array_in_the_GlobalJson() + { + var projectsSearchPaths = new[] {"somepath1", "somepath2"}; + + var json = new JObject(); + json.Add("projects", new JArray(projectsSearchPaths)); + var globalSettings = GetGlobalSettings(json); + + globalSettings.ProjectSearchPaths.Should().Contain(projectsSearchPaths); + } + + [Fact] + public void It_sets_searchPaths_to_sources_when_sources_is_an_array_in_the_GlobalJson() + { + var sourcesSearchPaths = new[] { "somepath1", "somepath2" }; + + var json = new JObject(); + json.Add("sources", new JArray(sourcesSearchPaths)); + var globalSettings = GetGlobalSettings(json); + + globalSettings.ProjectSearchPaths.Should().Contain(sourcesSearchPaths); + } + + [Fact] + public void It_sets_searchPaths_to_projects_when_both_projects_and_sources_are_arrays_in_the_GlobalJson() + { + var projectsSearchPaths = new[] { "somepath1", "somepath2" }; + var sourcesSearchPaths = new[] { "someotherpath1", "someotherpath2" }; + + var json = new JObject(); + json.Add("projects", new JArray(projectsSearchPaths)); + json.Add("sources", new JArray(sourcesSearchPaths)); + var globalSettings = GetGlobalSettings(json); + + globalSettings.ProjectSearchPaths.Should().Contain(projectsSearchPaths); + } + + [Fact] + public void It_leaves_packagesPath_null_when_packages_is_not_set_in_the_GlobalJson() + { + var json = new JObject(); + var globalSettings = GetGlobalSettings(json); + + globalSettings.PackagesPath.Should().BeNull(); + } + + [Fact] + public void It_sets_packagesPath_to_packages_when_it_is_set_in_the_GlobalJson() + { + const string somePackagesPath = "some packages path"; + + var json = new JObject(); + json.Add("packages", somePackagesPath); + var globalSettings = GetGlobalSettings(json); + + globalSettings.PackagesPath.Should().Be(somePackagesPath); + } + + [Fact] + public void It_sets_filePath_to_the_path_passed_in() + { + var json = new JObject(); + var globalSettings = GetGlobalSettings(json); + + globalSettings.FilePath.Should().Be(SomePath); + } + + public GlobalSettings GetGlobalSettings(JObject json) + { + using (var stream = new MemoryStream()) + { + using (var sw = new StreamWriter(stream, Encoding.UTF8, 256, true)) + { + using (var writer = new JsonTextWriter(sw)) + { + writer.Formatting = Formatting.Indented; + json.WriteTo(writer); + } + + stream.Position = 0; + return GlobalSettings.GetGlobalSettings(stream, SomePath); + } + } + } + } +} diff --git a/test/Microsoft.DotNet.ProjectModel.Tests/GivenThatIWantToReadNamedResources.cs b/test/Microsoft.DotNet.ProjectModel.Tests/GivenThatIWantToReadNamedResources.cs new file mode 100644 index 000000000..bb17e1e06 --- /dev/null +++ b/test/Microsoft.DotNet.ProjectModel.Tests/GivenThatIWantToReadNamedResources.cs @@ -0,0 +1,84 @@ +// 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 Newtonsoft.Json.Linq; +using Xunit; +using Microsoft.DotNet.ProjectModel.Files; +using FluentAssertions; +using System.IO; + +namespace Microsoft.DotNet.ProjectModel.Tests +{ + public class GivenThatIWantToReadNamedResources + { + private readonly string ProjectFilePath = AppContext.BaseDirectory; + + [Fact] + public void It_returns_an_empty_dictionary_when_no_namedResource_is_set() + { + var json = new JObject(); + + var namedResources = NamedResourceReader.ReadNamedResources(json, ProjectFilePath); + + namedResources.Should().BeEmpty(); + } + + [Fact] + public void It_throws_when_the_namedResource_is_not_a_Json_object() + { + var json = new JObject(); + json.Add("namedResource", "not an object"); + + Action action = () => NamedResourceReader.ReadNamedResources(json, ProjectFilePath); + + action.ShouldThrow("Value must be an object"); + } + + [Fact] + public void It_throws_when_a_specified_namedResource_value_is_not_a_string() + { + var json = new JObject(); + var namedResources = new JObject(); + json.Add("namedResource", namedResources); + + namedResources.Add("System.Strings", new JObject()); + + Action action = () => NamedResourceReader.ReadNamedResources(json, ProjectFilePath); + + action.ShouldThrow("Value must be string."); + } + + [Fact] + public void It_throws_when_a_specified_namedResource_value_contains_a_wild_card() + { + var json = new JObject(); + var namedResources = new JObject(); + json.Add("namedResource", namedResources); + + namedResources.Add("System.Strings", "*"); + + Action action = () => NamedResourceReader.ReadNamedResources(json, ProjectFilePath); + + action.ShouldThrow("Value cannot contain wildcards."); + } + + [Fact] + public void It_adds_named_resources_and_uses_the_full_path_for_their_values() + { + var json = new JObject(); + var namedResourcesJson = new JObject(); + json.Add("namedResource", namedResourcesJson); + + namedResourcesJson.Add("System.Strings", "System.Strings.resx"); + namedResourcesJson.Add("Another.System.Strings", "Another.System.Strings.resx"); + + var namedResources = NamedResourceReader.ReadNamedResources(json, ProjectFilePath); + + namedResources["System.Strings"].Should().Be( + Path.GetFullPath(Path.Combine(Path.GetDirectoryName(ProjectFilePath), "System.Strings.resx"))); + namedResources["Another.System.Strings"].Should().Be( + Path.GetFullPath(Path.Combine(Path.GetDirectoryName(ProjectFilePath), "Another.System.Strings.resx"))); + } + } +} diff --git a/test/Microsoft.DotNet.ProjectModel.Tests/project.json b/test/Microsoft.DotNet.ProjectModel.Tests/project.json index a7ee6b4d7..e7ff52ef5 100644 --- a/test/Microsoft.DotNet.ProjectModel.Tests/project.json +++ b/test/Microsoft.DotNet.ProjectModel.Tests/project.json @@ -1,5 +1,8 @@ { "version": "1.0.0-*", + "compilationOptions": { + "keyFile": "../../tools/test_key.snk" + }, "dependencies": { "Microsoft.NETCore.App": "1.0.0-rc2-23931", "System.Runtime.Serialization.Primitives": "4.1.1-rc2-23931", diff --git a/test/dotnet-build.Tests/BuildPortableTests.cs b/test/dotnet-build.Tests/BuildPortableTests.cs index 8653978f4..210ea83e3 100644 --- a/test/dotnet-build.Tests/BuildPortableTests.cs +++ b/test/dotnet-build.Tests/BuildPortableTests.cs @@ -2,13 +2,13 @@ using Microsoft.DotNet.Tools.Test.Utilities; using Xunit; using Microsoft.DotNet.TestFramework; +using Newtonsoft.Json.Linq; +using FluentAssertions; namespace Microsoft.DotNet.Tools.Builder.Tests { public class BuildPortableTests : TestBase { - - [Fact] public void BuildingAPortableProjectProducesDepsJsonFile() { @@ -49,6 +49,30 @@ namespace Microsoft.DotNet.Tools.Builder.Tests netstandardappOutput.Should().Exist().And.HaveFile("PortableApp.runtimeconfig.json"); } + [Fact] + public void TheRuntimeOptionsGetsCopiedFromProjectJsonToRuntimeConfigJson() + { + var testInstance = TestAssetsManager.CreateTestInstance("PortableTests") + .WithLockFiles(); + + var netstandardappOutput = Build(testInstance); + + var runtimeConfigJsonPath = Path.Combine(netstandardappOutput.FullName, "PortableApp.runtimeconfig.json"); + + using (var stream = new FileStream(runtimeConfigJsonPath, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + var reader = new StreamReader(stream); + + var rawProject = JObject.Parse(reader.ReadToEnd()); + var runtimeOptions = rawProject["runtimeOptions"]; + + runtimeOptions["somethingString"].Value().Should().Be("anything"); + runtimeOptions["somethingBoolean"].Value().Should().BeTrue(); + runtimeOptions["someArray"].ToObject().Should().Contain("one", "two"); + runtimeOptions["someObject"].Value()["someProperty"].Value().Should().Be("someValue"); + } + } + [Fact] public void BuildingAPortableProjectProducesARuntimeConfigDevJsonFile() { diff --git a/test/dotnet-build.Tests/BuildStandAloneTests.cs b/test/dotnet-build.Tests/BuildStandAloneTests.cs index ce870d949..e1dbfb6c6 100644 --- a/test/dotnet-build.Tests/BuildStandAloneTests.cs +++ b/test/dotnet-build.Tests/BuildStandAloneTests.cs @@ -8,6 +8,8 @@ using Microsoft.Extensions.PlatformAbstractions; using Xunit; using System.Linq; using Microsoft.DotNet.TestFramework; +using Newtonsoft.Json.Linq; +using FluentAssertions; namespace Microsoft.DotNet.Tools.Builder.Tests { @@ -24,6 +26,41 @@ namespace Microsoft.DotNet.Tools.Builder.Tests netstandardappOutput.Should().Exist().And.HaveFile("StandaloneApp.runtimeconfig.dev.json"); } + [Fact] + public void BuildingAStandAloneProjectProducesARuntimeConfigJsonFile() + { + var testInstance = TestAssetsManager.CreateTestInstance("PortableTests") + .WithLockFiles(); + + var netstandardappOutput = Build(testInstance); + + netstandardappOutput.Should().Exist().And.HaveFile("StandaloneApp.runtimeconfig.json"); + } + + [Fact] + public void TheRuntimeOptionsGetsCopiedFromProjectJsonToRuntimeConfigJson() + { + var testInstance = TestAssetsManager.CreateTestInstance("PortableTests") + .WithLockFiles(); + + var netstandardappOutput = Build(testInstance); + + var runtimeConfigJsonPath = Path.Combine(netstandardappOutput.FullName, "StandaloneApp.runtimeconfig.json"); + + using (var stream = new FileStream(runtimeConfigJsonPath, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + var reader = new StreamReader(stream); + + var rawProject = JObject.Parse(reader.ReadToEnd()); + var runtimeOptions = rawProject["runtimeOptions"]; + + runtimeOptions["somethingString"].Value().Should().Be("anything"); + runtimeOptions["somethingBoolean"].Value().Should().BeTrue(); + runtimeOptions["someArray"].ToObject().Should().Contain("one", "two"); + runtimeOptions["someObject"].Value()["someProperty"].Value().Should().Be("someValue"); + } + } + public DirectoryInfo Build(TestInstance testInstance) { var projectPath = Path.Combine(testInstance.TestRoot, "StandaloneApp"); diff --git a/test/dotnet-build.Tests/project.json b/test/dotnet-build.Tests/project.json index ce8b7fb91..b28d89f48 100644 --- a/test/dotnet-build.Tests/project.json +++ b/test/dotnet-build.Tests/project.json @@ -9,6 +9,8 @@ "Microsoft.DotNet.Cli.Utils": { "target": "project" }, + "Newtonsoft.Json": "7.0.1", + "System.Runtime.Serialization.Primitives": "4.1.1-rc2-23929", "xunit": "2.1.0", "dotnet-test-xunit": "1.0.0-dev-128011-22" }, diff --git a/tools/test_key.snk b/tools/test_key.snk new file mode 100644 index 000000000..fe413a523 Binary files /dev/null and b/tools/test_key.snk differ