diff --git a/src/NuGet.Core/NuGet.ProjectModel/DependencyGraphSpec.cs b/src/NuGet.Core/NuGet.ProjectModel/DependencyGraphSpec.cs index 67db6eff2b0..9ed7a32b54e 100644 --- a/src/NuGet.Core/NuGet.ProjectModel/DependencyGraphSpec.cs +++ b/src/NuGet.Core/NuGet.ProjectModel/DependencyGraphSpec.cs @@ -7,7 +7,6 @@ using System.IO; using System.Linq; using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using NuGet.Common; using NuGet.Packaging; @@ -253,7 +252,9 @@ public static DependencyGraphSpec Load(string path) case "projects": jsonReader.ReadObject(projectsPropertyName => { +#pragma warning disable CS0612 // Type or member is obsolete PackageSpec packageSpec = JsonPackageSpecReader.GetPackageSpec(jsonReader, path); +#pragma warning restore CS0612 // Type or member is obsolete dgspec._projects.Add(projectsPropertyName, packageSpec); }); diff --git a/src/NuGet.Core/NuGet.ProjectModel/FileFormatException.cs b/src/NuGet.Core/NuGet.ProjectModel/FileFormatException.cs index c2992999a27..7998fcb790b 100644 --- a/src/NuGet.Core/NuGet.ProjectModel/FileFormatException.cs +++ b/src/NuGet.Core/NuGet.ProjectModel/FileFormatException.cs @@ -10,6 +10,8 @@ namespace NuGet.ProjectModel { public class FileFormatException : Exception { + static internal string SurfaceMessage = "SurfaceMessage"; + public FileFormatException(string message) : base(message) { @@ -47,6 +49,14 @@ private FileFormatException WithLineInfo(int line, int column) return this; } + private FileFormatException WithLineInfo(long? line, long? column) + { + Line = unchecked((int)line.Value); + Column = unchecked((int)column); + + return this; + } + private FileFormatException WithLineInfo(IJsonLineInfo lineInfo) { Line = lineInfo.LineNumber; @@ -101,32 +111,50 @@ internal static FileFormatException Create(string message, int line, int column, return ex.WithFilePath(path).WithLineInfo(line, column); } - internal static FileFormatException Create(Exception exception, string path) + internal static FileFormatException Create(JsonReaderException exception, string path) { - var jex = exception as JsonReaderException; + string message; + message = string.Format(CultureInfo.CurrentCulture, + Strings.Log_ErrorReadingProjectJsonWithLocation, + path, exception.LineNumber, + exception.LinePosition, + exception.Message); + + return new FileFormatException(message, exception) + .WithFilePath(path) + .WithLineInfo(exception); + } + internal static FileFormatException Create(System.Text.Json.JsonException exception, string path) + { string message; - if (jex == null) + if (exception.Data.Contains(SurfaceMessage)) + { + message = exception.Message; + } + else if (exception.BytePositionInLine is not null && exception.LineNumber is not null) { message = string.Format(CultureInfo.CurrentCulture, - Strings.Log_ErrorReadingProjectJson, - path, + Strings.Log_ErrorReadingProjectJsonWithLocation, + path, exception.LineNumber, + exception.BytePositionInLine, exception.Message); - - return new FileFormatException(message, exception).WithFilePath(path); } else { message = string.Format(CultureInfo.CurrentCulture, - Strings.Log_ErrorReadingProjectJsonWithLocation, - path, jex.LineNumber, - jex.LinePosition, + Strings.Log_ErrorReadingProjectJson, + path, exception.Message); - - return new FileFormatException(message, exception) - .WithFilePath(path) - .WithLineInfo(jex); } + var fileFormatException = new FileFormatException(message, exception); + fileFormatException.WithFilePath(path); + if (exception.BytePositionInLine is not null && exception.LineNumber is not null) + { + fileFormatException.WithLineInfo(exception.LineNumber, exception.BytePositionInLine); + } + + return fileFormatException; } } } diff --git a/src/NuGet.Core/NuGet.ProjectModel/JsonPackageSpecReader.cs b/src/NuGet.Core/NuGet.ProjectModel/JsonPackageSpecReader.cs index 0a97ed258c0..122803d8bf5 100644 --- a/src/NuGet.Core/NuGet.ProjectModel/JsonPackageSpecReader.cs +++ b/src/NuGet.Core/NuGet.ProjectModel/JsonPackageSpecReader.cs @@ -2,28 +2,16 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Generic; -using System.Globalization; using System.IO; -using System.Linq; using System.Text; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NuGet.Common; -using NuGet.Configuration; -using NuGet.Frameworks; -using NuGet.LibraryModel; -using NuGet.Packaging.Core; -using NuGet.RuntimeModel; -using NuGet.Versioning; namespace NuGet.ProjectModel { public static class JsonPackageSpecReader { - private static readonly char[] DelimitedStringSeparators = { ' ', ',' }; - private static readonly char[] VersionSeparators = new[] { ';' }; - public static readonly string RestoreOptions = "restore"; public static readonly string RestoreSettings = "restoreSettings"; public static readonly string HideWarningsAndErrors = "hideWarningsAndErrors"; @@ -49,1757 +37,46 @@ public static PackageSpec GetPackageSpec(string json, string name, string packag } } - [Obsolete("This method is obsolete and will be removed in a future release.")] - public static PackageSpec GetPackageSpec(JObject json) - { - return GetPackageSpec(json, name: null, packageSpecPath: null, snapshotValue: null); - } - public static PackageSpec GetPackageSpec(Stream stream, string name, string packageSpecPath, string snapshotValue) { - using (var streamReader = new StreamReader(stream)) - using (var jsonReader = new JsonTextReader(streamReader)) - { - return GetPackageSpec(jsonReader, name, packageSpecPath, snapshotValue); - } - } - - [Obsolete("This method is obsolete and will be removed in a future release.")] - public static PackageSpec GetPackageSpec(JObject rawPackageSpec, string name, string packageSpecPath, string snapshotValue) - { - using (var stringReader = new StringReader(rawPackageSpec.ToString())) - using (var jsonReader = new JsonTextReader(stringReader)) + var useNj = EnvironmentVariableWrapper.Instance.GetEnvironmentVariable("NUGET_EXPERIMENTAL_USE_NJ_FOR_FILE_PARSING"); + if (string.IsNullOrEmpty(useNj) || useNj.Equals("false", StringComparison.OrdinalIgnoreCase)) { - return GetPackageSpec(jsonReader, name, packageSpecPath, snapshotValue); + return Utf8JsonStreamPackageSpecReader.GetPackageSpec(stream, name, packageSpecPath, snapshotValue); } - } - - internal static PackageSpec GetPackageSpec(JsonTextReader jsonReader, string packageSpecPath) - { - return GetPackageSpec(jsonReader, name: null, packageSpecPath, snapshotValue: null); - } - - private static PackageSpec GetPackageSpec(JsonTextReader jsonReader, string name, string packageSpecPath, string snapshotValue) - { - var packageSpec = new PackageSpec(); - - List compatibilityProfiles = null; - List runtimeDescriptions = null; - var wasPackOptionsSet = false; - var isMappingsNull = false; - - string filePath = name == null ? null : Path.GetFullPath(packageSpecPath); - - jsonReader.ReadObject(propertyName => + else { - if (string.IsNullOrWhiteSpace(propertyName)) - { - return; - } - - switch (propertyName) + using (var textReader = new StreamReader(stream)) + using (var jsonReader = new JsonTextReader(textReader)) { #pragma warning disable CS0612 // Type or member is obsolete - case "authors": - packageSpec.Authors = ReadStringArray(jsonReader) ?? Array.Empty(); - break; - - case "buildOptions": - ReadBuildOptions(jsonReader, packageSpec); - break; - - case "contentFiles": - List contentFiles = jsonReader.ReadStringArrayAsList(); - - if (contentFiles != null) - { - packageSpec.ContentFiles = contentFiles; - } - break; - - case "copyright": - packageSpec.Copyright = jsonReader.ReadNextTokenAsString(); - break; -#pragma warning restore CS0612 // Type or member is obsolete - - case "dependencies": - ReadDependencies( - jsonReader, - packageSpec.Dependencies, - filePath, - isGacOrFrameworkReference: false); - break; - -#pragma warning disable CS0612 // Type or member is obsolete - case "description": - packageSpec.Description = jsonReader.ReadNextTokenAsString(); - break; -#pragma warning restore CS0612 // Type or member is obsolete - - case "frameworks": - ReadFrameworks(jsonReader, packageSpec); - break; - -#pragma warning disable CS0612 // Type or member is obsolete - case "language": - packageSpec.Language = jsonReader.ReadNextTokenAsString(); - break; - - case "packInclude": - ReadPackInclude(jsonReader, packageSpec); - break; - - case "packOptions": - ReadPackOptions(jsonReader, packageSpec, ref isMappingsNull); - wasPackOptionsSet = true; - break; -#pragma warning restore CS0612 // Type or member is obsolete - - case "restore": - ReadMSBuildMetadata(jsonReader, packageSpec); - break; - - case "runtimes": - runtimeDescriptions = ReadRuntimes(jsonReader); - break; - -#pragma warning disable CS0612 // Type or member is obsolete - case "scripts": - ReadScripts(jsonReader, packageSpec); - break; -#pragma warning restore CS0612 // Type or member is obsolete - - case "supports": - compatibilityProfiles = ReadSupports(jsonReader); - break; - - case "title": - packageSpec.Title = jsonReader.ReadNextTokenAsString(); - break; - - case "version": - string version = jsonReader.ReadAsString(); - - if (version != null) - { - try - { -#pragma warning disable CS0612 // Type or member is obsolete - packageSpec.HasVersionSnapshot = PackageSpecUtility.IsSnapshotVersion(version); -#pragma warning restore CS0612 // Type or member is obsolete - packageSpec.Version = PackageSpecUtility.SpecifySnapshot(version, snapshotValue); - } - catch (Exception ex) - { - throw FileFormatException.Create(ex, version, packageSpec.FilePath); - } - } - break; - } - }); - - packageSpec.Name = name; - packageSpec.FilePath = name == null ? null : Path.GetFullPath(packageSpecPath); - -#pragma warning disable CS0612 // Type or member is obsolete - if (!wasPackOptionsSet) - { - packageSpec.Owners = Array.Empty(); - packageSpec.PackOptions = new PackOptions() - { - PackageType = Array.Empty() - }; - packageSpec.Tags = Array.Empty(); - } - - if (isMappingsNull) - { - packageSpec.PackOptions.Mappings = null; - } + return GetPackageSpec(jsonReader, packageSpecPath); #pragma warning restore CS0612 // Type or member is obsolete - - packageSpec.RuntimeGraph = new RuntimeGraph( - runtimeDescriptions ?? Enumerable.Empty(), - compatibilityProfiles ?? Enumerable.Empty()); - - if (packageSpec.Name == null) - { - packageSpec.Name = packageSpec.RestoreMetadata?.ProjectName; - } - - // Use the project.json path if one is set, otherwise use the project path - if (packageSpec.FilePath == null) - { - packageSpec.FilePath = packageSpec.RestoreMetadata?.ProjectJsonPath - ?? packageSpec.RestoreMetadata?.ProjectPath; - } - - return packageSpec; - } - - private static PackageType CreatePackageType(JsonTextReader jsonReader) - { - var name = (string)jsonReader.Value; - - return new PackageType(name, Packaging.Core.PackageType.EmptyVersion); - } - - [Obsolete] - private static void ReadBuildOptions(JsonTextReader jsonReader, PackageSpec packageSpec) - { - packageSpec.BuildOptions = new BuildOptions(); - - jsonReader.ReadObject(buildOptionsPropertyName => - { - if (buildOptionsPropertyName == "outputName") - { - packageSpec.BuildOptions.OutputName = jsonReader.ReadNextTokenAsString(); - } - }); - } - - private static void ReadCentralPackageVersions( - JsonTextReader jsonReader, - IDictionary centralPackageVersions, - string filePath) - { - jsonReader.ReadObject(propertyName => - { - int line = jsonReader.LineNumber; - int column = jsonReader.LinePosition; - - if (string.IsNullOrEmpty(propertyName)) - { - throw FileFormatException.Create( - "Unable to resolve central version ''.", - line, - column, - filePath); - } - - string version = jsonReader.ReadNextTokenAsString(); - - if (string.IsNullOrEmpty(version)) - { - throw FileFormatException.Create( - "The version cannot be null or empty.", - line, - column, - filePath); - } - - centralPackageVersions[propertyName] = new CentralPackageVersion(propertyName, VersionRange.Parse(version)); - }); - } - - private static CompatibilityProfile ReadCompatibilityProfile(JsonTextReader jsonReader, string profileName) - { - List sets = null; - - jsonReader.ReadObject(propertyName => - { - sets = sets ?? new List(); - - IEnumerable profiles = ReadCompatibilitySets(jsonReader, propertyName); - - sets.AddRange(profiles); - }); - - return new CompatibilityProfile(profileName, sets ?? Enumerable.Empty()); - } - - private static IEnumerable ReadCompatibilitySets(JsonTextReader jsonReader, string compatibilitySetName) - { - NuGetFramework framework = NuGetFramework.Parse(compatibilitySetName); - - IReadOnlyList values = jsonReader.ReadStringOrArrayOfStringsAsReadOnlyList() ?? Array.Empty(); - - foreach (string value in values) - { - yield return new FrameworkRuntimePair(framework, value); - } - } - - internal static void ReadDependencies( - JsonTextReader jsonReader, - IList results, - string packageSpecPath, - bool isGacOrFrameworkReference) - { - jsonReader.ReadObject(propertyName => - { - if (string.IsNullOrEmpty(propertyName)) - { - // Advance the reader's position to be able to report the line and column for the property value. - jsonReader.ReadNextToken(); - - throw FileFormatException.Create( - "Unable to resolve dependency ''.", - jsonReader.LineNumber, - jsonReader.LinePosition, - packageSpecPath); - } - - // Support - // "dependencies" : { - // "Name" : "1.0" - // } - - if (jsonReader.ReadNextToken()) - { - int dependencyValueLine = jsonReader.LineNumber; - int dependencyValueColumn = jsonReader.LinePosition; - var versionLine = 0; - var versionColumn = 0; - - var dependencyIncludeFlagsValue = LibraryIncludeFlags.All; - var dependencyExcludeFlagsValue = LibraryIncludeFlags.None; - var suppressParentFlagsValue = LibraryIncludeFlagUtils.DefaultSuppressParent; - List noWarn = null; - - // This method handles both the dependencies and framework assembly sections. - // Framework references should be limited to references. - // Dependencies should allow everything but framework references. - LibraryDependencyTarget targetFlagsValue = isGacOrFrameworkReference - ? LibraryDependencyTarget.Reference - : LibraryDependencyTarget.All & ~LibraryDependencyTarget.Reference; - - var autoReferenced = false; - var generatePathProperty = false; - var versionCentrallyManaged = false; - string aliases = null; - string dependencyVersionValue = null; - VersionRange versionOverride = null; - - if (jsonReader.TokenType == JsonToken.String) - { - dependencyVersionValue = (string)jsonReader.Value; - } - else if (jsonReader.TokenType == JsonToken.StartObject) - { - jsonReader.ReadProperties(dependenciesPropertyName => - { - IEnumerable values = null; - - switch (dependenciesPropertyName) - { - case "autoReferenced": - autoReferenced = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpecPath); - break; - - case "exclude": - values = jsonReader.ReadDelimitedString(); - dependencyExcludeFlagsValue = LibraryIncludeFlagUtils.GetFlags(values); - break; - - case "generatePathProperty": - generatePathProperty = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpecPath); - break; - - case "include": - values = jsonReader.ReadDelimitedString(); - dependencyIncludeFlagsValue = LibraryIncludeFlagUtils.GetFlags(values); - break; - - case "noWarn": - noWarn = ReadNuGetLogCodesList(jsonReader); - break; - - case "suppressParent": - values = jsonReader.ReadDelimitedString(); - suppressParentFlagsValue = LibraryIncludeFlagUtils.GetFlags(values); - break; - - case "target": - targetFlagsValue = ReadTarget(jsonReader, packageSpecPath, targetFlagsValue); - break; - - case "version": - if (jsonReader.ReadNextToken()) - { - versionLine = jsonReader.LineNumber; - versionColumn = jsonReader.LinePosition; - - dependencyVersionValue = (string)jsonReader.Value; - } - break; - case "versionOverride": - if (jsonReader.ReadNextToken()) - { - try - { - versionOverride = VersionRange.Parse((string)jsonReader.Value); - } - catch (Exception ex) - { - throw FileFormatException.Create( - ex, - jsonReader.LineNumber, - jsonReader.LinePosition, - packageSpecPath); - } - } - break; - case "versionCentrallyManaged": - versionCentrallyManaged = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpecPath); - break; - - case "aliases": - aliases = jsonReader.ReadAsString(); - break; - } - }); - } - - VersionRange dependencyVersionRange = null; - - if (!string.IsNullOrEmpty(dependencyVersionValue)) - { - try - { - dependencyVersionRange = VersionRange.Parse(dependencyVersionValue); - } - catch (Exception ex) - { - throw FileFormatException.Create( - ex, - versionLine, - versionColumn, - packageSpecPath); - } - } - - // Projects and References may have empty version ranges, Packages may not - if (dependencyVersionRange == null) - { - if ((targetFlagsValue & LibraryDependencyTarget.Package) == LibraryDependencyTarget.Package) - { - throw FileFormatException.Create( - new ArgumentException(Strings.MissingVersionOnDependency), - dependencyValueLine, - dependencyValueColumn, - packageSpecPath); - } - else - { - // Projects and references with no version property allow all versions - dependencyVersionRange = VersionRange.All; - } - } - - // the dependency flags are: Include flags - Exclude flags - var includeFlags = dependencyIncludeFlagsValue & ~dependencyExcludeFlagsValue; - var libraryDependency = new LibraryDependency() - { - LibraryRange = new LibraryRange() - { - Name = propertyName, - TypeConstraint = targetFlagsValue, - VersionRange = dependencyVersionRange - }, - IncludeType = includeFlags, - SuppressParent = suppressParentFlagsValue, - AutoReferenced = autoReferenced, - GeneratePathProperty = generatePathProperty, - VersionCentrallyManaged = versionCentrallyManaged, - Aliases = aliases, - // The ReferenceType is not persisted to the assets file - // Default to LibraryDependencyReferenceType.Direct on Read - ReferenceType = LibraryDependencyReferenceType.Direct, - VersionOverride = versionOverride - }; - - if (noWarn != null) - { - libraryDependency.NoWarn = noWarn; - } - - results.Add(libraryDependency); - } - }); - } - - internal static void ReadCentralTransitiveDependencyGroup( - JsonTextReader jsonReader, - IList results, - string packageSpecPath) - { - jsonReader.ReadObject(propertyName => - { - if (string.IsNullOrEmpty(propertyName)) - { - // Advance the reader's position to be able to report the line and column for the property value. - jsonReader.ReadNextToken(); - - throw FileFormatException.Create( - "Unable to resolve dependency ''.", - jsonReader.LineNumber, - jsonReader.LinePosition, - packageSpecPath); - } - - if (jsonReader.ReadNextToken()) - { - int dependencyValueLine = jsonReader.LineNumber; - int dependencyValueColumn = jsonReader.LinePosition; - var versionLine = 0; - var versionColumn = 0; - - var dependencyIncludeFlagsValue = LibraryIncludeFlags.All; - var dependencyExcludeFlagsValue = LibraryIncludeFlags.None; - var suppressParentFlagsValue = LibraryIncludeFlagUtils.DefaultSuppressParent; - string dependencyVersionValue = null; - - if (jsonReader.TokenType == JsonToken.String) - { - dependencyVersionValue = (string)jsonReader.Value; - } - else if (jsonReader.TokenType == JsonToken.StartObject) - { - jsonReader.ReadProperties(dependenciesPropertyName => - { - IEnumerable values = null; - - switch (dependenciesPropertyName) - { - case "exclude": - values = jsonReader.ReadDelimitedString(); - dependencyExcludeFlagsValue = LibraryIncludeFlagUtils.GetFlags(values); - break; - - case "include": - values = jsonReader.ReadDelimitedString(); - dependencyIncludeFlagsValue = LibraryIncludeFlagUtils.GetFlags(values); - break; - - case "suppressParent": - values = jsonReader.ReadDelimitedString(); - suppressParentFlagsValue = LibraryIncludeFlagUtils.GetFlags(values); - break; - - case "version": - if (jsonReader.ReadNextToken()) - { - versionLine = jsonReader.LineNumber; - versionColumn = jsonReader.LinePosition; - dependencyVersionValue = (string)jsonReader.Value; - } - break; - - default: - break; - } - }); - } - - VersionRange dependencyVersionRange = null; - - if (!string.IsNullOrEmpty(dependencyVersionValue)) - { - try - { - dependencyVersionRange = VersionRange.Parse(dependencyVersionValue); - } - catch (Exception ex) - { - throw FileFormatException.Create( - ex, - versionLine, - versionColumn, - packageSpecPath); - } - } - - if (dependencyVersionRange == null) - { - throw FileFormatException.Create( - new ArgumentException(Strings.MissingVersionOnDependency), - dependencyValueLine, - dependencyValueColumn, - packageSpecPath); - } - - // the dependency flags are: Include flags - Exclude flags - var includeFlags = dependencyIncludeFlagsValue & ~dependencyExcludeFlagsValue; - var libraryDependency = new LibraryDependency() - { - LibraryRange = new LibraryRange() - { - Name = propertyName, - TypeConstraint = LibraryDependencyTarget.Package, - VersionRange = dependencyVersionRange - }, - - IncludeType = includeFlags, - SuppressParent = suppressParentFlagsValue, - VersionCentrallyManaged = true, - ReferenceType = LibraryDependencyReferenceType.Transitive - }; - - results.Add(libraryDependency); } - }); - } - - private static void ReadDownloadDependencies( - JsonTextReader jsonReader, - IList downloadDependencies, - string packageSpecPath) - { - var seenIds = new HashSet(); - - if (jsonReader.ReadNextToken() && jsonReader.TokenType == JsonToken.StartArray) - { - do - { - string name = null; - string versionValue = null; - var isNameDefined = false; - var isVersionDefined = false; - int line = jsonReader.LineNumber; - int column = jsonReader.LinePosition; - int versionLine = 0; - int versionColumn = 0; - - jsonReader.ReadObject(propertyName => - { - switch (propertyName) - { - case "name": - isNameDefined = true; - name = jsonReader.ReadNextTokenAsString(); - break; - - case "version": - isVersionDefined = true; - versionValue = jsonReader.ReadNextTokenAsString(); - versionLine = jsonReader.LineNumber; - versionColumn = jsonReader.LinePosition; - break; - } - }, out line, out column); - - if (jsonReader.TokenType == JsonToken.EndArray) - { - break; - } - - if (!isNameDefined) - { - throw FileFormatException.Create( - "Unable to resolve downloadDependency ''.", - line, - column, - packageSpecPath); - } - - if (!seenIds.Add(name)) - { - // package ID already seen, only use first definition. - continue; - } - - if (string.IsNullOrEmpty(versionValue)) - { - throw FileFormatException.Create( - "The version cannot be null or empty", - isVersionDefined ? versionLine : line, - isVersionDefined ? versionColumn : column, - packageSpecPath); - } - - string[] versions = versionValue.Split(VersionSeparators, StringSplitOptions.RemoveEmptyEntries); - - foreach (string singleVersionValue in versions) - { - try - { - VersionRange version = VersionRange.Parse(singleVersionValue); - - downloadDependencies.Add(new DownloadDependency(name, version)); - } - catch (Exception ex) - { - throw FileFormatException.Create( - ex, - isVersionDefined ? versionLine : line, - isVersionDefined ? versionColumn : column, - packageSpecPath); - } - } - } while (jsonReader.TokenType == JsonToken.EndObject); } - } - - private static IReadOnlyList ReadEnumerableOfString(JsonTextReader jsonReader) - { - string value = jsonReader.ReadNextTokenAsString(); - - return value.Split(DelimitedStringSeparators, StringSplitOptions.RemoveEmptyEntries); - } - - private static void ReadFrameworkReferences( - JsonTextReader jsonReader, - ISet frameworkReferences, - string packageSpecPath) - { - jsonReader.ReadObject(frameworkName => - { - if (string.IsNullOrEmpty(frameworkName)) - { - // Advance the reader's position to be able to report the line and column for the property value. - jsonReader.ReadNextToken(); - - throw FileFormatException.Create( - "Unable to resolve frameworkReference.", - jsonReader.LineNumber, - jsonReader.LinePosition, - packageSpecPath); - } - var privateAssets = FrameworkDependencyFlagsUtils.Default; - - jsonReader.ReadObject(propertyName => - { - if (propertyName == "privateAssets") - { - IEnumerable strings = ReadEnumerableOfString(jsonReader); - - privateAssets = FrameworkDependencyFlagsUtils.GetFlags(strings); - } - }); - - frameworkReferences.Add(new FrameworkDependency(frameworkName, privateAssets)); - }); - } - - private static void ReadFrameworks(JsonTextReader jsonReader, PackageSpec packageSpec) - { - jsonReader.ReadObject(_ => - { - var frameworkLine = 0; - var frameworkColumn = 0; - - try - { - ReadTargetFrameworks(packageSpec, jsonReader, out frameworkLine, out frameworkColumn); - } - catch (Exception ex) - { - throw FileFormatException.Create(ex, frameworkLine, frameworkColumn, packageSpec.FilePath); - } - }); } - private static void ReadImports(PackageSpec packageSpec, JsonTextReader jsonReader, TargetFrameworkInformation targetFrameworkInformation) + [Obsolete("This method is obsolete and will be removed in a future release.")] + public static PackageSpec GetPackageSpec(JObject json) { - int lineNumber = jsonReader.LineNumber; - int linePosition = jsonReader.LinePosition; - - IReadOnlyList imports = jsonReader.ReadStringOrArrayOfStringsAsReadOnlyList(); - - if (imports != null && imports.Count > 0) - { - foreach (string import in imports.Where(element => !string.IsNullOrEmpty(element))) - { - NuGetFramework framework = NuGetFramework.Parse(import); - - if (!framework.IsSpecificFramework) - { - throw FileFormatException.Create( - string.Format( - CultureInfo.CurrentCulture, - Strings.Log_InvalidImportFramework, - import, - PackageSpec.PackageSpecFileName), - lineNumber, - linePosition, - packageSpec.FilePath); - } - - targetFrameworkInformation.Imports.Add(framework); - } - } + return GetPackageSpec(json, name: null, packageSpecPath: null, snapshotValue: null); } - private static void ReadMappings(JsonTextReader jsonReader, string mappingKey, IDictionary mappings) + [Obsolete("This method is obsolete and will be removed in a future release.")] + public static PackageSpec GetPackageSpec(JObject rawPackageSpec, string name, string packageSpecPath, string snapshotValue) { - if (jsonReader.ReadNextToken()) + using (var stringReader = new StringReader(rawPackageSpec.ToString())) + using (var jsonReader = new JsonTextReader(stringReader)) { - switch (jsonReader.TokenType) - { - case JsonToken.String: - { - var files = new IncludeExcludeFiles() - { - Include = new[] { (string)jsonReader.Value } - }; - - mappings.Add(mappingKey, files); - } - break; - - case JsonToken.StartArray: - { - IReadOnlyList include = jsonReader.ReadStringArrayAsReadOnlyListFromArrayStart(); - - var files = new IncludeExcludeFiles() - { - Include = include - }; - - mappings.Add(mappingKey, files); - } - break; - - case JsonToken.StartObject: - { - IReadOnlyList excludeFiles = null; - IReadOnlyList exclude = null; - IReadOnlyList includeFiles = null; - IReadOnlyList include = null; - - jsonReader.ReadProperties(filesPropertyName => - { - switch (filesPropertyName) - { - case "excludeFiles": - excludeFiles = jsonReader.ReadStringOrArrayOfStringsAsReadOnlyList(); - break; - - case "exclude": - exclude = jsonReader.ReadStringOrArrayOfStringsAsReadOnlyList(); - break; - - case "includeFiles": - includeFiles = jsonReader.ReadStringOrArrayOfStringsAsReadOnlyList(); - break; - - case "include": - include = jsonReader.ReadStringOrArrayOfStringsAsReadOnlyList(); - break; - } - }); - - if (include != null || includeFiles != null || exclude != null || excludeFiles != null) - { - var files = new IncludeExcludeFiles() - { - ExcludeFiles = excludeFiles, - Exclude = exclude, - IncludeFiles = includeFiles, - Include = include - }; - - mappings.Add(mappingKey, files); - } - } - break; - } + return NjPackageSpecReader.GetPackageSpec(jsonReader, name, packageSpecPath, snapshotValue); } } - private static void ReadMSBuildMetadata(JsonTextReader jsonReader, PackageSpec packageSpec) + [Obsolete] + internal static PackageSpec GetPackageSpec(JsonTextReader jsonReader, string packageSpecPath) { - var centralPackageVersionsManagementEnabled = false; - var centralPackageVersionOverrideDisabled = false; - var CentralPackageTransitivePinningEnabled = false; - List configFilePaths = null; - var crossTargeting = false; - List fallbackFolders = null; - List files = null; - var legacyPackagesDirectory = false; - ProjectRestoreMetadata msbuildMetadata = null; - List originalTargetFrameworks = null; - string outputPath = null; - string packagesConfigPath = null; - string packagesPath = null; - string projectJsonPath = null; - string projectName = null; - string projectPath = null; - ProjectStyle? projectStyle = null; - string projectUniqueName = null; - RestoreLockProperties restoreLockProperties = null; - var skipContentFileWrite = false; - List sources = null; - List targetFrameworks = null; - var validateRuntimeAssets = false; - WarningProperties warningProperties = null; - RestoreAuditProperties auditProperties = null; - - jsonReader.ReadObject(propertyName => - { - switch (propertyName) - { - case "centralPackageVersionsManagementEnabled": - centralPackageVersionsManagementEnabled = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpec.FilePath); - break; - - case "centralPackageVersionOverrideDisabled": - centralPackageVersionOverrideDisabled = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpec.FilePath); - break; - - case "CentralPackageTransitivePinningEnabled": - CentralPackageTransitivePinningEnabled = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpec.FilePath); - break; - - case "configFilePaths": - configFilePaths = jsonReader.ReadStringArrayAsList(); - break; - - case "crossTargeting": - crossTargeting = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpec.FilePath); - break; - - case "fallbackFolders": - fallbackFolders = jsonReader.ReadStringArrayAsList(); - break; - - case "files": - jsonReader.ReadObject(filePropertyName => - { - files = files ?? new List(); - - files.Add(new ProjectRestoreMetadataFile(filePropertyName, jsonReader.ReadNextTokenAsString())); - }); - break; - - case "frameworks": - targetFrameworks = ReadTargetFrameworks(jsonReader); - break; - - case "legacyPackagesDirectory": - legacyPackagesDirectory = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpec.FilePath); - break; - - case "originalTargetFrameworks": - originalTargetFrameworks = jsonReader.ReadStringArrayAsList(); - break; - - case "outputPath": - outputPath = jsonReader.ReadNextTokenAsString(); - break; - - case "packagesConfigPath": - packagesConfigPath = jsonReader.ReadNextTokenAsString(); - break; - - case "packagesPath": - packagesPath = jsonReader.ReadNextTokenAsString(); - break; - - case "projectJsonPath": - projectJsonPath = jsonReader.ReadNextTokenAsString(); - break; - - case "projectName": - projectName = jsonReader.ReadNextTokenAsString(); - break; - - case "projectPath": - projectPath = jsonReader.ReadNextTokenAsString(); - break; - - case "projectStyle": - string projectStyleString = jsonReader.ReadNextTokenAsString(); - - if (!string.IsNullOrEmpty(projectStyleString) - && Enum.TryParse(projectStyleString, ignoreCase: true, result: out ProjectStyle projectStyleValue)) - { - projectStyle = projectStyleValue; - } - break; - - case "projectUniqueName": - projectUniqueName = jsonReader.ReadNextTokenAsString(); - break; - - case "restoreLockProperties": - string nuGetLockFilePath = null; - var restoreLockedMode = false; - string restorePackagesWithLockFile = null; - - jsonReader.ReadObject(restoreLockPropertiesPropertyName => - { - switch (restoreLockPropertiesPropertyName) - { - case "nuGetLockFilePath": - nuGetLockFilePath = jsonReader.ReadNextTokenAsString(); - break; - - case "restoreLockedMode": - restoreLockedMode = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpec.FilePath); - break; - - case "restorePackagesWithLockFile": - restorePackagesWithLockFile = jsonReader.ReadNextTokenAsString(); - break; - } - }); - - restoreLockProperties = new RestoreLockProperties(restorePackagesWithLockFile, nuGetLockFilePath, restoreLockedMode); - break; - - case "restoreAuditProperties": - string enableAudit = null, auditLevel = null, auditMode = null; - jsonReader.ReadObject(auditPropertyName => - { - - switch (auditPropertyName) - { - case "enableAudit": - enableAudit = jsonReader.ReadNextTokenAsString(); - break; - - case "auditLevel": - auditLevel = jsonReader.ReadNextTokenAsString(); - break; - - case "auditMode": - auditMode = jsonReader.ReadNextTokenAsString(); - break; - } - }); - auditProperties = new RestoreAuditProperties() - { - EnableAudit = enableAudit, - AuditLevel = auditLevel, - AuditMode = auditMode, - }; - break; - - case "skipContentFileWrite": - skipContentFileWrite = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpec.FilePath); - break; - - case "sources": - jsonReader.ReadObject(sourcePropertyName => - { - sources = sources ?? new List(); - - sources.Add(new PackageSource(sourcePropertyName)); - }); - break; - - case "validateRuntimeAssets": - validateRuntimeAssets = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpec.FilePath); - break; - - case "warningProperties": - var allWarningsAsErrors = false; - var noWarn = new HashSet(); - var warnAsError = new HashSet(); - var warningsNotAsErrors = new HashSet(); - - jsonReader.ReadObject(warningPropertiesPropertyName => - { - switch (warningPropertiesPropertyName) - { - case "allWarningsAsErrors": - allWarningsAsErrors = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpec.FilePath); - break; - - case "noWarn": - ReadNuGetLogCodes(jsonReader, noWarn); - break; - - case "warnAsError": - ReadNuGetLogCodes(jsonReader, warnAsError); - break; - - case "warnNotAsError": - ReadNuGetLogCodes(jsonReader, warningsNotAsErrors); - break; - } - }); - - warningProperties = new WarningProperties(warnAsError, noWarn, allWarningsAsErrors, warningsNotAsErrors); - break; - } - }); - - if (projectStyle == ProjectStyle.PackagesConfig) - { - msbuildMetadata = new PackagesConfigProjectRestoreMetadata() - { - PackagesConfigPath = packagesConfigPath - }; - } - else - { - msbuildMetadata = new ProjectRestoreMetadata(); - } - - msbuildMetadata.CentralPackageVersionsEnabled = centralPackageVersionsManagementEnabled; - msbuildMetadata.CentralPackageVersionOverrideDisabled = centralPackageVersionOverrideDisabled; - msbuildMetadata.CentralPackageTransitivePinningEnabled = CentralPackageTransitivePinningEnabled; - msbuildMetadata.RestoreAuditProperties = auditProperties; - - if (configFilePaths != null) - { - msbuildMetadata.ConfigFilePaths = configFilePaths; - } - - msbuildMetadata.CrossTargeting = crossTargeting; - - if (fallbackFolders != null) - { - msbuildMetadata.FallbackFolders = fallbackFolders; - } - - if (files != null) - { - msbuildMetadata.Files = files; - } - - msbuildMetadata.LegacyPackagesDirectory = legacyPackagesDirectory; - - if (originalTargetFrameworks != null) - { - msbuildMetadata.OriginalTargetFrameworks = originalTargetFrameworks; - } - - msbuildMetadata.OutputPath = outputPath; - msbuildMetadata.PackagesPath = packagesPath; - msbuildMetadata.ProjectJsonPath = projectJsonPath; - msbuildMetadata.ProjectName = projectName; - msbuildMetadata.ProjectPath = projectPath; - - if (projectStyle.HasValue) - { - msbuildMetadata.ProjectStyle = projectStyle.Value; - } - - msbuildMetadata.ProjectUniqueName = projectUniqueName; - - if (restoreLockProperties != null) - { - msbuildMetadata.RestoreLockProperties = restoreLockProperties; - } - - msbuildMetadata.SkipContentFileWrite = skipContentFileWrite; - - if (sources != null) - { - msbuildMetadata.Sources = sources; - } - - if (targetFrameworks != null) - { - msbuildMetadata.TargetFrameworks = targetFrameworks; - } - - msbuildMetadata.ValidateRuntimeAssets = validateRuntimeAssets; - - if (warningProperties != null) - { - msbuildMetadata.ProjectWideWarningProperties = warningProperties; - } - - packageSpec.RestoreMetadata = msbuildMetadata; - } - - private static bool ReadNextTokenAsBoolOrFalse(JsonTextReader jsonReader, string filePath) - { - if (jsonReader.ReadNextToken() && jsonReader.TokenType == JsonToken.Boolean) - { - try - { - return (bool)jsonReader.Value; - } - catch (Exception ex) - { - throw FileFormatException.Create(ex, jsonReader.LineNumber, jsonReader.LinePosition, filePath); - } - } - - return false; - } - - private static void ReadNuGetLogCodes(JsonTextReader jsonReader, HashSet hashCodes) - { - if (jsonReader.ReadNextToken() && jsonReader.TokenType == JsonToken.StartArray) - { - while (jsonReader.ReadNextToken() && jsonReader.TokenType != JsonToken.EndArray) - { - if (jsonReader.TokenType == JsonToken.String && Enum.TryParse((string)jsonReader.Value, out NuGetLogCode code)) - { - hashCodes.Add(code); - } - } - } - } - - private static List ReadNuGetLogCodesList(JsonTextReader jsonReader) - { - List items = null; - - if (jsonReader.ReadNextToken() && jsonReader.TokenType == JsonToken.StartArray) - { - while (jsonReader.ReadNextToken() && jsonReader.TokenType != JsonToken.EndArray) - { - if (jsonReader.TokenType == JsonToken.String && Enum.TryParse((string)jsonReader.Value, out NuGetLogCode code)) - { - items = items ?? new List(); - - items.Add(code); - } - } - } - - return items; - } - - private static void ReadPackageTypes(PackageSpec packageSpec, JsonTextReader jsonReader) - { - var errorLine = 0; - var errorColumn = 0; - - IReadOnlyList packageTypes = null; - PackageType packageType = null; - - try - { - if (jsonReader.ReadNextToken()) - { - errorLine = jsonReader.LineNumber; - errorColumn = jsonReader.LinePosition; - - switch (jsonReader.TokenType) - { - case JsonToken.String: - packageType = CreatePackageType(jsonReader); - - packageTypes = new[] { packageType }; - break; - - case JsonToken.StartArray: - var types = new List(); - - while (jsonReader.ReadNextToken() && jsonReader.TokenType != JsonToken.EndArray) - { - if (jsonReader.TokenType != JsonToken.String) - { - throw FileFormatException.Create( - string.Format( - CultureInfo.CurrentCulture, - Strings.InvalidPackageType, - PackageSpec.PackageSpecFileName), - errorLine, - errorColumn, - packageSpec.FilePath); - } - - packageType = CreatePackageType(jsonReader); - - types.Add(packageType); - } - - packageTypes = types; - break; - - case JsonToken.Null: - break; - - default: - throw new InvalidCastException(); - } - -#pragma warning disable CS0612 // Type or member is obsolete - if (packageTypes != null) - { - packageSpec.PackOptions.PackageType = packageTypes; - } -#pragma warning restore CS0612 // Type or member is obsolete - } - } - catch (Exception) - { - throw FileFormatException.Create( - string.Format( - CultureInfo.CurrentCulture, - Strings.InvalidPackageType, - PackageSpec.PackageSpecFileName), - errorLine, - errorColumn, - packageSpec.FilePath); - } - } - - [Obsolete] - private static void ReadPackInclude(JsonTextReader jsonReader, PackageSpec packageSpec) - { - jsonReader.ReadObject(propertyName => - { - string propertyValue = jsonReader.ReadAsString(); - - packageSpec.PackInclude.Add(new KeyValuePair(propertyName, propertyValue)); - }); - } - - [Obsolete] - private static void ReadPackOptions(JsonTextReader jsonReader, PackageSpec packageSpec, ref bool isMappingsNull) - { - var wasMappingsRead = false; - - bool isPackOptionsValueAnObject = jsonReader.ReadObject(propertyName => - { - switch (propertyName) - { - case "files": - wasMappingsRead = ReadPackOptionsFiles(packageSpec, jsonReader, wasMappingsRead); - break; - - case "iconUrl": - packageSpec.IconUrl = jsonReader.ReadNextTokenAsString(); - break; - - case "licenseUrl": - packageSpec.LicenseUrl = jsonReader.ReadNextTokenAsString(); - break; - - case "owners": - string[] owners = ReadStringArray(jsonReader); - - if (owners != null) - { - packageSpec.Owners = owners; - } - break; - - case "packageType": - ReadPackageTypes(packageSpec, jsonReader); - break; - - case "projectUrl": - packageSpec.ProjectUrl = jsonReader.ReadNextTokenAsString(); - break; - - case "releaseNotes": - packageSpec.ReleaseNotes = jsonReader.ReadNextTokenAsString(); - break; - - case "requireLicenseAcceptance": - packageSpec.RequireLicenseAcceptance = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpec.FilePath); - break; - - case "summary": - packageSpec.Summary = jsonReader.ReadNextTokenAsString(); - break; - - case "tags": - string[] tags = ReadStringArray(jsonReader); - - if (tags != null) - { - packageSpec.Tags = tags; - } - break; - } - }); - - isMappingsNull = isPackOptionsValueAnObject && !wasMappingsRead; - } - - [Obsolete] - private static bool ReadPackOptionsFiles(PackageSpec packageSpec, JsonTextReader jsonReader, bool wasMappingsRead) - { - IReadOnlyList excludeFiles = null; - IReadOnlyList exclude = null; - IReadOnlyList includeFiles = null; - IReadOnlyList include = null; - - jsonReader.ReadObject(filesPropertyName => - { - switch (filesPropertyName) - { - case "excludeFiles": - excludeFiles = jsonReader.ReadStringOrArrayOfStringsAsReadOnlyList(); - break; - - case "exclude": - exclude = jsonReader.ReadStringOrArrayOfStringsAsReadOnlyList(); - break; - - case "includeFiles": - includeFiles = jsonReader.ReadStringOrArrayOfStringsAsReadOnlyList(); - break; - - case "include": - include = jsonReader.ReadStringOrArrayOfStringsAsReadOnlyList(); - break; - - case "mappings": - jsonReader.ReadObject(mappingsPropertyName => - { - wasMappingsRead = true; - - ReadMappings(jsonReader, mappingsPropertyName, packageSpec.PackOptions.Mappings); - }); - break; - } - }); - - if (include != null || includeFiles != null || exclude != null || excludeFiles != null) - { - packageSpec.PackOptions.IncludeExcludeFiles = new IncludeExcludeFiles() - { - ExcludeFiles = excludeFiles, - Exclude = exclude, - IncludeFiles = includeFiles, - Include = include - }; - } - - return wasMappingsRead; - } - - private static RuntimeDependencySet ReadRuntimeDependencySet(JsonTextReader jsonReader, string dependencySetName) - { - List dependencies = null; - - jsonReader.ReadObject(propertyName => - { - dependencies ??= new List(); - - var dependency = new RuntimePackageDependency(propertyName, VersionRange.Parse(jsonReader.ReadNextTokenAsString())); - - dependencies.Add(dependency); - }); - - return new RuntimeDependencySet( - dependencySetName, - dependencies); - } - - private static RuntimeDescription ReadRuntimeDescription(JsonTextReader jsonReader, string runtimeName) - { - List inheritedRuntimes = null; - List additionalDependencies = null; - - jsonReader.ReadObject(propertyName => - { - if (propertyName == "#import") - { - inheritedRuntimes = jsonReader.ReadStringArrayAsList(); - } - else - { - additionalDependencies ??= new List(); - - RuntimeDependencySet dependency = ReadRuntimeDependencySet(jsonReader, propertyName); - - additionalDependencies.Add(dependency); - } - }); - - return new RuntimeDescription( - runtimeName, - inheritedRuntimes, - additionalDependencies); - } - - private static List ReadRuntimes(JsonTextReader jsonReader) - { - var runtimeDescriptions = new List(); - - jsonReader.ReadObject(propertyName => - { - RuntimeDescription runtimeDescription = ReadRuntimeDescription(jsonReader, propertyName); - - runtimeDescriptions.Add(runtimeDescription); - }); - - return runtimeDescriptions; - } - - [Obsolete] - private static void ReadScripts(JsonTextReader jsonReader, PackageSpec packageSpec) - { - jsonReader.ReadObject(propertyName => - { - if (jsonReader.ReadNextToken()) - { - if (jsonReader.TokenType == JsonToken.String) - { - packageSpec.Scripts[propertyName] = new string[] { (string)jsonReader.Value }; - } - else if (jsonReader.TokenType == JsonToken.StartArray) - { - var list = new List(); - - while (jsonReader.ReadNextToken() && jsonReader.TokenType == JsonToken.String) - { - list.Add((string)jsonReader.Value); - } - - packageSpec.Scripts[propertyName] = list; - } - else - { - throw FileFormatException.Create( - string.Format(CultureInfo.CurrentCulture, "The value of a script in '{0}' can only be a string or an array of strings", PackageSpec.PackageSpecFileName), - jsonReader.LineNumber, - jsonReader.LinePosition, - packageSpec.FilePath); - } - } - }); - } - - private static string[] ReadStringArray(JsonTextReader jsonReader) - { - List list = jsonReader.ReadStringArrayAsList(); - - return list?.ToArray(); - } - - private static List ReadSupports(JsonTextReader jsonReader) - { - var compatibilityProfiles = new List(); - - jsonReader.ReadObject(propertyName => - { - CompatibilityProfile compatibilityProfile = ReadCompatibilityProfile(jsonReader, propertyName); - - compatibilityProfiles.Add(compatibilityProfile); - }); - - return compatibilityProfiles; - } - - private static LibraryDependencyTarget ReadTarget( - JsonTextReader jsonReader, - string packageSpecPath, - LibraryDependencyTarget targetFlagsValue) - { - if (jsonReader.ReadNextToken()) - { - var targetString = (string)jsonReader.Value; - - targetFlagsValue = LibraryDependencyTargetUtils.Parse(targetString); - - // Verify that the value specified is package, project, or external project - if (!ValidateDependencyTarget(targetFlagsValue)) - { - string message = string.Format( - CultureInfo.CurrentCulture, - Strings.InvalidDependencyTarget, - targetString); - - throw FileFormatException.Create( - message, - jsonReader.LineNumber, - jsonReader.LinePosition, - packageSpecPath); - } - } - - return targetFlagsValue; - } - - private static List ReadTargetFrameworks(JsonTextReader jsonReader) - { - var targetFrameworks = new List(); - - jsonReader.ReadObject(frameworkPropertyName => - { - NuGetFramework framework = NuGetFramework.Parse(frameworkPropertyName); - var frameworkGroup = new ProjectRestoreMetadataFrameworkInfo(framework); - - jsonReader.ReadObject(propertyName => - { - if (propertyName == "projectReferences") - { - jsonReader.ReadObject(projectReferencePropertyName => - { - string excludeAssets = null; - string includeAssets = null; - string privateAssets = null; - string projectReferenceProjectPath = null; - - jsonReader.ReadObject(projectReferenceObjectPropertyName => - { - switch (projectReferenceObjectPropertyName) - { - case "excludeAssets": - excludeAssets = jsonReader.ReadNextTokenAsString(); - break; - - case "includeAssets": - includeAssets = jsonReader.ReadNextTokenAsString(); - break; - - case "privateAssets": - privateAssets = jsonReader.ReadNextTokenAsString(); - break; - - case "projectPath": - projectReferenceProjectPath = jsonReader.ReadNextTokenAsString(); - break; - } - }); - - frameworkGroup.ProjectReferences.Add(new ProjectRestoreReference() - { - ProjectUniqueName = projectReferencePropertyName, - ProjectPath = projectReferenceProjectPath, - - IncludeAssets = LibraryIncludeFlagUtils.GetFlags( - flags: includeAssets, - defaultFlags: LibraryIncludeFlags.All), - - ExcludeAssets = LibraryIncludeFlagUtils.GetFlags( - flags: excludeAssets, - defaultFlags: LibraryIncludeFlags.None), - - PrivateAssets = LibraryIncludeFlagUtils.GetFlags( - flags: privateAssets, - defaultFlags: LibraryIncludeFlagUtils.DefaultSuppressParent), - }); - }); - } - else if (propertyName == "targetAlias") - { - frameworkGroup.TargetAlias = jsonReader.ReadNextTokenAsString(); - } - }); - - targetFrameworks.Add(frameworkGroup); - }); - - return targetFrameworks; - } - - private static void ReadTargetFrameworks(PackageSpec packageSpec, JsonTextReader jsonReader, out int frameworkLine, out int frameworkColumn) - { - frameworkLine = 0; - frameworkColumn = 0; - - NuGetFramework frameworkName = NuGetFramework.Parse((string)jsonReader.Value); - - var targetFrameworkInformation = new TargetFrameworkInformation(); - NuGetFramework secondaryFramework = default; - jsonReader.ReadObject(propertyName => - { - switch (propertyName) - { - case "assetTargetFallback": - targetFrameworkInformation.AssetTargetFallback = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpec.FilePath); - break; - - case "secondaryFramework": - var secondaryFrameworkString = jsonReader.ReadAsString(); - if (!string.IsNullOrEmpty(secondaryFrameworkString)) - { - secondaryFramework = NuGetFramework.Parse(secondaryFrameworkString); - } - break; - - case "centralPackageVersions": - ReadCentralPackageVersions( - jsonReader, - targetFrameworkInformation.CentralPackageVersions, - packageSpec.FilePath); - break; - - case "dependencies": - ReadDependencies( - jsonReader, - targetFrameworkInformation.Dependencies, - packageSpec.FilePath, - isGacOrFrameworkReference: false); - break; - - case "downloadDependencies": - ReadDownloadDependencies( - jsonReader, - targetFrameworkInformation.DownloadDependencies, - packageSpec.FilePath); - break; - - case "frameworkAssemblies": - ReadDependencies( - jsonReader, - targetFrameworkInformation.Dependencies, - packageSpec.FilePath, - isGacOrFrameworkReference: true); - break; - - case "frameworkReferences": - ReadFrameworkReferences( - jsonReader, - targetFrameworkInformation.FrameworkReferences, - packageSpec.FilePath); - break; - - case "imports": - ReadImports(packageSpec, jsonReader, targetFrameworkInformation); - break; - - case "runtimeIdentifierGraphPath": - targetFrameworkInformation.RuntimeIdentifierGraphPath = jsonReader.ReadNextTokenAsString(); - break; - - case "targetAlias": - targetFrameworkInformation.TargetAlias = jsonReader.ReadNextTokenAsString(); - break; - - case "warn": - targetFrameworkInformation.Warn = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpec.FilePath); - break; - } - }, out frameworkLine, out frameworkColumn); - - NuGetFramework updatedFramework = frameworkName; - - if (targetFrameworkInformation.Imports.Count > 0) - { - NuGetFramework[] imports = targetFrameworkInformation.Imports.ToArray(); - - if (targetFrameworkInformation.AssetTargetFallback) - { - updatedFramework = new AssetTargetFallbackFramework(GetDualCompatibilityFrameworkIfNeeded(frameworkName, secondaryFramework), imports); - } - else - { - updatedFramework = new FallbackFramework(GetDualCompatibilityFrameworkIfNeeded(frameworkName, secondaryFramework), imports); - } - } - else - { - updatedFramework = GetDualCompatibilityFrameworkIfNeeded(frameworkName, secondaryFramework); - } - - targetFrameworkInformation.FrameworkName = updatedFramework; - - packageSpec.TargetFrameworks.Add(targetFrameworkInformation); - } - - private static NuGetFramework GetDualCompatibilityFrameworkIfNeeded(NuGetFramework frameworkName, NuGetFramework secondaryFramework) - { - if (secondaryFramework != default) - { - return new DualCompatibilityFramework(frameworkName, secondaryFramework); - } - - return frameworkName; - } - - private static bool ValidateDependencyTarget(LibraryDependencyTarget targetValue) - { - var isValid = false; - - switch (targetValue) - { - case LibraryDependencyTarget.Package: - case LibraryDependencyTarget.Project: - case LibraryDependencyTarget.ExternalProject: - isValid = true; - break; - } - - return isValid; + return NjPackageSpecReader.GetPackageSpec(jsonReader, packageSpecPath); } } } diff --git a/src/NuGet.Core/NuGet.ProjectModel/JsonUtility.cs b/src/NuGet.Core/NuGet.ProjectModel/JsonUtility.cs index 742629ad4b5..c52d207a983 100644 --- a/src/NuGet.Core/NuGet.ProjectModel/JsonUtility.cs +++ b/src/NuGet.Core/NuGet.ProjectModel/JsonUtility.cs @@ -13,6 +13,8 @@ namespace NuGet.ProjectModel { internal static class JsonUtility { + private static ReadOnlySpan Utf8Bom => new byte[] { 0xEF, 0xBB, 0xBF }; + internal static readonly char[] PathSplitChars = new[] { LockFile.DirectorySeparatorChar }; /// @@ -43,6 +45,12 @@ internal static JObject LoadJson(TextReader reader) } } + internal static T LoadJson(Stream stream, Utf8JsonStreamReaderConverter converter) + { + var streamingJsonReader = new Utf8JsonStreamReader(stream); + return converter.Read(ref streamingJsonReader); + } + internal static PackageDependency ReadPackageDependency(string property, JToken json) { var versionStr = json.Value(); diff --git a/src/NuGet.Core/NuGet.ProjectModel/ListObjectConvertor.cs b/src/NuGet.Core/NuGet.ProjectModel/ListObjectConvertor.cs new file mode 100644 index 00000000000..2d1dffaaf1f --- /dev/null +++ b/src/NuGet.Core/NuGet.ProjectModel/ListObjectConvertor.cs @@ -0,0 +1,60 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace NuGet.ProjectModel +{ + /// + /// A to allow System.Text.Json to read/write where the list is setup as an object + /// + internal class ListObjectConvertor : JsonConverter> + { + public override IList Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (typeToConvert != typeof(IList)) + { + throw new InvalidOperationException(); + } + + if (reader.TokenType != JsonTokenType.StartObject) + { + return new List(0); + + } + //We use JsonObjects for the arrays so we advance to the first property in the object which is the name/ver of the first library + reader.Read(); + + if (reader.TokenType == JsonTokenType.EndObject) + { + return new List(0); + } + + var objectConverter = (JsonConverter)options.GetConverter(typeof(T)); + var listObjects = new List(); + do + { + listObjects.Add(objectConverter.Read(ref reader, typeof(T), options)); + //At this point we're looking at the EndObject token for the object, need to advance. + reader.Read(); + } + while (reader.TokenType != JsonTokenType.EndObject); + return listObjects; + } + + public override void Write(Utf8JsonWriter writer, IList value, JsonSerializerOptions options) + { + if (value is null) + { + writer.WriteNullValue(); + } + else + { + writer.WriteStringValue("hi"); + } + } + } +} diff --git a/src/NuGet.Core/NuGet.ProjectModel/LockFile/LockFileFormat.cs b/src/NuGet.Core/NuGet.ProjectModel/LockFile/LockFileFormat.cs index a03f1607291..1abf9b51e07 100644 --- a/src/NuGet.Core/NuGet.ProjectModel/LockFile/LockFileFormat.cs +++ b/src/NuGet.Core/NuGet.ProjectModel/LockFile/LockFileFormat.cs @@ -6,6 +6,7 @@ using System.Globalization; using System.IO; using System.Linq; +using System.Text; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NuGet.Common; @@ -53,16 +54,19 @@ public class LockFileFormat private const string FrameworkReferencesProperty = "frameworkReferences"; private const string CentralTransitiveDependencyGroupsProperty = "centralTransitiveDependencyGroups"; + [Obsolete] public LockFile Parse(string lockFileContent, string path) { return Parse(lockFileContent, NullLogger.Instance, path); } + [Obsolete] public LockFile Parse(string lockFileContent, ILogger log, string path) { - using (var reader = new StringReader(lockFileContent)) + byte[] byteArray = Encoding.UTF8.GetBytes(lockFileContent); + using (var stream = new MemoryStream(byteArray)) { - return Read(reader, log, path); + return Read(stream, log, path); } } @@ -86,17 +90,29 @@ public LockFile Read(Stream stream, string path) public LockFile Read(Stream stream, ILogger log, string path) { - using (var textReader = new StreamReader(stream)) + var useNj = EnvironmentVariableWrapper.Instance.GetEnvironmentVariable("NUGET_EXPERIMENTAL_USE_NJ_FOR_FILE_PARSING"); + if (string.IsNullOrEmpty(useNj) || useNj.Equals("false", StringComparison.OrdinalIgnoreCase)) { - return Read(textReader, log, path); + return Uf8JsonRead(stream, log, path); + } + else + { + using (var reader = new StreamReader(stream)) + { +#pragma warning disable CS0618 // Type or member is obsolete + return Read(reader, log, path); +#pragma warning restore CS0618 // Type or member is obsolete + } } } + [Obsolete("This method is deprecated. Use Read(Stream, string) instead.")] public LockFile Read(TextReader reader, string path) { return Read(reader, NullLogger.Instance, path); } + [Obsolete("This method is deprecated. Use Read(Stream, ILogger, string) instead.")] public LockFile Read(TextReader reader, ILogger log, string path) { try @@ -161,6 +177,30 @@ public string Render(LockFile lockFile) } } + private LockFile Uf8JsonRead(Stream stream, ILogger log, string path) + { + try + { + var lockFile = JsonUtility.LoadJson(stream, Utf8JsonReaderExtensions.LockFileConverter); + lockFile.Path = path; + return lockFile; + } + catch (Exception ex) + { + log.LogInformation(string.Format(CultureInfo.CurrentCulture, + Strings.Log_ErrorReadingLockFile, + path, ex.Message)); + + // Ran into parsing errors, mark it as unlocked and out-of-date + return new LockFile + { + Version = int.MinValue, + Path = path + }; + } + } + + [Obsolete] private static LockFile ReadLockFile(JObject cursor, string path) { var lockFile = new LockFile() @@ -913,6 +953,7 @@ private static void WriteCentralTransitiveDependencyGroup(IObjectWriter writer, writer.WriteObjectEnd(); } + [Obsolete] private static List ReadProjectFileTransitiveDependencyGroup(JObject json, string path) { var results = new List(); @@ -930,7 +971,7 @@ private static List ReadProjectFileTransitiveD NuGetFramework framework = NuGetFramework.Parse(frameworkPropertyName); var dependencies = new List(); - JsonPackageSpecReader.ReadCentralTransitiveDependencyGroup( + NjPackageSpecReader.ReadCentralTransitiveDependencyGroup( jsonReader: jsonReader, results: dependencies, packageSpecPath: path); diff --git a/src/NuGet.Core/NuGet.ProjectModel/LockFile/LockFileRuntimeTarget.cs b/src/NuGet.Core/NuGet.ProjectModel/LockFile/LockFileRuntimeTarget.cs index 77994cc1126..a02ea36fe81 100644 --- a/src/NuGet.Core/NuGet.ProjectModel/LockFile/LockFileRuntimeTarget.cs +++ b/src/NuGet.Core/NuGet.ProjectModel/LockFile/LockFileRuntimeTarget.cs @@ -1,8 +1,6 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System; - namespace NuGet.ProjectModel { public class LockFileRuntimeTarget : LockFileItem diff --git a/src/NuGet.Core/NuGet.ProjectModel/LockFile/Utf8JsonStreamAssetsLogMessageConverter.cs b/src/NuGet.Core/NuGet.ProjectModel/LockFile/Utf8JsonStreamAssetsLogMessageConverter.cs new file mode 100644 index 00000000000..170f2e9e2c5 --- /dev/null +++ b/src/NuGet.Core/NuGet.ProjectModel/LockFile/Utf8JsonStreamAssetsLogMessageConverter.cs @@ -0,0 +1,131 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json; +using NuGet.Common; + +namespace NuGet.ProjectModel +{ + /// + /// A to allow read JSON into + /// + internal class Utf8JsonStreamAssetsLogMessageConverter : Utf8JsonStreamReaderConverter + { + private static readonly byte[] Utf8Level = Encoding.UTF8.GetBytes(LogMessageProperties.LEVEL); + private static readonly byte[] Utf8Code = Encoding.UTF8.GetBytes(LogMessageProperties.CODE); + private static readonly byte[] Utf8WarningLevel = Encoding.UTF8.GetBytes(LogMessageProperties.WARNING_LEVEL); + private static readonly byte[] Utf8FilePath = Encoding.UTF8.GetBytes(LogMessageProperties.FILE_PATH); + private static readonly byte[] Utf8StartLineNumber = Encoding.UTF8.GetBytes(LogMessageProperties.START_LINE_NUMBER); + private static readonly byte[] Utf8StartColumnNumber = Encoding.UTF8.GetBytes(LogMessageProperties.START_COLUMN_NUMBER); + private static readonly byte[] Utf8EndLineNumber = Encoding.UTF8.GetBytes(LogMessageProperties.END_LINE_NUMBER); + private static readonly byte[] Utf8EndColumnNumber = Encoding.UTF8.GetBytes(LogMessageProperties.END_COLUMN_NUMBER); + private static readonly byte[] Utf8Message = Encoding.UTF8.GetBytes(LogMessageProperties.MESSAGE); + private static readonly byte[] Utf8LibraryId = Encoding.UTF8.GetBytes(LogMessageProperties.LIBRARY_ID); + private static readonly byte[] Utf8TargetGraphs = Encoding.UTF8.GetBytes(LogMessageProperties.TARGET_GRAPHS); + + public override AssetsLogMessage Read(ref Utf8JsonStreamReader reader) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException("Expected StartObject, found " + reader.TokenType); + } + + var isValid = true; + LogLevel level = default; + NuGetLogCode code = default; + //matching default warning level when AssetLogMessage object is created + WarningLevel warningLevel = WarningLevel.Severe; + string message = default; + string filePath = default; + int startLineNumber = default; + int startColNumber = default; + int endLineNumber = default; + int endColNumber = default; + string libraryId = default; + IReadOnlyList targetGraphs = null; + + while (reader.Read() && reader.TokenType == JsonTokenType.PropertyName) + { + if (!isValid) + { + reader.TrySkip(); + } + if (reader.ValueTextEquals(Utf8Level)) + { + var levelString = reader.ReadNextTokenAsString(); + isValid &= Enum.TryParse(levelString, out level); + } + else if (reader.ValueTextEquals(Utf8Code)) + { + var codeString = reader.ReadNextTokenAsString(); + isValid &= Enum.TryParse(codeString, out code); + } + else if (reader.ValueTextEquals(Utf8WarningLevel)) + { + reader.Read(); + warningLevel = (WarningLevel)Enum.ToObject(typeof(WarningLevel), reader.GetInt32()); + } + else if (reader.ValueTextEquals(Utf8FilePath)) + { + filePath = reader.ReadNextTokenAsString(); + } + else if (reader.ValueTextEquals(Utf8StartLineNumber)) + { + reader.Read(); + startLineNumber = reader.GetInt32(); + } + else if (reader.ValueTextEquals(Utf8StartColumnNumber)) + { + reader.Read(); + startColNumber = reader.GetInt32(); + } + else if (reader.ValueTextEquals(Utf8EndLineNumber)) + { + reader.Read(); + endLineNumber = reader.GetInt32(); + } + else if (reader.ValueTextEquals(Utf8EndColumnNumber)) + { + reader.Read(); + endColNumber = reader.GetInt32(); + } + else if (reader.ValueTextEquals(Utf8Message)) + { + message = reader.ReadNextTokenAsString(); + } + else if (reader.ValueTextEquals(Utf8LibraryId)) + { + libraryId = reader.ReadNextTokenAsString(); + } + else if (reader.ValueTextEquals(Utf8TargetGraphs)) + { + reader.Read(); + targetGraphs = reader.ReadStringArrayAsList(); + } + else + { + reader.TrySkip(); + } + } + if (isValid) + { + var assetLogMessage = new AssetsLogMessage(level, code, message) + { + TargetGraphs = targetGraphs ?? new List(0), + FilePath = filePath, + EndColumnNumber = endColNumber, + EndLineNumber = endLineNumber, + LibraryId = libraryId, + StartColumnNumber = startColNumber, + StartLineNumber = startLineNumber, + WarningLevel = warningLevel + }; + return assetLogMessage; + } + return null; + } + } +} diff --git a/src/NuGet.Core/NuGet.ProjectModel/LockFile/Utf8JsonStreamLockFileConverter.cs b/src/NuGet.Core/NuGet.ProjectModel/LockFile/Utf8JsonStreamLockFileConverter.cs new file mode 100644 index 00000000000..a9fbf5d2328 --- /dev/null +++ b/src/NuGet.Core/NuGet.ProjectModel/LockFile/Utf8JsonStreamLockFileConverter.cs @@ -0,0 +1,120 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json; +using NuGet.Frameworks; +using NuGet.LibraryModel; + +namespace NuGet.ProjectModel +{ + /// + /// A to allow read JSON into + /// + internal class Utf8JsonStreamLockFileConverter : Utf8JsonStreamReaderConverter + { + private static readonly byte[] Utf8Version = Encoding.UTF8.GetBytes("version"); + private static readonly byte[] Utf8Libraries = Encoding.UTF8.GetBytes("libraries"); + private static readonly byte[] Utf8Targets = Encoding.UTF8.GetBytes("targets"); + private static readonly byte[] Utf8ProjectFileDependencyGroups = Encoding.UTF8.GetBytes("projectFileDependencyGroups"); + private static readonly byte[] Utf8PackageFolders = Encoding.UTF8.GetBytes("packageFolders"); + private static readonly byte[] Utf8Project = Encoding.UTF8.GetBytes("project"); + private static readonly byte[] Utf8CentralTransitiveDependencyGroups = Encoding.UTF8.GetBytes("centralTransitiveDependencyGroups"); + private static readonly byte[] Utf8Logs = Encoding.UTF8.GetBytes("logs"); + + public override LockFile Read(ref Utf8JsonStreamReader reader) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException("Expected StartObject, found " + reader.TokenType); + } + + var lockFile = new LockFile(); + + while (reader.Read() && reader.TokenType == JsonTokenType.PropertyName) + { + if (reader.ValueTextEquals(Utf8Version)) + { + reader.Read(); + if (reader.TryGetInt32(out int version)) + { + lockFile.Version = version; + } + else + { + lockFile.Version = int.MinValue; + } + } + else if (reader.ValueTextEquals(Utf8Libraries)) + { + reader.Read(); + lockFile.Libraries = reader.ReadObjectAsList(Utf8JsonReaderExtensions.LockFileLibraryConverter); + } + else if (reader.ValueTextEquals(Utf8Targets)) + { + reader.Read(); + lockFile.Targets = reader.ReadObjectAsList(Utf8JsonReaderExtensions.LockFileTargetConverter); + } + else if (reader.ValueTextEquals(Utf8ProjectFileDependencyGroups)) + { + reader.Read(); + lockFile.ProjectFileDependencyGroups = reader.ReadObjectAsList(Utf8JsonReaderExtensions.ProjectFileDepencencyGroupConverter); + } + else if (reader.ValueTextEquals(Utf8PackageFolders)) + { + reader.Read(); + lockFile.PackageFolders = reader.ReadObjectAsList(Utf8JsonReaderExtensions.LockFileItemConverter); + } + else if (reader.ValueTextEquals(Utf8Project)) + { + reader.Read(); + lockFile.PackageSpec = Utf8JsonStreamPackageSpecReader.GetPackageSpec( + ref reader, + name: null, + packageSpecPath: null, + snapshotValue: null); + } + else if (reader.ValueTextEquals(Utf8CentralTransitiveDependencyGroups)) + { + var results = new List(); + if (reader.Read() && reader.TokenType == JsonTokenType.StartObject) + { + while (reader.Read() && reader.TokenType == JsonTokenType.PropertyName) + { + var frameworkPropertyName = reader.GetString(); + NuGetFramework framework = NuGetFramework.Parse(frameworkPropertyName); + var dependencies = new List(); + + Utf8JsonStreamPackageSpecReader.ReadCentralTransitiveDependencyGroup( + jsonReader: ref reader, + results: dependencies, + packageSpecPath: string.Empty); + results.Add(new CentralTransitiveDependencyGroup(framework, dependencies)); + } + } + lockFile.CentralTransitiveDependencyGroups = results; + } + else if (reader.ValueTextEquals(Utf8Logs)) + { + reader.ReadArrayOfObjects(lockFile.LogMessages, Utf8JsonReaderExtensions.AssetsLogMessageConverter); + } + else + { + reader.TrySkip(); + } + } + + if (!string.IsNullOrEmpty(lockFile.PackageSpec?.RestoreMetadata?.ProjectPath) && lockFile.LogMessages.Count > 0) + { + foreach (AssetsLogMessage message in lockFile.LogMessages.Where(x => string.IsNullOrEmpty(x.ProjectPath))) + { + message.FilePath = lockFile.PackageSpec.RestoreMetadata.ProjectPath; + } + } + + return lockFile; + } + } +} diff --git a/src/NuGet.Core/NuGet.ProjectModel/LockFile/Utf8JsonStreamLockFileItemConverter.cs b/src/NuGet.Core/NuGet.ProjectModel/LockFile/Utf8JsonStreamLockFileItemConverter.cs new file mode 100644 index 00000000000..f440d3ba705 --- /dev/null +++ b/src/NuGet.Core/NuGet.ProjectModel/LockFile/Utf8JsonStreamLockFileItemConverter.cs @@ -0,0 +1,51 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Text.Json; + +namespace NuGet.ProjectModel +{ + /// + /// A to allow read JSON into + /// + internal class Utf8JsonStreamLockFileItemConverter : Utf8JsonStreamReaderConverter where T : LockFileItem + { + public override T Read(ref Utf8JsonStreamReader reader) + { + var genericType = typeof(T); + + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException("Expected PropertyName, found " + reader.TokenType); + } + + //We want to read the property name right away + var lockItemPath = reader.GetString(); + LockFileItem lockFileItem; + if (genericType == typeof(LockFileContentFile)) + { + lockFileItem = new LockFileContentFile(lockItemPath); + } + else if (genericType == typeof(LockFileRuntimeTarget)) + { + lockFileItem = new LockFileRuntimeTarget(lockItemPath); + } + else + { + lockFileItem = new LockFileItem(lockItemPath); + } + + reader.Read(); + if (reader.TokenType == JsonTokenType.StartObject) + { + while (reader.Read() && reader.TokenType == JsonTokenType.PropertyName) + { + var propertyName = reader.GetString(); + lockFileItem.Properties[propertyName] = reader.ReadNextTokenAsString(); + } + } + + return lockFileItem as T; + } + } +} diff --git a/src/NuGet.Core/NuGet.ProjectModel/LockFile/Utf8JsonStreamLockFileLibraryConverter.cs b/src/NuGet.Core/NuGet.ProjectModel/LockFile/Utf8JsonStreamLockFileLibraryConverter.cs new file mode 100644 index 00000000000..3237d580658 --- /dev/null +++ b/src/NuGet.Core/NuGet.ProjectModel/LockFile/Utf8JsonStreamLockFileLibraryConverter.cs @@ -0,0 +1,89 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Text; +using System.Text.Json; +using NuGet.Versioning; + +namespace NuGet.ProjectModel +{ + /// + /// A to allow read JSON into + /// + internal class Utf8JsonStreamLockFileLibraryConverter : Utf8JsonStreamReaderConverter + { + private static readonly byte[] Utf8Sha512 = Encoding.UTF8.GetBytes("sha512"); + private static readonly byte[] Utf8Type = Encoding.UTF8.GetBytes("type"); + private static readonly byte[] Utf8Path = Encoding.UTF8.GetBytes("path"); + private static readonly byte[] Utf8MsbuildProject = Encoding.UTF8.GetBytes("msbuildProject"); + private static readonly byte[] Utf8Servicable = Encoding.UTF8.GetBytes("servicable"); + private static readonly byte[] Utf8HasTools = Encoding.UTF8.GetBytes("hasTools"); + private static readonly byte[] Utf8Files = Encoding.UTF8.GetBytes("files"); + private static readonly char[] Separators = new[] { '/' }; + + public override LockFileLibrary Read(ref Utf8JsonStreamReader reader) + { + + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException("Expected PropertyName, found " + reader.TokenType); + } + + var lockFileLibrary = new LockFileLibrary(); + //We want to read the property name right away + var propertyName = reader.GetString(); + var parts = propertyName.Split(Separators, 2); + lockFileLibrary.Name = parts[0]; + if (parts.Length == 2) + { + lockFileLibrary.Version = NuGetVersion.Parse(parts[1]); + } + + reader.Read(); + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException("Expected StartObject, found " + reader.TokenType); + } + + while (reader.Read() && reader.TokenType == JsonTokenType.PropertyName) + { + if (reader.ValueTextEquals(Utf8Type)) + { + lockFileLibrary.Type = reader.ReadNextTokenAsString(); + } + else if (reader.ValueTextEquals(Utf8Path)) + { + lockFileLibrary.Path = reader.ReadNextTokenAsString(); + } + else if (reader.ValueTextEquals(Utf8MsbuildProject)) + { + lockFileLibrary.MSBuildProject = reader.ReadNextTokenAsString(); + } + else if (reader.ValueTextEquals(Utf8Sha512)) + { + lockFileLibrary.Sha512 = reader.ReadNextTokenAsString(); + } + else if (reader.ValueTextEquals(Utf8Servicable)) + { + reader.Read(); + lockFileLibrary.IsServiceable = reader.GetBoolean(); + } + else if (reader.ValueTextEquals(Utf8HasTools)) + { + reader.Read(); + lockFileLibrary.HasTools = reader.GetBoolean(); + } + else if (reader.ValueTextEquals(Utf8Files)) + { + reader.Read(); + reader.ReadStringArrayAsIList(lockFileLibrary.Files); + } + else + { + reader.TrySkip(); + } + } + return lockFileLibrary; + } + } +} diff --git a/src/NuGet.Core/NuGet.ProjectModel/LockFile/Utf8JsonStreamLockFileTargetConverter.cs b/src/NuGet.Core/NuGet.ProjectModel/LockFile/Utf8JsonStreamLockFileTargetConverter.cs new file mode 100644 index 00000000000..60718ed6898 --- /dev/null +++ b/src/NuGet.Core/NuGet.ProjectModel/LockFile/Utf8JsonStreamLockFileTargetConverter.cs @@ -0,0 +1,37 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Text.Json; +using NuGet.Frameworks; + +namespace NuGet.ProjectModel +{ + /// + /// A to allow read JSON into + /// + internal class Utf8JsonStreamLockFileTargetConverter : Utf8JsonStreamReaderConverter + { + public override LockFileTarget Read(ref Utf8JsonStreamReader reader) + { + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException("Expected PropertyName, found " + reader.TokenType); + } + + var lockFileTarget = new LockFileTarget(); + //We want to read the property name right away + var propertyName = reader.GetString(); + var parts = propertyName.Split(JsonUtility.PathSplitChars, 2); + lockFileTarget.TargetFramework = NuGetFramework.Parse(parts[0]); + if (parts.Length == 2) + { + lockFileTarget.RuntimeIdentifier = parts[1]; + } + + reader.Read(); + lockFileTarget.Libraries = reader.ReadObjectAsList(Utf8JsonReaderExtensions.LockFileTargetLibraryConverter); + + return lockFileTarget; + } + } +} diff --git a/src/NuGet.Core/NuGet.ProjectModel/LockFile/Utf8JsonStreamLockFileTargetLibraryConverter.cs b/src/NuGet.Core/NuGet.ProjectModel/LockFile/Utf8JsonStreamLockFileTargetLibraryConverter.cs new file mode 100644 index 00000000000..f62206d9b54 --- /dev/null +++ b/src/NuGet.Core/NuGet.ProjectModel/LockFile/Utf8JsonStreamLockFileTargetLibraryConverter.cs @@ -0,0 +1,203 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Text; +using System.Text.Json; +using NuGet.Packaging.Core; +using NuGet.Versioning; + +namespace NuGet.ProjectModel +{ + /// + /// A to allow read JSON into + /// + internal class Utf8JsonStreamLockFileTargetLibraryConverter : Utf8JsonStreamReaderConverter + { + private static readonly byte[] Utf8Type = Encoding.UTF8.GetBytes("type"); + private static readonly byte[] Utf8Framework = Encoding.UTF8.GetBytes("framework"); + private static readonly byte[] Utf8Dependencies = Encoding.UTF8.GetBytes("dependencies"); + private static readonly byte[] Utf8FrameworkAssemblies = Encoding.UTF8.GetBytes("frameworkAssemblies"); + private static readonly byte[] Utf8Runtime = Encoding.UTF8.GetBytes("runtime"); + private static readonly byte[] Utf8Compile = Encoding.UTF8.GetBytes("compile"); + private static readonly byte[] Utf8Resource = Encoding.UTF8.GetBytes("resource"); + private static readonly byte[] Utf8Native = Encoding.UTF8.GetBytes("native"); + private static readonly byte[] Utf8Build = Encoding.UTF8.GetBytes("build"); + private static readonly byte[] Utf8BuildMultiTargeting = Encoding.UTF8.GetBytes("buildMultiTargeting"); + private static readonly byte[] Utf8ContentFiles = Encoding.UTF8.GetBytes("contentFiles"); + private static readonly byte[] Utf8RuntimeTargets = Encoding.UTF8.GetBytes("runtimeTargets"); + private static readonly byte[] Utf8Tools = Encoding.UTF8.GetBytes("tools"); + private static readonly byte[] Utf8Embed = Encoding.UTF8.GetBytes("embed"); + private static readonly byte[] Utf8FrameworkReferences = Encoding.UTF8.GetBytes("frameworkReferences"); + + private IList ReadPackageDependencyList(ref Utf8JsonStreamReader reader) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + return new List(0); + } + + var packageDependencies = new List(); + while (reader.Read() && reader.TokenType == JsonTokenType.PropertyName) + { + string propertyName = reader.GetString(); + string versionString = reader.ReadNextTokenAsString(); + + packageDependencies.Add(new PackageDependency( + propertyName, + versionString == null ? null : VersionRange.Parse(versionString))); + } + return packageDependencies; + } + + public override LockFileTargetLibrary Read(ref Utf8JsonStreamReader reader) + { + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException("Expected PropertyName, found " + reader.TokenType); + } + + var lockFileTargetLibrary = new LockFileTargetLibrary(); + + //We want to read the property name right away + var propertyName = reader.GetString(); +#pragma warning disable CA1307 // Specify StringComparison + int slashIndex = propertyName.IndexOf('/'); +#pragma warning restore CA1307 // Specify StringComparison + if (slashIndex == -1) + { + lockFileTargetLibrary.Name = propertyName; + } + else + { + lockFileTargetLibrary.Name = propertyName.Substring(0, slashIndex); + lockFileTargetLibrary.Version = NuGetVersion.Parse(propertyName.Substring(slashIndex + 1)); + } + + reader.Read(); + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException("Expected StartObject, found " + reader.TokenType); + } + + while (reader.Read() && reader.TokenType == JsonTokenType.PropertyName) + { + if (reader.ValueTextEquals(Utf8Type)) + { + lockFileTargetLibrary.Type = reader.ReadNextTokenAsString(); + } + else if (reader.ValueTextEquals(Utf8Framework)) + { + lockFileTargetLibrary.Framework = reader.ReadNextTokenAsString(); + } + else if (reader.ValueTextEquals(Utf8Dependencies)) + { + reader.Read(); + if (ReadPackageDependencyList(ref reader) is { Count: not 0 } packageDependencies) + { + lockFileTargetLibrary.Dependencies = packageDependencies; + } + } + else if (reader.ValueTextEquals(Utf8FrameworkAssemblies)) + { + reader.Read(); + lockFileTargetLibrary.FrameworkAssemblies = reader.ReadStringArrayAsIList(new List()); + } + else if (reader.ValueTextEquals(Utf8Runtime)) + { + reader.Read(); + if (reader.ReadObjectAsList(Utf8JsonReaderExtensions.LockFileItemConverter) is { Count: not 0 } runtimeAssemblies) + { + lockFileTargetLibrary.RuntimeAssemblies = runtimeAssemblies; + } + } + else if (reader.ValueTextEquals(Utf8Compile)) + { + reader.Read(); + if (reader.ReadObjectAsList(Utf8JsonReaderExtensions.LockFileItemConverter) is { Count: not 0 } compileTimeAssemblies) + { + lockFileTargetLibrary.CompileTimeAssemblies = compileTimeAssemblies; + } + } + else if (reader.ValueTextEquals(Utf8Resource)) + { + reader.Read(); + if (reader.ReadObjectAsList(Utf8JsonReaderExtensions.LockFileItemConverter) is { Count: not 0 } resourceAssemblies) + { + lockFileTargetLibrary.ResourceAssemblies = resourceAssemblies; + } + } + else if (reader.ValueTextEquals(Utf8Native)) + { + reader.Read(); + if (reader.ReadObjectAsList(Utf8JsonReaderExtensions.LockFileItemConverter) is { Count: not 0 } nativeLibraries) + { + lockFileTargetLibrary.NativeLibraries = nativeLibraries; + } + } + else if (reader.ValueTextEquals(Utf8Build)) + { + reader.Read(); + if (reader.ReadObjectAsList(Utf8JsonReaderExtensions.LockFileItemConverter) is { Count: not 0 } build) + { + lockFileTargetLibrary.Build = build; + } + } + else if (reader.ValueTextEquals(Utf8BuildMultiTargeting)) + { + reader.Read(); + if (reader.ReadObjectAsList(Utf8JsonReaderExtensions.LockFileItemConverter) is { Count: not 0 } buildMultiTargeting) + { + lockFileTargetLibrary.BuildMultiTargeting = buildMultiTargeting; + } + } + else if (reader.ValueTextEquals(Utf8ContentFiles)) + { + reader.Read(); + if (reader.ReadObjectAsList(Utf8JsonReaderExtensions.LockFileContentFileConverter) is { Count: not 0 } contentFiles) + { + lockFileTargetLibrary.ContentFiles = contentFiles; + } + } + else if (reader.ValueTextEquals(Utf8RuntimeTargets)) + { + reader.Read(); + if (reader.ReadObjectAsList(Utf8JsonReaderExtensions.LockFileRuntimeTargetConverter) is { Count: not 0 } runtimeTargets) + { + lockFileTargetLibrary.RuntimeTargets = runtimeTargets; + } + } + else if (reader.ValueTextEquals(Utf8Tools)) + { + reader.Read(); + if (reader.ReadObjectAsList(Utf8JsonReaderExtensions.LockFileItemConverter) is { Count: not 0 } toolsAssemblies) + { + lockFileTargetLibrary.ToolsAssemblies = toolsAssemblies; + } + } + else if (reader.ValueTextEquals(Utf8Embed)) + { + reader.Read(); + if (reader.ReadObjectAsList(Utf8JsonReaderExtensions.LockFileItemConverter) is { Count: not 0 } embedAssemblies) + { + lockFileTargetLibrary.EmbedAssemblies = embedAssemblies; + } + } + else if (reader.ValueTextEquals(Utf8FrameworkReferences)) + { + reader.Read(); + if (reader.ReadStringArrayAsIList(new List()) is { Count: not 0 } frameworkReferences) + { + lockFileTargetLibrary.FrameworkReferences = frameworkReferences; + } + } + else + { + reader.TrySkip(); + } + } + lockFileTargetLibrary.Freeze(); + return lockFileTargetLibrary; + } + } +} diff --git a/src/NuGet.Core/NuGet.ProjectModel/NjPackageSpecReader.cs b/src/NuGet.Core/NuGet.ProjectModel/NjPackageSpecReader.cs new file mode 100644 index 00000000000..9df79f0fe08 --- /dev/null +++ b/src/NuGet.Core/NuGet.ProjectModel/NjPackageSpecReader.cs @@ -0,0 +1,1765 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using Newtonsoft.Json; +using NuGet.Common; +using NuGet.Configuration; +using NuGet.Frameworks; +using NuGet.LibraryModel; +using NuGet.Packaging.Core; +using NuGet.RuntimeModel; +using NuGet.Versioning; + +namespace NuGet.ProjectModel +{ + [Obsolete] + internal static class NjPackageSpecReader + { + private static readonly char[] DelimitedStringSeparators = { ' ', ',' }; + private static readonly char[] VersionSeparators = new[] { ';' }; + + internal static PackageSpec GetPackageSpec(string json, string name, string packageSpecPath, string snapshotValue = null) + { + using (var stringReader = new StringReader(json)) + using (var jsonReader = new JsonTextReader(stringReader)) + { + return GetPackageSpec(jsonReader, name, packageSpecPath, snapshotValue); + } + } + + internal static PackageSpec GetPackageSpec(JsonTextReader jsonReader, string packageSpecPath) + { + return GetPackageSpec(jsonReader, name: null, packageSpecPath, snapshotValue: null); + } + + internal static PackageSpec GetPackageSpec(JsonTextReader jsonReader, string name, string packageSpecPath, string snapshotValue) + { + var packageSpec = new PackageSpec(); + + List compatibilityProfiles = null; + List runtimeDescriptions = null; + var wasPackOptionsSet = false; + var isMappingsNull = false; + + string filePath = name == null ? null : Path.GetFullPath(packageSpecPath); + + jsonReader.ReadObject(propertyName => + { + if (string.IsNullOrWhiteSpace(propertyName)) + { + return; + } + + switch (propertyName) + { +#pragma warning disable CS0612 // Type or member is obsolete + case "authors": + packageSpec.Authors = ReadStringArray(jsonReader) ?? Array.Empty(); + break; + + case "buildOptions": + ReadBuildOptions(jsonReader, packageSpec); + break; + + case "contentFiles": + List contentFiles = jsonReader.ReadStringArrayAsList(); + + if (contentFiles != null) + { + packageSpec.ContentFiles = contentFiles; + } + break; + + case "copyright": + packageSpec.Copyright = jsonReader.ReadNextTokenAsString(); + break; +#pragma warning restore CS0612 // Type or member is obsolete + + case "dependencies": + ReadDependencies( + jsonReader, + packageSpec.Dependencies, + filePath, + isGacOrFrameworkReference: false); + break; + +#pragma warning disable CS0612 // Type or member is obsolete + case "description": + packageSpec.Description = jsonReader.ReadNextTokenAsString(); + break; +#pragma warning restore CS0612 // Type or member is obsolete + + case "frameworks": + ReadFrameworks(jsonReader, packageSpec); + break; + +#pragma warning disable CS0612 // Type or member is obsolete + case "language": + packageSpec.Language = jsonReader.ReadNextTokenAsString(); + break; + + case "packInclude": + ReadPackInclude(jsonReader, packageSpec); + break; + + case "packOptions": + ReadPackOptions(jsonReader, packageSpec, ref isMappingsNull); + wasPackOptionsSet = true; + break; +#pragma warning restore CS0612 // Type or member is obsolete + + case "restore": + ReadMSBuildMetadata(jsonReader, packageSpec); + break; + + case "runtimes": + runtimeDescriptions = ReadRuntimes(jsonReader); + break; + +#pragma warning disable CS0612 // Type or member is obsolete + case "scripts": + ReadScripts(jsonReader, packageSpec); + break; +#pragma warning restore CS0612 // Type or member is obsolete + + case "supports": + compatibilityProfiles = ReadSupports(jsonReader); + break; + + case "title": + packageSpec.Title = jsonReader.ReadNextTokenAsString(); + break; + + case "version": + string version = jsonReader.ReadAsString(); + + if (version != null) + { + try + { +#pragma warning disable CS0612 // Type or member is obsolete + packageSpec.HasVersionSnapshot = PackageSpecUtility.IsSnapshotVersion(version); +#pragma warning restore CS0612 // Type or member is obsolete + packageSpec.Version = PackageSpecUtility.SpecifySnapshot(version, snapshotValue); + } + catch (Exception ex) + { + throw FileFormatException.Create(ex, version, packageSpec.FilePath); + } + } + break; + } + }); + + packageSpec.Name = name; + packageSpec.FilePath = name == null ? null : Path.GetFullPath(packageSpecPath); + +#pragma warning disable CS0612 // Type or member is obsolete + if (!wasPackOptionsSet) + { + packageSpec.Owners = Array.Empty(); + packageSpec.PackOptions = new PackOptions() + { + PackageType = Array.Empty() + }; + packageSpec.Tags = Array.Empty(); + } + + if (isMappingsNull) + { + packageSpec.PackOptions.Mappings = null; + } +#pragma warning restore CS0612 // Type or member is obsolete + + packageSpec.RuntimeGraph = new RuntimeGraph( + runtimeDescriptions ?? Enumerable.Empty(), + compatibilityProfiles ?? Enumerable.Empty()); + + if (packageSpec.Name == null) + { + packageSpec.Name = packageSpec.RestoreMetadata?.ProjectName; + } + + // Use the project.json path if one is set, otherwise use the project path + if (packageSpec.FilePath == null) + { + packageSpec.FilePath = packageSpec.RestoreMetadata?.ProjectJsonPath + ?? packageSpec.RestoreMetadata?.ProjectPath; + } + + return packageSpec; + } + + + internal static void ReadCentralTransitiveDependencyGroup( + JsonTextReader jsonReader, + IList results, + string packageSpecPath) + { + jsonReader.ReadObject(propertyName => + { + if (string.IsNullOrEmpty(propertyName)) + { + // Advance the reader's position to be able to report the line and column for the property value. + jsonReader.ReadNextToken(); + + throw FileFormatException.Create( + "Unable to resolve dependency ''.", + jsonReader.LineNumber, + jsonReader.LinePosition, + packageSpecPath); + } + + if (jsonReader.ReadNextToken()) + { + int dependencyValueLine = jsonReader.LineNumber; + int dependencyValueColumn = jsonReader.LinePosition; + var versionLine = 0; + var versionColumn = 0; + + var dependencyIncludeFlagsValue = LibraryIncludeFlags.All; + var dependencyExcludeFlagsValue = LibraryIncludeFlags.None; + var suppressParentFlagsValue = LibraryIncludeFlagUtils.DefaultSuppressParent; + string dependencyVersionValue = null; + + if (jsonReader.TokenType == JsonToken.String) + { + dependencyVersionValue = (string)jsonReader.Value; + } + else if (jsonReader.TokenType == JsonToken.StartObject) + { + jsonReader.ReadProperties(dependenciesPropertyName => + { + IEnumerable values = null; + + switch (dependenciesPropertyName) + { + case "exclude": + values = jsonReader.ReadDelimitedString(); + dependencyExcludeFlagsValue = LibraryIncludeFlagUtils.GetFlags(values); + break; + + case "include": + values = jsonReader.ReadDelimitedString(); + dependencyIncludeFlagsValue = LibraryIncludeFlagUtils.GetFlags(values); + break; + + case "suppressParent": + values = jsonReader.ReadDelimitedString(); + suppressParentFlagsValue = LibraryIncludeFlagUtils.GetFlags(values); + break; + + case "version": + if (jsonReader.ReadNextToken()) + { + versionLine = jsonReader.LineNumber; + versionColumn = jsonReader.LinePosition; + dependencyVersionValue = (string)jsonReader.Value; + } + break; + + default: + break; + } + }); + } + + VersionRange dependencyVersionRange = null; + + if (!string.IsNullOrEmpty(dependencyVersionValue)) + { + try + { + dependencyVersionRange = VersionRange.Parse(dependencyVersionValue); + } + catch (Exception ex) + { + throw FileFormatException.Create( + ex, + versionLine, + versionColumn, + packageSpecPath); + } + } + + if (dependencyVersionRange == null) + { + throw FileFormatException.Create( + new ArgumentException(Strings.MissingVersionOnDependency), + dependencyValueLine, + dependencyValueColumn, + packageSpecPath); + } + + // the dependency flags are: Include flags - Exclude flags + var includeFlags = dependencyIncludeFlagsValue & ~dependencyExcludeFlagsValue; + var libraryDependency = new LibraryDependency() + { + LibraryRange = new LibraryRange() + { + Name = propertyName, + TypeConstraint = LibraryDependencyTarget.Package, + VersionRange = dependencyVersionRange + }, + + IncludeType = includeFlags, + SuppressParent = suppressParentFlagsValue, + VersionCentrallyManaged = true, + ReferenceType = LibraryDependencyReferenceType.Transitive + }; + + results.Add(libraryDependency); + } + }); + } + + + private static PackageType CreatePackageType(JsonTextReader jsonReader) + { + var name = (string)jsonReader.Value; + + return new PackageType(name, Packaging.Core.PackageType.EmptyVersion); + } + + [Obsolete] + private static void ReadBuildOptions(JsonTextReader jsonReader, PackageSpec packageSpec) + { + packageSpec.BuildOptions = new BuildOptions(); + + jsonReader.ReadObject(buildOptionsPropertyName => + { + if (buildOptionsPropertyName == "outputName") + { + packageSpec.BuildOptions.OutputName = jsonReader.ReadNextTokenAsString(); + } + }); + } + + private static void ReadCentralPackageVersions( + JsonTextReader jsonReader, + IDictionary centralPackageVersions, + string filePath) + { + jsonReader.ReadObject(propertyName => + { + int line = jsonReader.LineNumber; + int column = jsonReader.LinePosition; + + if (string.IsNullOrEmpty(propertyName)) + { + throw FileFormatException.Create( + "Unable to resolve central version ''.", + line, + column, + filePath); + } + + string version = jsonReader.ReadNextTokenAsString(); + + if (string.IsNullOrEmpty(version)) + { + throw FileFormatException.Create( + "The version cannot be null or empty.", + line, + column, + filePath); + } + + centralPackageVersions[propertyName] = new CentralPackageVersion(propertyName, VersionRange.Parse(version)); + }); + } + + private static CompatibilityProfile ReadCompatibilityProfile(JsonTextReader jsonReader, string profileName) + { + List sets = null; + + jsonReader.ReadObject(propertyName => + { + sets = sets ?? new List(); + + IEnumerable profiles = ReadCompatibilitySets(jsonReader, propertyName); + + sets.AddRange(profiles); + }); + + return new CompatibilityProfile(profileName, sets ?? Enumerable.Empty()); + } + + private static IEnumerable ReadCompatibilitySets(JsonTextReader jsonReader, string compatibilitySetName) + { + NuGetFramework framework = NuGetFramework.Parse(compatibilitySetName); + + IReadOnlyList values = jsonReader.ReadStringOrArrayOfStringsAsReadOnlyList() ?? Array.Empty(); + + foreach (string value in values) + { + yield return new FrameworkRuntimePair(framework, value); + } + } + + private static void ReadDependencies( + JsonTextReader jsonReader, + IList results, + string packageSpecPath, + bool isGacOrFrameworkReference) + { + jsonReader.ReadObject(propertyName => + { + if (string.IsNullOrEmpty(propertyName)) + { + // Advance the reader's position to be able to report the line and column for the property value. + jsonReader.ReadNextToken(); + + throw FileFormatException.Create( + "Unable to resolve dependency ''.", + jsonReader.LineNumber, + jsonReader.LinePosition, + packageSpecPath); + } + + // Support + // "dependencies" : { + // "Name" : "1.0" + // } + + if (jsonReader.ReadNextToken()) + { + int dependencyValueLine = jsonReader.LineNumber; + int dependencyValueColumn = jsonReader.LinePosition; + var versionLine = 0; + var versionColumn = 0; + + var dependencyIncludeFlagsValue = LibraryIncludeFlags.All; + var dependencyExcludeFlagsValue = LibraryIncludeFlags.None; + var suppressParentFlagsValue = LibraryIncludeFlagUtils.DefaultSuppressParent; + List noWarn = null; + + // This method handles both the dependencies and framework assembly sections. + // Framework references should be limited to references. + // Dependencies should allow everything but framework references. + LibraryDependencyTarget targetFlagsValue = isGacOrFrameworkReference + ? LibraryDependencyTarget.Reference + : LibraryDependencyTarget.All & ~LibraryDependencyTarget.Reference; + + var autoReferenced = false; + var generatePathProperty = false; + var versionCentrallyManaged = false; + string aliases = null; + string dependencyVersionValue = null; + VersionRange versionOverride = null; + + if (jsonReader.TokenType == JsonToken.String) + { + dependencyVersionValue = (string)jsonReader.Value; + } + else if (jsonReader.TokenType == JsonToken.StartObject) + { + jsonReader.ReadProperties(dependenciesPropertyName => + { + IEnumerable values = null; + + switch (dependenciesPropertyName) + { + case "autoReferenced": + autoReferenced = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpecPath); + break; + + case "exclude": + values = jsonReader.ReadDelimitedString(); + dependencyExcludeFlagsValue = LibraryIncludeFlagUtils.GetFlags(values); + break; + + case "generatePathProperty": + generatePathProperty = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpecPath); + break; + + case "include": + values = jsonReader.ReadDelimitedString(); + dependencyIncludeFlagsValue = LibraryIncludeFlagUtils.GetFlags(values); + break; + + case "noWarn": + noWarn = ReadNuGetLogCodesList(jsonReader); + break; + + case "suppressParent": + values = jsonReader.ReadDelimitedString(); + suppressParentFlagsValue = LibraryIncludeFlagUtils.GetFlags(values); + break; + + case "target": + targetFlagsValue = ReadTarget(jsonReader, packageSpecPath, targetFlagsValue); + break; + + case "version": + if (jsonReader.ReadNextToken()) + { + versionLine = jsonReader.LineNumber; + versionColumn = jsonReader.LinePosition; + + dependencyVersionValue = (string)jsonReader.Value; + } + break; + case "versionOverride": + if (jsonReader.ReadNextToken()) + { + try + { + versionOverride = VersionRange.Parse((string)jsonReader.Value); + } + catch (Exception ex) + { + throw FileFormatException.Create( + ex, + jsonReader.LineNumber, + jsonReader.LinePosition, + packageSpecPath); + } + } + break; + case "versionCentrallyManaged": + versionCentrallyManaged = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpecPath); + break; + + case "aliases": + aliases = jsonReader.ReadAsString(); + break; + } + }); + } + + VersionRange dependencyVersionRange = null; + + if (!string.IsNullOrEmpty(dependencyVersionValue)) + { + try + { + dependencyVersionRange = VersionRange.Parse(dependencyVersionValue); + } + catch (Exception ex) + { + throw FileFormatException.Create( + ex, + versionLine, + versionColumn, + packageSpecPath); + } + } + + // Projects and References may have empty version ranges, Packages may not + if (dependencyVersionRange == null) + { + if ((targetFlagsValue & LibraryDependencyTarget.Package) == LibraryDependencyTarget.Package) + { + throw FileFormatException.Create( + new ArgumentException(Strings.MissingVersionOnDependency), + dependencyValueLine, + dependencyValueColumn, + packageSpecPath); + } + else + { + // Projects and references with no version property allow all versions + dependencyVersionRange = VersionRange.All; + } + } + + // the dependency flags are: Include flags - Exclude flags + var includeFlags = dependencyIncludeFlagsValue & ~dependencyExcludeFlagsValue; + var libraryDependency = new LibraryDependency() + { + LibraryRange = new LibraryRange() + { + Name = propertyName, + TypeConstraint = targetFlagsValue, + VersionRange = dependencyVersionRange + }, + IncludeType = includeFlags, + SuppressParent = suppressParentFlagsValue, + AutoReferenced = autoReferenced, + GeneratePathProperty = generatePathProperty, + VersionCentrallyManaged = versionCentrallyManaged, + Aliases = aliases, + // The ReferenceType is not persisted to the assets file + // Default to LibraryDependencyReferenceType.Direct on Read + ReferenceType = LibraryDependencyReferenceType.Direct, + VersionOverride = versionOverride + }; + + if (noWarn != null) + { + libraryDependency.NoWarn = noWarn; + } + + results.Add(libraryDependency); + } + }); + } + + private static void ReadDownloadDependencies( + JsonTextReader jsonReader, + IList downloadDependencies, + string packageSpecPath) + { + var seenIds = new HashSet(); + + if (jsonReader.ReadNextToken() && jsonReader.TokenType == JsonToken.StartArray) + { + do + { + string name = null; + string versionValue = null; + var isNameDefined = false; + var isVersionDefined = false; + int line = jsonReader.LineNumber; + int column = jsonReader.LinePosition; + int versionLine = 0; + int versionColumn = 0; + + jsonReader.ReadObject(propertyName => + { + switch (propertyName) + { + case "name": + isNameDefined = true; + name = jsonReader.ReadNextTokenAsString(); + break; + + case "version": + isVersionDefined = true; + versionValue = jsonReader.ReadNextTokenAsString(); + versionLine = jsonReader.LineNumber; + versionColumn = jsonReader.LinePosition; + break; + } + }, out line, out column); + + if (jsonReader.TokenType == JsonToken.EndArray) + { + break; + } + + if (!isNameDefined) + { + throw FileFormatException.Create( + "Unable to resolve downloadDependency ''.", + line, + column, + packageSpecPath); + } + + if (!seenIds.Add(name)) + { + // package ID already seen, only use first definition. + continue; + } + + if (string.IsNullOrEmpty(versionValue)) + { + throw FileFormatException.Create( + "The version cannot be null or empty", + isVersionDefined ? versionLine : line, + isVersionDefined ? versionColumn : column, + packageSpecPath); + } + + string[] versions = versionValue.Split(VersionSeparators, StringSplitOptions.RemoveEmptyEntries); + + foreach (string singleVersionValue in versions) + { + try + { + VersionRange version = VersionRange.Parse(singleVersionValue); + + downloadDependencies.Add(new DownloadDependency(name, version)); + } + catch (Exception ex) + { + throw FileFormatException.Create( + ex, + isVersionDefined ? versionLine : line, + isVersionDefined ? versionColumn : column, + packageSpecPath); + } + } + } while (jsonReader.TokenType == JsonToken.EndObject); + } + } + + private static IReadOnlyList ReadEnumerableOfString(JsonTextReader jsonReader) + { + string value = jsonReader.ReadNextTokenAsString(); + + return value.Split(DelimitedStringSeparators, StringSplitOptions.RemoveEmptyEntries); + } + + private static void ReadFrameworkReferences( + JsonTextReader jsonReader, + ISet frameworkReferences, + string packageSpecPath) + { + jsonReader.ReadObject(frameworkName => + { + if (string.IsNullOrEmpty(frameworkName)) + { + // Advance the reader's position to be able to report the line and column for the property value. + jsonReader.ReadNextToken(); + + throw FileFormatException.Create( + "Unable to resolve frameworkReference.", + jsonReader.LineNumber, + jsonReader.LinePosition, + packageSpecPath); + } + + var privateAssets = FrameworkDependencyFlagsUtils.Default; + + jsonReader.ReadObject(propertyName => + { + if (propertyName == "privateAssets") + { + IEnumerable strings = ReadEnumerableOfString(jsonReader); + + privateAssets = FrameworkDependencyFlagsUtils.GetFlags(strings); + } + }); + + frameworkReferences.Add(new FrameworkDependency(frameworkName, privateAssets)); + }); + } + + private static void ReadFrameworks(JsonTextReader jsonReader, PackageSpec packageSpec) + { + jsonReader.ReadObject(_ => + { + var frameworkLine = 0; + var frameworkColumn = 0; + + try + { + ReadTargetFrameworks(packageSpec, jsonReader, out frameworkLine, out frameworkColumn); + } + catch (Exception ex) + { + throw FileFormatException.Create(ex, frameworkLine, frameworkColumn, packageSpec.FilePath); + } + }); + } + + private static void ReadImports(PackageSpec packageSpec, JsonTextReader jsonReader, TargetFrameworkInformation targetFrameworkInformation) + { + int lineNumber = jsonReader.LineNumber; + int linePosition = jsonReader.LinePosition; + + IReadOnlyList imports = jsonReader.ReadStringOrArrayOfStringsAsReadOnlyList(); + + if (imports != null && imports.Count > 0) + { + foreach (string import in imports.Where(element => !string.IsNullOrEmpty(element))) + { + NuGetFramework framework = NuGetFramework.Parse(import); + + if (!framework.IsSpecificFramework) + { + throw FileFormatException.Create( + string.Format( + CultureInfo.CurrentCulture, + Strings.Log_InvalidImportFramework, + import, + PackageSpec.PackageSpecFileName), + lineNumber, + linePosition, + packageSpec.FilePath); + } + + targetFrameworkInformation.Imports.Add(framework); + } + } + } + + private static void ReadMappings(JsonTextReader jsonReader, string mappingKey, IDictionary mappings) + { + if (jsonReader.ReadNextToken()) + { + switch (jsonReader.TokenType) + { + case JsonToken.String: + { + var files = new IncludeExcludeFiles() + { + Include = new[] { (string)jsonReader.Value } + }; + + mappings.Add(mappingKey, files); + } + break; + + case JsonToken.StartArray: + { + IReadOnlyList include = jsonReader.ReadStringArrayAsReadOnlyListFromArrayStart(); + + var files = new IncludeExcludeFiles() + { + Include = include + }; + + mappings.Add(mappingKey, files); + } + break; + + case JsonToken.StartObject: + { + IReadOnlyList excludeFiles = null; + IReadOnlyList exclude = null; + IReadOnlyList includeFiles = null; + IReadOnlyList include = null; + + jsonReader.ReadProperties(filesPropertyName => + { + switch (filesPropertyName) + { + case "excludeFiles": + excludeFiles = jsonReader.ReadStringOrArrayOfStringsAsReadOnlyList(); + break; + + case "exclude": + exclude = jsonReader.ReadStringOrArrayOfStringsAsReadOnlyList(); + break; + + case "includeFiles": + includeFiles = jsonReader.ReadStringOrArrayOfStringsAsReadOnlyList(); + break; + + case "include": + include = jsonReader.ReadStringOrArrayOfStringsAsReadOnlyList(); + break; + } + }); + + if (include != null || includeFiles != null || exclude != null || excludeFiles != null) + { + var files = new IncludeExcludeFiles() + { + ExcludeFiles = excludeFiles, + Exclude = exclude, + IncludeFiles = includeFiles, + Include = include + }; + + mappings.Add(mappingKey, files); + } + } + break; + } + } + } + + private static void ReadMSBuildMetadata(JsonTextReader jsonReader, PackageSpec packageSpec) + { + var centralPackageVersionsManagementEnabled = false; + var centralPackageVersionOverrideDisabled = false; + var CentralPackageTransitivePinningEnabled = false; + List configFilePaths = null; + var crossTargeting = false; + List fallbackFolders = null; + List files = null; + var legacyPackagesDirectory = false; + ProjectRestoreMetadata msbuildMetadata = null; + List originalTargetFrameworks = null; + string outputPath = null; + string packagesConfigPath = null; + string packagesPath = null; + string projectJsonPath = null; + string projectName = null; + string projectPath = null; + ProjectStyle? projectStyle = null; + string projectUniqueName = null; + RestoreLockProperties restoreLockProperties = null; + var skipContentFileWrite = false; + List sources = null; + List targetFrameworks = null; + var validateRuntimeAssets = false; + WarningProperties warningProperties = null; + RestoreAuditProperties auditProperties = null; + + jsonReader.ReadObject(propertyName => + { + switch (propertyName) + { + case "centralPackageVersionsManagementEnabled": + centralPackageVersionsManagementEnabled = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpec.FilePath); + break; + + case "centralPackageVersionOverrideDisabled": + centralPackageVersionOverrideDisabled = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpec.FilePath); + break; + + case "CentralPackageTransitivePinningEnabled": + CentralPackageTransitivePinningEnabled = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpec.FilePath); + break; + + case "configFilePaths": + configFilePaths = jsonReader.ReadStringArrayAsList(); + break; + + case "crossTargeting": + crossTargeting = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpec.FilePath); + break; + + case "fallbackFolders": + fallbackFolders = jsonReader.ReadStringArrayAsList(); + break; + + case "files": + jsonReader.ReadObject(filePropertyName => + { + files = files ?? new List(); + + files.Add(new ProjectRestoreMetadataFile(filePropertyName, jsonReader.ReadNextTokenAsString())); + }); + break; + + case "frameworks": + targetFrameworks = ReadTargetFrameworks(jsonReader); + break; + + case "legacyPackagesDirectory": + legacyPackagesDirectory = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpec.FilePath); + break; + + case "originalTargetFrameworks": + originalTargetFrameworks = jsonReader.ReadStringArrayAsList(); + break; + + case "outputPath": + outputPath = jsonReader.ReadNextTokenAsString(); + break; + + case "packagesConfigPath": + packagesConfigPath = jsonReader.ReadNextTokenAsString(); + break; + + case "packagesPath": + packagesPath = jsonReader.ReadNextTokenAsString(); + break; + + case "projectJsonPath": + projectJsonPath = jsonReader.ReadNextTokenAsString(); + break; + + case "projectName": + projectName = jsonReader.ReadNextTokenAsString(); + break; + + case "projectPath": + projectPath = jsonReader.ReadNextTokenAsString(); + break; + + case "projectStyle": + string projectStyleString = jsonReader.ReadNextTokenAsString(); + + if (!string.IsNullOrEmpty(projectStyleString) + && Enum.TryParse(projectStyleString, ignoreCase: true, result: out ProjectStyle projectStyleValue)) + { + projectStyle = projectStyleValue; + } + break; + + case "projectUniqueName": + projectUniqueName = jsonReader.ReadNextTokenAsString(); + break; + + case "restoreLockProperties": + string nuGetLockFilePath = null; + var restoreLockedMode = false; + string restorePackagesWithLockFile = null; + + jsonReader.ReadObject(restoreLockPropertiesPropertyName => + { + switch (restoreLockPropertiesPropertyName) + { + case "nuGetLockFilePath": + nuGetLockFilePath = jsonReader.ReadNextTokenAsString(); + break; + + case "restoreLockedMode": + restoreLockedMode = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpec.FilePath); + break; + + case "restorePackagesWithLockFile": + restorePackagesWithLockFile = jsonReader.ReadNextTokenAsString(); + break; + } + }); + + restoreLockProperties = new RestoreLockProperties(restorePackagesWithLockFile, nuGetLockFilePath, restoreLockedMode); + break; + + case "restoreAuditProperties": + string enableAudit = null, auditLevel = null, auditMode = null; + jsonReader.ReadObject(auditPropertyName => + { + + switch (auditPropertyName) + { + case "enableAudit": + enableAudit = jsonReader.ReadNextTokenAsString(); + break; + + case "auditLevel": + auditLevel = jsonReader.ReadNextTokenAsString(); + break; + + case "auditMode": + auditMode = jsonReader.ReadNextTokenAsString(); + break; + } + }); + auditProperties = new RestoreAuditProperties() + { + EnableAudit = enableAudit, + AuditLevel = auditLevel, + AuditMode = auditMode, + }; + break; + + case "skipContentFileWrite": + skipContentFileWrite = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpec.FilePath); + break; + + case "sources": + jsonReader.ReadObject(sourcePropertyName => + { + sources = sources ?? new List(); + + sources.Add(new PackageSource(sourcePropertyName)); + }); + break; + + case "validateRuntimeAssets": + validateRuntimeAssets = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpec.FilePath); + break; + + case "warningProperties": + var allWarningsAsErrors = false; + var noWarn = new HashSet(); + var warnAsError = new HashSet(); + var warningsNotAsErrors = new HashSet(); + + jsonReader.ReadObject(warningPropertiesPropertyName => + { + switch (warningPropertiesPropertyName) + { + case "allWarningsAsErrors": + allWarningsAsErrors = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpec.FilePath); + break; + + case "noWarn": + ReadNuGetLogCodes(jsonReader, noWarn); + break; + + case "warnAsError": + ReadNuGetLogCodes(jsonReader, warnAsError); + break; + + case "warnNotAsError": + ReadNuGetLogCodes(jsonReader, warningsNotAsErrors); + break; + } + }); + + warningProperties = new WarningProperties(warnAsError, noWarn, allWarningsAsErrors, warningsNotAsErrors); + break; + } + }); + + if (projectStyle == ProjectStyle.PackagesConfig) + { + msbuildMetadata = new PackagesConfigProjectRestoreMetadata() + { + PackagesConfigPath = packagesConfigPath + }; + } + else + { + msbuildMetadata = new ProjectRestoreMetadata(); + } + + msbuildMetadata.CentralPackageVersionsEnabled = centralPackageVersionsManagementEnabled; + msbuildMetadata.CentralPackageVersionOverrideDisabled = centralPackageVersionOverrideDisabled; + msbuildMetadata.CentralPackageTransitivePinningEnabled = CentralPackageTransitivePinningEnabled; + msbuildMetadata.RestoreAuditProperties = auditProperties; + + if (configFilePaths != null) + { + msbuildMetadata.ConfigFilePaths = configFilePaths; + } + + msbuildMetadata.CrossTargeting = crossTargeting; + + if (fallbackFolders != null) + { + msbuildMetadata.FallbackFolders = fallbackFolders; + } + + if (files != null) + { + msbuildMetadata.Files = files; + } + + msbuildMetadata.LegacyPackagesDirectory = legacyPackagesDirectory; + + if (originalTargetFrameworks != null) + { + msbuildMetadata.OriginalTargetFrameworks = originalTargetFrameworks; + } + + msbuildMetadata.OutputPath = outputPath; + msbuildMetadata.PackagesPath = packagesPath; + msbuildMetadata.ProjectJsonPath = projectJsonPath; + msbuildMetadata.ProjectName = projectName; + msbuildMetadata.ProjectPath = projectPath; + + if (projectStyle.HasValue) + { + msbuildMetadata.ProjectStyle = projectStyle.Value; + } + + msbuildMetadata.ProjectUniqueName = projectUniqueName; + + if (restoreLockProperties != null) + { + msbuildMetadata.RestoreLockProperties = restoreLockProperties; + } + + msbuildMetadata.SkipContentFileWrite = skipContentFileWrite; + + if (sources != null) + { + msbuildMetadata.Sources = sources; + } + + if (targetFrameworks != null) + { + msbuildMetadata.TargetFrameworks = targetFrameworks; + } + + msbuildMetadata.ValidateRuntimeAssets = validateRuntimeAssets; + + if (warningProperties != null) + { + msbuildMetadata.ProjectWideWarningProperties = warningProperties; + } + + packageSpec.RestoreMetadata = msbuildMetadata; + } + + private static bool ReadNextTokenAsBoolOrFalse(JsonTextReader jsonReader, string filePath) + { + if (jsonReader.ReadNextToken() && jsonReader.TokenType == JsonToken.Boolean) + { + try + { + return (bool)jsonReader.Value; + } + catch (Exception ex) + { + throw FileFormatException.Create(ex, jsonReader.LineNumber, jsonReader.LinePosition, filePath); + } + } + + return false; + } + + private static void ReadNuGetLogCodes(JsonTextReader jsonReader, HashSet hashCodes) + { + if (jsonReader.ReadNextToken() && jsonReader.TokenType == JsonToken.StartArray) + { + while (jsonReader.ReadNextToken() && jsonReader.TokenType != JsonToken.EndArray) + { + if (jsonReader.TokenType == JsonToken.String && Enum.TryParse((string)jsonReader.Value, out NuGetLogCode code)) + { + hashCodes.Add(code); + } + } + } + } + + private static List ReadNuGetLogCodesList(JsonTextReader jsonReader) + { + List items = null; + + if (jsonReader.ReadNextToken() && jsonReader.TokenType == JsonToken.StartArray) + { + while (jsonReader.ReadNextToken() && jsonReader.TokenType != JsonToken.EndArray) + { + if (jsonReader.TokenType == JsonToken.String && Enum.TryParse((string)jsonReader.Value, out NuGetLogCode code)) + { + items = items ?? new List(); + + items.Add(code); + } + } + } + + return items; + } + + private static void ReadPackageTypes(PackageSpec packageSpec, JsonTextReader jsonReader) + { + var errorLine = 0; + var errorColumn = 0; + + IReadOnlyList packageTypes = null; + PackageType packageType = null; + + try + { + if (jsonReader.ReadNextToken()) + { + errorLine = jsonReader.LineNumber; + errorColumn = jsonReader.LinePosition; + + switch (jsonReader.TokenType) + { + case JsonToken.String: + packageType = CreatePackageType(jsonReader); + + packageTypes = new[] { packageType }; + break; + + case JsonToken.StartArray: + var types = new List(); + + while (jsonReader.ReadNextToken() && jsonReader.TokenType != JsonToken.EndArray) + { + if (jsonReader.TokenType != JsonToken.String) + { + throw FileFormatException.Create( + string.Format( + CultureInfo.CurrentCulture, + Strings.InvalidPackageType, + PackageSpec.PackageSpecFileName), + errorLine, + errorColumn, + packageSpec.FilePath); + } + + packageType = CreatePackageType(jsonReader); + + types.Add(packageType); + } + + packageTypes = types; + break; + + case JsonToken.Null: + break; + + default: + throw new InvalidCastException(); + } + +#pragma warning disable CS0612 // Type or member is obsolete + if (packageTypes != null) + { + packageSpec.PackOptions.PackageType = packageTypes; + } +#pragma warning restore CS0612 // Type or member is obsolete + } + } + catch (Exception) + { + throw FileFormatException.Create( + string.Format( + CultureInfo.CurrentCulture, + Strings.InvalidPackageType, + PackageSpec.PackageSpecFileName), + errorLine, + errorColumn, + packageSpec.FilePath); + } + } + + [Obsolete] + private static void ReadPackInclude(JsonTextReader jsonReader, PackageSpec packageSpec) + { + jsonReader.ReadObject(propertyName => + { + string propertyValue = jsonReader.ReadAsString(); + + packageSpec.PackInclude.Add(new KeyValuePair(propertyName, propertyValue)); + }); + } + + [Obsolete] + private static void ReadPackOptions(JsonTextReader jsonReader, PackageSpec packageSpec, ref bool isMappingsNull) + { + var wasMappingsRead = false; + + bool isPackOptionsValueAnObject = jsonReader.ReadObject(propertyName => + { + switch (propertyName) + { + case "files": + wasMappingsRead = ReadPackOptionsFiles(packageSpec, jsonReader, wasMappingsRead); + break; + + case "iconUrl": + packageSpec.IconUrl = jsonReader.ReadNextTokenAsString(); + break; + + case "licenseUrl": + packageSpec.LicenseUrl = jsonReader.ReadNextTokenAsString(); + break; + + case "owners": + string[] owners = ReadStringArray(jsonReader); + + if (owners != null) + { + packageSpec.Owners = owners; + } + break; + + case "packageType": + ReadPackageTypes(packageSpec, jsonReader); + break; + + case "projectUrl": + packageSpec.ProjectUrl = jsonReader.ReadNextTokenAsString(); + break; + + case "releaseNotes": + packageSpec.ReleaseNotes = jsonReader.ReadNextTokenAsString(); + break; + + case "requireLicenseAcceptance": + packageSpec.RequireLicenseAcceptance = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpec.FilePath); + break; + + case "summary": + packageSpec.Summary = jsonReader.ReadNextTokenAsString(); + break; + + case "tags": + string[] tags = ReadStringArray(jsonReader); + + if (tags != null) + { + packageSpec.Tags = tags; + } + break; + } + }); + + isMappingsNull = isPackOptionsValueAnObject && !wasMappingsRead; + } + + [Obsolete] + private static bool ReadPackOptionsFiles(PackageSpec packageSpec, JsonTextReader jsonReader, bool wasMappingsRead) + { + IReadOnlyList excludeFiles = null; + IReadOnlyList exclude = null; + IReadOnlyList includeFiles = null; + IReadOnlyList include = null; + + jsonReader.ReadObject(filesPropertyName => + { + switch (filesPropertyName) + { + case "excludeFiles": + excludeFiles = jsonReader.ReadStringOrArrayOfStringsAsReadOnlyList(); + break; + + case "exclude": + exclude = jsonReader.ReadStringOrArrayOfStringsAsReadOnlyList(); + break; + + case "includeFiles": + includeFiles = jsonReader.ReadStringOrArrayOfStringsAsReadOnlyList(); + break; + + case "include": + include = jsonReader.ReadStringOrArrayOfStringsAsReadOnlyList(); + break; + + case "mappings": + jsonReader.ReadObject(mappingsPropertyName => + { + wasMappingsRead = true; + + ReadMappings(jsonReader, mappingsPropertyName, packageSpec.PackOptions.Mappings); + }); + break; + } + }); + + if (include != null || includeFiles != null || exclude != null || excludeFiles != null) + { + packageSpec.PackOptions.IncludeExcludeFiles = new IncludeExcludeFiles() + { + ExcludeFiles = excludeFiles, + Exclude = exclude, + IncludeFiles = includeFiles, + Include = include + }; + } + + return wasMappingsRead; + } + + private static RuntimeDependencySet ReadRuntimeDependencySet(JsonTextReader jsonReader, string dependencySetName) + { + List dependencies = null; + + jsonReader.ReadObject(propertyName => + { + dependencies ??= new List(); + + var dependency = new RuntimePackageDependency(propertyName, VersionRange.Parse(jsonReader.ReadNextTokenAsString())); + + dependencies.Add(dependency); + }); + + return new RuntimeDependencySet( + dependencySetName, + dependencies); + } + + private static RuntimeDescription ReadRuntimeDescription(JsonTextReader jsonReader, string runtimeName) + { + List inheritedRuntimes = null; + List additionalDependencies = null; + + jsonReader.ReadObject(propertyName => + { + if (propertyName == "#import") + { + inheritedRuntimes = jsonReader.ReadStringArrayAsList(); + } + else + { + additionalDependencies ??= new List(); + + RuntimeDependencySet dependency = ReadRuntimeDependencySet(jsonReader, propertyName); + + additionalDependencies.Add(dependency); + } + }); + + return new RuntimeDescription( + runtimeName, + inheritedRuntimes, + additionalDependencies); + } + + private static List ReadRuntimes(JsonTextReader jsonReader) + { + var runtimeDescriptions = new List(); + + jsonReader.ReadObject(propertyName => + { + RuntimeDescription runtimeDescription = ReadRuntimeDescription(jsonReader, propertyName); + + runtimeDescriptions.Add(runtimeDescription); + }); + + return runtimeDescriptions; + } + + [Obsolete] + private static void ReadScripts(JsonTextReader jsonReader, PackageSpec packageSpec) + { + jsonReader.ReadObject(propertyName => + { + if (jsonReader.ReadNextToken()) + { + if (jsonReader.TokenType == JsonToken.String) + { + packageSpec.Scripts[propertyName] = new string[] { (string)jsonReader.Value }; + } + else if (jsonReader.TokenType == JsonToken.StartArray) + { + var list = new List(); + + while (jsonReader.ReadNextToken() && jsonReader.TokenType == JsonToken.String) + { + list.Add((string)jsonReader.Value); + } + + packageSpec.Scripts[propertyName] = list; + } + else + { + throw FileFormatException.Create( + string.Format(CultureInfo.CurrentCulture, "The value of a script in '{0}' can only be a string or an array of strings", PackageSpec.PackageSpecFileName), + jsonReader.LineNumber, + jsonReader.LinePosition, + packageSpec.FilePath); + } + } + }); + } + + private static string[] ReadStringArray(JsonTextReader jsonReader) + { + List list = jsonReader.ReadStringArrayAsList(); + + return list?.ToArray(); + } + + private static List ReadSupports(JsonTextReader jsonReader) + { + var compatibilityProfiles = new List(); + + jsonReader.ReadObject(propertyName => + { + CompatibilityProfile compatibilityProfile = ReadCompatibilityProfile(jsonReader, propertyName); + + compatibilityProfiles.Add(compatibilityProfile); + }); + + return compatibilityProfiles; + } + + private static LibraryDependencyTarget ReadTarget( + JsonTextReader jsonReader, + string packageSpecPath, + LibraryDependencyTarget targetFlagsValue) + { + if (jsonReader.ReadNextToken()) + { + var targetString = (string)jsonReader.Value; + + targetFlagsValue = LibraryDependencyTargetUtils.Parse(targetString); + + // Verify that the value specified is package, project, or external project + if (!ValidateDependencyTarget(targetFlagsValue)) + { + string message = string.Format( + CultureInfo.CurrentCulture, + Strings.InvalidDependencyTarget, + targetString); + + throw FileFormatException.Create( + message, + jsonReader.LineNumber, + jsonReader.LinePosition, + packageSpecPath); + } + } + + return targetFlagsValue; + } + + private static List ReadTargetFrameworks(JsonTextReader jsonReader) + { + var targetFrameworks = new List(); + + jsonReader.ReadObject(frameworkPropertyName => + { + NuGetFramework framework = NuGetFramework.Parse(frameworkPropertyName); + var frameworkGroup = new ProjectRestoreMetadataFrameworkInfo(framework); + + jsonReader.ReadObject(propertyName => + { + if (propertyName == "projectReferences") + { + jsonReader.ReadObject(projectReferencePropertyName => + { + string excludeAssets = null; + string includeAssets = null; + string privateAssets = null; + string projectReferenceProjectPath = null; + + jsonReader.ReadObject(projectReferenceObjectPropertyName => + { + switch (projectReferenceObjectPropertyName) + { + case "excludeAssets": + excludeAssets = jsonReader.ReadNextTokenAsString(); + break; + + case "includeAssets": + includeAssets = jsonReader.ReadNextTokenAsString(); + break; + + case "privateAssets": + privateAssets = jsonReader.ReadNextTokenAsString(); + break; + + case "projectPath": + projectReferenceProjectPath = jsonReader.ReadNextTokenAsString(); + break; + } + }); + + frameworkGroup.ProjectReferences.Add(new ProjectRestoreReference() + { + ProjectUniqueName = projectReferencePropertyName, + ProjectPath = projectReferenceProjectPath, + + IncludeAssets = LibraryIncludeFlagUtils.GetFlags( + flags: includeAssets, + defaultFlags: LibraryIncludeFlags.All), + + ExcludeAssets = LibraryIncludeFlagUtils.GetFlags( + flags: excludeAssets, + defaultFlags: LibraryIncludeFlags.None), + + PrivateAssets = LibraryIncludeFlagUtils.GetFlags( + flags: privateAssets, + defaultFlags: LibraryIncludeFlagUtils.DefaultSuppressParent), + }); + }); + } + else if (propertyName == "targetAlias") + { + frameworkGroup.TargetAlias = jsonReader.ReadNextTokenAsString(); + } + }); + + targetFrameworks.Add(frameworkGroup); + }); + + return targetFrameworks; + } + + private static void ReadTargetFrameworks(PackageSpec packageSpec, JsonTextReader jsonReader, out int frameworkLine, out int frameworkColumn) + { + frameworkLine = 0; + frameworkColumn = 0; + + NuGetFramework frameworkName = NuGetFramework.Parse((string)jsonReader.Value); + + var targetFrameworkInformation = new TargetFrameworkInformation(); + NuGetFramework secondaryFramework = default; + jsonReader.ReadObject(propertyName => + { + switch (propertyName) + { + case "assetTargetFallback": + targetFrameworkInformation.AssetTargetFallback = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpec.FilePath); + break; + + case "secondaryFramework": + var secondaryFrameworkString = jsonReader.ReadAsString(); + if (!string.IsNullOrEmpty(secondaryFrameworkString)) + { + secondaryFramework = NuGetFramework.Parse(secondaryFrameworkString); + } + break; + + case "centralPackageVersions": + ReadCentralPackageVersions( + jsonReader, + targetFrameworkInformation.CentralPackageVersions, + packageSpec.FilePath); + break; + + case "dependencies": + ReadDependencies( + jsonReader, + targetFrameworkInformation.Dependencies, + packageSpec.FilePath, + isGacOrFrameworkReference: false); + break; + + case "downloadDependencies": + ReadDownloadDependencies( + jsonReader, + targetFrameworkInformation.DownloadDependencies, + packageSpec.FilePath); + break; + + case "frameworkAssemblies": + ReadDependencies( + jsonReader, + targetFrameworkInformation.Dependencies, + packageSpec.FilePath, + isGacOrFrameworkReference: true); + break; + + case "frameworkReferences": + ReadFrameworkReferences( + jsonReader, + targetFrameworkInformation.FrameworkReferences, + packageSpec.FilePath); + break; + + case "imports": + ReadImports(packageSpec, jsonReader, targetFrameworkInformation); + break; + + case "runtimeIdentifierGraphPath": + targetFrameworkInformation.RuntimeIdentifierGraphPath = jsonReader.ReadNextTokenAsString(); + break; + + case "targetAlias": + targetFrameworkInformation.TargetAlias = jsonReader.ReadNextTokenAsString(); + break; + + case "warn": + targetFrameworkInformation.Warn = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpec.FilePath); + break; + } + }, out frameworkLine, out frameworkColumn); + + NuGetFramework updatedFramework = frameworkName; + + if (targetFrameworkInformation.Imports.Count > 0) + { + NuGetFramework[] imports = targetFrameworkInformation.Imports.ToArray(); + + if (targetFrameworkInformation.AssetTargetFallback) + { + updatedFramework = new AssetTargetFallbackFramework(GetDualCompatibilityFrameworkIfNeeded(frameworkName, secondaryFramework), imports); + } + else + { + updatedFramework = new FallbackFramework(GetDualCompatibilityFrameworkIfNeeded(frameworkName, secondaryFramework), imports); + } + } + else + { + updatedFramework = GetDualCompatibilityFrameworkIfNeeded(frameworkName, secondaryFramework); + } + + targetFrameworkInformation.FrameworkName = updatedFramework; + + packageSpec.TargetFrameworks.Add(targetFrameworkInformation); + } + + private static NuGetFramework GetDualCompatibilityFrameworkIfNeeded(NuGetFramework frameworkName, NuGetFramework secondaryFramework) + { + if (secondaryFramework != default) + { + return new DualCompatibilityFramework(frameworkName, secondaryFramework); + } + + return frameworkName; + } + + private static bool ValidateDependencyTarget(LibraryDependencyTarget targetValue) + { + var isValid = false; + + switch (targetValue) + { + case LibraryDependencyTarget.Package: + case LibraryDependencyTarget.Project: + case LibraryDependencyTarget.ExternalProject: + isValid = true; + break; + } + + return isValid; + } + } +} diff --git a/src/NuGet.Core/NuGet.ProjectModel/Utf8JsonPackageSpecStreamReader.cs b/src/NuGet.Core/NuGet.ProjectModel/Utf8JsonPackageSpecStreamReader.cs new file mode 100644 index 00000000000..6186d5f4677 --- /dev/null +++ b/src/NuGet.Core/NuGet.ProjectModel/Utf8JsonPackageSpecStreamReader.cs @@ -0,0 +1,1860 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.Json; +using NuGet.Common; +using NuGet.Configuration; +using NuGet.Frameworks; +using NuGet.LibraryModel; +using NuGet.Packaging.Core; +using NuGet.RuntimeModel; +using NuGet.Versioning; + +namespace NuGet.ProjectModel +{ + internal static class Utf8JsonStreamPackageSpecReader + { + private static readonly char[] VersionSeparators = new[] { ';' }; + private static readonly byte[] Utf8Authors = Encoding.UTF8.GetBytes("authors"); + private static readonly byte[] BuildOptionsUtf8 = Encoding.UTF8.GetBytes("buildOptions"); + private static readonly byte[] Utf8ContentFiles = Encoding.UTF8.GetBytes("contentFiles"); + private static readonly byte[] Utf8Copyright = Encoding.UTF8.GetBytes("copyright"); + private static readonly byte[] Utf8Dependencies = Encoding.UTF8.GetBytes("dependencies"); + private static readonly byte[] Utf8Description = Encoding.UTF8.GetBytes("description"); + private static readonly byte[] Utf8Language = Encoding.UTF8.GetBytes("language"); + private static readonly byte[] Utf8PackInclude = Encoding.UTF8.GetBytes("packInclude"); + private static readonly byte[] Utf8PackOptions = Encoding.UTF8.GetBytes("packOptions"); + private static readonly byte[] Utf8Scripts = Encoding.UTF8.GetBytes("scripts"); + private static readonly byte[] Utf8Frameworks = Encoding.UTF8.GetBytes("frameworks"); + private static readonly byte[] Utf8Restore = Encoding.UTF8.GetBytes("restore"); + private static readonly byte[] Utf8Runtimes = Encoding.UTF8.GetBytes("runtimes"); + private static readonly byte[] Utf8Supports = Encoding.UTF8.GetBytes("supports"); + private static readonly byte[] Utf8Title = Encoding.UTF8.GetBytes("title"); + private static readonly byte[] Utf8Version = Encoding.UTF8.GetBytes("version"); + private static readonly byte[] Utf8OutputName = Encoding.UTF8.GetBytes("outputName"); + private static readonly byte[] Utf8AutoReferenced = Encoding.UTF8.GetBytes("autoReferenced"); + private static readonly byte[] Utf8Exclude = Encoding.UTF8.GetBytes("exclude"); + private static readonly byte[] Utf8GeneratePathProperty = Encoding.UTF8.GetBytes("generatePathProperty"); + private static readonly byte[] Utf8Include = Encoding.UTF8.GetBytes("include"); + private static readonly byte[] Utf8NoWarn = Encoding.UTF8.GetBytes("noWarn"); + private static readonly byte[] Utf8SuppressParent = Encoding.UTF8.GetBytes("suppressParent"); + private static readonly byte[] Utf8Target = Encoding.UTF8.GetBytes("target"); + private static readonly byte[] Utf8VersionOverride = Encoding.UTF8.GetBytes("versionOverride"); + private static readonly byte[] Utf8VersionCentrallyManaged = Encoding.UTF8.GetBytes("versionCentrallyManaged"); + private static readonly byte[] Utf8Aliases = Encoding.UTF8.GetBytes("aliases"); + private static readonly byte[] Utf8Name = Encoding.UTF8.GetBytes("name"); + private static readonly byte[] Utf8PrivateAssets = Encoding.UTF8.GetBytes("privateAssets"); + private static readonly byte[] Utf8ExcludeFiles = Encoding.UTF8.GetBytes("excludeFiles"); + private static readonly byte[] Utf8IncludeFiles = Encoding.UTF8.GetBytes("includeFiles"); + private static readonly byte[] Utf8CentralPackageVersionsManagementEnabled = Encoding.UTF8.GetBytes("centralPackageVersionsManagementEnabled"); + private static readonly byte[] Utf8CentralPackageVersionOverrideDisabled = Encoding.UTF8.GetBytes("centralPackageVersionOverrideDisabled"); + private static readonly byte[] Utf8CentralPackageTransitivePinningEnabled = Encoding.UTF8.GetBytes("CentralPackageTransitivePinningEnabled"); + private static readonly byte[] Utf8ConfigFilePaths = Encoding.UTF8.GetBytes("configFilePaths"); + private static readonly byte[] Utf8CrossTargeting = Encoding.UTF8.GetBytes("crossTargeting"); + private static readonly byte[] Utf8FallbackFolders = Encoding.UTF8.GetBytes("fallbackFolders"); + private static readonly byte[] Utf8Files = Encoding.UTF8.GetBytes("files"); + private static readonly byte[] Utf8LegacyPackagesDirectory = Encoding.UTF8.GetBytes("legacyPackagesDirectory"); + private static readonly byte[] Utf8OriginalTargetFrameworks = Encoding.UTF8.GetBytes("originalTargetFrameworks"); + private static readonly byte[] Utf8OutputPath = Encoding.UTF8.GetBytes("outputPath"); + private static readonly byte[] Utf8PackagesConfigPath = Encoding.UTF8.GetBytes("packagesConfigPath"); + private static readonly byte[] Utf8PackagesPath = Encoding.UTF8.GetBytes("packagesPath"); + private static readonly byte[] Utf8ProjectJsonPath = Encoding.UTF8.GetBytes("projectJsonPath"); + private static readonly byte[] Utf8ProjectName = Encoding.UTF8.GetBytes("projectName"); + private static readonly byte[] Utf8ProjectPath = Encoding.UTF8.GetBytes("projectPath"); + private static readonly byte[] Utf8ProjectStyle = Encoding.UTF8.GetBytes("projectStyle"); + private static readonly byte[] Utf8ProjectUniqueName = Encoding.UTF8.GetBytes("projectUniqueName"); + private static readonly byte[] Utf8RestoreLockProperties = Encoding.UTF8.GetBytes("restoreLockProperties"); + private static readonly byte[] Utf8NuGetLockFilePath = Encoding.UTF8.GetBytes("nuGetLockFilePath"); + private static readonly byte[] Utf8RestoreLockedMode = Encoding.UTF8.GetBytes("restoreLockedMode"); + private static readonly byte[] Utf8RestorePackagesWithLockFile = Encoding.UTF8.GetBytes("restorePackagesWithLockFile"); + private static readonly byte[] Utf8RestoreAuditProperties = Encoding.UTF8.GetBytes("restoreAuditProperties"); + private static readonly byte[] Utf8EnableAudit = Encoding.UTF8.GetBytes("enableAudit"); + private static readonly byte[] Utf8AuditLevel = Encoding.UTF8.GetBytes("auditLevel"); + private static readonly byte[] Utf8AuditMode = Encoding.UTF8.GetBytes("auditMode"); + private static readonly byte[] Utf8SkipContentFileWrite = Encoding.UTF8.GetBytes("skipContentFileWrite"); + private static readonly byte[] Utf8Sources = Encoding.UTF8.GetBytes("sources"); + private static readonly byte[] Utf8ValidateRuntimeAssets = Encoding.UTF8.GetBytes("validateRuntimeAssets"); + private static readonly byte[] Utf8WarningProperties = Encoding.UTF8.GetBytes("warningProperties"); + private static readonly byte[] Utf8AllWarningsAsErrors = Encoding.UTF8.GetBytes("allWarningsAsErrors"); + private static readonly byte[] Utf8WarnAsError = Encoding.UTF8.GetBytes("warnAsError"); + private static readonly byte[] Utf8WarnNotAsError = Encoding.UTF8.GetBytes("warnNotAsError"); + private static readonly byte[] Utf8ExcludeAssets = Encoding.UTF8.GetBytes("excludeAssets"); + private static readonly byte[] Utf8IncludeAssets = Encoding.UTF8.GetBytes("includeAssets"); + private static readonly byte[] Utf8TargetAlias = Encoding.UTF8.GetBytes("targetAlias"); + private static readonly byte[] Utf8AssetTargetFallback = Encoding.UTF8.GetBytes("assetTargetFallback"); + private static readonly byte[] Utf8SecondaryFramework = Encoding.UTF8.GetBytes("secondaryFramework"); + private static readonly byte[] Utf8CentralPackageVersions = Encoding.UTF8.GetBytes("centralPackageVersions"); + private static readonly byte[] Utf8DownloadDependencies = Encoding.UTF8.GetBytes("downloadDependencies"); + private static readonly byte[] Utf8FrameworkAssemblies = Encoding.UTF8.GetBytes("frameworkAssemblies"); + private static readonly byte[] Utf8FrameworkReferences = Encoding.UTF8.GetBytes("frameworkReferences"); + private static readonly byte[] Utf8Imports = Encoding.UTF8.GetBytes("imports"); + private static readonly byte[] Utf8RuntimeIdentifierGraphPath = Encoding.UTF8.GetBytes("runtimeIdentifierGraphPath"); + private static readonly byte[] Utf8Warn = Encoding.UTF8.GetBytes("warn"); + private static readonly byte[] Utf8IconUrl = Encoding.UTF8.GetBytes("iconUrl"); + private static readonly byte[] Utf8LicenseUrl = Encoding.UTF8.GetBytes("licenseUrl"); + private static readonly byte[] Utf8Owners = Encoding.UTF8.GetBytes("owners"); + private static readonly byte[] Utf8PackageType = Encoding.UTF8.GetBytes("packageType"); + private static readonly byte[] Utf8ProjectUrl = Encoding.UTF8.GetBytes("projectUrl"); + private static readonly byte[] Utf8ReleaseNotes = Encoding.UTF8.GetBytes("releaseNotes"); + private static readonly byte[] Utf8RequireLicenseAcceptance = Encoding.UTF8.GetBytes("requireLicenseAcceptance"); + private static readonly byte[] Utf8Summary = Encoding.UTF8.GetBytes("summary"); + private static readonly byte[] Utf8Tags = Encoding.UTF8.GetBytes("tags"); + private static readonly byte[] Utf8Mappings = Encoding.UTF8.GetBytes("mappings"); + private static readonly byte[] Utf8HashTagImport = Encoding.UTF8.GetBytes("#import"); + private static readonly byte[] Utf8ProjectReferences = Encoding.UTF8.GetBytes("projectReferences"); + + + internal static PackageSpec GetPackageSpec(string json, string name, string packageSpecPath, string snapshotValue = null) + { + using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(json))) + { + return GetPackageSpec(stream, name, packageSpecPath, snapshotValue); + } + } + + internal static PackageSpec GetPackageSpec(Stream stream, string name, string packageSpecPath, string snapshotValue) + { + var reader = new Utf8JsonStreamReader(stream); + PackageSpec packageSpec; + try + { + packageSpec = GetPackageSpec(ref reader, name, packageSpecPath, snapshotValue); + } + catch (JsonException innerException) + { + throw FileFormatException.Create(innerException, packageSpecPath); + } + + if (!string.IsNullOrEmpty(name)) + { + packageSpec.Name = name; + if (!string.IsNullOrEmpty(packageSpecPath)) + { + packageSpec.FilePath = Path.GetFullPath(packageSpecPath); + + } + } + return packageSpec; + } + + internal static PackageSpec GetPackageSpec(ref Utf8JsonStreamReader jsonReader, string name, string packageSpecPath, string snapshotValue) + { + var packageSpec = new PackageSpec(); + + List compatibilityProfiles = null; + List runtimeDescriptions = null; + var wasPackOptionsSet = false; + var isMappingsNull = false; + string filePath = name == null ? null : Path.GetFullPath(packageSpecPath); + + if (jsonReader.TokenType == JsonTokenType.StartObject) + { + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + if (jsonReader.ValueTextEquals(string.Empty)) + { + jsonReader.TrySkip(); + } +#pragma warning disable CS0612 // Type or member is obsolete + else if (jsonReader.ValueTextEquals(Utf8Authors)) + { + jsonReader.Read(); + if (jsonReader.TokenType == JsonTokenType.StartArray) + { + packageSpec.Authors = jsonReader.ReadStringArrayAsList()?.ToArray(); + } + if (packageSpec.Authors == null) + { + packageSpec.Authors = Array.Empty(); + } + } + else if (jsonReader.ValueTextEquals(BuildOptionsUtf8)) + { + ReadBuildOptions(ref jsonReader, packageSpec); + } + else if (jsonReader.ValueTextEquals(Utf8ContentFiles)) + { + jsonReader.Read(); + jsonReader.ReadStringArrayAsIList(packageSpec.ContentFiles); + } + else if (jsonReader.ValueTextEquals(Utf8Copyright)) + { + packageSpec.Copyright = jsonReader.ReadNextTokenAsString(); + } + else if (jsonReader.ValueTextEquals(Utf8Description)) + { + packageSpec.Description = jsonReader.ReadNextTokenAsString(); + } + else if (jsonReader.ValueTextEquals(Utf8Language)) + { + packageSpec.Language = jsonReader.ReadNextTokenAsString(); + } + else if (jsonReader.ValueTextEquals(Utf8PackInclude)) + { + ReadPackInclude(ref jsonReader, packageSpec); + } + else if (jsonReader.ValueTextEquals(Utf8PackOptions)) + { + ReadPackOptions(ref jsonReader, packageSpec, ref isMappingsNull); + wasPackOptionsSet = true; + } + else if (jsonReader.ValueTextEquals(Utf8Scripts)) + { + ReadScripts(ref jsonReader, packageSpec); + } +#pragma warning restore CS0612 // Type or member is + else if (jsonReader.ValueTextEquals(Utf8Dependencies)) + { + ReadDependencies( + ref jsonReader, + packageSpec.Dependencies, + filePath, + isGacOrFrameworkReference: false); + } + else if (jsonReader.ValueTextEquals(Utf8Frameworks)) + { + ReadFrameworks(ref jsonReader, packageSpec); + } + else if (jsonReader.ValueTextEquals(Utf8Restore)) + { + ReadMSBuildMetadata(ref jsonReader, packageSpec); + } + else if (jsonReader.ValueTextEquals(Utf8Runtimes)) + { + runtimeDescriptions = ReadRuntimes(ref jsonReader); + } + else if (jsonReader.ValueTextEquals(Utf8Supports)) + { + compatibilityProfiles = ReadSupports(ref jsonReader); + } + else if (jsonReader.ValueTextEquals(Utf8Title)) + { + packageSpec.Title = jsonReader.ReadNextTokenAsString(); + } + else if (jsonReader.ValueTextEquals(Utf8Version)) + { + string version = jsonReader.ReadNextTokenAsString(); + if (version != null) + { + try + { +#pragma warning disable CS0612 // Type or member is obsolete + packageSpec.HasVersionSnapshot = PackageSpecUtility.IsSnapshotVersion(version); +#pragma warning restore CS0612 // Type or member is obsolete + packageSpec.Version = PackageSpecUtility.SpecifySnapshot(version, snapshotValue); + } + catch (Exception ex) + { + throw new JsonException($"Error processing versino property. version: {version}", innerException: ex); + } + } + } + else + { + jsonReader.TrySkip(); + } + } + } + packageSpec.Name = name; + packageSpec.FilePath = name == null ? null : Path.GetFullPath(packageSpecPath); + +#pragma warning disable CS0612 // Type or member is obsolete + if (!wasPackOptionsSet) + { + packageSpec.Owners = Array.Empty(); + packageSpec.PackOptions = new PackOptions() + { + PackageType = Array.Empty() + }; + packageSpec.Tags = Array.Empty(); + } + + if (isMappingsNull) + { + packageSpec.PackOptions.Mappings = null; + } +#pragma warning restore CS0612 // Type or member is obsolete + + packageSpec.RuntimeGraph = new RuntimeGraph( + runtimeDescriptions ?? Enumerable.Empty(), + compatibilityProfiles ?? Enumerable.Empty()); + + if (packageSpec.Name == null) + { + packageSpec.Name = packageSpec.RestoreMetadata?.ProjectName; + } + + // Use the project.json path if one is set, otherwise use the project path + if (packageSpec.FilePath == null) + { + packageSpec.FilePath = packageSpec.RestoreMetadata?.ProjectJsonPath + ?? packageSpec.RestoreMetadata?.ProjectPath; + } + + return packageSpec; + } + + internal static void ReadCentralTransitiveDependencyGroup( + ref Utf8JsonStreamReader jsonReader, + IList results, + string packageSpecPath) + { + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject) + { + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + var propertyName = jsonReader.GetString(); + if (string.IsNullOrEmpty(propertyName)) + { + var exception = new JsonException("Unable to resolve dependency ''."); + exception.Data.Add(FileFormatException.SurfaceMessage, true); + throw exception; + } + + if (jsonReader.Read()) + { + var dependencyIncludeFlagsValue = LibraryIncludeFlags.All; + var dependencyExcludeFlagsValue = LibraryIncludeFlags.None; + var suppressParentFlagsValue = LibraryIncludeFlagUtils.DefaultSuppressParent; + string dependencyVersionValue = null; + + if (jsonReader.TokenType == JsonTokenType.String) + { + dependencyVersionValue = jsonReader.GetString(); + } + else if (jsonReader.TokenType == JsonTokenType.StartObject) + { + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + IEnumerable values = null; + + if (jsonReader.ValueTextEquals(Utf8Exclude)) + { + values = jsonReader.ReadDelimitedString(); + dependencyExcludeFlagsValue = LibraryIncludeFlagUtils.GetFlags(values); + } + else if (jsonReader.ValueTextEquals(Utf8Include)) + { + values = jsonReader.ReadDelimitedString(); + dependencyIncludeFlagsValue = LibraryIncludeFlagUtils.GetFlags(values); + } + else if (jsonReader.ValueTextEquals(Utf8SuppressParent)) + { + values = jsonReader.ReadDelimitedString(); + suppressParentFlagsValue = LibraryIncludeFlagUtils.GetFlags(values); + } + else if (jsonReader.ValueTextEquals(Utf8Version)) + { + if (jsonReader.Read()) + { + dependencyVersionValue = jsonReader.GetString(); + } + } + else + { + jsonReader.TrySkip(); + } + } + } + + VersionRange dependencyVersionRange = null; + + if (!string.IsNullOrEmpty(dependencyVersionValue)) + { + try + { + dependencyVersionRange = VersionRange.Parse(dependencyVersionValue); + } + catch (Exception ex) + { + throw new JsonException(null, ex); + } + } + + if (dependencyVersionRange == null) + { + throw new JsonException(Strings.MissingVersionOnDependency, new ArgumentException(Strings.MissingVersionOnDependency)); + } + + // the dependency flags are: Include flags - Exclude flags + var includeFlags = dependencyIncludeFlagsValue & ~dependencyExcludeFlagsValue; + var libraryDependency = new LibraryDependency() + { + LibraryRange = new LibraryRange() + { + Name = propertyName, + TypeConstraint = LibraryDependencyTarget.Package, + VersionRange = dependencyVersionRange + }, + + IncludeType = includeFlags, + SuppressParent = suppressParentFlagsValue, + VersionCentrallyManaged = true, + ReferenceType = LibraryDependencyReferenceType.Transitive + }; + + results.Add(libraryDependency); + } + } + } + } + + private static void ReadDependencies( + ref Utf8JsonStreamReader jsonReader, + IList results, + string packageSpecPath, + bool isGacOrFrameworkReference) + { + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject) + { + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + var propertyName = jsonReader.GetString(); + if (string.IsNullOrEmpty(propertyName)) + { + var exception = new JsonException("Unable to resolve dependency ''."); + exception.Data.Add(FileFormatException.SurfaceMessage, true); + throw exception; + } + + // Support + // "dependencies" : { + // "Name" : "1.0" + // } + + if (jsonReader.Read()) + { + var dependencyIncludeFlagsValue = LibraryIncludeFlags.All; + var dependencyExcludeFlagsValue = LibraryIncludeFlags.None; + var suppressParentFlagsValue = LibraryIncludeFlagUtils.DefaultSuppressParent; + List noWarn = null; + + // This method handles both the dependencies and framework assembly sections. + // Framework references should be limited to references. + // Dependencies should allow everything but framework references. + LibraryDependencyTarget targetFlagsValue = isGacOrFrameworkReference + ? LibraryDependencyTarget.Reference + : LibraryDependencyTarget.All & ~LibraryDependencyTarget.Reference; + + var autoReferenced = false; + var generatePathProperty = false; + var versionCentrallyManaged = false; + string aliases = null; + string dependencyVersionValue = null; + VersionRange versionOverride = null; + + if (jsonReader.TokenType == JsonTokenType.String) + { + dependencyVersionValue = jsonReader.GetString(); + } + else if (jsonReader.TokenType == JsonTokenType.StartObject) + { + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + IEnumerable values = null; + if (jsonReader.ValueTextEquals(Utf8AutoReferenced)) + { + autoReferenced = jsonReader.ReadNextTokenAsBoolOrFalse(); + } + else if (jsonReader.ValueTextEquals(Utf8Exclude)) + { + values = jsonReader.ReadDelimitedString(); + dependencyExcludeFlagsValue = LibraryIncludeFlagUtils.GetFlags(values); + } + else if (jsonReader.ValueTextEquals(Utf8GeneratePathProperty)) + { + generatePathProperty = jsonReader.ReadNextTokenAsBoolOrFalse(); + } + else if (jsonReader.ValueTextEquals(Utf8Include)) + { + values = jsonReader.ReadDelimitedString(); + dependencyIncludeFlagsValue = LibraryIncludeFlagUtils.GetFlags(values); + } + else if (jsonReader.ValueTextEquals(Utf8NoWarn)) + { + noWarn = ReadNuGetLogCodesList(ref jsonReader); + } + else if (jsonReader.ValueTextEquals(Utf8SuppressParent)) + { + values = jsonReader.ReadDelimitedString(); + suppressParentFlagsValue = LibraryIncludeFlagUtils.GetFlags(values); + } + else if (jsonReader.ValueTextEquals(Utf8Target)) + { + targetFlagsValue = ReadTarget(ref jsonReader, packageSpecPath, targetFlagsValue); + } + else if (jsonReader.ValueTextEquals(Utf8Version)) + { + if (jsonReader.Read()) + { + dependencyVersionValue = jsonReader.GetString(); + } + } + else if (jsonReader.ValueTextEquals(Utf8VersionOverride)) + { + if (jsonReader.Read()) + { + var versionPropValue = jsonReader.GetString(); + try + { + versionOverride = VersionRange.Parse(versionPropValue); + } + catch (Exception ex) + { + throw new JsonException(ex.Message, innerException: ex); + } + } + } + else if (jsonReader.ValueTextEquals(Utf8VersionCentrallyManaged)) + { + versionCentrallyManaged = jsonReader.ReadNextTokenAsBoolOrFalse(); + } + else if (jsonReader.ValueTextEquals(Utf8Aliases)) + { + aliases = jsonReader.ReadNextTokenAsString(); + } + else + { + jsonReader.TrySkip(); + } + } + } + + VersionRange dependencyVersionRange = null; + + if (!string.IsNullOrEmpty(dependencyVersionValue)) + { + try + { + dependencyVersionRange = VersionRange.Parse(dependencyVersionValue); + } + catch (Exception ex) + { + throw new JsonException(ex.Message, ex); + } + } + + // Projects and References may have empty version ranges, Packages may not + if (dependencyVersionRange == null) + { + if ((targetFlagsValue & LibraryDependencyTarget.Package) == LibraryDependencyTarget.Package) + { + throw new JsonException(Strings.MissingVersionOnDependency, new ArgumentException(Strings.MissingVersionOnDependency)); + } + else + { + // Projects and references with no version property allow all versions + dependencyVersionRange = VersionRange.All; + } + } + + // the dependency flags are: Include flags - Exclude flags + var includeFlags = dependencyIncludeFlagsValue & ~dependencyExcludeFlagsValue; + var libraryDependency = new LibraryDependency() + { + LibraryRange = new LibraryRange() + { + Name = propertyName, + TypeConstraint = targetFlagsValue, + VersionRange = dependencyVersionRange + }, + IncludeType = includeFlags, + SuppressParent = suppressParentFlagsValue, + AutoReferenced = autoReferenced, + GeneratePathProperty = generatePathProperty, + VersionCentrallyManaged = versionCentrallyManaged, + Aliases = aliases, + // The ReferenceType is not persisted to the assets file + // Default to LibraryDependencyReferenceType.Direct on Read + ReferenceType = LibraryDependencyReferenceType.Direct, + VersionOverride = versionOverride + }; + + if (noWarn != null) + { + libraryDependency.NoWarn = noWarn; + } + + results.Add(libraryDependency); + } + } + } + } + + private static PackageType CreatePackageType(ref Utf8JsonStreamReader jsonReader) + { + var name = jsonReader.GetString(); + + return new PackageType(name, Packaging.Core.PackageType.EmptyVersion); + } + + [Obsolete] + private static void ReadBuildOptions(ref Utf8JsonStreamReader jsonReader, PackageSpec packageSpec) + { + packageSpec.BuildOptions = new BuildOptions(); + + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject) + { + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + if (jsonReader.ValueTextEquals(Utf8OutputName)) + { + packageSpec.BuildOptions.OutputName = jsonReader.ReadNextTokenAsString(); + } + else + { + jsonReader.TrySkip(); + } + } + } + } + + private static void ReadCentralPackageVersions( + ref Utf8JsonStreamReader jsonReader, + IDictionary centralPackageVersions) + { + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject) + { + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + var propertyName = jsonReader.GetString(); + + if (string.IsNullOrEmpty(propertyName)) + { + var exception = new JsonException("Unable to resolve central version ''."); + throw exception; + } + + string version = jsonReader.ReadNextTokenAsString(); + + if (string.IsNullOrEmpty(version)) + { + var exception = new JsonException("The version cannot be null or empty."); + throw exception; + } + + centralPackageVersions[propertyName] = new CentralPackageVersion(propertyName, VersionRange.Parse(version)); + } + } + } + + private static CompatibilityProfile ReadCompatibilityProfile(ref Utf8JsonStreamReader jsonReader, string profileName) + { + List sets = null; + + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject) + { + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + var propertyName = jsonReader.GetString(); + sets = sets ?? new List(); + + IReadOnlyList values = jsonReader.ReadNextStringOrArrayOfStringsAsReadOnlyList() ?? Array.Empty(); + + IEnumerable profiles = ReadCompatibilitySets(values, propertyName); + + sets.AddRange(profiles); + } + } + return new CompatibilityProfile(profileName, sets ?? Enumerable.Empty()); + } + + private static IEnumerable ReadCompatibilitySets(IReadOnlyList values, string compatibilitySetName) + { + NuGetFramework framework = NuGetFramework.Parse(compatibilitySetName); + + foreach (string value in values) + { + yield return new FrameworkRuntimePair(framework, value); + } + } + + private static void ReadDownloadDependencies( + ref Utf8JsonStreamReader jsonReader, + IList downloadDependencies, + string packageSpecPath) + { + var seenIds = new HashSet(); + + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartArray) + { + do + { + string name = null; + string versionValue = null; + var isNameDefined = false; + + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject) + { + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + if (jsonReader.ValueTextEquals(Utf8Name)) + { + isNameDefined = true; + name = jsonReader.ReadNextTokenAsString(); + } + else if (jsonReader.ValueTextEquals(Utf8Version)) + { + versionValue = jsonReader.ReadNextTokenAsString(); + } + else + { + jsonReader.TrySkip(); + } + } + } + + if (jsonReader.TokenType == JsonTokenType.EndArray) + { + break; + } + + if (!isNameDefined) + { + var exception = new JsonException("Unable to resolve downloadDependency ''."); + throw exception; + } + + if (!seenIds.Add(name)) + { + // package ID already seen, only use first definition. + continue; + } + + if (string.IsNullOrEmpty(versionValue)) + { + var exception = new JsonException("The version cannot be null or empty"); + throw exception; + } + + string[] versions = versionValue.Split(VersionSeparators, StringSplitOptions.RemoveEmptyEntries); + + foreach (string singleVersionValue in versions) + { + try + { + VersionRange version = VersionRange.Parse(singleVersionValue); + + downloadDependencies.Add(new DownloadDependency(name, version)); + } + catch (Exception ex) + { + throw new JsonException(ex.Message, ex); + } + } + } while (jsonReader.TokenType == JsonTokenType.EndObject); + } + } + + private static void ReadFrameworkReferences( + ref Utf8JsonStreamReader jsonReader, + ISet frameworkReferences, + string packageSpecPath) + { + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject) + { + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + var frameworkName = jsonReader.GetString(); + if (string.IsNullOrEmpty(frameworkName)) + { + var exception = new JsonException("Unable to resolve frameworkReference."); + throw exception; + } + + var privateAssets = FrameworkDependencyFlagsUtils.Default; + + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject) + { + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + if (jsonReader.ValueTextEquals(Utf8PrivateAssets)) + { + IEnumerable strings = jsonReader.ReadDelimitedString(); + + privateAssets = FrameworkDependencyFlagsUtils.GetFlags(strings); + } + else + { + jsonReader.TrySkip(); + } + } + } + + frameworkReferences.Add(new FrameworkDependency(frameworkName, privateAssets)); + } + } + } + + private static void ReadFrameworks(ref Utf8JsonStreamReader reader, PackageSpec packageSpec) + { + if (reader.Read() && reader.TokenType == JsonTokenType.StartObject) + { + while (reader.Read() && reader.TokenType == JsonTokenType.PropertyName) + { + ReadTargetFrameworks(packageSpec, ref reader); + } + } + } + + private static void ReadImports(PackageSpec packageSpec, ref Utf8JsonStreamReader jsonReader, TargetFrameworkInformation targetFrameworkInformation) + { + IReadOnlyList imports = jsonReader.ReadNextStringOrArrayOfStringsAsReadOnlyList(); + + if (imports != null && imports.Count > 0) + { + foreach (string import in imports.Where(element => !string.IsNullOrEmpty(element))) + { + NuGetFramework framework = NuGetFramework.Parse(import); + + if (!framework.IsSpecificFramework) + { + var exception = new JsonException(string.Format( + CultureInfo.CurrentCulture, + Strings.Log_InvalidImportFramework, + import, + PackageSpec.PackageSpecFileName)); + throw exception; + } + + targetFrameworkInformation.Imports.Add(framework); + } + } + } + + private static void ReadMappings(ref Utf8JsonStreamReader jsonReader, string mappingKey, IDictionary mappings) + { + if (jsonReader.Read()) + { + switch (jsonReader.TokenType) + { + case JsonTokenType.String: + { + var files = new IncludeExcludeFiles() + { + Include = new[] { (string)jsonReader.GetString() } + }; + + mappings.Add(mappingKey, files); + } + break; + case JsonTokenType.StartArray: + { + IReadOnlyList include = jsonReader.ReadStringArrayAsReadOnlyListFromArrayStart(); + + var files = new IncludeExcludeFiles() + { + Include = include + }; + + mappings.Add(mappingKey, files); + } + break; + case JsonTokenType.StartObject: + { + IReadOnlyList excludeFiles = null; + IReadOnlyList exclude = null; + IReadOnlyList includeFiles = null; + IReadOnlyList include = null; + + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + if (jsonReader.ValueTextEquals(Utf8ExcludeFiles)) + { + excludeFiles = jsonReader.ReadNextStringOrArrayOfStringsAsReadOnlyList(); + } + else if (jsonReader.ValueTextEquals(Utf8Exclude)) + { + exclude = jsonReader.ReadNextStringOrArrayOfStringsAsReadOnlyList(); + } + else if (jsonReader.ValueTextEquals(Utf8IncludeFiles)) + { + includeFiles = jsonReader.ReadNextStringOrArrayOfStringsAsReadOnlyList(); + } + else if (jsonReader.ValueTextEquals(Utf8Include)) + { + include = jsonReader.ReadNextStringOrArrayOfStringsAsReadOnlyList(); + } + else + { + jsonReader.TrySkip(); + } + } + + if (include != null || includeFiles != null || exclude != null || excludeFiles != null) + { + var files = new IncludeExcludeFiles() + { + ExcludeFiles = excludeFiles, + Exclude = exclude, + IncludeFiles = includeFiles, + Include = include + }; + + mappings.Add(mappingKey, files); + } + } + break; + } + } + } + + private static void ReadMSBuildMetadata(ref Utf8JsonStreamReader jsonReader, PackageSpec packageSpec) + { + var centralPackageVersionsManagementEnabled = false; + var centralPackageVersionOverrideDisabled = false; + var CentralPackageTransitivePinningEnabled = false; + List configFilePaths = null; + var crossTargeting = false; + List fallbackFolders = null; + List files = null; + var legacyPackagesDirectory = false; + ProjectRestoreMetadata msbuildMetadata = null; + List originalTargetFrameworks = null; + string outputPath = null; + string packagesConfigPath = null; + string packagesPath = null; + string projectJsonPath = null; + string projectName = null; + string projectPath = null; + ProjectStyle? projectStyle = null; + string projectUniqueName = null; + RestoreLockProperties restoreLockProperties = null; + var skipContentFileWrite = false; + List sources = null; + List targetFrameworks = null; + var validateRuntimeAssets = false; + WarningProperties warningProperties = null; + RestoreAuditProperties auditProperties = null; + + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject) + { + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + if (jsonReader.ValueTextEquals(Utf8CentralPackageVersionsManagementEnabled)) + { + centralPackageVersionsManagementEnabled = jsonReader.ReadNextTokenAsBoolOrFalse(); + } + else if (jsonReader.ValueTextEquals(Utf8CentralPackageVersionOverrideDisabled)) + { + centralPackageVersionOverrideDisabled = jsonReader.ReadNextTokenAsBoolOrFalse(); + } + else if (jsonReader.ValueTextEquals(Utf8CentralPackageTransitivePinningEnabled)) + { + CentralPackageTransitivePinningEnabled = jsonReader.ReadNextTokenAsBoolOrFalse(); + } + else if (jsonReader.ValueTextEquals(Utf8ConfigFilePaths)) + { + jsonReader.Read(); + configFilePaths = jsonReader.ReadStringArrayAsList(); + } + else if (jsonReader.ValueTextEquals(Utf8CrossTargeting)) + { + crossTargeting = jsonReader.ReadNextTokenAsBoolOrFalse(); + } + else if (jsonReader.ValueTextEquals(Utf8FallbackFolders)) + { + jsonReader.Read(); + fallbackFolders = jsonReader.ReadStringArrayAsList(); + } + else if (jsonReader.ValueTextEquals(Utf8Files)) + { + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject) + { + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + var filePropertyName = jsonReader.GetString(); + files = files ?? new List(); + + files.Add(new ProjectRestoreMetadataFile(filePropertyName, jsonReader.ReadNextTokenAsString())); + } + } + } + else if (jsonReader.ValueTextEquals(Utf8Frameworks)) + { + targetFrameworks = ReadTargetFrameworks(ref jsonReader); + } + else if (jsonReader.ValueTextEquals(Utf8LegacyPackagesDirectory)) + { + legacyPackagesDirectory = jsonReader.ReadNextTokenAsBoolOrFalse(); + } + else if (jsonReader.ValueTextEquals(Utf8OriginalTargetFrameworks)) + { + jsonReader.Read(); + originalTargetFrameworks = jsonReader.ReadStringArrayAsList(); + } + else if (jsonReader.ValueTextEquals(Utf8OutputPath)) + { + outputPath = jsonReader.ReadNextTokenAsString(); + } + else if (jsonReader.ValueTextEquals(Utf8PackagesConfigPath)) + { + packagesConfigPath = jsonReader.ReadNextTokenAsString(); + } + else if (jsonReader.ValueTextEquals(Utf8PackagesPath)) + { + packagesPath = jsonReader.ReadNextTokenAsString(); + } + else if (jsonReader.ValueTextEquals(Utf8ProjectJsonPath)) + { + projectJsonPath = jsonReader.ReadNextTokenAsString(); + } + else if (jsonReader.ValueTextEquals(Utf8ProjectName)) + { + projectName = jsonReader.ReadNextTokenAsString(); + } + else if (jsonReader.ValueTextEquals(Utf8ProjectPath)) + { + projectPath = jsonReader.ReadNextTokenAsString(); + } + else if (jsonReader.ValueTextEquals(Utf8ProjectStyle)) + { + string projectStyleString = jsonReader.ReadNextTokenAsString(); + + if (!string.IsNullOrEmpty(projectStyleString) + && Enum.TryParse(projectStyleString, ignoreCase: true, result: out ProjectStyle projectStyleValue)) + { + projectStyle = projectStyleValue; + } + } + else if (jsonReader.ValueTextEquals(Utf8ProjectUniqueName)) + { + projectUniqueName = jsonReader.ReadNextTokenAsString(); + } + else if (jsonReader.ValueTextEquals(Utf8RestoreLockProperties)) + { + string nuGetLockFilePath = null; + var restoreLockedMode = false; + string restorePackagesWithLockFile = null; + + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject) + { + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + if (jsonReader.ValueTextEquals(Utf8NuGetLockFilePath)) + { + nuGetLockFilePath = jsonReader.ReadNextTokenAsString(); + } + else if (jsonReader.ValueTextEquals(Utf8RestoreLockedMode)) + { + restoreLockedMode = jsonReader.ReadNextTokenAsBoolOrFalse(); + } + else if (jsonReader.ValueTextEquals(Utf8RestorePackagesWithLockFile)) + { + restorePackagesWithLockFile = jsonReader.ReadNextTokenAsString(); + } + else + { + jsonReader.TrySkip(); + } + } + } + restoreLockProperties = new RestoreLockProperties(restorePackagesWithLockFile, nuGetLockFilePath, restoreLockedMode); + } + else if (jsonReader.ValueTextEquals(Utf8RestoreAuditProperties)) + { + string enableAudit = null, auditLevel = null, auditMode = null; + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject) + { + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + if (jsonReader.ValueTextEquals(Utf8EnableAudit)) + { + enableAudit = jsonReader.ReadNextTokenAsString(); + } + else if (jsonReader.ValueTextEquals(Utf8AuditLevel)) + { + auditLevel = jsonReader.ReadNextTokenAsString(); + } + else if (jsonReader.ValueTextEquals(Utf8AuditMode)) + { + auditMode = jsonReader.ReadNextTokenAsString(); + } + else + { + jsonReader.TrySkip(); + } + } + } + auditProperties = new RestoreAuditProperties() + { + EnableAudit = enableAudit, + AuditLevel = auditLevel, + AuditMode = auditMode, + }; + } + else if (jsonReader.ValueTextEquals(Utf8SkipContentFileWrite)) + { + skipContentFileWrite = jsonReader.ReadNextTokenAsBoolOrFalse(); + } + else if (jsonReader.ValueTextEquals(Utf8Sources)) + { + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject) + { + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + var sourcePropertyName = jsonReader.GetString(); + sources = sources ?? new List(); + + sources.Add(new PackageSource(sourcePropertyName)); + jsonReader.TrySkip(); + } + } + } + else if (jsonReader.ValueTextEquals(Utf8ValidateRuntimeAssets)) + { + validateRuntimeAssets = jsonReader.ReadNextTokenAsBoolOrFalse(); + } + else if (jsonReader.ValueTextEquals(Utf8WarningProperties)) + { + var allWarningsAsErrors = false; + var noWarn = new HashSet(); + var warnAsError = new HashSet(); + var warningsNotAsErrors = new HashSet(); + + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject) + { + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + if (jsonReader.ValueTextEquals(Utf8AllWarningsAsErrors)) + { + allWarningsAsErrors = jsonReader.ReadNextTokenAsBoolOrFalse(); + } + else if (jsonReader.ValueTextEquals(Utf8NoWarn)) + { + ReadNuGetLogCodes(ref jsonReader, noWarn); + } + else if (jsonReader.ValueTextEquals(Utf8WarnAsError)) + { + ReadNuGetLogCodes(ref jsonReader, warnAsError); + } + else if (jsonReader.ValueTextEquals(Utf8WarnNotAsError)) + { + ReadNuGetLogCodes(ref jsonReader, warningsNotAsErrors); + } + else + { + jsonReader.TrySkip(); + } + } + } + + warningProperties = new WarningProperties(warnAsError, noWarn, allWarningsAsErrors, warningsNotAsErrors); + } + else + { + jsonReader.TrySkip(); + } + } + } + + if (projectStyle == ProjectStyle.PackagesConfig) + { + msbuildMetadata = new PackagesConfigProjectRestoreMetadata() + { + PackagesConfigPath = packagesConfigPath + }; + } + else + { + msbuildMetadata = new ProjectRestoreMetadata(); + } + + msbuildMetadata.CentralPackageVersionsEnabled = centralPackageVersionsManagementEnabled; + msbuildMetadata.CentralPackageVersionOverrideDisabled = centralPackageVersionOverrideDisabled; + msbuildMetadata.CentralPackageTransitivePinningEnabled = CentralPackageTransitivePinningEnabled; + msbuildMetadata.RestoreAuditProperties = auditProperties; + + if (configFilePaths != null) + { + msbuildMetadata.ConfigFilePaths = configFilePaths; + } + + msbuildMetadata.CrossTargeting = crossTargeting; + + if (fallbackFolders != null) + { + msbuildMetadata.FallbackFolders = fallbackFolders; + } + + if (files != null) + { + msbuildMetadata.Files = files; + } + + msbuildMetadata.LegacyPackagesDirectory = legacyPackagesDirectory; + + if (originalTargetFrameworks != null) + { + msbuildMetadata.OriginalTargetFrameworks = originalTargetFrameworks; + } + + msbuildMetadata.OutputPath = outputPath; + msbuildMetadata.PackagesPath = packagesPath; + msbuildMetadata.ProjectJsonPath = projectJsonPath; + msbuildMetadata.ProjectName = projectName; + msbuildMetadata.ProjectPath = projectPath; + + if (projectStyle.HasValue) + { + msbuildMetadata.ProjectStyle = projectStyle.Value; + } + + msbuildMetadata.ProjectUniqueName = projectUniqueName; + + if (restoreLockProperties != null) + { + msbuildMetadata.RestoreLockProperties = restoreLockProperties; + } + + msbuildMetadata.SkipContentFileWrite = skipContentFileWrite; + + if (sources != null) + { + msbuildMetadata.Sources = sources; + } + + if (targetFrameworks != null) + { + msbuildMetadata.TargetFrameworks = targetFrameworks; + } + + msbuildMetadata.ValidateRuntimeAssets = validateRuntimeAssets; + + if (warningProperties != null) + { + msbuildMetadata.ProjectWideWarningProperties = warningProperties; + } + + packageSpec.RestoreMetadata = msbuildMetadata; + } + + private static void ReadNuGetLogCodes(ref Utf8JsonStreamReader jsonReader, HashSet hashCodes) + { + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartArray) + { + while (jsonReader.Read() && jsonReader.TokenType != JsonTokenType.EndArray) + { + if (jsonReader.TokenType == JsonTokenType.String && Enum.TryParse(jsonReader.GetString(), out NuGetLogCode code)) + { + hashCodes.Add(code); + } + } + } + } + + private static List ReadNuGetLogCodesList(ref Utf8JsonStreamReader jsonReader) + { + List items = null; + + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartArray) + { + while (jsonReader.Read() && jsonReader.TokenType != JsonTokenType.EndArray) + { + if (jsonReader.TokenType == JsonTokenType.String && Enum.TryParse(jsonReader.GetString(), out NuGetLogCode code)) + { + items = items ?? new List(); + + items.Add(code); + } + } + } + return items; + } + + private static void ReadPackageTypes(PackageSpec packageSpec, ref Utf8JsonStreamReader jsonReader) + { + IReadOnlyList packageTypes = null; + PackageType packageType = null; + + try + { + if (jsonReader.Read()) + { + switch (jsonReader.TokenType) + { + case JsonTokenType.String: + packageType = CreatePackageType(ref jsonReader); + + packageTypes = new[] { packageType }; + break; + + case JsonTokenType.StartArray: + var types = new List(); + + while (jsonReader.Read() && jsonReader.TokenType != JsonTokenType.EndArray) + { + if (jsonReader.TokenType != JsonTokenType.String) + { + var exception = new JsonException(string.Format( + CultureInfo.CurrentCulture, + Strings.InvalidPackageType, + PackageSpec.PackageSpecFileName)); + exception.Data.Add(FileFormatException.SurfaceMessage, true); + throw exception; + } + + packageType = CreatePackageType(ref jsonReader); + + types.Add(packageType); + } + + packageTypes = types; + break; + + case JsonTokenType.Null: + break; + default: + throw new InvalidCastException(); + } + +#pragma warning disable CS0612 // Type or member is obsolete + if (packageTypes != null) + { + packageSpec.PackOptions.PackageType = packageTypes; + } +#pragma warning restore CS0612 // Type or member is obsolete + } + } + catch (Exception) + { + var exception = new JsonException(string.Format( + CultureInfo.CurrentCulture, + Strings.InvalidPackageType, + PackageSpec.PackageSpecFileName)); + exception.Data.Add(FileFormatException.SurfaceMessage, true); + throw exception; + } + } + + [Obsolete] + private static void ReadPackInclude(ref Utf8JsonStreamReader jsonReader, PackageSpec packageSpec) + { + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject) + { + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + string propertyName = jsonReader.GetString(); + string propertyValue = jsonReader.ReadNextTokenAsString(); + + packageSpec.PackInclude.Add(new KeyValuePair(propertyName, propertyValue)); + } + } + } + + [Obsolete] + private static void ReadPackOptions(ref Utf8JsonStreamReader jsonReader, PackageSpec packageSpec, ref bool isMappingsNull) + { + var wasMappingsRead = false; + bool isPackOptionsValueAnObject = false; + + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject) + { + isPackOptionsValueAnObject = true; + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + if (jsonReader.ValueTextEquals(Utf8Files)) + { + wasMappingsRead = ReadPackOptionsFiles(packageSpec, ref jsonReader, wasMappingsRead); + } + else if (jsonReader.ValueTextEquals(Utf8IconUrl)) + { + packageSpec.IconUrl = jsonReader.ReadNextTokenAsString(); + } + else if (jsonReader.ValueTextEquals(Utf8LicenseUrl)) + { + packageSpec.LicenseUrl = jsonReader.ReadNextTokenAsString(); + } + else if (jsonReader.ValueTextEquals(Utf8Owners)) + { + jsonReader.Read(); + string[] owners = jsonReader.ReadStringArrayAsIList()?.ToArray(); + if (owners != null) + { + packageSpec.Owners = owners; + } + } + else if (jsonReader.ValueTextEquals(Utf8PackageType)) + { + ReadPackageTypes(packageSpec, ref jsonReader); + } + else if (jsonReader.ValueTextEquals(Utf8ProjectUrl)) + { + packageSpec.ProjectUrl = jsonReader.ReadNextTokenAsString(); + } + else if (jsonReader.ValueTextEquals(Utf8ReleaseNotes)) + { + packageSpec.ReleaseNotes = jsonReader.ReadNextTokenAsString(); + } + else if (jsonReader.ValueTextEquals(Utf8RequireLicenseAcceptance)) + { + packageSpec.RequireLicenseAcceptance = jsonReader.ReadNextTokenAsBoolOrFalse(); + } + else if (jsonReader.ValueTextEquals(Utf8Summary)) + { + packageSpec.Summary = jsonReader.ReadNextTokenAsString(); + } + else if (jsonReader.ValueTextEquals(Utf8Tags)) + { + jsonReader.Read(); + string[] tags = jsonReader.ReadStringArrayAsIList()?.ToArray(); + + if (tags != null) + { + packageSpec.Tags = tags; + } + } + else + { + jsonReader.TrySkip(); + } + } + } + isMappingsNull = isPackOptionsValueAnObject && !wasMappingsRead; + } + + [Obsolete] + private static bool ReadPackOptionsFiles(PackageSpec packageSpec, ref Utf8JsonStreamReader jsonReader, bool wasMappingsRead) + { + IReadOnlyList excludeFiles = null; + IReadOnlyList exclude = null; + IReadOnlyList includeFiles = null; + IReadOnlyList include = null; + + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject) + { + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + var filesPropertyName = jsonReader.GetString(); + if (jsonReader.ValueTextEquals(Utf8ExcludeFiles)) + { + excludeFiles = jsonReader.ReadNextStringOrArrayOfStringsAsReadOnlyList(); + } + else if (jsonReader.ValueTextEquals(Utf8Exclude)) + { + exclude = jsonReader.ReadNextStringOrArrayOfStringsAsReadOnlyList(); + } + else if (jsonReader.ValueTextEquals(Utf8IncludeFiles)) + { + includeFiles = jsonReader.ReadNextStringOrArrayOfStringsAsReadOnlyList(); + } + else if (jsonReader.ValueTextEquals(Utf8Include)) + { + include = jsonReader.ReadNextStringOrArrayOfStringsAsReadOnlyList(); + } + else if (jsonReader.ValueTextEquals(Utf8Mappings)) + { + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject) + { + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + wasMappingsRead = true; + var mappingsPropertyName = jsonReader.GetString(); + ReadMappings(ref jsonReader, mappingsPropertyName, packageSpec.PackOptions.Mappings); + } + } + } + else + { + jsonReader.TrySkip(); + } + } + } + + if (include != null || includeFiles != null || exclude != null || excludeFiles != null) + { + packageSpec.PackOptions.IncludeExcludeFiles = new IncludeExcludeFiles() + { + ExcludeFiles = excludeFiles, + Exclude = exclude, + IncludeFiles = includeFiles, + Include = include + }; + } + + return wasMappingsRead; + } + + private static RuntimeDependencySet ReadRuntimeDependencySet(ref Utf8JsonStreamReader jsonReader, string dependencySetName) + { + List dependencies = null; + + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject) + { + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + var propertyName = jsonReader.GetString(); + dependencies ??= new List(); + + var dependency = new RuntimePackageDependency(propertyName, VersionRange.Parse(jsonReader.ReadNextTokenAsString())); + + dependencies.Add(dependency); + } + } + + return new RuntimeDependencySet( + dependencySetName, + dependencies); + } + + private static RuntimeDescription ReadRuntimeDescription(ref Utf8JsonStreamReader jsonReader, string runtimeName) + { + List inheritedRuntimes = null; + List additionalDependencies = null; + + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject) + { + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + if (jsonReader.ValueTextEquals(Utf8HashTagImport)) + { + jsonReader.Read(); + inheritedRuntimes = jsonReader.ReadStringArrayAsList(); + } + else + { + var propertyName = jsonReader.GetString(); + additionalDependencies ??= new List(); + + RuntimeDependencySet dependency = ReadRuntimeDependencySet(ref jsonReader, propertyName); + + additionalDependencies.Add(dependency); + } + } + } + + return new RuntimeDescription( + runtimeName, + inheritedRuntimes, + additionalDependencies); + } + + private static List ReadRuntimes(ref Utf8JsonStreamReader jsonReader) + { + var runtimeDescriptions = new List(); + + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject) + { + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + RuntimeDescription runtimeDescription = ReadRuntimeDescription(ref jsonReader, jsonReader.GetString()); + + runtimeDescriptions.Add(runtimeDescription); + } + } + + return runtimeDescriptions; + } + + [Obsolete] + private static void ReadScripts(ref Utf8JsonStreamReader jsonReader, PackageSpec packageSpec) + { + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject) + { + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + var propertyName = jsonReader.GetString(); + if (jsonReader.Read()) + { + if (jsonReader.TokenType == JsonTokenType.String) + { + packageSpec.Scripts[propertyName] = new string[] { (string)jsonReader.GetString() }; + } + else if (jsonReader.TokenType == JsonTokenType.StartArray) + { + var list = new List(); + + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.String) + { + list.Add(jsonReader.GetString()); + } + + packageSpec.Scripts[propertyName] = list; + } + else + { + var exception = new JsonException(string.Format(CultureInfo.CurrentCulture, "The value of a script in '{0}' can only be a string or an array of strings", PackageSpec.PackageSpecFileName)); + exception.Data.Add(FileFormatException.SurfaceMessage, true); + throw exception; + } + } + } + } + } + + private static List ReadSupports(ref Utf8JsonStreamReader jsonReader) + { + var compatibilityProfiles = new List(); + + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject) + { + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + var propertyName = jsonReader.GetString(); + CompatibilityProfile compatibilityProfile = ReadCompatibilityProfile(ref jsonReader, propertyName); + + compatibilityProfiles.Add(compatibilityProfile); + } + } + return compatibilityProfiles; + } + + private static LibraryDependencyTarget ReadTarget( + ref Utf8JsonStreamReader jsonReader, + string packageSpecPath, + LibraryDependencyTarget targetFlagsValue) + { + if (jsonReader.Read()) + { + var targetString = jsonReader.GetString(); + + targetFlagsValue = LibraryDependencyTargetUtils.Parse(targetString); + + // Verify that the value specified is package, project, or external project + if (!ValidateDependencyTarget(targetFlagsValue)) + { + string message = string.Format( + CultureInfo.CurrentCulture, + Strings.InvalidDependencyTarget, + targetString); + var exception = new JsonException(message); + throw exception; + } + } + + return targetFlagsValue; + } + + private static List ReadTargetFrameworks(ref Utf8JsonStreamReader jsonReader) + { + var targetFrameworks = new List(); + + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject) + { + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + var frameworkPropertyName = jsonReader.GetString(); + NuGetFramework framework = NuGetFramework.Parse(frameworkPropertyName); + var frameworkGroup = new ProjectRestoreMetadataFrameworkInfo(framework); + + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject) + { + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + if (jsonReader.ValueTextEquals(Utf8ProjectReferences)) + { + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject) + { + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + var projectReferencePropertyName = jsonReader.GetString(); + string excludeAssets = null; + string includeAssets = null; + string privateAssets = null; + string projectReferenceProjectPath = null; + + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject) + { + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + if (jsonReader.ValueTextEquals(Utf8ExcludeAssets)) + { + excludeAssets = jsonReader.ReadNextTokenAsString(); + } + else if (jsonReader.ValueTextEquals(Utf8IncludeAssets)) + { + includeAssets = jsonReader.ReadNextTokenAsString(); + } + else if (jsonReader.ValueTextEquals(Utf8PrivateAssets)) + { + privateAssets = jsonReader.ReadNextTokenAsString(); + } + else if (jsonReader.ValueTextEquals(Utf8ProjectPath)) + { + projectReferenceProjectPath = jsonReader.ReadNextTokenAsString(); + } + else + { + jsonReader.TrySkip(); + } + + } + } + + frameworkGroup.ProjectReferences.Add(new ProjectRestoreReference() + { + ProjectUniqueName = projectReferencePropertyName, + ProjectPath = projectReferenceProjectPath, + + IncludeAssets = LibraryIncludeFlagUtils.GetFlags( + flags: includeAssets, + defaultFlags: LibraryIncludeFlags.All), + + ExcludeAssets = LibraryIncludeFlagUtils.GetFlags( + flags: excludeAssets, + defaultFlags: LibraryIncludeFlags.None), + + PrivateAssets = LibraryIncludeFlagUtils.GetFlags( + flags: privateAssets, + defaultFlags: LibraryIncludeFlagUtils.DefaultSuppressParent), + }); + } + } + } + else if (jsonReader.ValueTextEquals(Utf8TargetAlias)) + { + frameworkGroup.TargetAlias = jsonReader.ReadNextTokenAsString(); + } + else + { + jsonReader.TrySkip(); + } + } + + targetFrameworks.Add(frameworkGroup); + } + } + } + return targetFrameworks; + } + + private static void ReadTargetFrameworks(PackageSpec packageSpec, ref Utf8JsonStreamReader jsonReader) + { + var frameworkName = NuGetFramework.Parse(jsonReader.GetString()); + + var targetFrameworkInformation = new TargetFrameworkInformation(); + NuGetFramework secondaryFramework = default; + + if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject) + { + while (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.PropertyName) + { + if (jsonReader.ValueTextEquals(Utf8AssetTargetFallback)) + { + targetFrameworkInformation.AssetTargetFallback = jsonReader.ReadNextTokenAsBoolOrFalse(); + } + else if (jsonReader.ValueTextEquals(Utf8SecondaryFramework)) + { + var secondaryFrameworkString = jsonReader.ReadNextTokenAsString(); + if (!string.IsNullOrEmpty(secondaryFrameworkString)) + { + secondaryFramework = NuGetFramework.Parse(secondaryFrameworkString); + } + } + else if (jsonReader.ValueTextEquals(Utf8CentralPackageVersions)) + { + ReadCentralPackageVersions( + ref jsonReader, + targetFrameworkInformation.CentralPackageVersions); + } + else if (jsonReader.ValueTextEquals(Utf8Dependencies)) + { + ReadDependencies( + ref jsonReader, + targetFrameworkInformation.Dependencies, + packageSpec.FilePath, + isGacOrFrameworkReference: false); + } + else if (jsonReader.ValueTextEquals(Utf8DownloadDependencies)) + { + ReadDownloadDependencies( + ref jsonReader, + targetFrameworkInformation.DownloadDependencies, + packageSpec.FilePath); + } + else if (jsonReader.ValueTextEquals(Utf8FrameworkAssemblies)) + { + ReadDependencies( + ref jsonReader, + targetFrameworkInformation.Dependencies, + packageSpec.FilePath, + isGacOrFrameworkReference: true); + } + else if (jsonReader.ValueTextEquals(Utf8FrameworkReferences)) + { + ReadFrameworkReferences( + ref jsonReader, + targetFrameworkInformation.FrameworkReferences, + packageSpec.FilePath); + } + else if (jsonReader.ValueTextEquals(Utf8Imports)) + { + ReadImports(packageSpec, ref jsonReader, targetFrameworkInformation); + } + else if (jsonReader.ValueTextEquals(Utf8RuntimeIdentifierGraphPath)) + { + targetFrameworkInformation.RuntimeIdentifierGraphPath = jsonReader.ReadNextTokenAsString(); + } + else if (jsonReader.ValueTextEquals(Utf8TargetAlias)) + { + targetFrameworkInformation.TargetAlias = jsonReader.ReadNextTokenAsString(); + } + else if (jsonReader.ValueTextEquals(Utf8Warn)) + { + targetFrameworkInformation.Warn = jsonReader.ReadNextTokenAsBoolOrFalse(); + } + else + { + jsonReader.TrySkip(); + } + } + } + + NuGetFramework updatedFramework = frameworkName; + + if (targetFrameworkInformation.Imports.Count > 0) + { + NuGetFramework[] imports = targetFrameworkInformation.Imports.ToArray(); + + if (targetFrameworkInformation.AssetTargetFallback) + { + updatedFramework = new AssetTargetFallbackFramework(GetDualCompatibilityFrameworkIfNeeded(frameworkName, secondaryFramework), imports); + } + else + { + updatedFramework = new FallbackFramework(GetDualCompatibilityFrameworkIfNeeded(frameworkName, secondaryFramework), imports); + } + } + else + { + updatedFramework = GetDualCompatibilityFrameworkIfNeeded(frameworkName, secondaryFramework); + } + + targetFrameworkInformation.FrameworkName = updatedFramework; + + packageSpec.TargetFrameworks.Add(targetFrameworkInformation); + } + + private static NuGetFramework GetDualCompatibilityFrameworkIfNeeded(NuGetFramework frameworkName, NuGetFramework secondaryFramework) + { + if (secondaryFramework != default) + { + return new DualCompatibilityFramework(frameworkName, secondaryFramework); + } + + return frameworkName; + } + + private static bool ValidateDependencyTarget(LibraryDependencyTarget targetValue) + { + var isValid = false; + + switch (targetValue) + { + case LibraryDependencyTarget.Package: + case LibraryDependencyTarget.Project: + case LibraryDependencyTarget.ExternalProject: + isValid = true; + break; + } + + return isValid; + } + } +} diff --git a/src/NuGet.Core/NuGet.ProjectModel/Utf8JsonReaderExtensions.cs b/src/NuGet.Core/NuGet.ProjectModel/Utf8JsonReaderExtensions.cs new file mode 100644 index 00000000000..ca010c1daee --- /dev/null +++ b/src/NuGet.Core/NuGet.ProjectModel/Utf8JsonReaderExtensions.cs @@ -0,0 +1,233 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace NuGet.ProjectModel +{ + internal static class Utf8JsonReaderExtensions + { + private static readonly char[] DelimitedStringDelimiters = new char[] { ' ', ',' }; + internal static readonly Utf8JsonStreamLockFileConverter LockFileConverter = new Utf8JsonStreamLockFileConverter(); + internal static readonly Utf8JsonStreamLockFileItemConverter LockFileItemConverter = new Utf8JsonStreamLockFileItemConverter(); + internal static readonly Utf8JsonStreamLockFileItemConverter LockFileContentFileConverter = new Utf8JsonStreamLockFileItemConverter(); + internal static readonly Utf8JsonStreamLockFileItemConverter LockFileRuntimeTargetConverter = new Utf8JsonStreamLockFileItemConverter(); + internal static readonly Utf8JsonStreamLockFileTargetLibraryConverter LockFileTargetLibraryConverter = new Utf8JsonStreamLockFileTargetLibraryConverter(); + internal static readonly Utf8JsonStreamLockFileLibraryConverter LockFileLibraryConverter = new Utf8JsonStreamLockFileLibraryConverter(); + internal static readonly Utf8JsonStreamLockFileTargetConverter LockFileTargetConverter = new Utf8JsonStreamLockFileTargetConverter(); + internal static readonly Utf8JsonStreamProjectFileDependencyGroupConverter ProjectFileDepencencyGroupConverter = new Utf8JsonStreamProjectFileDependencyGroupConverter(); + internal static readonly Utf8JsonStreamAssetsLogMessageConverter AssetsLogMessageConverter = new Utf8JsonStreamAssetsLogMessageConverter(); + + internal static IReadOnlyList ReadDelimitedString(this ref Utf8JsonReader reader) + { + if (ReadNextToken(ref reader)) + { + switch (reader.TokenType) + { + case JsonTokenType.String: + var value = reader.GetString(); + + return value.Split(DelimitedStringDelimiters, StringSplitOptions.RemoveEmptyEntries); + + default: + var invalidCastException = new InvalidCastException(); + throw new JsonException(invalidCastException.Message, invalidCastException); + } + } + + return null; + } + + internal static bool ReadNextToken(this ref Utf8JsonReader reader) + { + bool wasRead; + + while ((wasRead = reader.Read()) && reader.TokenType == JsonTokenType.Comment) + { + } + + return wasRead; + } + + internal static bool ReadNextTokenAsBoolOrFalse(this ref Utf8JsonReader reader) + { + if (reader.ReadNextToken() && (reader.TokenType == JsonTokenType.False || reader.TokenType == JsonTokenType.True)) + { + return reader.GetBoolean(); + } + return false; + } + + internal static string ReadNextTokenAsString(this ref Utf8JsonReader reader) + { + if (ReadNextToken(ref reader)) + { + return ReadTokenAsString(ref reader); + } + + return null; + } + + internal static IList ReadObjectAsList(this ref Utf8JsonReader reader, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + return new List(0); + + } + //We use JsonObjects for the arrays so we advance to the first property in the object which is the name/ver of the first library + reader.ReadNextToken(); + + if (reader.TokenType == JsonTokenType.EndObject) + { + return new List(0); + } + + var objectConverter = (JsonConverter)options.GetConverter(typeof(T)); + var listObjects = new List(); + do + { + listObjects.Add(objectConverter.Read(ref reader, typeof(T), options)); + //At this point we're looking at the EndObject token for the object, need to advance. + reader.ReadNextToken(); + } + while (reader.TokenType != JsonTokenType.EndObject); + return listObjects; + } + + internal static List ReadNextStringArrayAsList(this ref Utf8JsonReader reader, List strings = null) + { + ReadNextToken(ref reader); + return ReadStringArrayAsList(ref reader, strings); + } + + internal static List ReadStringArrayAsList(this ref Utf8JsonReader reader, List strings = null) + { + if (reader.TokenType == JsonTokenType.StartArray) + { + while (ReadNextToken(ref reader) && reader.TokenType != JsonTokenType.EndArray) + { + string value = ReadTokenAsString(ref reader); + + strings = strings ?? new List(); + + strings.Add(value); + } + } + + return strings; + } + + + internal static IList ReadStringArrayAsIList(this ref Utf8JsonReader reader, IList strings = null) + { + if (reader.TokenType == JsonTokenType.StartArray) + { + while (ReadNextToken(ref reader) && reader.TokenType != JsonTokenType.EndArray) + { + string value = ReadTokenAsString(ref reader); + + strings = strings ?? new List(); + + strings.Add(value); + } + } + + return strings; + } + + internal static void ReadArrayOfObjects(this ref Utf8JsonReader reader, JsonSerializerOptions options, IList objectList) where T1 : T2 + { + if (objectList is null) + { + return; + } + + var type = typeof(T1); + var objectConverter = (JsonConverter)options.GetConverter(type); + + if (ReadNextToken(ref reader) && reader.TokenType == JsonTokenType.StartArray) + { + while (ReadNextToken(ref reader) && reader.TokenType != JsonTokenType.EndArray) + { + var convertedObject = objectConverter.Read(ref reader, type, options); + if (convertedObject != null) + { + objectList.Add(convertedObject); + } + } + } + + } + + internal static IReadOnlyList ReadNextStringOrArrayOfStringsAsReadOnlyList(this ref Utf8JsonReader reader) + { + if (ReadNextToken(ref reader)) + { + switch (reader.TokenType) + { + case JsonTokenType.String: + return new[] { (string)reader.GetString() }; + + case JsonTokenType.StartArray: + return ReadStringArrayAsReadOnlyListFromArrayStart(ref reader); + + case JsonTokenType.StartObject: + return null; + } + } + + return null; + } + + internal static IReadOnlyList ReadStringArrayAsReadOnlyListFromArrayStart(this ref Utf8JsonReader reader) + { + List strings = null; + + while (ReadNextToken(ref reader) && reader.TokenType != JsonTokenType.EndArray) + { + string value = ReadTokenAsString(ref reader); + + strings = strings ?? new List(); + + strings.Add(value); + } + + return (IReadOnlyList)strings ?? Array.Empty(); + } + + internal static string ReadTokenAsString(this ref Utf8JsonReader reader) + { + switch (reader.TokenType) + { + case JsonTokenType.True: + case JsonTokenType.False: + return reader.GetBoolean().ToString(); + case JsonTokenType.Number: + if (reader.TryGetInt16(out short shortValue)) + { + return shortValue.ToString(); + } + if (reader.TryGetInt32(out int intValue)) + { + return intValue.ToString(); + } + else if (reader.TryGetInt64(out long longValue)) + { + return longValue.ToString(); + } + return reader.GetDouble().ToString(); + case JsonTokenType.String: + return reader.GetString(); + case JsonTokenType.None: + case JsonTokenType.Null: + return null; + default: + throw new InvalidCastException(); + } + } + } +} diff --git a/src/NuGet.Core/NuGet.ProjectModel/Utf8JsonStreamProjectFileDependencyGroupConverter.cs b/src/NuGet.Core/NuGet.ProjectModel/Utf8JsonStreamProjectFileDependencyGroupConverter.cs new file mode 100644 index 00000000000..b5fa624f09b --- /dev/null +++ b/src/NuGet.Core/NuGet.ProjectModel/Utf8JsonStreamProjectFileDependencyGroupConverter.cs @@ -0,0 +1,28 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Text.Json; + +namespace NuGet.ProjectModel +{ + /// + /// A to allow read JSON into + /// + internal class Utf8JsonStreamProjectFileDependencyGroupConverter : Utf8JsonStreamReaderConverter + { + public override ProjectFileDependencyGroup Read(ref Utf8JsonStreamReader reader) + { + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException("Expected PropertyName, found " + reader.TokenType); + } + + var frameworkName = reader.GetString(); + reader.Read(); + var dependencies = reader.ReadStringArrayAsIList(new List()); + + return new ProjectFileDependencyGroup(frameworkName, dependencies); + } + } +} diff --git a/src/NuGet.Core/NuGet.ProjectModel/Utf8JsonStreamReader.cs b/src/NuGet.Core/NuGet.ProjectModel/Utf8JsonStreamReader.cs new file mode 100644 index 00000000000..45a8b4111f1 --- /dev/null +++ b/src/NuGet.Core/NuGet.ProjectModel/Utf8JsonStreamReader.cs @@ -0,0 +1,262 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.Json; + +namespace NuGet.ProjectModel +{ + internal ref struct Utf8JsonStreamReader + { + private static readonly char[] DelimitedStringDelimiters = new char[] { ' ', ',' }; + + private ReadOnlySpan _utf8Bom = new byte[] { 0xEF, 0xBB, 0xBF }; + private Utf8JsonReader _reader; + private byte[] _buffer; + private Stream _stream; + + internal Utf8JsonStreamReader(Stream stream) + { + if (stream is null) + { + throw new ArgumentNullException(nameof(stream)); + } + + _stream = stream; + var firstThreeBytes = new byte[3]; + _stream.Read(firstThreeBytes, 0, 3); + _buffer = new byte[1024]; + var offset = 0; + if (!_utf8Bom.SequenceEqual(firstThreeBytes)) + { + firstThreeBytes.CopyTo(_buffer, 0); + offset = 3; + } + _stream.Read(_buffer, offset, _buffer.Length - offset); + _reader = new Utf8JsonReader(_buffer, isFinalBlock: false, state: new JsonReaderState(new JsonReaderOptions + { + AllowTrailingCommas = true, + CommentHandling = JsonCommentHandling.Skip, + })); + _reader.Read(); + } + + internal bool IsFinalBlock => _reader.IsFinalBlock; + + internal int BufferSize => _buffer.Length; + + internal JsonTokenType TokenType => _reader.TokenType; + + internal bool ValueTextEquals(ReadOnlySpan utf8Text) => _reader.ValueTextEquals(utf8Text); + + internal bool ValueTextEquals(string text) => _reader.ValueTextEquals(text); + + internal bool TryGetInt32(out int value) => _reader.TryGetInt32(out value); + + internal string GetString() => _reader.GetString(); + + internal bool GetBoolean() => _reader.GetBoolean(); + + internal int GetInt32() => _reader.GetInt32(); + + internal bool Read() + { + bool wasRead; + while (!(wasRead = _reader.Read()) && !_reader.IsFinalBlock) + { + GetMoreBytesFromStream(); + } + return wasRead; + } + + internal bool TrySkip() + { + bool wasSkipped; + while (!(wasSkipped = _reader.TrySkip()) && !_reader.IsFinalBlock) + { + GetMoreBytesFromStream(); + } + return wasSkipped; + } + + internal IList ReadObjectAsList(Utf8JsonStreamReaderConverter streamReaderConverter) + { + if (TokenType != JsonTokenType.StartObject) + { + return new List(0); + + } + //We use JsonObjects for the arrays so we advance to the first property in the object which is the name/ver of the first library + Read(); + + if (TokenType == JsonTokenType.EndObject) + { + return new List(0); + } + + var listObjects = new List(); + do + { + listObjects.Add(streamReaderConverter.Read(ref this)); + //At this point we're looking at the EndObject token for the object, need to advance. + Read(); + } + while (TokenType != JsonTokenType.EndObject); + return listObjects; + } + + internal void ReadArrayOfObjects(IList objectList, Utf8JsonStreamReaderConverter streamReaderConverter) where T1 : T2 + { + if (objectList is null) + { + return; + } + + if (Read() && TokenType == JsonTokenType.StartArray) + { + while (Read() && TokenType != JsonTokenType.EndArray) + { + var convertedObject = streamReaderConverter.Read(ref this); + if (convertedObject != null) + { + objectList.Add(convertedObject); + } + } + } + } + + internal string ReadNextTokenAsString() + { + if (Read()) + { + return _reader.ReadTokenAsString(); + } + + return null; + } + + internal string GetCurrentBufferAsString() => Encoding.UTF8.GetString(_buffer); + + internal IList ReadStringArrayAsIList(IList strings = null) + { + if (TokenType == JsonTokenType.StartArray) + { + while (Read() && TokenType != JsonTokenType.EndArray) + { + string value = _reader.ReadTokenAsString(); + + strings = strings ?? new List(); + + strings.Add(value); + } + } + + return strings; + } + + internal IReadOnlyList ReadDelimitedString() + { + if (Read()) + { + switch (TokenType) + { + case JsonTokenType.String: + var value = GetString(); + + return value.Split(DelimitedStringDelimiters, StringSplitOptions.RemoveEmptyEntries); + + default: + var invalidCastException = new InvalidCastException(); + throw new JsonException(invalidCastException.Message, invalidCastException); + } + } + + return null; + } + + internal List ReadStringArrayAsList(List strings = null) + { + if (TokenType == JsonTokenType.StartArray) + { + while (Read() && TokenType != JsonTokenType.EndArray) + { + string value = _reader.ReadTokenAsString(); + + strings = strings ?? new List(); + + strings.Add(value); + } + } + + return strings; + } + + internal bool ReadNextTokenAsBoolOrFalse() + { + if (Read() && (TokenType == JsonTokenType.False || TokenType == JsonTokenType.True)) + { + return GetBoolean(); + } + return false; + } + + internal IReadOnlyList ReadNextStringOrArrayOfStringsAsReadOnlyList() + { + if (Read()) + { + switch (_reader.TokenType) + { + case JsonTokenType.String: + return new[] { (string)_reader.GetString() }; + + case JsonTokenType.StartArray: + return ReadStringArrayAsReadOnlyListFromArrayStart(); + + case JsonTokenType.StartObject: + return null; + } + } + + return null; + } + + internal IReadOnlyList ReadStringArrayAsReadOnlyListFromArrayStart() + { + List strings = null; + + while (Read() && _reader.TokenType != JsonTokenType.EndArray) + { + string value = _reader.ReadTokenAsString(); + + strings = strings ?? new List(); + + strings.Add(value); + } + + return (IReadOnlyList)strings ?? Array.Empty(); + } + + private void GetMoreBytesFromStream() + { + if (_reader.BytesConsumed < _buffer.Length) + { + ReadOnlySpan leftover = _buffer.AsSpan((int)_reader.BytesConsumed); + if (leftover.Length == _buffer.Length) + { + Array.Resize(ref _buffer, _buffer.Length * 2); + } + leftover.CopyTo(_buffer); + _stream.Read(_buffer, leftover.Length, _buffer.Length - leftover.Length); + } + else + { + _stream.Read(_buffer, 0, _buffer.Length); + } + _reader = new Utf8JsonReader(_buffer, isFinalBlock: _stream.Length == _stream.Position, _reader.CurrentState); + } + } +} diff --git a/src/NuGet.Core/NuGet.ProjectModel/Utf8JsonStreamReaderConverter.cs b/src/NuGet.Core/NuGet.ProjectModel/Utf8JsonStreamReaderConverter.cs new file mode 100644 index 00000000000..167750c3828 --- /dev/null +++ b/src/NuGet.Core/NuGet.ProjectModel/Utf8JsonStreamReaderConverter.cs @@ -0,0 +1,13 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +namespace NuGet.ProjectModel +{ + /// + /// An abstract class that defines a function for reading a into a + /// + /// + internal abstract class Utf8JsonStreamReaderConverter + { + public abstract T Read(ref Utf8JsonStreamReader reader); + } +} diff --git a/test/NuGet.Clients.Tests/NuGet.CommandLine.Test/NetworkCallCountTest.cs b/test/NuGet.Clients.Tests/NuGet.CommandLine.Test/NetworkCallCountTest.cs index 9ced3e2f338..a04e6c73169 100644 --- a/test/NuGet.Clients.Tests/NuGet.CommandLine.Test/NetworkCallCountTest.cs +++ b/test/NuGet.Clients.Tests/NuGet.CommandLine.Test/NetworkCallCountTest.cs @@ -1553,23 +1553,23 @@ private void CreateMixedConfigAndJson(string workingPath) Util.CreateFile(proj3Dir, "project.json", @"{ - 'dependencies': { - 'packageD': '1.0.0', - 'packageE': '1.0.*' + ""dependencies"": { + ""packageD"": ""1.0.0"", + ""packageE"": ""1.0.*"" }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"); Util.CreateFile(proj4Dir, "project.json", @"{ - 'dependencies': { - 'packageE': '1.0.0', - 'packageF': '*' + ""dependencies"": { + ""packageE"": ""1.0.0"", + ""packageF"": ""*"" }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"); diff --git a/test/NuGet.Clients.Tests/NuGet.CommandLine.Test/NuGetListCommandTest.cs b/test/NuGet.Clients.Tests/NuGet.CommandLine.Test/NuGetListCommandTest.cs index a1a66e735d6..e7e597bb347 100644 --- a/test/NuGet.Clients.Tests/NuGet.CommandLine.Test/NuGetListCommandTest.cs +++ b/test/NuGet.Clients.Tests/NuGet.CommandLine.Test/NuGetListCommandTest.cs @@ -9,7 +9,6 @@ using System.Text; using FluentAssertions; using NuGet.Common; -using NuGet.Configuration.Test; using NuGet.Packaging; using NuGet.Test.Utility; using Test.Utility; diff --git a/test/NuGet.Clients.Tests/NuGet.CommandLine.Test/NuGetRestoreCommandTest.cs b/test/NuGet.Clients.Tests/NuGet.CommandLine.Test/NuGetRestoreCommandTest.cs index 571e6fd5b39..8fd0249e601 100644 --- a/test/NuGet.Clients.Tests/NuGet.CommandLine.Test/NuGetRestoreCommandTest.cs +++ b/test/NuGet.Clients.Tests/NuGet.CommandLine.Test/NuGetRestoreCommandTest.cs @@ -1288,12 +1288,12 @@ public void RestoreCommand_FromProjectJson_RelativeGlobalPackagesFolder() var projectJson = @"{ - 'dependencies': { - 'packageA': '1.1.0', - 'packageB': '2.2.0' + ""dependencies"": { + ""packageA"": ""1.1.0"", + ""packageB"": ""2.2.0"" }, - 'frameworks': { - 'netcore50': { } + ""frameworks"": { + ""netcore50"": { } } }"; @@ -1992,12 +1992,12 @@ public void RestoreCommand_FromSolutionFile_ProjectsInParentDir() Util.CreateFile(Path.Combine(basePath, "A", "A.Util"), "project.json", @"{ - 'dependencies': { - 'packageA': '1.1.0', - 'packageB': '2.2.0' + ""dependencies"": { + ""packageA"": ""1.1.0"", + ""packageB"": ""2.2.0"" }, - 'frameworks': { - 'netcore50': { } + ""frameworks"": { + ""netcore50"": { } } }"); Util.CreateFile(Path.Combine(basePath, "B"), "B.csproj", @@ -2022,10 +2022,10 @@ public void RestoreCommand_FromSolutionFile_ProjectsInParentDir() Util.CreateFile(Path.Combine(basePath, "B"), "project.json", @"{ - 'dependencies': { + ""dependencies"": { }, - 'frameworks': { - 'netcore50': { } + ""frameworks"": { + ""netcore50"": { } } }"); diff --git a/test/NuGet.Clients.Tests/NuGet.CommandLine.Test/RestoreProjectJsonTest.cs b/test/NuGet.Clients.Tests/NuGet.CommandLine.Test/RestoreProjectJsonTest.cs index 4702b9f82f4..c766955223d 100644 --- a/test/NuGet.Clients.Tests/NuGet.CommandLine.Test/RestoreProjectJsonTest.cs +++ b/test/NuGet.Clients.Tests/NuGet.CommandLine.Test/RestoreProjectJsonTest.cs @@ -41,11 +41,11 @@ public async Task RestoreProjectJson_MinClientVersionFailAsync() await SimpleTestPackageUtility.CreatePackagesAsync(repositoryPath, packageContext); var projectJson = @"{ - 'dependencies': { - 'packageA': '1.0.0' + ""dependencies"": { + ""packageA"": ""1.0.0"" }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"; @@ -90,20 +90,20 @@ public void RestoreProjectJson_RestoreFolder_VerifyFailure() Util.CreateFile(projectDir1, "project.json", @"{ - 'dependencies': { + ""dependencies"": { }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"); Util.CreateFile(projectDir2, "project.json", @"{ - 'version': '1.0.0-*', - 'dependencies': { + ""version"": ""1.0.0-*"", + ""dependencies"": { }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"); @@ -151,10 +151,10 @@ public void RestoreProjectJson_RestoreForSingleProject() Util.CreateFile(projectDir1, "project.json", @"{ - 'dependencies': { + ""dependencies"": { }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"); @@ -163,11 +163,11 @@ public void RestoreProjectJson_RestoreForSingleProject() Util.CreateFile(projectDir2, "project.json", @"{ - 'version': '1.0.0-*', - 'dependencies': { + ""version"": ""1.0.0-*"", + ""dependencies"": { }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"); @@ -267,10 +267,10 @@ public async Task RestoreProjectJson_RestoreWithFallbackFolderAsync() Util.CreateFile(projectDir1, "project.json", @"{ - 'dependencies': { + ""dependencies"": { }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"); @@ -279,13 +279,13 @@ public async Task RestoreProjectJson_RestoreWithFallbackFolderAsync() Util.CreateFile(projectDir2, "project.json", @"{ - 'version': '1.0.0-*', - 'dependencies': { - 'packageA': '1.0.0', - 'packageB': '1.0.0' + ""version"": ""1.0.0-*"", + ""dependencies"": { + ""packageA"": ""1.0.0"", + ""packageB"": ""1.0.0"" }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"); @@ -332,10 +332,10 @@ public void RestoreProjectJson_RestoreFromSlnWithCsproj() Util.CreateFile(projectDir1, "project.json", @"{ - 'dependencies': { + ""dependencies"": { }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"); @@ -412,26 +412,26 @@ public void RestoreProjectJson_RestoreFromSlnWithCsproj_InconsitentCaseForProjec Util.CreateFile(projectDir1, "project.json", @"{ - 'dependencies': { + ""dependencies"": { }, - 'frameworks': { - 'net45': { } + ""frameworks"": { + ""net45"": { } } }"); Util.CreateFile(projectDir2, "project.json", @"{ - 'dependencies': { + ""dependencies"": { }, - 'frameworks': { - 'net45': { } + ""frameworks"": { + ""net45"": { } } }"); Util.CreateFile(projectDir3, "project.json", @"{ - 'dependencies': { + ""dependencies"": { }, - 'frameworks': { - 'net45': { } + ""frameworks"": { + ""net45"": { } } }"); @@ -570,10 +570,10 @@ public void RestoreProjectJson_P2PTimeouts(string timeout, int projectCount, int Util.CreateFile(projectDir, "project.json", @"{ - 'dependencies': { + ""dependencies"": { }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"); @@ -687,10 +687,10 @@ public async Task RestoreProjectJson_RestoreFromSlnWithReferenceOutputAssemblyFa Util.CreateFile(projectDir1, "project.json", @"{ - 'dependencies': { + ""dependencies"": { }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"); @@ -713,12 +713,12 @@ public async Task RestoreProjectJson_RestoreFromSlnWithReferenceOutputAssemblyFa Util.CreateFile(projectDir2, "project.json", @"{ - 'version': '1.0.0-*', - 'dependencies': { + ""version"": ""1.0.0-*"", + ""dependencies"": { ""packageA"": ""1.0.0"" }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"); @@ -897,10 +897,10 @@ public void RestoreProjectJson_RestoreCSProj() Util.CreateFile(projectDir1, "project.json", @"{ - 'dependencies': { + ""dependencies"": { }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"); @@ -947,10 +947,10 @@ public void RestoreProjectJson_RestoreUnknownProj() Util.CreateFile(projectDir1, "project.json", @"{ - 'dependencies': { + ""dependencies"": { }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"); @@ -1002,11 +1002,11 @@ public async Task RestoreProjectJson_RestoreFromSlnWithUnknownProjAndCsproj() Util.CreateFile(projectDir1, "project.json", @"{ - 'dependencies': { - 'packageA': '1.1.0-beta-*' + ""dependencies"": { + ""packageA"": ""1.1.0-beta-*"" }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"); @@ -1014,12 +1014,12 @@ public async Task RestoreProjectJson_RestoreFromSlnWithUnknownProjAndCsproj() Util.CreateFile(projectDir2, "project.json", @"{ - 'version': '1.0.0-*', - 'dependencies': { - 'packageA': '1.1.0-beta-*' + ""version"": ""1.0.0-*"", + ""dependencies"": { + ""packageA"": ""1.1.0-beta-*"" }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"); @@ -1112,11 +1112,11 @@ public async Task RestoreProjectJson_RestoreFromSlnUsesNuGetFolderSettingsAsync( Util.CreateFile(projectDir1, "project.json", @"{ - 'dependencies': { - 'packageA': '1.0.0' + ""dependencies"": { + ""packageA"": ""1.0.0"" }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"); @@ -1124,12 +1124,12 @@ public async Task RestoreProjectJson_RestoreFromSlnUsesNuGetFolderSettingsAsync( Util.CreateFile(projectDir2, "project.json", @"{ - 'version': '1.0.0-*', - 'dependencies': { - 'packageB': '1.0.0' + ""version"": ""1.0.0-*"", + ""dependencies"": { + ""packageB"": ""1.0.0"" }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"); @@ -1215,11 +1215,11 @@ public void RestoreProjectJson_FloatReleaseLabelHighestPrelease() Util.CreateTestPackage("packageA", "1.0.0-beta-01", repositoryPath); Util.CreateTestPackage("packageA", "1.0.0-beta-02", repositoryPath); var projectJson = @"{ - 'dependencies': { - 'packageA': '1.0.0-*' + ""dependencies"": { + ""packageA"": ""1.0.0-*"" }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"; @@ -1271,11 +1271,11 @@ public void RestoreProjectJson_FloatReleaseLabelTakesStable() Util.CreateTestPackage("packageA", "1.0.0-beta-01", repositoryPath); Util.CreateTestPackage("packageA", "1.0.0-beta-02", repositoryPath); var projectJson = @"{ - 'dependencies': { - 'packageA': '1.0.0-*' + ""dependencies"": { + ""packageA"": ""1.0.0-*"" }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"; @@ -1383,11 +1383,11 @@ public void RestoreProjectJson_RestoreFiltersToStablePackages() Util.CreateTestPackage("packageB", "2.0.0-beta", repositoryPath); Util.CreateTestPackage("packageB", "3.0.0", repositoryPath); var projectJson = @"{ - 'dependencies': { - 'packageA': '1.0.0' + ""dependencies"": { + ""packageA"": ""1.0.0"" }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"; @@ -1439,12 +1439,12 @@ public void RestoreProjectJson_RestoreBumpsFromStableToPrereleaseWhenNeeded() Util.CreateTestPackage("packageC", "1.0.0", repositoryPath); Util.CreateTestPackage("packageC", "2.0.0-beta", repositoryPath); var projectJson = @"{ - 'dependencies': { - 'packageA': '1.0.0', - 'packageB': '1.0.0-*' + ""dependencies"": { + ""packageA"": ""1.0.0"", + ""packageB"": ""1.0.0-*"" }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"; @@ -1495,12 +1495,12 @@ public void RestoreProjectJson_RestoreDowngradesStableDependency() Util.CreateTestPackage("packageC", "3.0.0", repositoryPath); Util.CreateTestPackage("packageC", "2.1.0", repositoryPath); var projectJson = @"{ - 'dependencies': { - 'packageA': '1.0.0', - 'packageB': '1.0.0' + ""dependencies"": { + ""packageA"": ""1.0.0"", + ""packageB"": ""1.0.0"" }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"; @@ -1551,12 +1551,12 @@ public void RestoreProjectJson_RestoreDowngradesFromStableToPrereleaseWhenNeeded Util.CreateTestPackage("packageC", "2.0.0-beta", repositoryPath); Util.CreateConfigForGlobalPackagesFolder(workingPath); var projectJson = @"{ - 'dependencies': { - 'packageA': '1.0.0', - 'packageB': '1.0.0-*' + ""dependencies"": { + ""packageA"": ""1.0.0"", + ""packageB"": ""1.0.0-*"" }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"; @@ -1613,21 +1613,21 @@ public async Task RestoreProjectJson_SolutionFileWithAllProjectsInOneFolder() Util.CreateFile(projectDir, "testA.project.json", @"{ - 'dependencies': { - 'packageA': '1.1.0-beta-*' + ""dependencies"": { + ""packageA"": ""1.1.0-beta-*"" }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"); Util.CreateFile(projectDir, "testB.project.json", @"{ - 'dependencies': { - 'packageA': '1.1.0-beta-*' + ""dependencies"": { + ""packageA"": ""1.1.0-beta-*"" }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"); @@ -1724,11 +1724,11 @@ public async Task RestoreProjectJson_GenerateFilesWithProjectNameFromCSProj() Util.CreateFile(workingPath, "test.project.json", @"{ - 'dependencies': { - 'packageA': '1.1.0-beta-*' + ""dependencies"": { + ""packageA"": ""1.1.0-beta-*"" }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"); @@ -1785,11 +1785,11 @@ public async Task RestoreProjectJson_GenerateTargetsFileFromSln() Util.CreateFile(projectDir, "project.json", @"{ - 'dependencies': { - 'packageA': '1.1.0-beta-*' + ""dependencies"": { + ""packageA"": ""1.1.0-beta-*"" }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"); @@ -1876,12 +1876,12 @@ public async Task RestoreProjectJson_GenerateTargetsFileFromCSProj() Util.CreateFile(workingPath, "project.json", @"{ - 'dependencies': { - 'packageA': '1.1.0-beta-*', - 'packageB': '2.2.0-beta-*' + ""dependencies"": { + ""packageA"": ""1.1.0-beta-*"", + ""packageB"": ""2.2.0-beta-*"" }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"); @@ -1972,12 +1972,12 @@ public async Task RestoreProjectJson_GenerateTargetsForFallbackFolderAsync() Util.CreateFile(projectDir, "project.json", @"{ - 'dependencies': { - 'packageA': '1.1.0-beta-*', - 'packageB': '2.2.0-beta-*' + ""dependencies"": { + ""packageA"": ""1.1.0-beta-*"", + ""packageB"": ""2.2.0-beta-*"" }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"); @@ -2038,12 +2038,12 @@ public async Task RestoreProjectJson_GenerateTargetsFileFromNuProj() Util.CreateFile(workingPath, "project.json", @"{ - 'dependencies': { - 'packageA': '1.1.0-beta-*', - 'packageB': '2.2.0-beta-*' + ""dependencies"": { + ""packageA"": ""1.1.0-beta-*"", + ""packageB"": ""2.2.0-beta-*"" }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"); @@ -2103,12 +2103,12 @@ public async Task RestoreProjectJson_GenerateTargetsFileWithFolder() Util.CreateFile(workingPath, "project.json", @"{ - 'dependencies': { - 'packageA': '1.1.0-beta-*', - 'packageB': '2.2.0-beta-*' + ""dependencies"": { + ""packageA"": ""1.1.0-beta-*"", + ""packageB"": ""2.2.0-beta-*"" }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"); @@ -2165,11 +2165,11 @@ public async Task RestoreProjectJson_GenerateTargetsForRootBuildFolderIgnoreSubF Util.CreateFile(workingPath, "project.json", @"{ - 'dependencies': { - 'packageA': '3.1.0', + ""dependencies"": { + ""packageA"": ""3.1.0"", }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"); @@ -2232,12 +2232,12 @@ public async Task RestoreProjectJson_GenerateTargetsPersistsWithMultipleRestores Util.CreateFile(workingPath, "project.json", @"{ - 'dependencies': { - 'packageA': '1.1.0-beta-*', - 'packageB': '2.2.0-beta-*' + ""dependencies"": { + ""packageA"": ""1.1.0-beta-*"", + ""packageB"": ""2.2.0-beta-*"" }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"); @@ -2322,12 +2322,12 @@ public void RestoreProjectJson_CorruptedLockFile() Util.CreateTestPackage("packageA", "1.1.0", repositoryPath); Util.CreateTestPackage("packageB", "2.2.0", repositoryPath); var projectJson = @"{ - 'dependencies': { - 'packageA': '1.1.0', - 'packageB': '2.2.0' + ""dependencies"": { + ""packageA"": ""1.1.0"", + ""packageB"": ""2.2.0"" }, - 'frameworks': { - 'uap10.0': { } + ""frameworks"": { + ""uap10.0"": { } } }"; diff --git a/test/NuGet.Clients.Tests/NuGet.CommandLine.Test/Util.cs b/test/NuGet.Clients.Tests/NuGet.CommandLine.Test/Util.cs index 861456d7b3a..f051ef54ca4 100644 --- a/test/NuGet.Clients.Tests/NuGet.CommandLine.Test/Util.cs +++ b/test/NuGet.Clients.Tests/NuGet.CommandLine.Test/Util.cs @@ -994,14 +994,14 @@ public static void CreateConfigFile(string path, string configFileName, string t public static string GetProjectJsonFileContents(string targetFramework, IEnumerable packages) { - var dependencies = string.Join(", ", packages.Select(package => $"'{package.Id}': '{package.Version}'")); + var dependencies = string.Join(", ", packages.Select(package => $"\"{package.Id}\": \"{package.Version}\"")); return $@" {{ - 'dependencies': {{ + ""dependencies"": {{ {dependencies} }}, - 'frameworks': {{ - '{targetFramework}': {{ }} + ""frameworks"": {{ + ""{targetFramework}"": {{ }} }} }}"; } diff --git a/test/NuGet.Clients.Tests/NuGet.VisualStudio.Implementation.Test/SolutionExplorer/Models/AssetsFileDependenciesSnapshotTests.cs b/test/NuGet.Clients.Tests/NuGet.VisualStudio.Implementation.Test/SolutionExplorer/Models/AssetsFileDependenciesSnapshotTests.cs index c08ef5984d9..846b33dc9e8 100644 --- a/test/NuGet.Clients.Tests/NuGet.VisualStudio.Implementation.Test/SolutionExplorer/Models/AssetsFileDependenciesSnapshotTests.cs +++ b/test/NuGet.Clients.Tests/NuGet.VisualStudio.Implementation.Test/SolutionExplorer/Models/AssetsFileDependenciesSnapshotTests.cs @@ -59,7 +59,9 @@ public void ParseLibraries_IgnoreCaseInDependenciesTree_Succeeds() var lockFilePath = """C:\repo\obj\project.assets.json"""; +#pragma warning disable CS0612 // Type or member is obsolete var lockFile = new LockFileFormat().Parse(lockFileContent, lockFilePath); +#pragma warning restore CS0612 // Type or member is obsolete var dependencies = AssetsFileDependenciesSnapshot.ParseLibraries(lockFile, lockFile.Targets.First(), ImmutableArray.Empty); @@ -98,7 +100,9 @@ public void ParseLibraries_LogForUnknownLibrary_AddsUnknownLibraryType() var lockFilePath = """C:\repo\obj\project.assets.json"""; +#pragma warning disable CS0612 // Type or member is obsolete var lockFile = new LockFileFormat().Parse(lockFileContent, lockFilePath); +#pragma warning restore CS0612 // Type or member is obsolete var logMessages = ImmutableArray.Create(new AssetsFileLogMessage(lockFilePath, lockFile.LogMessages.Single())); @@ -148,7 +152,9 @@ public void ParseLibraries_LogForUnknownLibrary_WithAbsolutePath_AddsUnknownLibr var lockFilePath = """C:\repo\obj\project.assets.json"""; +#pragma warning disable CS0612 // Type or member is obsolete var lockFile = new LockFileFormat().Parse(lockFileContent, lockFilePath); +#pragma warning restore CS0612 // Type or member is obsolete var logMessages = ImmutableArray.Create(new AssetsFileLogMessage(lockFilePath, lockFile.LogMessages.Single())); diff --git a/test/NuGet.Core.FuncTests/NuGet.Commands.FuncTest/RestoreCommandTests.cs b/test/NuGet.Core.FuncTests/NuGet.Commands.FuncTest/RestoreCommandTests.cs index ba9f416afa6..41ae512a142 100644 --- a/test/NuGet.Core.FuncTests/NuGet.Commands.FuncTest/RestoreCommandTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.Commands.FuncTest/RestoreCommandTests.cs @@ -1912,7 +1912,9 @@ public async Task RestoreCommand_LockedLockFileWithOutOfDateProjectAsync() var spec = JsonPackageSpecReader.GetPackageSpec(project, "TestProject", specPath); var lockFileFormat = new LockFileFormat(); +#pragma warning disable CS0612 // Type or member is obsolete var lockFile = lockFileFormat.Parse(lockFileContent, "In Memory"); +#pragma warning restore CS0612 // Type or member is obsolete var logger = new TestLogger(); var request = new TestRestoreRequest(spec, sources, packagesDir, logger) diff --git a/test/NuGet.Core.FuncTests/NuGet.Commands.FuncTest/UWPRestoreTests.cs b/test/NuGet.Core.FuncTests/NuGet.Commands.FuncTest/UWPRestoreTests.cs index bb47d8b7fe4..d90a64e820a 100644 --- a/test/NuGet.Core.FuncTests/NuGet.Commands.FuncTest/UWPRestoreTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.Commands.FuncTest/UWPRestoreTests.cs @@ -35,7 +35,9 @@ public void UWPRestore_ReadV1LockFile() var format = new LockFileFormat(); // Act +#pragma warning disable CS0612 // Type or member is obsolete lockFile = format.Parse(reader.ReadToEnd(), "c:\\project.lock.json"); +#pragma warning restore CS0612 // Type or member is obsolete } // Assert @@ -61,7 +63,9 @@ public void UWPRestore_ReadLockFileRoundTrip() var path = Path.Combine(workingDir, "project.lock.json"); // Act +#pragma warning disable CS0612 // Type or member is obsolete var lockFile = format.Parse(json.ToString(), path); +#pragma warning restore CS0612 // Type or member is obsolete format.Write(path, lockFile); var jsonOutput = JObject.Parse(File.ReadAllText(path)); diff --git a/test/NuGet.Core.Tests/NuGet.PackageManagement.Test/BuildIntegration/BuildIntegrationTestUtility.cs b/test/NuGet.Core.Tests/NuGet.PackageManagement.Test/BuildIntegration/BuildIntegrationTestUtility.cs index 6e67ad8b44a..8547cdf1b75 100644 --- a/test/NuGet.Core.Tests/NuGet.PackageManagement.Test/BuildIntegration/BuildIntegrationTestUtility.cs +++ b/test/NuGet.Core.Tests/NuGet.PackageManagement.Test/BuildIntegration/BuildIntegrationTestUtility.cs @@ -37,12 +37,12 @@ public static JObject BasicConfig } public const string ProjectJsonWithPackage = @"{ - 'dependencies': { - 'EntityFramework': '5.0.0' + ""dependencies"": { + ""EntityFramework"": ""5.0.0"" }, - 'frameworks': { - 'net46': { } - } + ""frameworks"": { + ""net46"": { } + } }"; public static ExternalProjectReference CreateReference(string name) diff --git a/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/DependencyTargetTests.cs b/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/DependencyTargetTests.cs index b86dcd11bba..6915813d959 100644 --- a/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/DependencyTargetTests.cs +++ b/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/DependencyTargetTests.cs @@ -159,11 +159,47 @@ public void DependencyTarget_UnknownValueFails() // Assert Assert.NotNull(exception); - Assert.Equal("Invalid dependency target value 'blah'.", exception.Message); + Assert.Equal("Error reading 'project.json' : Invalid dependency target value 'blah'.", exception.Message); Assert.EndsWith("project.json", exception.Path); - Assert.Equal(5, exception.Line); } + //[Fact] + //public void DependencyTarget_UnknownValueFails() + //{ + // // Arrange + // var json = @"{ + // ""dependencies"": { + // ""packageA"": { + // ""version"": ""1.0.0"", + // ""target"": ""blah"" + // } + // }, + // ""frameworks"": { + // ""net46"": {} + // } + // }"; + + + // // Act + // FileFormatException exception = null; + + // try + // { + // var spec = JsonPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + // var dependency = spec.Dependencies.Single(); + // } + // catch (FileFormatException ex) + // { + // exception = ex; + // } + + // // Assert + // Assert.NotNull(exception); + // Assert.Equal("Invalid dependency target value 'blah'.", exception.Message); + // Assert.EndsWith("project.json", exception.Path); + // Assert.Equal(5, exception.Line); + //} + [Fact] public void DependencyTarget_NonWhiteListValueFails() { @@ -196,11 +232,48 @@ public void DependencyTarget_NonWhiteListValueFails() // Assert Assert.NotNull(exception); - Assert.Equal("Invalid dependency target value 'winmd'.", exception.Message); + Assert.Equal("Error reading 'project.json' : Invalid dependency target value 'winmd'.", exception.Message); Assert.EndsWith("project.json", exception.Path); - Assert.Equal(5, exception.Line); } + + //[Fact] + //public void DependencyTarget_NonWhiteListValueFails() + //{ + // // Arrange + // var json = @"{ + // ""dependencies"": { + // ""packageA"": { + // ""version"": ""1.0.0"", + // ""target"": ""winmd"" + // } + // }, + // ""frameworks"": { + // ""net46"": {} + // } + // }"; + + + // // Act + // FileFormatException exception = null; + + // try + // { + // var spec = JsonPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + // var dependency = spec.Dependencies.Single(); + // } + // catch (FileFormatException ex) + // { + // exception = ex; + // } + + // // Assert + // Assert.NotNull(exception); + // Assert.Equal("Invalid dependency target value 'winmd'.", exception.Message); + // Assert.EndsWith("project.json", exception.Path); + // Assert.Equal(5, exception.Line); + //} + [Fact] public void DependencyTarget_MultipleValuesFail() { @@ -219,25 +292,51 @@ public void DependencyTarget_MultipleValuesFail() // Act - FileFormatException exception = null; - - try - { - var spec = JsonPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); - var dependency = spec.Dependencies.Single(); - } - catch (FileFormatException ex) - { - exception = ex; - } + var exception = Assert.Throws(() => JsonPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json")); // Assert Assert.NotNull(exception); - Assert.Equal("Invalid dependency target value 'package,project'.", exception.Message); + Assert.Equal("Error reading 'project.json' : Invalid dependency target value 'package,project'.", exception.Message); Assert.EndsWith("project.json", exception.Path); - Assert.Equal(5, exception.Line); } + //[Fact] + //public void DependencyTarget_MultipleValuesFail() + //{ + // // Arrange + // var json = @"{ + // ""dependencies"": { + // ""packageA"": { + // ""version"": ""1.0.0"", + // ""target"": ""package,project"" + // } + // }, + // ""frameworks"": { + // ""net46"": {} + // } + // }"; + + + // // Act + // FileFormatException exception = null; + + // try + // { + // var spec = JsonPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + // var dependency = spec.Dependencies.Single(); + // } + // catch (FileFormatException ex) + // { + // exception = ex; + // } + + // // Assert + // Assert.NotNull(exception); + // Assert.Equal("Invalid dependency target value 'package,project'.", exception.Message); + // Assert.EndsWith("project.json", exception.Path); + // Assert.Equal(5, exception.Line); + //} + [Fact] public void DependencyTarget_AcceptsWhitespace() { diff --git a/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/LockFileFormatTests.cs b/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/LockFileFormatTests.cs index 54977dc03b8..4ba7fef7071 100644 --- a/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/LockFileFormatTests.cs +++ b/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/LockFileFormatTests.cs @@ -7,6 +7,7 @@ using System.Linq; using Newtonsoft.Json.Linq; using NuGet.Common; +using NuGet.Configuration; using NuGet.Frameworks; using NuGet.LibraryModel; using NuGet.Packaging.Core; @@ -148,9 +149,11 @@ public void LockFileFormat_LockedPropertyIsIgnored() var lockFileFormat = new LockFileFormat(); // Act +#pragma warning disable CS0612 // Type or member is obsolete var lockFileTrue = lockFileFormat.Parse(lockFileContentTrue, "In Memory"); var lockFileFalse = lockFileFormat.Parse(lockFileContentFalse, "In Memory"); var lockFileMissing = lockFileFormat.Parse(lockFileContentMissing, "In Memory"); +#pragma warning restore CS0612 // Type or member is obsolete var lockFileTrueString = lockFileFormat.Render(lockFileTrue); var lockFileFalseString = lockFileFormat.Render(lockFileFalse); @@ -207,7 +210,9 @@ public void LockFileFormat_ReadsLockFileWithNoTools() } }"; var lockFileFormat = new LockFileFormat(); +#pragma warning disable CS0612 // Type or member is obsolete var lockFile = lockFileFormat.Parse(lockFileContent, "In Memory"); +#pragma warning restore CS0612 // Type or member is obsolete Assert.Equal(1, lockFile.Version); @@ -2019,7 +2024,9 @@ public void LockFileFormat_ReadsLockFileWithTools() } }"; var lockFileFormat = new LockFileFormat(); +#pragma warning disable CS0612 // Type or member is obsolete var lockFile = lockFileFormat.Parse(lockFileContent, "In Memory"); +#pragma warning restore CS0612 // Type or member is obsolete Assert.Equal(1, lockFile.Version); @@ -2052,6 +2059,552 @@ public void LockFileFormat_ReadsLockFileWithTools() Assert.Empty(netPlatDepGroup.Dependencies); } + [Fact] + public void LockFileFormat_ReadsLockFully() + { + var lockFileContent = @"{ + ""version"": 3, + ""targets"": { + ""net6.0"": { + ""Microsoft.AspNetCore.JsonPatch/6.0.4"": { + ""type"": ""package"", + ""framework"": "".NETCoreApp,Version=v6.0"", + ""dependencies"": { + ""Microsoft.CSharp"": ""4.7.0"", + ""Newtonsoft.Json"": ""13.0.1"" + }, + ""frameworkAssemblies"": [ + ""System.Configuration"" + ], + ""compile"": { + ""lib/net6.0/Microsoft.AspNetCore.JsonPatch.dll"": {} + }, + ""runtime"": { + ""lib/net6.0/Microsoft.AspNetCore.JsonPatch.dll"": { + ""property1"": ""val1"", + ""property2"": 12, + ""property3"": true, + ""property4"": false, + } + }, + ""resource"": { + }, + ""contentFiles"": { + ""baz"": { + ""copyToOutput"": false + }, + ""foo"": { + ""copyToOutput"": true, + ""outputPath"": ""bar"" + } + }, + ""runtimeTargets"": { + ""foo"": { + ""copyToOutput"": 33 + }, + ""fooz"": { + ""copyToOutput"": true, + ""outputPath"": ""bar"" + } + }, + ""frameworkReferences"": [ + ""Microsoft.Windows.Desktop|WPF"", + ""Microsoft.Windows.Desktop|WindowsForms"" + ] + }, + ""Project10/1.0.0"": { + ""type"": ""project"", + ""framework"": "".NETCoreApp,Version=v6.0"" + }, + ""Microsoft.Extensions.ApiDescription.Server/3.0.0"": { + ""type"": ""package"", + ""build"": { + ""build/Microsoft.Extensions.ApiDescription.Server.props"": {}, + ""build/Microsoft.Extensions.ApiDescription.Server.targets"": {} + }, + ""buildMultiTargeting"": { + ""buildMultiTargeting/Microsoft.Extensions.ApiDescription.Server.props"": {}, + ""buildMultiTargeting/Microsoft.Extensions.ApiDescription.Server.targets"": {} + } + }, + ""runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0"": { + ""type"": ""package"", + ""runtimeTargets"": { + ""runtimes/debian.8-x64/native/System.Security.Cryptography.Native.OpenSsl.so"": { + ""assetType"": ""native"", + ""rid"": ""debian.8-x64"" + } + } + } + }, + "".NETCoreApp,Version=v2.1"": { + ""packageA.interop/1.0.0"": { + ""compile"": { + ""lib/netstandard2.0/packageA.interop.dll"": {} + }, + ""embed"": { + ""embed/netstandard2.0/packageA.interop.dll"": {} + }, + ""runtime"": { + ""lib/netstandard2.0/packageA.interop.dll"": {} + } + } + } + }, + ""libraries"": { + ""packageA.interop/1.0.0"": { + ""sha512"": ""WFRsJnfRzXYIiDJRbTXGctncx6Hw1F/uS2c5a5CzUwHuA3D/CM152F2HjWt12dLgH0BOcGvcRjKl2AfJ6MnHVg=="", + ""type"": ""package"", + ""files"": [ + ""_rels/.rels"", + ""packageA.interop.nuspec"", + ""lib/netstandard2.0/packageA.interop.dll"", + ""embed/netstandard2.0/packageA.interop.dll"", + ""package/services/metadata/core-properties/b7eb2b260f1846d69b1ccf1a4e614180.psmdcp"", + ""[Content_Types].xml"" + ] + }, + ""System.Runtime/4.0.20-beta-22927"": { + ""sha512"": ""sup3rs3cur3"", + ""type"": ""package"", + ""path"": ""C:\\\\a\\test\\path"", + ""msbuildProject"": ""bar"", + ""servicable"": true, + ""hasTools"": true, + ""files"": [ + ""System.Runtime.nuspec"" + ] + } + }, + ""projectFileDependencyGroups"": { + """": [ + ""System.Runtime [4.0.10-beta-*, )"" + ], + "".NETPlatform,Version=v5.0"": [] + }, + ""packageFolders"": { + ""a"": { + ""location"": ""loc"", + ""propbool"": false + }, + ""b"": {} + }, +""project"": { + ""version"": ""1.0.0"", + ""authors"": [ ""person1"", ""person2"" ], + ""copyright"": ""microsoft"", + ""description"": ""test project"", + ""language"": ""en-us"", + ""restore"": { + ""centralPackageVersionsManagementEnabled"": true, + ""centralPackageVersionOverrideDisabled"": false, + ""CentralPackageTransitivePinningEnabled"": true, + ""legacyPackagesDirectory"": true, + ""outputPath"": ""X:\\ProjectPath\\obj\\"", + ""packagesConfigPath"": ""X:\\ProjectPath\\packages.config"", + ""packagesPath"": ""X:\\ProjectPath\\packages"", + ""projectJsonPath"": ""X:\\ProjectPath\\project.json"", + ""projectName"": ""ProjectPath"", + ""projectPath"": ""X:\\ProjectPath\\ProjectPath.csproj"", + ""projectStyle"": ""Standalone"", + ""projectUniqueName"": ""X:\\ProjectPath\\ProjectPath.csproj"", + ""restoreLockProperties"": { + ""nuGetLockFilePath"": ""X:\\ProjectPath\\obj\\project.assets.json"", + ""restoreLockedMode"": false, + ""restorePackagesWithLockFile"": ""true"" + }, + ""restoreAuditProperties"": { + ""enableAudit"": ""true"", + ""auditLevel"": ""four"", + ""auditMode"": ""intrusive"" + }, + ""skipContentFileWrite"": true, + ""sources"": { + ""source1"": {}, + ""source2"": { + ""noOne"": ""cares about me :("" + } + }, + ""validateRuntimeAssets"": false, + ""warningProperties"": { + ""allWarningsAsErrors"": true, + ""noWarn"": [""NU1005"",""NU1011""], + ""warnAsError"": [""NU1005""], + ""warnNotAsError"": [""NU1011""] + }, + ""configFilePaths"": [], + ""crossTargeting"": true, + ""fallbackFolders"": [], + ""originalTargetFrameworks"": [ + ""netcoreapp10"" + ], + ""files"": { + ""file1"": ""X:\\ProjectPath\\ProjectPath.csproj"" + }, + ""frameworks"": { + ""netcoreapp1.0"": { + ""targetAlias"": ""netcoreapp10"", + ""projectReferences"": { + ""project1"": { + ""excludeAssets"": ""build"", + ""includeAssets"": ""runtime"", + ""privateAssets"": ""all"", + ""projectPath"": ""X:\\ProjectPath\\ProjectPath.csproj"" + } + } + } + }, + }, + ""packOptions"": { + ""files"": { + ""excludeFiles"": ""**/*.txt"", + ""exclude"": [""excl1"", ""excl2""], + ""includeFiles"": [], + ""include"": [""incl1"", ""incl2""], + ""mappings"": { + ""a"": ""first"", + ""b"": [""first"", ""second""], + ""c"": { + ""excludeFiles"": ""**/*.txt"", + ""exclude"": [""excl1"", ""excl2""], + ""includeFiles"": [], + ""include"": [""incl1"", ""incl2""], + } + } + }, + ""iconUrl"": ""icon.png"", + ""licenseUrl"": ""license/url"", + ""projectUrl"": ""project/url"", + ""releaseNotes"": ""some note s"", + ""summary"": ""sum mary"", + ""tags"": [], + ""requireLicenseAcceptance"": false, + ""owners"": [""first"", ""person""], + ""packageType"": [""package1"", ""package2""], + }, + ""dependencies"": { + ""library1"": ""1.0.0"", + ""library2"": { + ""autoReferenced"": true, + ""exclude"": ""build compile"", + ""generatePathProperty"": false, + ""include"": ""runtime,native"", + ""noWarn"": [""NU1005"",""NU1011""], + ""suppressParent"": """", + ""target"": ""package"", + ""version"": ""1.2.3"", + ""versionOverride"": ""1.2.6"", + ""versionCentrallyManaged"": true, + ""aliases"": ""alias"", + }, + }, + ""scripts"": { + ""script1"": ""script1.cmd"", + ""script2"": [""script1.ps1"", ""script2.js""] + }, + ""buildOptions"": { + ""outputName"": ""someoutput"", + }, + ""contentFiles"": [ + ""outputName"", + ""someoutput"" + ], + ""frameworks"": { + ""netcoreapp1.0"": { + ""assetTargetFallback"": true, + ""secondaryFramework"": ""dotnet"", + ""centralPackageVersions"": { + ""package1"": ""1.0.0"", + ""package2"": ""2.0.0"" + }, + ""dependencies"": { + ""Microsoft.NET.Sdk"": { + ""suppressParent"": ""All"", + ""target"": ""Package"", + ""version"": ""[1.0.0-alpha-20161104-2, )"" + }, + ""Microsoft.NETCore.App"": { + ""target"": ""Package"", + ""version"": ""[1.0.1, )"" + } + }, + ""downloadDependencies"": [ + { + ""name"": ""dep1"", + ""version"": ""1.0.0"", + }, + { + ""name"": ""dep1"", + ""version"": ""2.0.0"", + }, + { + ""name"": ""dep2"", + ""version"": ""2.0.0;[1.0.1, )"", + } + ], + ""frameworkAssemblies"": { }, + ""frameworkReferences"": { + ""frameworkName1"": { + ""privateAssets"": ""all,none"", + }, + ""frameworkName2"": { + ""privateAssets"": ""all"", + } + }, + ""imports"": [ ""netstandard1.1"", ""netstandard1.2"", ""dnxcore50"" ], + ""runtimeIdentifierGraphPath"": ""path\\to\\sdk\\3.0.100\\runtime.json"", + ""targetAlias"" : ""minNetVersion"", + ""warn"" : false, + }, + ""netstandard1.1"": { + ""assetTargetFallback"": false, + ""imports"": [ ""netstandard1.1"", ""netstandard1.2"", ""dnxcore50"" ] + }, + ""net6.0"": { + ""secondaryFramework"": ""dotnet"", + }, + ""dotnet"": { + } + }, + ""packInclude"": { + ""pack1"": ""val"", + ""pack2"": ""hello"", + }, + }, +""logs"": [ + { + ""code"": ""NU1000"", + ""level"": ""Error"", + ""message"": ""test log message"" + }, + { + ""code"": ""NU1000"", + ""level"": ""Warning"", + ""warningLevel"": 1, + ""message"": ""test log message"" + }, + { + ""code"": ""NU1000"", + ""level"": ""Warning"", + ""warningLevel"": 2, + ""message"": ""test log message"" + }, + { + ""code"": ""NU1000"", + ""level"": ""Error"", + ""message"": ""test log message"" + } + ] +}"; + var lockFileFormat = new LockFileFormat(); + +#pragma warning disable CS0612 // Type or member is obsolete + var lockFile = lockFileFormat.Parse(lockFileContent, "In Memory"); +#pragma warning restore CS0612 // Type or member is obsolete + + Assert.Equal(3, lockFile.Version); + + //Libraries validation + Assert.Equal(2, lockFile.Libraries.Count); + var runtimeLibrary = lockFile.Libraries.FirstOrDefault(x => x.Name == "System.Runtime"); + Assert.NotNull(runtimeLibrary); + Assert.Equal(NuGetVersion.Parse("4.0.20-beta-22927"), runtimeLibrary.Version); + Assert.Equal("package", runtimeLibrary.Type); + Assert.Equal("C:\\\\a\\test\\path", runtimeLibrary.Path); + Assert.Equal("bar", runtimeLibrary.MSBuildProject); + Assert.Equal("sup3rs3cur3", runtimeLibrary.Sha512); + Assert.True(runtimeLibrary.IsServiceable); + Assert.True(runtimeLibrary.HasTools); + Assert.Equal(1, runtimeLibrary.Files.Count); + Assert.Equal("System.Runtime.nuspec", runtimeLibrary.Files[0]); + + //Target validation + Assert.Equal(2, lockFile.Targets.Count); + var net60Framework = NuGetFramework.Parse("net6.0"); + var target = lockFile.Targets.FirstOrDefault(x => x.Name == "net6.0"); + Assert.Equal(net60Framework, target.TargetFramework); + Assert.Equal(4, target.Libraries.Count); + var runtimeTargetLibrary = target.Libraries.FirstOrDefault(x => x.Name == "Microsoft.AspNetCore.JsonPatch"); + Assert.NotNull(runtimeTargetLibrary); + Assert.Equal(NuGetVersion.Parse("6.0.4"), runtimeTargetLibrary.Version); + Assert.Equal("package", runtimeTargetLibrary.Type); + Assert.Equal(".NETCoreApp,Version=v6.0", runtimeTargetLibrary.Framework); + Assert.Equal(".NETCoreApp,Version=v6.0", runtimeTargetLibrary.Framework); + Assert.Equal(2, runtimeTargetLibrary.Dependencies.Count); + Assert.Equal("Microsoft.CSharp", runtimeTargetLibrary.Dependencies[0].Id); + Assert.Equal(VersionRange.Parse("4.7.0"), runtimeTargetLibrary.Dependencies[0].VersionRange); + Assert.Equal(1, runtimeTargetLibrary.FrameworkAssemblies.Count); + Assert.Equal("System.Configuration", runtimeTargetLibrary.FrameworkAssemblies[0]); + Assert.Equal(1, runtimeTargetLibrary.RuntimeAssemblies.Count); + Assert.Equal("lib/net6.0/Microsoft.AspNetCore.JsonPatch.dll", runtimeTargetLibrary.RuntimeAssemblies[0].Path); + Assert.Equal(4, runtimeTargetLibrary.RuntimeAssemblies[0].Properties.Count); + Assert.Equal("val1", runtimeTargetLibrary.RuntimeAssemblies[0].Properties["property1"]); + Assert.Equal("12", runtimeTargetLibrary.RuntimeAssemblies[0].Properties["property2"]); + Assert.Equal("True", runtimeTargetLibrary.RuntimeAssemblies[0].Properties["property3"]); + Assert.Equal("False", runtimeTargetLibrary.RuntimeAssemblies[0].Properties["property4"]); + Assert.Equal(1, runtimeTargetLibrary.CompileTimeAssemblies.Count); + Assert.Equal("lib/net6.0/Microsoft.AspNetCore.JsonPatch.dll", runtimeTargetLibrary.CompileTimeAssemblies[0].Path); + Assert.Equal(0, runtimeTargetLibrary.CompileTimeAssemblies[0].Properties.Count); + Assert.Equal(0, runtimeTargetLibrary.ResourceAssemblies.Count); + Assert.Equal(0, runtimeTargetLibrary.NativeLibraries.Count); + Assert.Equal(0, runtimeTargetLibrary.Build.Count); + Assert.Equal(0, runtimeTargetLibrary.BuildMultiTargeting.Count); + Assert.Equal(2, runtimeTargetLibrary.ContentFiles.Count); + Assert.Equal("baz", runtimeTargetLibrary.ContentFiles[0].Path); + Assert.Equal("False", runtimeTargetLibrary.ContentFiles[0].Properties["copyToOutput"]); + Assert.Equal(2, runtimeTargetLibrary.RuntimeTargets.Count); + Assert.Equal("foo", runtimeTargetLibrary.RuntimeTargets[0].Path); + Assert.Equal("33", runtimeTargetLibrary.RuntimeTargets[0].Properties["copyToOutput"]); + Assert.Equal(0, runtimeTargetLibrary.ToolsAssemblies.Count); + Assert.Equal(0, runtimeTargetLibrary.EmbedAssemblies.Count); + Assert.Equal(2, runtimeTargetLibrary.FrameworkReferences.Count); + Assert.Equal("Microsoft.Windows.Desktop|WPF", runtimeTargetLibrary.FrameworkReferences[0]); + + //Project file dependency tests + Assert.Equal(2, lockFile.ProjectFileDependencyGroups.Count); + var emptyDepGroup = lockFile.ProjectFileDependencyGroups.First(); + Assert.True(string.IsNullOrEmpty(emptyDepGroup.FrameworkName)); + Assert.Equal("System.Runtime [4.0.10-beta-*, )", emptyDepGroup.Dependencies.Single()); + var netPlatDepGroup = lockFile.ProjectFileDependencyGroups.Last(); + Assert.Equal(NuGetFramework.Parse("dotnet").DotNetFrameworkName, netPlatDepGroup.FrameworkName); + Assert.Empty(netPlatDepGroup.Dependencies); + + //Package folders + Assert.Equal(2, lockFile.PackageFolders.Count); + var aFolder = lockFile.PackageFolders.First(); + Assert.Equal("loc", aFolder.Properties["location"]); + + //Package spec tests + var packageSpec = lockFile.PackageSpec; +#pragma warning disable CS0612 // Type or member is obsolete + Assert.Equal(2, packageSpec.Authors.Count()); + Assert.Equal("someoutput", packageSpec.BuildOptions.OutputName); + Assert.Equal(2, packageSpec.ContentFiles.Count()); + Assert.Equal("outputName", packageSpec.ContentFiles[0]); + Assert.Equal("microsoft", packageSpec.Copyright); + Assert.Equal("test project", packageSpec.Description); + Assert.Equal("en-us", packageSpec.Language); + Assert.Equal(2, packageSpec.PackInclude.Count()); + Assert.Equal("val", packageSpec.PackInclude["pack1"]); + Assert.Equal("hello", packageSpec.PackInclude["pack2"]); + Assert.Equal(1, packageSpec.PackOptions.IncludeExcludeFiles.ExcludeFiles.Count()); + Assert.Equal("**/*.txt", packageSpec.PackOptions.IncludeExcludeFiles.ExcludeFiles[0]); + Assert.Equal(2, packageSpec.PackOptions.IncludeExcludeFiles.Exclude.Count()); + Assert.Equal("excl1", packageSpec.PackOptions.IncludeExcludeFiles.Exclude[0]); + Assert.Equal("excl2", packageSpec.PackOptions.IncludeExcludeFiles.Exclude[1]); + Assert.Equal(0, packageSpec.PackOptions.IncludeExcludeFiles.IncludeFiles.Count()); + Assert.Equal(2, packageSpec.PackOptions.IncludeExcludeFiles.Include.Count()); + Assert.Equal(3, packageSpec.PackOptions.Mappings.Count()); + Assert.Equal(1, packageSpec.PackOptions.Mappings["a"].Include.Count()); + Assert.Equal("first", packageSpec.PackOptions.Mappings["a"].Include[0]); + Assert.Equal(2, packageSpec.PackOptions.Mappings["b"].Include.Count()); + Assert.Equal("first", packageSpec.PackOptions.Mappings["b"].Include[0]); + Assert.Equal("second", packageSpec.PackOptions.Mappings["b"].Include[1]); + Assert.Equal(1, packageSpec.PackOptions.Mappings["c"].ExcludeFiles.Count()); + Assert.Equal(2, packageSpec.PackOptions.Mappings["c"].Exclude.Count()); + Assert.Equal(0, packageSpec.PackOptions.Mappings["c"].IncludeFiles.Count()); + Assert.Equal(2, packageSpec.PackOptions.Mappings["c"].Include.Count()); + Assert.Equal("icon.png", packageSpec.IconUrl); + Assert.Equal("license/url", packageSpec.LicenseUrl); + Assert.Equal("project/url", packageSpec.ProjectUrl); + Assert.Equal("some note s", packageSpec.ReleaseNotes); + Assert.Equal("sum mary", packageSpec.Summary); + Assert.Equal(0, packageSpec.Tags.Length); + Assert.Equal(2, packageSpec.Scripts.Count); + Assert.Equal(1, packageSpec.Scripts["script1"].Count()); + Assert.Equal("script1.cmd", packageSpec.Scripts["script1"].First()); + Assert.Equal(2, packageSpec.Scripts["script2"].Count()); +#pragma warning restore CS0612 // Type or member is obsolete + Assert.Equal(2, packageSpec.Dependencies.Count()); + var library1 = packageSpec.Dependencies[0]; + Assert.Equal("library1", library1.Name); + Assert.Equal(VersionRange.Parse("1.0.0"), library1.LibraryRange.VersionRange); + var library2 = packageSpec.Dependencies[1]; + Assert.Equal("library2", library2.Name); + Assert.True(library2.AutoReferenced); + Assert.Equal((LibraryIncludeFlags.None | LibraryIncludeFlags.Runtime | LibraryIncludeFlags.Native) & ~(LibraryIncludeFlags.None | LibraryIncludeFlags.Build | LibraryIncludeFlags.Compile), library2.IncludeType); + Assert.Equal(2, library2.NoWarn.Count); + Assert.Equal(NuGetLogCode.NU1005, library2.NoWarn[0]); + Assert.Equal(LibraryIncludeFlags.None, library2.SuppressParent); + Assert.Equal(LibraryDependencyTarget.Package, library2.LibraryRange.TypeConstraint); + Assert.Equal(VersionRange.Parse("1.2.3"), library2.LibraryRange.VersionRange); + Assert.Equal(VersionRange.Parse("1.2.6"), library2.VersionOverride); + Assert.True(library2.VersionCentrallyManaged); + Assert.False(library2.GeneratePathProperty); + Assert.Equal("alias", library2.Aliases); + + Assert.Equal(4, packageSpec.TargetFrameworks.Count()); + var framework1 = packageSpec.TargetFrameworks[0]; + Assert.True(framework1.AssetTargetFallback); + Assert.False(framework1.Warn); + Assert.Equal("minNetVersion", framework1.TargetAlias); + Assert.Equal("path\\to\\sdk\\3.0.100\\runtime.json", framework1.RuntimeIdentifierGraphPath); + Assert.Equal(2, framework1.CentralPackageVersions.Count); + Assert.Equal("package1", framework1.CentralPackageVersions["package1"].Name); + Assert.Equal(VersionRange.Parse("1.0.0"), framework1.CentralPackageVersions["package1"].VersionRange); + Assert.Equal(2, framework1.Dependencies.Count); + Assert.Equal(3, framework1.DownloadDependencies.Count); + Assert.Equal("dep1", framework1.DownloadDependencies[0].Name); + Assert.Equal(VersionRange.Parse("1.0.0"), framework1.DownloadDependencies[0].VersionRange); + Assert.Equal("dep2", framework1.DownloadDependencies[1].Name); + Assert.Equal(VersionRange.Parse("2.0.0"), framework1.DownloadDependencies[1].VersionRange); + Assert.Equal("dep2", framework1.DownloadDependencies[2].Name); + Assert.Equal(VersionRange.Parse("[1.0.1, )"), framework1.DownloadDependencies[2].VersionRange); + Assert.Equal(2, framework1.FrameworkReferences.Count); + Assert.Equal("frameworkName1", framework1.FrameworkReferences.First().Name); + Assert.Equal(FrameworkDependencyFlags.All | FrameworkDependencyFlags.None, framework1.FrameworkReferences.First().PrivateAssets); + Assert.Equal("frameworkName2", framework1.FrameworkReferences.Last().Name); + Assert.Equal(FrameworkDependencyFlags.All, framework1.FrameworkReferences.Last().PrivateAssets); + Assert.Equal(3, framework1.Imports.Count); + Assert.Equal(NuGetFramework.Parse("netstandard1.1"), framework1.Imports[0]); + Assert.Equal(typeof(AssetTargetFallbackFramework), framework1.FrameworkName.GetType()); + Assert.Equal(NuGetFramework.Parse("netcoreapp1.0"), framework1.FrameworkName); + + var framework2 = packageSpec.TargetFrameworks[1]; + Assert.Equal(typeof(FallbackFramework), framework2.FrameworkName.GetType()); + Assert.Equal(NuGetFramework.Parse("netstandard1.1"), framework2.FrameworkName); + + var framework3 = packageSpec.TargetFrameworks[2]; + Assert.Equal(typeof(DualCompatibilityFramework), framework3.FrameworkName.GetType()); + Assert.Equal(NuGetFramework.Parse("net6.0"), framework3.FrameworkName); + + var framework4 = packageSpec.TargetFrameworks[3]; + Assert.Equal(typeof(NuGetFramework), framework4.FrameworkName.GetType()); + Assert.Equal(NuGetFramework.Parse("dotnet"), framework4.FrameworkName); + + Assert.Equal(typeof(ProjectRestoreMetadata), packageSpec.RestoreMetadata.GetType()); + Assert.True(packageSpec.RestoreMetadata.CentralPackageTransitivePinningEnabled); + Assert.False(packageSpec.RestoreMetadata.CentralPackageVersionOverrideDisabled); + Assert.True(packageSpec.RestoreMetadata.CentralPackageVersionsEnabled); + Assert.Equal(1, packageSpec.RestoreMetadata.OriginalTargetFrameworks.Count); + Assert.Equal("X:\\ProjectPath\\obj\\", packageSpec.RestoreMetadata.OutputPath); + Assert.Equal("X:\\ProjectPath\\packages", packageSpec.RestoreMetadata.PackagesPath); + Assert.Equal("X:\\ProjectPath\\project.json", packageSpec.RestoreMetadata.ProjectJsonPath); + Assert.Equal("ProjectPath", packageSpec.RestoreMetadata.ProjectName); + Assert.Equal(ProjectStyle.Standalone, packageSpec.RestoreMetadata.ProjectStyle); + Assert.Equal("X:\\ProjectPath\\ProjectPath.csproj", packageSpec.RestoreMetadata.ProjectUniqueName); + Assert.Equal("X:\\ProjectPath\\obj\\project.assets.json", packageSpec.RestoreMetadata.RestoreLockProperties.NuGetLockFilePath); + Assert.Equal("true", packageSpec.RestoreMetadata.RestoreLockProperties.RestorePackagesWithLockFile); + Assert.False(packageSpec.RestoreMetadata.RestoreLockProperties.RestoreLockedMode); + Assert.Equal("true", packageSpec.RestoreMetadata.RestoreAuditProperties.EnableAudit); + Assert.Equal("four", packageSpec.RestoreMetadata.RestoreAuditProperties.AuditLevel); + Assert.Equal("intrusive", packageSpec.RestoreMetadata.RestoreAuditProperties.AuditMode); + Assert.True(packageSpec.RestoreMetadata.SkipContentFileWrite); + Assert.Equal(2, packageSpec.RestoreMetadata.Sources.Count); + Assert.Equal(new PackageSource("source1"), packageSpec.RestoreMetadata.Sources[0]); + Assert.Equal(new PackageSource("source2"), packageSpec.RestoreMetadata.Sources[1]); + Assert.False(packageSpec.RestoreMetadata.ValidateRuntimeAssets); + Assert.Equal(0, packageSpec.RestoreMetadata.ConfigFilePaths.Count); + Assert.True(packageSpec.RestoreMetadata.CrossTargeting); + Assert.Equal(0, packageSpec.RestoreMetadata.FallbackFolders.Count); + Assert.Equal(1, packageSpec.RestoreMetadata.OriginalTargetFrameworks.Count); + Assert.Equal(1, packageSpec.RestoreMetadata.Files.Count); + Assert.Equal(new ProjectRestoreMetadataFile("file1", "X:\\ProjectPath\\ProjectPath.csproj"), packageSpec.RestoreMetadata.Files[0]); + Assert.Equal(1, packageSpec.RestoreMetadata.TargetFrameworks.Count); + Assert.Equal(2, packageSpec.RestoreMetadata.ProjectWideWarningProperties.NoWarn.Count); + + } + [Fact] public void LockFileFormat_ReadsLockFileWithEmbedAssemblies() { @@ -2095,7 +2648,9 @@ public void LockFileFormat_ReadsLockFileWithEmbedAssemblies() }"; var lockFileFormat = new LockFileFormat(); +#pragma warning disable CS0612 // Type or member is obsolete var lockFile = lockFileFormat.Parse(lockFileContent, "In Memory"); +#pragma warning restore CS0612 // Type or member is obsolete Assert.Equal(1, lockFile.Version); diff --git a/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/LockFileTests.cs b/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/LockFileTests.cs index 8131591068f..5349c0ab817 100644 --- a/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/LockFileTests.cs +++ b/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/LockFileTests.cs @@ -859,7 +859,9 @@ public void LockFile_GetTarget_WithNuGetFramework_ReturnsCorrectLockFileTarget() { // Arrange var expectedJson = ResourceTestUtility.GetResource("NuGet.ProjectModel.Test.compiler.resources.sample.assets.json", typeof(LockFileTests)); +#pragma warning disable CS0612 // Type or member is obsolete var lockFile = new LockFileFormat().Parse(expectedJson, Path.GetTempPath()); +#pragma warning restore CS0612 // Type or member is obsolete NuGetFramework nuGetFramework = NuGetFramework.ParseComponents(".NETCoreApp,Version=v5.0", "Windows,Version=7.0"); // Act @@ -874,7 +876,9 @@ public void LockFile_GetTarget_WithAlias_ReturnsCorrectLockFileTarget() { // Arrange var expectedJson = ResourceTestUtility.GetResource("NuGet.ProjectModel.Test.compiler.resources.sample.assets.json", typeof(LockFileTests)); +#pragma warning disable CS0612 // Type or member is obsolete var lockFile = new LockFileFormat().Parse(expectedJson, Path.GetTempPath()); +#pragma warning restore CS0612 // Type or member is obsolete NuGetFramework nuGetFramework = NuGetFramework.ParseComponents(".NETCoreApp,Version=v5.0", "Windows,Version=7.0"); // Act diff --git a/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/JsonPackageSpecReaderTests.cs b/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/NjJsonPackageSpecReaderTests.cs similarity index 96% rename from test/NuGet.Core.Tests/NuGet.ProjectModel.Test/JsonPackageSpecReaderTests.cs rename to test/NuGet.Core.Tests/NuGet.ProjectModel.Test/NjJsonPackageSpecReaderTests.cs index 14e73f00898..1562442f107 100644 --- a/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/JsonPackageSpecReaderTests.cs +++ b/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/NjJsonPackageSpecReaderTests.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using System.Text; +using System.Text.Json; using FluentAssertions; using Newtonsoft.Json; using NuGet.Common; @@ -20,7 +21,8 @@ namespace NuGet.ProjectModel.Test { [UseCulture("")] // Fix tests failing on systems with non-English locales - public class JsonPackageSpecReaderTests + [Obsolete] + public class NjJsonPackageSpecReaderTests { [Fact] public void PackageSpecReader_PackageMissingVersion() @@ -42,7 +44,7 @@ public void PackageSpecReader_PackageMissingVersion() try { - var spec = JsonPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + var spec = NjPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); } catch (Exception ex) { @@ -69,7 +71,7 @@ public void PackageSpecReader_ProjectMissingVersion() }"; // Act - var spec = JsonPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + var spec = NjPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); var range = spec.Dependencies.Single().LibraryRange.VersionRange; // Assert @@ -96,7 +98,7 @@ public void PackageSpecReader_PackageEmptyVersion() try { - var spec = JsonPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + var spec = NjPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); } catch (Exception ex) { @@ -127,7 +129,7 @@ public void PackageSpecReader_PackageWhitespaceVersion() try { - var spec = JsonPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + var spec = NjPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); } catch (Exception ex) { @@ -153,7 +155,7 @@ public void PackageSpecReader_FrameworkAssemblyEmptyVersion() }"; // Act - var spec = JsonPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + var spec = NjPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); var range = spec.TargetFrameworks.Single().Dependencies.Single().LibraryRange.VersionRange; // Assert @@ -175,7 +177,7 @@ public void PackageSpecReader_ExplicitIncludesOverrideTypePlatform() }"; // Act - var actual = JsonPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + var actual = NjPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); // Assert var dep = actual.Dependencies.FirstOrDefault(d => d.Name.Equals("redist")); @@ -209,7 +211,7 @@ public void PackageSpecReader_ExplicitIncludesOverrideTypePlatform() public void PackageSpecReader_PackOptions_Default(string json) { // Arrange & Act - var actual = JsonPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + var actual = NjPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); // Assert Assert.NotNull(actual.PackOptions); @@ -251,7 +253,7 @@ public void PackageSpecReader_PackOptions_ValidPackageType(string json, string[] .ToArray(); // Act - var actual = JsonPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + var actual = NjPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); // Assert Assert.NotNull(actual.PackOptions); @@ -315,7 +317,7 @@ public void PackageSpecReader_PackOptions_InvalidPackageType(string json) { // Arrange & Act & Assert var actual = Assert.Throws( - () => JsonPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json")); + () => NjPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json")); Assert.Contains("The pack options package type must be a string or array of strings in 'project.json'.", actual.Message); } @@ -337,7 +339,7 @@ public void PackageSpecReader_PackOptions_Files1() } } }"; - var actual = JsonPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + var actual = NjPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); // Assert Assert.NotNull(actual.PackOptions); @@ -379,7 +381,7 @@ public void PackageSpecReader_PackOptions_Files2() } } }"; - var actual = JsonPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + var actual = NjPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); // Assert Assert.NotNull(actual.PackOptions); @@ -436,7 +438,7 @@ public void PackageSpecReader_PackOptions_Files2() public void PackageSpecReader_BuildOptions(string json, string expectedValue, bool nullBuildOptions) { // Arrange & Act - var actual = JsonPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + var actual = NjPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); // Assert if (nullBuildOptions) @@ -467,7 +469,7 @@ public void PackageSpecReader_ReadsWithoutRestoreSettings() }, }"; - var actual = JsonPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + var actual = NjPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); // Assert Assert.NotNull(actual); @@ -495,7 +497,7 @@ public void PackageSpecReader_ReadsDependencyWithMultipleNoWarn() }, }"; - var actual = JsonPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + var actual = NjPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); // Assert var dep = actual.Dependencies.FirstOrDefault(d => d.Name.Equals("packageA")); @@ -525,7 +527,7 @@ public void PackageSpecReader_ReadsDependencyWithSingleNoWarn() }, }"; - var actual = JsonPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + var actual = NjPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); // Assert var dep = actual.Dependencies.FirstOrDefault(d => d.Name.Equals("packageA")); @@ -553,7 +555,7 @@ public void PackageSpecReader_ReadsDependencyWithSingleEmptyNoWarn() }, }"; - var actual = JsonPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + var actual = NjPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); // Assert var dep = actual.Dependencies.FirstOrDefault(d => d.Name.Equals("packageA")); @@ -616,7 +618,7 @@ public void PackageSpecReader_ReadsRestoreMetadataWithWarningProperties() } }"; - var actual = JsonPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + var actual = NjPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); // Assert var metadata = actual.RestoreMetadata; @@ -682,7 +684,7 @@ public void PackageSpecReader_ReadsRestoreMetadataWithWarningPropertiesAndNo_NoW } }"; - var actual = JsonPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + var actual = NjPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); // Assert var metadata = actual.RestoreMetadata; @@ -743,7 +745,7 @@ public void PackageSpecReader_ReadsRestoreMetadataWithWarningPropertiesAndNo_War } }"; - var actual = JsonPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + var actual = NjPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); // Assert var metadata = actual.RestoreMetadata; @@ -806,7 +808,7 @@ public void PackageSpecReader_ReadsRestoreMetadataWithWarningPropertiesAndNo_All } }"; - var actual = JsonPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + var actual = NjPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); // Assert var metadata = actual.RestoreMetadata; @@ -864,7 +866,7 @@ public void PackageSpecReader_ReadsRestoreMetadataWithEmptyWarningPropertiesAnd( } }"; - var actual = JsonPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + var actual = NjPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); // Assert var metadata = actual.RestoreMetadata; @@ -917,7 +919,7 @@ public void PackageSpecReader_ReadsRestoreMetadataWithNoWarningProperties() } }"; - var actual = JsonPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + var actual = NjPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); // Assert var metadata = actual.RestoreMetadata; @@ -948,7 +950,7 @@ public void PackageSpecReader_RuntimeIdentifierPathNullIfEmpty() }"; // Act - var spec = JsonPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + var spec = NjPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); // Assert Assert.Null(spec.TargetFrameworks.First().RuntimeIdentifierGraphPath); @@ -3370,7 +3372,7 @@ public void GetPackageSpec_WhenScriptsValueIsInvalid_Throws() FileFormatException exception = Assert.Throws(() => GetPackageSpec(json)); Assert.Equal("The value of a script in 'project.json' can only be a string or an array of strings", exception.Message); - Assert.Equal(1, exception.Line); + Assert.Equal(0, exception.Line); Assert.Equal(17, exception.Column); Assert.Null(exception.InnerException); } @@ -3477,7 +3479,7 @@ public void PackageSpecReader_ReadsRestoreMetadataWithAliases() } }"; - var actual = JsonPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + var actual = NjPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); // Assert var metadata = actual.RestoreMetadata; @@ -3524,7 +3526,70 @@ public void PackageSpecReader_Read() { var dependencies = new List(); NuGetFramework framework = NuGetFramework.Parse(frameworkPropertyName); - JsonPackageSpecReader.ReadCentralTransitiveDependencyGroup( + NjPackageSpecReader.ReadCentralTransitiveDependencyGroup( + jsonReader: jsonReader, + results: dependencies, + packageSpecPath: "SomePath"); + results.Add(new CentralTransitiveDependencyGroup(framework, dependencies)); + }); + }); + } + + // Assert + Assert.Equal(2, results.Count); + Assert.Equal(".NETCoreApp,Version=v3.1", results.ElementAt(0).FrameworkName); + var firstGroup = results.ElementAt(0); + Assert.Equal(1, firstGroup.TransitiveDependencies.Count()); + Assert.Equal("Build", firstGroup.TransitiveDependencies.First().IncludeType.ToString()); + Assert.Equal("All", firstGroup.TransitiveDependencies.First().SuppressParent.ToString()); + Assert.Equal("[1.0.0, )", firstGroup.TransitiveDependencies.First().LibraryRange.VersionRange.ToNormalizedString()); + Assert.True(firstGroup.TransitiveDependencies.First().VersionCentrallyManaged); + + var secondGroup = results.ElementAt(1); + Assert.Equal(1, secondGroup.TransitiveDependencies.Count()); + Assert.Equal("Build", secondGroup.TransitiveDependencies.First().IncludeType.ToString()); + Assert.Equal("All", secondGroup.TransitiveDependencies.First().SuppressParent.ToString()); + Assert.Equal("[2.0.0, )", secondGroup.TransitiveDependencies.First().LibraryRange.VersionRange.ToNormalizedString()); + Assert.True(secondGroup.TransitiveDependencies.First().VersionCentrallyManaged); + } + + [Fact] + public void PackageSpecReader_NjRead() + { + // Arrange + var json = @"{ + ""centralTransitiveDependencyGroups"": { + "".NETCoreApp,Version=v3.1"": { + ""Foo"": { + ""exclude"": ""Native"", + ""include"": ""Build"", + ""suppressParent"": ""All"", + ""version"": ""1.0.0"" + } + }, + "".NETCoreApp,Version=v3.0"": { + ""Bar"": { + ""exclude"": ""Native"", + ""include"": ""Build"", + ""suppressParent"": ""All"", + ""version"": ""2.0.0"" + } + } + } + }"; + + // Act + var results = new List(); + using (var stringReader = new StringReader(json.ToString())) + using (var jsonReader = new JsonTextReader(stringReader)) + { + jsonReader.ReadObject(ctdPropertyName => + { + jsonReader.ReadObject(frameworkPropertyName => + { + var dependencies = new List(); + NuGetFramework framework = NuGetFramework.Parse(frameworkPropertyName); + NjPackageSpecReader.ReadCentralTransitiveDependencyGroup( jsonReader: jsonReader, results: dependencies, packageSpecPath: "SomePath"); @@ -3598,9 +3663,10 @@ public void GetPackageSpec_WithRestoreAuditProperties_ReturnsRestoreAuditPropert private static PackageSpec GetPackageSpec(string json) { - using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(json))) + using (var stringReader = new StringReader(json)) + using (var stream = new JsonTextReader(stringReader)) { - return JsonPackageSpecReader.GetPackageSpec(stream, name: null, packageSpecPath: null, snapshotValue: null); + return NjPackageSpecReader.GetPackageSpec(stream, name: null, packageSpecPath: null, snapshotValue: null); } } diff --git a/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/Utf8JsonReaderExtensionsTests.cs b/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/Utf8JsonReaderExtensionsTests.cs new file mode 100644 index 00000000000..c80c2299302 --- /dev/null +++ b/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/Utf8JsonReaderExtensionsTests.cs @@ -0,0 +1,557 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json; +using Xunit; + +namespace NuGet.ProjectModel.Test +{ + [UseCulture("")] // Fix tests failing on systems with non-English locales + public class Utf8JsonReaderExtensionsTests + { + [Fact] + public void ReadDelimitedString_WhenValueIsNull_Throws() + { + var json = Encoding.UTF8.GetBytes("{\"a\":null}"); + + var reader = new Utf8JsonReader(json); + reader.Read(); + reader.Read(); + Exception exceptionThrown = null; + try + { + reader.ReadDelimitedString(); + } + catch (Exception ex) + { + exceptionThrown = ex; + } + Assert.NotNull(exceptionThrown); + Assert.IsType(typeof(JsonException), exceptionThrown); + Assert.NotNull(exceptionThrown.InnerException); + Assert.IsType(typeof(InvalidCastException), exceptionThrown.InnerException); + Assert.Equal(JsonTokenType.Null, reader.TokenType); + } + + [Theory] + [InlineData("true", JsonTokenType.True)] + [InlineData("false", JsonTokenType.False)] + [InlineData("-2", JsonTokenType.Number)] + [InlineData("3.14", JsonTokenType.Number)] + [InlineData("{}", JsonTokenType.StartObject)] + [InlineData("[]", JsonTokenType.StartArray)] + [InlineData("[true]", JsonTokenType.StartArray)] + [InlineData("[-2]", JsonTokenType.StartArray)] + [InlineData("[3.14]", JsonTokenType.StartArray)] + [InlineData("[\"a\", \"b\"]", JsonTokenType.StartArray)] + + public void ReadDelimitedString_WhenValueIsNotString_Throws(string value, JsonTokenType expectedTokenType) + { + var json = $"{{\"a\":{value}}}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + var reader = new Utf8JsonReader(encodedBytes); + reader.Read(); + reader.Read(); + + Exception exceptionThrown = null; + try + { + reader.ReadDelimitedString(); + } + catch (Exception ex) + { + exceptionThrown = ex; + } + + Assert.NotNull(exceptionThrown); + Assert.IsType(typeof(JsonException), exceptionThrown); + Assert.NotNull(exceptionThrown.InnerException); + Assert.IsType(typeof(InvalidCastException), exceptionThrown.InnerException); + Assert.Equal(expectedTokenType, reader.TokenType); + } + + [Fact] + public void ReadDelimitedString_WhenValueIsString_ReturnsValue() + { + const string expectedResult = "b"; + var json = $"{{\"a\":\"{expectedResult}\"}}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + var reader = new Utf8JsonReader(encodedBytes); + reader.Read(); + reader.Read(); + IEnumerable actualResults = reader.ReadDelimitedString(); + Assert.Collection(actualResults, actualResult => Assert.Equal(expectedResult, actualResult)); + Assert.Equal(JsonTokenType.String, reader.TokenType); + } + + [Theory] + [InlineData("b,c,d")] + [InlineData("b c d")] + public void ReadDelimitedString_WhenValueIsDelimitedString_ReturnsValues(string value) + { + string[] expectedResults = value.Split(new[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries); + var json = $"{{\"a\":\"{value}\"}}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + var reader = new Utf8JsonReader(encodedBytes); + reader.Read(); + reader.Read(); + IEnumerable actualResults = reader.ReadDelimitedString(); + Assert.Equal(expectedResults, actualResults); + Assert.Equal(JsonTokenType.String, reader.TokenType); + } + + [Fact] + public void ReadNextToken_WhenAtEndOfStream_ReturnsFalse() + { + var encodedBytes = Encoding.UTF8.GetBytes("{}"); + var reader = new Utf8JsonReader(encodedBytes); + Assert.True(reader.ReadNextToken()); + Assert.True(reader.ReadNextToken()); + Assert.False(reader.ReadNextToken()); + Assert.Equal(JsonTokenType.EndObject, reader.TokenType); + } + + [Theory] + [InlineData("null", null)] + [InlineData("true", "True")] + [InlineData("false", "False")] + [InlineData("-2", "-2")] + [InlineData("3.14", "3.14")] + [InlineData("\"b\"", "b")] + public void ReadTokenAsString_WhenValueIsConvertibleToString_ReturnsValueAsString( + string value, + string expectedResult) + { + var json = $"{{\"a\":{value}}}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + var reader = new Utf8JsonReader(encodedBytes); + reader.Read(); + reader.Read(); + reader.Read(); + string actualResult = reader.ReadTokenAsString(); + Assert.Equal(expectedResult, actualResult); + } + + [Theory] + [InlineData("[]")] + [InlineData("{}")] + public void ReadTokenAsString_WhenValueIsNotConvertibleToString_Throws(string value) + { + var json = $"{{\"a\":{value}}}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + var reader = new Utf8JsonReader(encodedBytes); + reader.Read(); + reader.Read(); + reader.Read(); + Exception exceptionThrown = null; + try + { + reader.ReadNextTokenAsString(); + } + catch (Exception ex) + { + exceptionThrown = ex; + } + Assert.IsType(typeof(InvalidCastException), exceptionThrown); + } + + [Theory] + [InlineData("true", true)] + [InlineData("false", false)] + public void ReadNextTokenAsBoolOrFalse_WithValidValues_ReturnsBoolean(string value, bool expectedResult) + { + var json = $"{{\"a\":{value}}}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + var reader = new Utf8JsonReader(encodedBytes); + reader.Read(); + reader.Read(); + bool actualResult = reader.ReadNextTokenAsBoolOrFalse(); + Assert.Equal(expectedResult, actualResult); + } + + [Theory] + [InlineData("\"words\"")] + [InlineData("-3")] + [InlineData("3.3")] + [InlineData("[]")] + [InlineData("{}")] + public void ReadNextTokenAsBoolOrFalse_WithInvalidValues_ReturnsFalse(string value) + { + var json = $"{{\"a\":{value}}}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + var reader = new Utf8JsonReader(encodedBytes); + reader.Read(); + reader.Read(); + bool actualResult = reader.ReadNextTokenAsBoolOrFalse(); + Assert.False(actualResult); + } + + [Fact] + public void ReadObjectAsList_WithoutStartObject_ReturnEmptyList() + { + var json = "[]"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + var reader = new Utf8JsonReader(encodedBytes); + reader.Read(); + IList actualResults = reader.ReadObjectAsList(new JsonSerializerOptions()); + Assert.Empty(actualResults); + } + + [Fact] + public void ReadObjectAsList_WithEmptyObject_ReturnEmptyList() + { + var json = "{}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + var reader = new Utf8JsonReader(encodedBytes); + reader.Read(); + IList actualResults = reader.ReadObjectAsList(new JsonSerializerOptions()); + Assert.Empty(actualResults); + } + + [Theory] + [InlineData("null")] + [InlineData("\"b\"")] + [InlineData("{}")] + public void ReadStringArrayAsList_WhenValueIsNotArray_ReturnsNull(string value) + { + var json = $"{{\"a\":{value}}}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + var reader = new Utf8JsonReader(encodedBytes); + reader.Read(); + reader.Read(); + reader.Read(); + Assert.NotEqual(JsonTokenType.PropertyName, reader.TokenType); + List actualValues = reader.ReadStringArrayAsList(); + Assert.Null(actualValues); + } + + [Fact] + public void ReadStringArrayAsList_WhenValueIsEmptyArray_ReturnsNull() + { + var encodedBytes = Encoding.UTF8.GetBytes("{\"a\":[]}"); + var reader = new Utf8JsonReader(encodedBytes); + reader.Read(); + reader.Read(); + reader.Read(); + Assert.NotEqual(JsonTokenType.PropertyName, reader.TokenType); + List actualValues = reader.ReadStringArrayAsList(); + Assert.Null(actualValues); + } + + [Fact] + public void ReadStringArrayAsList_WithSupportedTypes_ReturnsStringArray() + { + var encodedBytes = Encoding.UTF8.GetBytes("[\"a\",-2,3.14,true,null]"); + var reader = new Utf8JsonReader(encodedBytes); + reader.Read(); + + List actualValues = reader.ReadStringArrayAsList(); + + Assert.Collection( + actualValues, + actualValue => Assert.Equal("a", actualValue), + actualValue => Assert.Equal("-2", actualValue), + actualValue => Assert.Equal("3.14", actualValue), + actualValue => Assert.Equal("True", actualValue), + actualValue => Assert.Equal(null, actualValue)); + Assert.Equal(JsonTokenType.EndArray, reader.TokenType); + } + + [Theory] + [InlineData("[]")] + [InlineData("{}")] + public void ReadStringArrayAsList_WithUnsupportedTypes_Throws(string element) + { + var encodedBytes = Encoding.UTF8.GetBytes($"[{element}]"); + var reader = new Utf8JsonReader(encodedBytes); + reader.Read(); + Exception exceptionThrown = null; + try + { + reader.ReadStringArrayAsList(); + } + catch (Exception ex) + { + exceptionThrown = ex; + } + Assert.IsType(typeof(InvalidCastException), exceptionThrown); + } + + [Theory] + [InlineData("null")] + [InlineData("\"b\"")] + [InlineData("{}")] + public void ReadStringArrayAsIList_WhenValueIsNotArray_ReturnsNull(string value) + { + var json = $"{{\"a\":{value}}}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + var reader = new Utf8JsonReader(encodedBytes); + reader.Read(); + reader.Read(); + reader.Read(); + Assert.NotEqual(JsonTokenType.PropertyName, reader.TokenType); + IList actualValues = reader.ReadStringArrayAsIList(); + Assert.Null(actualValues); + } + + [Fact] + public void ReadStringArrayAsIList_WhenValueIsEmptyArray_ReturnsNull() + { + var encodedBytes = Encoding.UTF8.GetBytes("{\"a\":[]}"); + var reader = new Utf8JsonReader(encodedBytes); + reader.Read(); + reader.Read(); + reader.Read(); + Assert.NotEqual(JsonTokenType.PropertyName, reader.TokenType); + IList actualValues = reader.ReadStringArrayAsIList(); + Assert.Null(actualValues); + } + + [Fact] + public void ReadStringArrayAsIList_WithSupportedTypes_ReturnsStringArray() + { + var encodedBytes = Encoding.UTF8.GetBytes("[\"a\",-2,3.14,true,null]"); + var reader = new Utf8JsonReader(encodedBytes); + reader.Read(); + + IList actualValues = reader.ReadStringArrayAsIList(); + + Assert.Collection( + actualValues, + actualValue => Assert.Equal("a", actualValue), + actualValue => Assert.Equal("-2", actualValue), + actualValue => Assert.Equal("3.14", actualValue), + actualValue => Assert.Equal("True", actualValue), + actualValue => Assert.Equal(null, actualValue)); + Assert.Equal(JsonTokenType.EndArray, reader.TokenType); + } + + [Theory] + [InlineData("[]")] + [InlineData("{}")] + public void ReadStringArrayAsIList_WithUnsupportedTypes_Throws(string element) + { + var encodedBytes = Encoding.UTF8.GetBytes($"[{element}]"); + var reader = new Utf8JsonReader(encodedBytes); + reader.Read(); + Exception exceptionThrown = null; + try + { + reader.ReadStringArrayAsIList(); + } + catch (Exception ex) + { + exceptionThrown = ex; + } + Assert.IsType(typeof(InvalidCastException), exceptionThrown); + } + + [Fact] + public void ReadNextStringOrArrayOfStringsAsReadOnlyList_WhenValueIsNull_ReturnsNull() + { + const string json = "{\"a\":null}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + var reader = new Utf8JsonReader(encodedBytes); + reader.Read(); + reader.Read(); + IEnumerable actualResults = reader.ReadNextStringOrArrayOfStringsAsReadOnlyList(); + Assert.Null(actualResults); + Assert.Equal(JsonTokenType.Null, reader.TokenType); + } + + [Theory] + [InlineData("true", JsonTokenType.True)] + [InlineData("false", JsonTokenType.False)] + [InlineData("-2", JsonTokenType.Number)] + [InlineData("3.14", JsonTokenType.Number)] + [InlineData("{}", JsonTokenType.StartObject)] + public void ReadNextStringOrArrayOfStringsAsReadOnlyList_WhenValueIsNotString_ReturnsNull( + string value, + JsonTokenType expectedTokenType) + { + var json = $"{{\"a\":{value}}}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + var reader = new Utf8JsonReader(encodedBytes); + reader.Read(); + reader.Read(); + Assert.Equal(JsonTokenType.PropertyName, reader.TokenType); + + IEnumerable actualResults = reader.ReadNextStringOrArrayOfStringsAsReadOnlyList(); + + Assert.Null(actualResults); + Assert.Equal(expectedTokenType, reader.TokenType); + } + + [Fact] + public void ReadNextStringOrArrayOfStringsAsReadOnlyList_WhenValueIsString_ReturnsValue() + { + const string expectedResult = "b"; + var json = $"{{\"a\":\"{expectedResult}\"}}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + var reader = new Utf8JsonReader(encodedBytes); + reader.Read(); + reader.Read(); + Assert.Equal(JsonTokenType.PropertyName, reader.TokenType); + + IEnumerable actualResults = reader.ReadNextStringOrArrayOfStringsAsReadOnlyList(); + + Assert.Collection(actualResults, actualResult => Assert.Equal(expectedResult, actualResult)); + Assert.Equal(JsonTokenType.String, reader.TokenType); + } + + [Theory] + [InlineData("b,c,d")] + [InlineData("b c d")] + public void ReadNextStringOrArrayOfStringsAsReadOnlyList_WhenValueIsDelimitedString_ReturnsValue(string expectedResult) + { + var json = $"{{\"a\":\"{expectedResult}\"}}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + var reader = new Utf8JsonReader(encodedBytes); + reader.Read(); + reader.Read(); + Assert.Equal(JsonTokenType.PropertyName, reader.TokenType); + + IEnumerable actualResults = reader.ReadNextStringOrArrayOfStringsAsReadOnlyList(); + + Assert.Collection(actualResults, actualResult => Assert.Equal(expectedResult, actualResult)); + Assert.Equal(JsonTokenType.String, reader.TokenType); + } + + [Fact] + public void ReadNextStringOrArrayOfStringsAsReadOnlyList_WhenValueIsEmptyArray_ReturnsEmptyList() + { + const string json = "{\"a\":[]}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + var reader = new Utf8JsonReader(encodedBytes); + reader.Read(); + reader.Read(); + Assert.Equal(JsonTokenType.PropertyName, reader.TokenType); + + IReadOnlyList actualResults = reader.ReadNextStringOrArrayOfStringsAsReadOnlyList(); + + Assert.Empty(actualResults); + Assert.Equal(JsonTokenType.EndArray, reader.TokenType); + } + + [Theory] + [InlineData("null", null)] + [InlineData("true", "True")] + [InlineData("-2", "-2")] + [InlineData("3.14", "3.14")] + [InlineData("\"b\"", "b")] + public void ReadNextStringOrArrayOfStringsAsReadOnlyList_WhenValueIsConvertibleToString_ReturnsValueAsString( + string value, + string expectedResult) + { + var json = $"{{\"a\":[{value}]}}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + var reader = new Utf8JsonReader(encodedBytes); + reader.Read(); + reader.Read(); + + Assert.Equal(JsonTokenType.PropertyName, reader.TokenType); + + IEnumerable actualResults = reader.ReadNextStringOrArrayOfStringsAsReadOnlyList(); + + Assert.Collection(actualResults, actualResult => Assert.Equal(expectedResult, actualResult)); + Assert.Equal(JsonTokenType.EndArray, reader.TokenType); + } + + [Theory] + [InlineData("[]", JsonTokenType.StartArray)] + [InlineData("{}", JsonTokenType.StartObject)] + public void ReadNextStringOrArrayOfStringsAsReadOnlyList_WhenValueIsNotConvertibleToString_ReturnsValueAsString( + string value, + JsonTokenType expectedToken) + { + var json = $"{{\"a\":[{value}]}}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + var reader = new Utf8JsonReader(encodedBytes); + reader.Read(); + reader.Read(); + Assert.Equal(JsonTokenType.PropertyName, reader.TokenType); + + Exception exceptionThrown = null; + try + { + reader.ReadNextStringOrArrayOfStringsAsReadOnlyList(); + } + catch (Exception ex) + { + exceptionThrown = ex; + } + Assert.NotNull(exceptionThrown); + Assert.IsType(typeof(InvalidCastException), exceptionThrown); + Assert.Equal(expectedToken, reader.TokenType); + } + + [Fact] + public void ReadNextStringOrArrayOfStringsAsReadOnlyList_WhenValueIsArrayOfStrings_ReturnsValues() + { + string[] expectedResults = { "b", "c" }; + var json = $"{{\"a\":[{string.Join(",", expectedResults.Select(expectedResult => $"\"{expectedResult}\""))}]}}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + var reader = new Utf8JsonReader(encodedBytes); + reader.Read(); + reader.Read(); + Assert.Equal(JsonTokenType.PropertyName, reader.TokenType); + + IEnumerable actualResults = reader.ReadNextStringOrArrayOfStringsAsReadOnlyList(); + + Assert.Equal(expectedResults, actualResults); + Assert.Equal(JsonTokenType.EndArray, reader.TokenType); + } + + [Fact] + public void ReadStringArrayAsReadOnlyListFromArrayStart_WhenValuesAreConvertibleToString_ReturnsReadOnlyList() + { + const string json = "[null, true, -2, 3.14, \"a\"]"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + var reader = new Utf8JsonReader(encodedBytes); + reader.Read(); + Assert.Equal(JsonTokenType.StartArray, reader.TokenType); + + IEnumerable actualResults = reader.ReadStringArrayAsReadOnlyListFromArrayStart(); + + Assert.Collection( + actualResults, + actualResult => Assert.Equal(null, actualResult), + actualResult => Assert.Equal("True", actualResult), + actualResult => Assert.Equal("-2", actualResult), + actualResult => Assert.Equal("3.14", actualResult), + actualResult => Assert.Equal("a", actualResult)); + Assert.Equal(JsonTokenType.EndArray, reader.TokenType); + } + + [Theory] + [InlineData("[]", JsonTokenType.StartArray)] + [InlineData("{}", JsonTokenType.StartObject)] + public void ReadStringArrayAsReadOnlyListFromArrayStart_WhenValuesAreNotConvertibleToString_Throws( + string value, + JsonTokenType expectedToken) + { + var json = $"[{value}]"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + var reader = new Utf8JsonReader(encodedBytes); + reader.Read(); + Assert.Equal(JsonTokenType.StartArray, reader.TokenType); + + Exception exceptionThrown = null; + try + { + reader.ReadStringArrayAsReadOnlyListFromArrayStart(); + } + catch (Exception ex) + { + exceptionThrown = ex; + } + Assert.NotNull(exceptionThrown); + Assert.IsType(typeof(InvalidCastException), exceptionThrown); + Assert.Equal(expectedToken, reader.TokenType); + } + } +} diff --git a/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/Utf8JsonStreamPackageSpecReaderTests.cs b/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/Utf8JsonStreamPackageSpecReaderTests.cs new file mode 100644 index 00000000000..4d97217c904 --- /dev/null +++ b/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/Utf8JsonStreamPackageSpecReaderTests.cs @@ -0,0 +1,3635 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.Json; +using FluentAssertions; +using NuGet.Common; +using NuGet.Configuration; +using NuGet.Frameworks; +using NuGet.LibraryModel; +using NuGet.Packaging.Core; +using NuGet.RuntimeModel; +using NuGet.Versioning; +using Xunit; + +namespace NuGet.ProjectModel.Test +{ + [UseCulture("")] // Fix tests failing on systems with non-English locales + public class Utf8JsonStreamPackageSpecReaderTests + { + [Fact] + public void PackageSpecReader_PackageMissingVersion() + { + // Arrange + var json = @"{ + ""dependencies"": { + ""packageA"": { + ""type"": ""build"" + } + }, + ""frameworks"": { + ""net46"": {} + } + }"; + + // Act + Exception exception = null; + + try + { + var spec = Utf8JsonStreamPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + } + catch (Exception ex) + { + exception = ex; + } + + // Assert + Assert.Contains("specify a version range", exception.Message); + } + + [Fact] + public void PackageSpecReader_ProjectMissingVersion() + { + // Arrange + var json = @"{ + ""dependencies"": { + ""packageA"": { + ""target"": ""project"" + } + }, + ""frameworks"": { + ""net46"": {} + } + }"; + + // Act + var spec = Utf8JsonStreamPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + var range = spec.Dependencies.Single().LibraryRange.VersionRange; + + // Assert + Assert.Equal(VersionRange.All, range); + } + + [Fact] + public void PackageSpecReader_PackageEmptyVersion() + { + // Arrange + var json = @"{ + ""dependencies"": { + ""packageA"": { + ""target"": ""package"", + ""version"": """" + } + }, + ""frameworks"": { + ""net46"": {} + } + }"; + + Exception exception = null; + + try + { + var spec = Utf8JsonStreamPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + } + catch (Exception ex) + { + exception = ex; + } + + // Assert + Assert.Contains("specify a version range", exception.Message); + } + + [Fact] + public void PackageSpecReader_PackageWhitespaceVersion() + { + // Arrange + var json = @"{ + ""dependencies"": { + ""packageA"": { + ""target"": ""package"", + ""version"": "" "" + } + }, + ""frameworks"": { + ""net46"": {} + } + }"; + + Exception exception = null; + + try + { + var spec = Utf8JsonStreamPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + } + catch (Exception ex) + { + exception = ex; + } + + // Assert + Assert.Contains("not a valid version string", exception.Message); + } + + [Fact] + public void PackageSpecReader_FrameworkAssemblyEmptyVersion() + { + // Arrange + var json = @"{ + ""frameworks"": { + ""net46"": { + ""frameworkAssemblies"": { + ""packageA"": """" + } + } + } + }"; + + // Act + var spec = Utf8JsonStreamPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + var range = spec.TargetFrameworks.Single().Dependencies.Single().LibraryRange.VersionRange; + + // Assert + Assert.Equal(VersionRange.All, range); + } + + [Fact] + public void PackageSpecReader_ExplicitIncludesOverrideTypePlatform() + { + // Arrange + var json = @"{ + ""dependencies"": { + ""redist"": { + ""version"": ""1.0.0"", + ""type"": ""platform"", + ""include"": ""analyzers"" + } + } + }"; + + // Act + var actual = Utf8JsonStreamPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + + // Assert + var dep = actual.Dependencies.FirstOrDefault(d => d.Name.Equals("redist")); + Assert.NotNull(dep); + + var expected = LibraryIncludeFlags.Analyzers; + Assert.Equal(expected, dep.IncludeType); + } + + [Theory] + [InlineData("{}")] + [InlineData(@"{ + ""packOptions"": {} + }")] + [InlineData(@"{ + ""packOptions"": { + ""foo"": [1, 2] + } + }")] + [InlineData(@"{ + ""packOptions"": { + ""packageType"": null + } + }")] + [InlineData(@"{ + ""packOptions"": { + ""packageType"": [] + } + }")] +#pragma warning disable CS0612 // Type or member is obsolete + public void PackageSpecReader_PackOptions_Default(string json) + { + // Arrange & Act + var actual = Utf8JsonStreamPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + + // Assert + Assert.NotNull(actual.PackOptions); + Assert.NotNull(actual.PackOptions.PackageType); + Assert.Empty(actual.PackOptions.PackageType); + } + + [Theory] + [InlineData(@"{ + ""packOptions"": { + ""packageType"": ""foo"" + } + }", new[] { "foo" })] + [InlineData(@"{ + ""packOptions"": { + ""packageType"": ""foo, bar"" + } + }", new[] { "foo, bar" })] + [InlineData(@"{ + ""packOptions"": { + ""packageType"": [ ""foo"" ] + } + }", new[] { "foo" })] + [InlineData(@"{ + ""packOptions"": { + ""packageType"": [ ""foo, bar"" ] + } + }", new[] { "foo, bar" })] + [InlineData(@"{ + ""packOptions"": { + ""packageType"": [ ""foo"", ""bar"" ] + } + }", new[] { "foo", "bar" })] + public void PackageSpecReader_PackOptions_ValidPackageType(string json, string[] expectedNames) + { + // Arrange + var expected = expectedNames + .Select(n => new PackageType(n, PackageType.EmptyVersion)) + .ToArray(); + + // Act + var actual = Utf8JsonStreamPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + + // Assert + Assert.NotNull(actual.PackOptions); + Assert.NotNull(actual.PackOptions.PackageType); + Assert.Equal(expected, actual.PackOptions.PackageType.ToArray()); + } + + [Theory] + [InlineData(@"{ + ""packOptions"": { + ""packageType"": 1 + } + }")] + [InlineData(@"{ + ""packOptions"": { + ""packageType"": false + } + }")] + [InlineData(@"{ + ""packOptions"": { + ""packageType"": 1.0 + } + }")] + [InlineData(@"{ + ""packOptions"": { + ""packageType"": {} + } + }")] + [InlineData(@"{ + ""packOptions"": { + ""packageType"": { + ""name"": ""foo"" + } + } + }")] + [InlineData(@"{ + ""packOptions"": { + ""packageType"": [ + { ""name"": ""foo"" }, + { ""name"": ""bar"" } + ] + } + }")] + [InlineData(@"{ + ""packOptions"": { + ""packageType"": [ + ""foo"", + null + ] + } + }")] + [InlineData(@"{ + ""packOptions"": { + ""packageType"": [ + ""foo"", + true + ] + } + }")] + public void PackageSpecReader_PackOptions_InvalidPackageType(string json) + { + // Arrange & Act & Assert + var actual = Assert.Throws( + () => Utf8JsonStreamPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json")); + + Assert.Contains("The pack options package type must be a string or array of strings in 'project.json'.", actual.Message); + } + + [Fact] + public void PackageSpecReader_PackOptions_Files1() + { + // Arrange & Act + var json = @"{ + ""packOptions"": { + ""files"": { + ""include"": ""file1"", + ""exclude"": ""file2"", + ""includeFiles"": ""file3"", + ""excludeFiles"": ""file4"", + ""mappings"": { + ""dest/path"": ""./src/path"" + } + } + } + }"; + var actual = Utf8JsonStreamPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + + // Assert + Assert.NotNull(actual.PackOptions); + Assert.Equal(1, actual.PackOptions.IncludeExcludeFiles.Include.Count); + Assert.Equal(1, actual.PackOptions.IncludeExcludeFiles.Exclude.Count); + Assert.Equal(1, actual.PackOptions.IncludeExcludeFiles.IncludeFiles.Count); + Assert.Equal(1, actual.PackOptions.IncludeExcludeFiles.ExcludeFiles.Count); + Assert.Equal("file1", actual.PackOptions.IncludeExcludeFiles.Include.First()); + Assert.Equal("file2", actual.PackOptions.IncludeExcludeFiles.Exclude.First()); + Assert.Equal("file3", actual.PackOptions.IncludeExcludeFiles.IncludeFiles.First()); + Assert.Equal("file4", actual.PackOptions.IncludeExcludeFiles.ExcludeFiles.First()); + Assert.NotNull(actual.PackOptions.Mappings); + Assert.Equal(1, actual.PackOptions.Mappings.Count()); + Assert.Equal("dest/path", actual.PackOptions.Mappings.First().Key); + Assert.Equal(1, actual.PackOptions.Mappings.First().Value.Include.Count()); + Assert.Null(actual.PackOptions.Mappings.First().Value.Exclude); + Assert.Null(actual.PackOptions.Mappings.First().Value.IncludeFiles); + Assert.Null(actual.PackOptions.Mappings.First().Value.ExcludeFiles); + Assert.Equal("./src/path", actual.PackOptions.Mappings.First().Value.Include.First()); + } + + [Fact] + public void PackageSpecReader_PackOptions_Files2() + { + // Arrange & Act + var json = @"{ + ""packOptions"": { + ""files"": { + ""include"": [""file1a"", ""file1b""], + ""exclude"": [""file2a"", ""file2b""], + ""includeFiles"": [""file3a"", ""file3b""], + ""excludeFiles"": [""file4a"", ""file4b""], + ""mappings"": { + ""dest/path1"": [""./src/path1"", ""./src/path2""], + ""dest/path2"": { + ""includeFiles"": [""map1a"", ""map1b""], + }, + } + } + } + }"; + var actual = Utf8JsonStreamPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + + // Assert + Assert.NotNull(actual.PackOptions); + Assert.Equal(2, actual.PackOptions.IncludeExcludeFiles.Include.Count); + Assert.Equal(2, actual.PackOptions.IncludeExcludeFiles.Exclude.Count); + Assert.Equal(2, actual.PackOptions.IncludeExcludeFiles.IncludeFiles.Count); + Assert.Equal(2, actual.PackOptions.IncludeExcludeFiles.ExcludeFiles.Count); + Assert.Equal("file1a", actual.PackOptions.IncludeExcludeFiles.Include.First()); + Assert.Equal("file2a", actual.PackOptions.IncludeExcludeFiles.Exclude.First()); + Assert.Equal("file3a", actual.PackOptions.IncludeExcludeFiles.IncludeFiles.First()); + Assert.Equal("file4a", actual.PackOptions.IncludeExcludeFiles.ExcludeFiles.First()); + Assert.Equal("file1b", actual.PackOptions.IncludeExcludeFiles.Include.Last()); + Assert.Equal("file2b", actual.PackOptions.IncludeExcludeFiles.Exclude.Last()); + Assert.Equal("file3b", actual.PackOptions.IncludeExcludeFiles.IncludeFiles.Last()); + Assert.Equal("file4b", actual.PackOptions.IncludeExcludeFiles.ExcludeFiles.Last()); + Assert.NotNull(actual.PackOptions.Mappings); + Assert.Equal(2, actual.PackOptions.Mappings.Count()); + Assert.Equal("dest/path1", actual.PackOptions.Mappings.First().Key); + Assert.Equal("dest/path2", actual.PackOptions.Mappings.Last().Key); + Assert.Equal(2, actual.PackOptions.Mappings.First().Value.Include.Count()); + Assert.Null(actual.PackOptions.Mappings.First().Value.Exclude); + Assert.Null(actual.PackOptions.Mappings.First().Value.IncludeFiles); + Assert.Null(actual.PackOptions.Mappings.First().Value.ExcludeFiles); + Assert.Equal("./src/path1", actual.PackOptions.Mappings.First().Value.Include.First()); + Assert.Equal("./src/path2", actual.PackOptions.Mappings.First().Value.Include.Last()); + Assert.Null(actual.PackOptions.Mappings.Last().Value.Include); + Assert.Null(actual.PackOptions.Mappings.Last().Value.Exclude); + Assert.Null(actual.PackOptions.Mappings.Last().Value.ExcludeFiles); + Assert.Equal("map1a", actual.PackOptions.Mappings.Last().Value.IncludeFiles.First()); + Assert.Equal("map1b", actual.PackOptions.Mappings.Last().Value.IncludeFiles.Last()); + } + + [Theory] + [InlineData("{}", null, true)] + [InlineData(@"{ + ""buildOptions"": {} + }", null, false)] + [InlineData(@"{ + ""buildOptions"": { + ""outputName"": ""dllName"" + } + }", "dllName", false)] + [InlineData(@"{ + ""buildOptions"": { + ""outputName"": ""dllName2"", + ""emitEntryPoint"": true + } + }", "dllName2", false)] + [InlineData(@"{ + ""buildOptions"": { + ""outputName"": null + } + }", null, false)] + public void PackageSpecReader_BuildOptions(string json, string expectedValue, bool nullBuildOptions) + { + // Arrange & Act + var actual = Utf8JsonStreamPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + + // Assert + if (nullBuildOptions) + { + Assert.Null(actual.BuildOptions); + } + else + { + Assert.NotNull(actual.BuildOptions); + Assert.Equal(expectedValue, actual.BuildOptions.OutputName); + } + } +#pragma warning restore CS0612 // Type or member is obsolete + + [Fact] + public void PackageSpecReader_ReadsWithoutRestoreSettings() + { + // Arrange + var json = @"{ + ""dependencies"": { + ""packageA"": { + ""target"": ""package"", + ""version"": ""1.0.0"" + } + }, + ""frameworks"": { + ""net46"": {} + }, + }"; + + var actual = Utf8JsonStreamPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + + // Assert + Assert.NotNull(actual); + Assert.NotNull(actual.RestoreSettings); + Assert.False(actual.RestoreSettings.HideWarningsAndErrors); + } + + [Fact] + public void PackageSpecReader_ReadsDependencyWithMultipleNoWarn() + { + // Arrange + var json = @"{ + ""dependencies"": { + ""packageA"": { + ""target"": ""package"", + ""version"": ""1.0.0"", + ""noWarn"": [ + ""NU1500"", + ""NU1107"" + ] + } + }, + ""frameworks"": { + ""net46"": {} + }, + }"; + + var actual = Utf8JsonStreamPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + + // Assert + var dep = actual.Dependencies.FirstOrDefault(d => d.Name.Equals("packageA")); + Assert.NotNull(dep); + Assert.NotNull(dep.NoWarn); + Assert.Equal(dep.NoWarn.Count, 2); + Assert.True(dep.NoWarn.Contains(NuGetLogCode.NU1500)); + Assert.True(dep.NoWarn.Contains(NuGetLogCode.NU1107)); + } + + [Fact] + public void PackageSpecReader_ReadsDependencyWithSingleNoWarn() + { + // Arrange + var json = @"{ + ""dependencies"": { + ""packageA"": { + ""target"": ""package"", + ""version"": ""1.0.0"", + ""noWarn"": [ + ""NU1500"" + ] + } + }, + ""frameworks"": { + ""net46"": {} + }, + }"; + + var actual = Utf8JsonStreamPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + + // Assert + var dep = actual.Dependencies.FirstOrDefault(d => d.Name.Equals("packageA")); + Assert.NotNull(dep); + Assert.NotNull(dep.NoWarn); + Assert.Equal(dep.NoWarn.Count, 1); + Assert.True(dep.NoWarn.Contains(NuGetLogCode.NU1500)); + } + + [Fact] + public void PackageSpecReader_ReadsDependencyWithSingleEmptyNoWarn() + { + // Arrange + var json = @"{ + ""dependencies"": { + ""packageA"": { + ""target"": ""package"", + ""version"": ""1.0.0"", + ""noWarn"": [ + ] + } + }, + ""frameworks"": { + ""net46"": {} + }, + }"; + + var actual = Utf8JsonStreamPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + + // Assert + var dep = actual.Dependencies.FirstOrDefault(d => d.Name.Equals("packageA")); + Assert.NotNull(dep); + Assert.NotNull(dep.NoWarn); + Assert.Equal(dep.NoWarn.Count, 0); + } + + [Fact] + public void PackageSpecReader_ReadsRestoreMetadataWithWarningProperties() + { + // Arrange + var json = @"{ + ""restore"": { + ""projectUniqueName"": ""projectUniqueName"", + ""projectName"": ""projectName"", + ""projectPath"": ""projectPath"", + ""projectJsonPath"": ""projectJsonPath"", + ""packagesPath"": ""packagesPath"", + ""outputPath"": ""outputPath"", + ""projectStyle"": ""PackageReference"", + ""crossTargeting"": true, + ""configFilePaths"": [ + ""b"", + ""a"", + ""c"" + ], + ""fallbackFolders"": [ + ""b"", + ""a"", + ""c"" + ], + ""originalTargetFrameworks"": [ + ""b"", + ""a"", + ""c"" + ], + ""sources"": { + ""source"": {} + }, + ""frameworks"": { + ""frameworkidentifier123-frameworkprofile"": { + ""projectReferences"": {} + } + }, + ""warningProperties"": { + ""allWarningsAsErrors"": true, + ""noWarn"": [ + ""NU1601"", + ], + ""warnAsError"": [ + ""NU1500"", + ""NU1501"" + ], + ""warnNotAsError"": [ + ""NU1801"", + ""NU1802"" + ] + } + } +}"; + + var actual = Utf8JsonStreamPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + + // Assert + var metadata = actual.RestoreMetadata; + var warningProperties = actual.RestoreMetadata.ProjectWideWarningProperties; + + Assert.NotNull(metadata); + Assert.NotNull(warningProperties); + Assert.True(warningProperties.AllWarningsAsErrors); + Assert.Equal(1, warningProperties.NoWarn.Count); + Assert.True(warningProperties.NoWarn.Contains(NuGetLogCode.NU1601)); + Assert.Equal(2, warningProperties.WarningsAsErrors.Count); + Assert.True(warningProperties.WarningsAsErrors.Contains(NuGetLogCode.NU1500)); + Assert.True(warningProperties.WarningsAsErrors.Contains(NuGetLogCode.NU1501)); + Assert.Equal(2, warningProperties.WarningsNotAsErrors.Count); + Assert.True(warningProperties.WarningsNotAsErrors.Contains(NuGetLogCode.NU1801)); + Assert.True(warningProperties.WarningsNotAsErrors.Contains(NuGetLogCode.NU1802)); + } + + [Fact] + public void PackageSpecReader_ReadsRestoreMetadataWithWarningPropertiesAndNo_NoWarn() + { + // Arrange + var json = @"{ + ""restore"": { + ""projectUniqueName"": ""projectUniqueName"", + ""projectName"": ""projectName"", + ""projectPath"": ""projectPath"", + ""projectJsonPath"": ""projectJsonPath"", + ""packagesPath"": ""packagesPath"", + ""outputPath"": ""outputPath"", + ""projectStyle"": ""PackageReference"", + ""crossTargeting"": true, + ""configFilePaths"": [ + ""b"", + ""a"", + ""c"" + ], + ""fallbackFolders"": [ + ""b"", + ""a"", + ""c"" + ], + ""originalTargetFrameworks"": [ + ""b"", + ""a"", + ""c"" + ], + ""sources"": { + ""source"": {} + }, + ""frameworks"": { + ""frameworkidentifier123-frameworkprofile"": { + ""projectReferences"": {} + } + }, + ""warningProperties"": { + ""allWarningsAsErrors"": true, + ""warnAsError"": [ + ""NU1500"", + ""NU1501"" + ] + } + } +}"; + + var actual = Utf8JsonStreamPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + + // Assert + var metadata = actual.RestoreMetadata; + var warningProperties = actual.RestoreMetadata.ProjectWideWarningProperties; + + Assert.NotNull(metadata); + Assert.NotNull(warningProperties); + Assert.True(warningProperties.AllWarningsAsErrors); + Assert.Equal(0, warningProperties.NoWarn.Count); + Assert.Equal(2, warningProperties.WarningsAsErrors.Count); + Assert.True(warningProperties.WarningsAsErrors.Contains(NuGetLogCode.NU1500)); + Assert.True(warningProperties.WarningsAsErrors.Contains(NuGetLogCode.NU1501)); + } + + [Fact] + public void PackageSpecReader_ReadsRestoreMetadataWithWarningPropertiesAndNo_WarnAsError() + { + // Arrange + var json = @"{ + ""restore"": { + ""projectUniqueName"": ""projectUniqueName"", + ""projectName"": ""projectName"", + ""projectPath"": ""projectPath"", + ""projectJsonPath"": ""projectJsonPath"", + ""packagesPath"": ""packagesPath"", + ""outputPath"": ""outputPath"", + ""projectStyle"": ""PackageReference"", + ""crossTargeting"": true, + ""configFilePaths"": [ + ""b"", + ""a"", + ""c"" + ], + ""fallbackFolders"": [ + ""b"", + ""a"", + ""c"" + ], + ""originalTargetFrameworks"": [ + ""b"", + ""a"", + ""c"" + ], + ""sources"": { + ""source"": {} + }, + ""frameworks"": { + ""frameworkidentifier123-frameworkprofile"": { + ""projectReferences"": {} + } + }, + ""warningProperties"": { + ""allWarningsAsErrors"": true, + ""noWarn"": [ + ""NU1601"", + ] + } + } +}"; + + var actual = Utf8JsonStreamPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + + // Assert + var metadata = actual.RestoreMetadata; + var warningProperties = actual.RestoreMetadata.ProjectWideWarningProperties; + + Assert.NotNull(metadata); + Assert.NotNull(warningProperties); + Assert.True(warningProperties.AllWarningsAsErrors); + Assert.Equal(1, warningProperties.NoWarn.Count); + Assert.True(warningProperties.NoWarn.Contains(NuGetLogCode.NU1601)); + Assert.Equal(0, warningProperties.WarningsAsErrors.Count); + } + + [Fact] + public void PackageSpecReader_ReadsRestoreMetadataWithWarningPropertiesAndNo_AllWarningsAsErrors() + { + // Arrange + var json = @"{ + ""restore"": { + ""projectUniqueName"": ""projectUniqueName"", + ""projectName"": ""projectName"", + ""projectPath"": ""projectPath"", + ""projectJsonPath"": ""projectJsonPath"", + ""packagesPath"": ""packagesPath"", + ""outputPath"": ""outputPath"", + ""projectStyle"": ""PackageReference"", + ""crossTargeting"": true, + ""configFilePaths"": [ + ""b"", + ""a"", + ""c"" + ], + ""fallbackFolders"": [ + ""b"", + ""a"", + ""c"" + ], + ""originalTargetFrameworks"": [ + ""b"", + ""a"", + ""c"" + ], + ""sources"": { + ""source"": {} + }, + ""frameworks"": { + ""frameworkidentifier123-frameworkprofile"": { + ""projectReferences"": {} + } + }, + ""warningProperties"": { + ""noWarn"": [ + ""NU1601"", + ], + ""warnAsError"": [ + ""NU1500"", + ""NU1501"" + ] + } + } +}"; + + var actual = Utf8JsonStreamPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + + // Assert + var metadata = actual.RestoreMetadata; + var warningProperties = actual.RestoreMetadata.ProjectWideWarningProperties; + + Assert.NotNull(metadata); + Assert.NotNull(warningProperties); + Assert.False(warningProperties.AllWarningsAsErrors); + Assert.Equal(1, warningProperties.NoWarn.Count); + Assert.True(warningProperties.NoWarn.Contains(NuGetLogCode.NU1601)); + Assert.Equal(2, warningProperties.WarningsAsErrors.Count); + Assert.True(warningProperties.WarningsAsErrors.Contains(NuGetLogCode.NU1500)); + Assert.True(warningProperties.WarningsAsErrors.Contains(NuGetLogCode.NU1501)); + } + + [Fact] + public void PackageSpecReader_ReadsRestoreMetadataWithEmptyWarningPropertiesAnd() + { + // Arrange + var json = @"{ + ""restore"": { + ""projectUniqueName"": ""projectUniqueName"", + ""projectName"": ""projectName"", + ""projectPath"": ""projectPath"", + ""projectJsonPath"": ""projectJsonPath"", + ""packagesPath"": ""packagesPath"", + ""outputPath"": ""outputPath"", + ""projectStyle"": ""PackageReference"", + ""crossTargeting"": true, + ""configFilePaths"": [ + ""b"", + ""a"", + ""c"" + ], + ""fallbackFolders"": [ + ""b"", + ""a"", + ""c"" + ], + ""originalTargetFrameworks"": [ + ""b"", + ""a"", + ""c"" + ], + ""sources"": { + ""source"": {} + }, + ""frameworks"": { + ""frameworkidentifier123-frameworkprofile"": { + ""projectReferences"": {} + } + }, + ""warningProperties"": { + } + } +}"; + + var actual = Utf8JsonStreamPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + + // Assert + var metadata = actual.RestoreMetadata; + var warningProperties = actual.RestoreMetadata.ProjectWideWarningProperties; + + Assert.NotNull(metadata); + Assert.NotNull(warningProperties); + Assert.False(warningProperties.AllWarningsAsErrors); + Assert.Equal(0, warningProperties.NoWarn.Count); + Assert.Equal(0, warningProperties.WarningsAsErrors.Count); + } + + [Fact] + public void PackageSpecReader_ReadsRestoreMetadataWithNoWarningProperties() + { + // Arrange + var json = @"{ + ""restore"": { + ""projectUniqueName"": ""projectUniqueName"", + ""projectName"": ""projectName"", + ""projectPath"": ""projectPath"", + ""projectJsonPath"": ""projectJsonPath"", + ""packagesPath"": ""packagesPath"", + ""outputPath"": ""outputPath"", + ""projectStyle"": ""PackageReference"", + ""crossTargeting"": true, + ""configFilePaths"": [ + ""b"", + ""a"", + ""c"" + ], + ""fallbackFolders"": [ + ""b"", + ""a"", + ""c"" + ], + ""originalTargetFrameworks"": [ + ""b"", + ""a"", + ""c"" + ], + ""sources"": { + ""source"": {} + }, + ""frameworks"": { + ""frameworkidentifier123-frameworkprofile"": { + ""projectReferences"": {} + } + } + } +}"; + + var actual = Utf8JsonStreamPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + + // Assert + var metadata = actual.RestoreMetadata; + var warningProperties = actual.RestoreMetadata.ProjectWideWarningProperties; + + Assert.NotNull(metadata); + Assert.NotNull(warningProperties); + } + + [Fact] + public void PackageSpecReader_RuntimeIdentifierPathNullIfEmpty() + { + // Arrange + var json = @"{ + ""frameworks"": { + ""net46"": { + ""dependencies"": { + ""packageA"": { + ""target"": ""package"", + ""version"": ""1.0.0"", + ""noWarn"": [ + ""NU1500"" + ] + } + } + } + } + }"; + + // Act + var spec = Utf8JsonStreamPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + + // Assert + Assert.Null(spec.TargetFrameworks.First().RuntimeIdentifierGraphPath); + } + +#pragma warning disable CS0612 // Type or member is obsolete + [Fact] + public void GetPackageSpec_WhenAuthorsPropertyIsAbsent_ReturnsEmptyAuthors() + { + PackageSpec packageSpec = GetPackageSpec("{}"); + + Assert.Empty(packageSpec.Authors); + } + + [Fact] + public void GetPackageSpec_WhenAuthorsValueIsNull_ReturnsEmptyAuthors() + { + PackageSpec packageSpec = GetPackageSpec("{\"authors\":null}"); + + Assert.Empty(packageSpec.Authors); + } + + [Fact] + public void GetPackageSpec_WhenAuthorsValueIsString_ReturnsEmptyAuthors() + { + PackageSpec packageSpec = GetPackageSpec("{\"authors\":\"b\"}"); + + Assert.Empty(packageSpec.Authors); + } + + [Theory] + [InlineData("")] + [InlineData("/**/")] + public void GetPackageSpec_WhenAuthorsValueIsEmptyArray_ReturnsEmptyAuthors(string value) + { + PackageSpec packageSpec = GetPackageSpec($"{{\"authors\":[{value}]}}"); + + Assert.Empty(packageSpec.Authors); + } + + [Theory] + [InlineData("{}")] + [InlineData("[]")] + public void GetPackageSpec_WhenAuthorsValueElementIsNotConvertibleToString_Throws(string value) + { + var json = $"{{\"authors\":[{value}]}}"; + + Assert.Throws(() => GetPackageSpec(json)); + } + + [Theory] + [InlineData("\"a\"", "a")] + [InlineData("true", "True")] + [InlineData("-2", "-2")] + [InlineData("3.14", "3.14")] + public void GetPackageSpec_WhenAuthorsValueElementIsConvertibleToString_ReturnsAuthor(string value, string expectedValue) + { + PackageSpec packageSpec = GetPackageSpec($"{{\"authors\":[{value}]}}"); + + Assert.Collection(packageSpec.Authors, author => Assert.Equal(expectedValue, author)); + } + + [Fact] + public void GetPackageSpec_WhenBuildOptionsPropertyIsAbsent_ReturnsNullBuildOptions() + { + PackageSpec packageSpec = GetPackageSpec("{}"); + + Assert.Null(packageSpec.BuildOptions); + } + + [Fact] + public void GetPackageSpec_WhenBuildOptionsValueIsEmptyObject_ReturnsBuildOptions() + { + PackageSpec packageSpec = GetPackageSpec("{\"buildOptions\":{}}"); + + Assert.NotNull(packageSpec.BuildOptions); + Assert.Null(packageSpec.BuildOptions.OutputName); + } + + [Fact] + public void GetPackageSpec_WhenBuildOptionsValueOutputNameIsNull_ReturnsNullOutputName() + { + PackageSpec packageSpec = GetPackageSpec("{\"buildOptions\":{\"outputName\":null}}"); + + Assert.Null(packageSpec.BuildOptions.OutputName); + } + + [Fact] + public void GetPackageSpec_WhenBuildOptionsValueOutputNameIsValid_ReturnsOutputName() + { + const string expectedResult = "a"; + + var json = $"{{\"buildOptions\":{{\"outputName\":\"{expectedResult}\"}}}}"; + + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedResult, packageSpec.BuildOptions.OutputName); + } + + [Theory] + [InlineData("-2", "-2")] + [InlineData("3.14", "3.14")] + [InlineData("true", "True")] + public void GetPackageSpec_WhenBuildOptionsValueOutputNameIsConvertibleToString_ReturnsOutputName(string outputName, string expectedValue) + { + PackageSpec packageSpec = GetPackageSpec($"{{\"buildOptions\":{{\"outputName\":{outputName}}}}}"); + + Assert.Equal(expectedValue, packageSpec.BuildOptions.OutputName); + } + + [Fact] + public void GetPackageSpec_WhenContentFilesPropertyIsAbsent_ReturnsEmptyContentFiles() + { + PackageSpec packageSpec = GetPackageSpec("{}"); + + Assert.Empty(packageSpec.ContentFiles); + } + + [Fact] + public void GetPackageSpec_WhenContentFilesValueIsNull_ReturnsEmptyContentFiles() + { + PackageSpec packageSpec = GetPackageSpec("{\"contentFiles\":null}"); + + Assert.Empty(packageSpec.ContentFiles); + } + + [Fact] + public void GetPackageSpec_WhenContentFilesValueIsString_ReturnsEmptyContentFiles() + { + PackageSpec packageSpec = GetPackageSpec("{\"contentFiles\":\"a\"}"); + + Assert.Empty(packageSpec.ContentFiles); + } + + [Theory] + [InlineData("")] + [InlineData("/**/")] + public void GetPackageSpec_WhenContentFilesValueIsEmptyArray_ReturnsEmptyContentFiles(string value) + { + PackageSpec packageSpec = GetPackageSpec($"{{\"contentFiles\":[{value}]}}"); + + Assert.Empty(packageSpec.ContentFiles); + } + + [Theory] + [InlineData("{}")] + [InlineData("[]")] + public void GetPackageSpec_WhenContentFilesValueElementIsNotConvertibleToString_Throws(string value) + { + var json = $"{{\"contentFiles\":[{value}]}}"; + + Assert.Throws(() => GetPackageSpec(json)); + } + + [Theory] + [InlineData("\"a\"", "a")] + [InlineData("true", "True")] + [InlineData("-2", "-2")] + [InlineData("3.14", "3.14")] + public void GetPackageSpec_WhenContentFilesValueElementIsConvertibleToString_ReturnsContentFile(string value, string expectedValue) + { + PackageSpec packageSpec = GetPackageSpec($"{{\"contentFiles\":[{value}]}}"); + + Assert.Collection(packageSpec.ContentFiles, contentFile => Assert.Equal(expectedValue, contentFile)); + } + + [Fact] + public void GetPackageSpec_WhenCopyrightPropertyIsAbsent_ReturnsNullCopyright() + { + PackageSpec packageSpec = GetPackageSpec("{}"); + + Assert.Null(packageSpec.Copyright); + } + + [Fact] + public void GetPackageSpec_WhenCopyrightValueIsNull_ReturnsNullCopyright() + { + PackageSpec packageSpec = GetPackageSpec("{\"copyright\":null}"); + + Assert.Null(packageSpec.Copyright); + } + + [Fact] + public void GetPackageSpec_WhenCopyrightValueIsString_ReturnsCopyright() + { + const string expectedResult = "a"; + + PackageSpec packageSpec = GetPackageSpec($"{{\"copyright\":\"{expectedResult}\"}}"); + + Assert.Equal(expectedResult, packageSpec.Copyright); + } + + [Theory] + [InlineData("\"a\"", "a")] + [InlineData("true", "True")] + [InlineData("-2", "-2")] + [InlineData("3.14", "3.14")] + public void GetPackageSpec_WhenCopyrightValueIsConvertibleToString_ReturnsCopyright(string value, string expectedValue) + { + PackageSpec packageSpec = GetPackageSpec($"{{\"copyright\":{value}}}"); + + Assert.Equal(expectedValue, packageSpec.Copyright); + } +#pragma warning restore CS0612 // Type or member is obsolete + + [Fact] + public void GetPackageSpec_WhenDependenciesPropertyIsAbsent_ReturnsEmptyDependencies() + { + PackageSpec packageSpec = GetPackageSpec("{}"); + + Assert.Empty(packageSpec.Dependencies); + } + + [Fact] + public void GetPackageSpec_WhenDependenciesValueIsNull_ReturnsEmptyDependencies() + { + PackageSpec packageSpec = GetPackageSpec("{\"dependencies\":null}"); + + Assert.Empty(packageSpec.Dependencies); + } + + [Fact] + public void GetPackageSpec_WhenDependenciesDependencyNameIsEmptyString_Throws() + { + const string json = "{\"dependencies\":{\"\":{}}}"; + + FileFormatException exception = Assert.Throws(() => GetPackageSpec(json)); + + Assert.Equal("Unable to resolve dependency ''.", exception.Message); + } + + [Fact] + public void GetPackageSpec_WhenDependenciesDependencyValueIsVersionString_ReturnsDependencyVersionRange() + { + var expectedResult = new LibraryRange( + name: "a", + new VersionRange(new NuGetVersion("1.2.3")), + LibraryDependencyTarget.All & ~LibraryDependencyTarget.Reference); + var json = $"{{\"dependencies\":{{\"{expectedResult.Name}\":\"{expectedResult.VersionRange.ToShortString()}\"}}}}"; + + LibraryDependency dependency = GetDependency(json); + + Assert.Equal(expectedResult, dependency.LibraryRange); + } + + [Fact] + public void GetPackageSpec_WhenDependenciesDependencyValueIsVersionRangeString_ReturnsDependencyVersionRange() + { + var expectedResult = new LibraryRange( + name: "a", + new VersionRange(new NuGetVersion("1.2.3"), includeMinVersion: true, new NuGetVersion("4.5.6"), includeMaxVersion: false), + LibraryDependencyTarget.All & ~LibraryDependencyTarget.Reference); + var json = $"{{\"dependencies\":{{\"{expectedResult.Name}\":\"{expectedResult.VersionRange}\"}}}}"; + + LibraryDependency dependency = GetDependency(json); + + Assert.Equal(expectedResult, dependency.LibraryRange); + } + + [Theory] + [InlineData(LibraryDependencyTarget.None)] + [InlineData(LibraryDependencyTarget.Assembly)] + [InlineData(LibraryDependencyTarget.Reference)] + [InlineData(LibraryDependencyTarget.WinMD)] + [InlineData(LibraryDependencyTarget.All)] + [InlineData(LibraryDependencyTarget.PackageProjectExternal)] + public void GetPackageSpec_WhenDependenciesDependencyTargetIsUnsupported_Throws(LibraryDependencyTarget target) + { + var json = $"{{\"dependencies\":{{\"a\":{{\"version\":\"1.2.3\",\"target\":\"{target}\"}}}}}}"; + + FileFormatException exception = Assert.Throws(() => GetPackageSpec(json)); + + Assert.Equal($"Error reading '' : Invalid dependency target value '{target}'.", exception.Message); + } + + [Fact] + public void GetPackageSpec_WhenDependenciesDependencyAutoreferencedPropertyIsAbsent_ReturnsFalseAutoreferenced() + { + LibraryDependency dependency = GetDependency($"{{\"dependencies\":{{\"a\":{{\"target\":\"Project\"}}}}}}"); + + Assert.False(dependency.AutoReferenced); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void GetPackageSpec_WhenDependenciesDependencyAutoreferencedValueIsBool_ReturnsBoolAutoreferenced(bool expectedValue) + { + var json = $"{{\"dependencies\":{{\"a\":{{\"autoReferenced\":{expectedValue.ToString().ToLower()},\"target\":\"Project\"}}}}}}"; + + LibraryDependency dependency = GetDependency(json); + + Assert.Equal(expectedValue, dependency.AutoReferenced); + } + + [Theory] + [InlineData("exclude")] + [InlineData("include")] + [InlineData("suppressParent")] + public void GetPackageSpec_WhenDependenciesDependencyValueIsArray_Throws(string propertyName) + { + var json = $"{{\"dependencies\":{{\"a\":{{\"{propertyName}\":[\"b\"]}}}}}}"; + + var exception = Assert.Throws(() => GetPackageSpec(json)); + Assert.IsType(exception.InnerException); + Assert.IsType(exception.InnerException.InnerException); + + } + + [Fact] + public void GetPackageSpec_WhenDependenciesDependencyIncludeAndExcludePropertiesAreAbsent_ReturnsAllIncludeType() + { + const string json = "{\"dependencies\":{\"a\":{\"version\":\"1.0.0\"}}}"; + + LibraryDependency dependency = GetDependency(json); + + Assert.Equal(LibraryIncludeFlags.All, dependency.IncludeType); + } + + [Theory] + [InlineData("\"Native\"", LibraryIncludeFlags.Native)] + [InlineData("\"Analyzers, Native\"", LibraryIncludeFlags.Analyzers | LibraryIncludeFlags.Native)] + public void GetPackageSpec_WhenDependenciesDependencyExcludeValueIsValid_ReturnsIncludeType( + string value, + LibraryIncludeFlags result) + { + var json = $"{{\"dependencies\":{{\"a\":{{\"exclude\":{value},\"version\":\"1.0.0\"}}}}}}"; + + LibraryDependency dependency = GetDependency(json); + + Assert.Equal(LibraryIncludeFlags.All & ~result, dependency.IncludeType); + } + + [Theory] + [InlineData("\"Native\"", LibraryIncludeFlags.Native)] + [InlineData("\"Analyzers, Native\"", LibraryIncludeFlags.Analyzers | LibraryIncludeFlags.Native)] + public void GetPackageSpec_WhenDependenciesDependencyIncludeValueIsValid_ReturnsIncludeType( + string value, + LibraryIncludeFlags expectedResult) + { + var json = $"{{\"dependencies\":{{\"a\":{{\"include\":{value},\"version\":\"1.0.0\"}}}}}}"; + + LibraryDependency dependency = GetDependency(json); + + Assert.Equal(expectedResult, dependency.IncludeType); + } + + [Fact] + public void GetPackageSpec_WhenDependenciesDependencyIncludeValueOverridesTypeValue_ReturnsIncludeType() + { + const string json = "{\"dependencies\":{\"a\":{\"include\":\"ContentFiles\",\"type\":\"BecomesNupkgDependency, SharedFramework\",\"version\":\"1.0.0\"}}}"; + + LibraryDependency dependency = GetDependency(json); + + Assert.Equal(LibraryIncludeFlags.ContentFiles, dependency.IncludeType); + } + + [Fact] + public void GetPackageSpec_WhenDependenciesDependencySuppressParentValueOverridesTypeValue_ReturnsSuppressParent() + { + const string json = "{\"dependencies\":{\"a\":{\"suppressParent\":\"ContentFiles\",\"type\":\"SharedFramework\",\"version\":\"1.0.0\"}}}"; + + LibraryDependency dependency = GetDependency(json); + + Assert.Equal(LibraryIncludeFlags.ContentFiles, dependency.SuppressParent); + } + + [Fact] + public void GetPackageSpec_WhenDependenciesDependencySuppressParentPropertyIsAbsent_ReturnsSuppressParent() + { + const string json = "{\"dependencies\":{\"a\":{\"version\":\"1.0.0\"}}}"; + + LibraryDependency dependency = GetDependency(json); + + Assert.Equal(LibraryIncludeFlagUtils.DefaultSuppressParent, dependency.SuppressParent); + } + + [Theory] + [InlineData("\"Compile\"", LibraryIncludeFlags.Compile)] + [InlineData("\"Analyzers, Compile\"", LibraryIncludeFlags.Analyzers | LibraryIncludeFlags.Compile)] + public void GetPackageSpec_WhenDependenciesDependencySuppressParentValueIsValid_ReturnsSuppressParent( + string value, + LibraryIncludeFlags expectedResult) + { + var json = $"{{\"dependencies\":{{\"a\":{{\"suppressParent\":{value},\"version\":\"1.0.0\"}}}}}}"; + + LibraryDependency dependency = GetDependency(json); + + Assert.Equal(expectedResult, dependency.SuppressParent); + } + + [Fact] + public void GetPackageSpec_WhenDependenciesDependencyVersionValueIsInvalid_Throws() + { + const string json = "{\"dependencies\":{\"a\":{\"version\":\"b\"}}}"; + + FileFormatException exception = Assert.Throws(() => GetPackageSpec(json)); + + Assert.Equal("Error reading '' : 'b' is not a valid version string.", exception.Message); + Assert.IsType(exception.InnerException); + Assert.IsType(exception.InnerException.InnerException); + Assert.Null(exception.InnerException.InnerException.InnerException); + } + + [Fact] + public void GetPackageSpec_WhenDependenciesDependencyTargetPropertyIsAbsent_ReturnsTarget() + { + const string json = "{\"dependencies\":{\"a\":{\"version\":\"1.0.0\"}}}"; + + LibraryDependency dependency = GetDependency(json); + + Assert.Equal(LibraryDependencyTarget.All & ~LibraryDependencyTarget.Reference, dependency.LibraryRange.TypeConstraint); + } + + [Fact] + public void GetPackageSpec_WhenDependenciesDependencyTargetValueIsPackageAndVersionPropertyIsAbsent_Throws() + { + const string json = "{\"dependencies\":{\"a\":{\"target\":\"Package\"}}}"; + + FileFormatException exception = Assert.Throws(() => GetPackageSpec(json)); + + Assert.Equal("Error reading '' : Package dependencies must specify a version range.", exception.Message); + Assert.IsType(exception.InnerException); + Assert.IsType(exception.InnerException.InnerException); + Assert.Null(exception.InnerException.InnerException.InnerException); + } + + [Fact] + public void GetPackageSpec_WhenDependenciesDependencyTargetValueIsProjectAndVersionPropertyIsAbsent_ReturnsAllVersionRange() + { + LibraryDependency dependency = GetDependency("{\"dependencies\":{\"a\":{\"target\":\"Project\"}}}"); + + Assert.Equal(VersionRange.All, dependency.LibraryRange.VersionRange); + } + + [Fact] + public void GetPackageSpec_WhenDependenciesDependencyNoWarnPropertyIsAbsent_ReturnsEmptyNoWarns() + { + const string json = "{\"dependencies\":{\"a\":{\"version\":\"1.0.0\"}}}"; + + LibraryDependency dependency = GetDependency(json); + + Assert.Empty(dependency.NoWarn); + } + + [Fact] + public void GetPackageSpec_WhenDependenciesDependencyNoWarnValueIsValid_ReturnsNoWarns() + { + NuGetLogCode[] expectedResults = { NuGetLogCode.NU1000, NuGetLogCode.NU3000 }; + var json = $"{{\"dependencies\":{{\"a\":{{\"noWarn\":[\"{expectedResults[0].ToString()}\",\"{expectedResults[1].ToString()}\"],\"version\":\"1.0.0\"}}}}}}"; + + LibraryDependency dependency = GetDependency(json); + + Assert.Collection( + dependency.NoWarn, + noWarn => Assert.Equal(expectedResults[0], noWarn), + noWarn => Assert.Equal(expectedResults[1], noWarn)); + } + + [Fact] + public void GetPackageSpec_WhenDependenciesDependencyGeneratePathPropertyPropertyIsAbsent_ReturnsFalseGeneratePathProperty() + { + const string json = "{\"dependencies\":{\"a\":{\"version\":\"1.0.0\"}}}"; + + LibraryDependency dependency = GetDependency(json); + + Assert.False(dependency.GeneratePathProperty); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void GetPackageSpec_WhenDependenciesDependencyGeneratePathPropertyValueIsValid_ReturnsGeneratePathProperty(bool expectedResult) + { + var json = $"{{\"dependencies\":{{\"a\":{{\"generatePathProperty\":{expectedResult.ToString().ToLowerInvariant()},\"version\":\"1.0.0\"}}}}}}"; + + LibraryDependency dependency = GetDependency(json); + + Assert.Equal(expectedResult, dependency.GeneratePathProperty); + } + + [Fact] + public void GetPackageSpec_WhenDependenciesDependencyTypePropertyIsAbsent_ReturnsDefaultTypeConstraint() + { + const string json = "{\"dependencies\":{\"a\":{\"version\":\"1.0.0\"}}}"; + + LibraryDependency dependency = GetDependency(json); + + Assert.Equal( + LibraryDependencyTarget.All & ~LibraryDependencyTarget.Reference, + dependency.LibraryRange.TypeConstraint); + } + + [Fact] + public void GetPackageSpec_WhenDependenciesDependencyVersionCentrallyManagedPropertyIsAbsent_ReturnsFalseVersionCentrallyManaged() + { + LibraryDependency dependency = GetDependency($"{{\"dependencies\":{{\"a\":{{\"target\":\"Package\",\"version\":\"1.0.0\"}}}}}}"); + + Assert.False(dependency.VersionCentrallyManaged); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void GetPackageSpec_WhenDependenciesDependencyVersionCentrallyManagedValueIsBool_ReturnsBoolVersionCentrallyManaged(bool expectedValue) + { + var json = $"{{\"dependencies\":{{\"a\":{{\"versionCentrallyManaged\":{expectedValue.ToString().ToLower()},\"target\":\"Package\",\"version\":\"1.0.0\"}}}}}}"; + + LibraryDependency dependency = GetDependency(json); + + Assert.Equal(expectedValue, dependency.VersionCentrallyManaged); + } + +#pragma warning disable CS0612 // Type or member is obsolete + [Fact] + public void GetPackageSpec_WhenDescriptionPropertyIsAbsent_ReturnsNullDescription() + { + PackageSpec packageSpec = GetPackageSpec("{}"); + + Assert.Null(packageSpec.Description); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData("b")] + public void GetPackageSpec_WhenDescriptionValueIsValid_ReturnsDescription(string expectedResult) + { + string description = expectedResult == null ? "null" : $"\"{expectedResult}\""; + PackageSpec packageSpec = GetPackageSpec($"{{\"description\":{description}}}"); + + Assert.Equal(expectedResult, packageSpec.Description); + } + + [Fact] + public void GetPackageSpec_WhenLanguagePropertyIsAbsent_ReturnsNullLanguage() + { + PackageSpec packageSpec = GetPackageSpec("{}"); + + Assert.Null(packageSpec.Language); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData("b")] + public void GetPackageSpec_WhenLanguageValueIsValid_ReturnsLanguage(string expectedResult) + { + string language = expectedResult == null ? "null" : $"\"{expectedResult}\""; + PackageSpec packageSpec = GetPackageSpec($"{{\"language\":{language}}}"); + + Assert.Equal(expectedResult, packageSpec.Language); + } +#pragma warning restore CS0612 // Type or member is obsolete + + [Fact] + public void GetPackageSpec_WhenFrameworksPropertyIsAbsent_ReturnsEmptyFrameworks() + { + PackageSpec packageSpec = GetPackageSpec("{}"); + + Assert.Empty(packageSpec.TargetFrameworks); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksValueIsEmptyObject_ReturnsEmptyFrameworks() + { + PackageSpec packageSpec = GetPackageSpec("{\"frameworks\":{}}"); + + Assert.Empty(packageSpec.TargetFrameworks); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksAssetTargetFallbackPropertyIsAbsent_ReturnsFalseAssetTargetFallback() + { + TargetFrameworkInformation framework = GetFramework("{\"frameworks\":{\"a\":{}}}"); + + Assert.False(framework.AssetTargetFallback); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void GetPackageSpec_WhenFrameworksAssetTargetFallbackValueIsValid_ReturnsAssetTargetFallback(bool expectedValue) + { + var json = $"{{\"frameworks\":{{\"a\":{{\"assetTargetFallback\":{expectedValue.ToString().ToLowerInvariant()}}}}}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + + Assert.Equal(expectedValue, framework.AssetTargetFallback); + } + + [Fact] + public void GetPackageSpec_WithAssetTargetFallbackAndImportsValues_ReturnsValidAssetTargetFallbackFramework() + { + var json = $"{{\"frameworks\":{{\"net5.0\":{{\"assetTargetFallback\": true, \"imports\": [\"net472\", \"net471\"]}}}}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + + framework.AssetTargetFallback.Should().BeTrue(); + var assetTargetFallback = framework.FrameworkName as AssetTargetFallbackFramework; + assetTargetFallback.RootFramework.Should().Be(FrameworkConstants.CommonFrameworks.Net50); + assetTargetFallback.Fallback.Should().HaveCount(2); + assetTargetFallback.Fallback.First().Should().Be(FrameworkConstants.CommonFrameworks.Net472); + assetTargetFallback.Fallback.Last().Should().Be(FrameworkConstants.CommonFrameworks.Net471); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksCentralPackageVersionsPropertyIsAbsent_ReturnsEmptyCentralPackageVersions() + { + TargetFrameworkInformation framework = GetFramework("{\"frameworks\":{\"a\":{}}}"); + + Assert.Empty(framework.CentralPackageVersions); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksCentralPackageVersionsValueIsEmptyObject_ReturnsEmptyCentralPackageVersions() + { + TargetFrameworkInformation framework = GetFramework("{\"frameworks\":{\"a\":{\"centralPackageVersions\":{}}}}"); + + Assert.Empty(framework.CentralPackageVersions); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksCentralPackageVersionsVersionPropertyNameIsEmptyString_Throws() + { + var json = "{\"frameworks\":{\"a\":{\"centralPackageVersions\":{\"\":\"1.0.0\"}}}}"; + + FileFormatException exception = Assert.Throws(() => GetPackageSpec(json)); + + Assert.Equal("Error reading '' : Unable to resolve central version ''.", exception.Message); + Assert.IsType(exception.InnerException); + Assert.Null(exception.InnerException.InnerException); + } + + [Theory] + [InlineData("null")] + [InlineData("\"\"")] + public void GetPackageSpec_WhenFrameworksCentralPackageVersionsVersionPropertyValueIsNullOrEmptyString_Throws(string value) + { + var json = $"{{\"frameworks\":{{\"a\":{{\"centralPackageVersions\":{{\"b\":{value}}}}}}}}}"; + + FileFormatException exception = Assert.Throws(() => GetPackageSpec(json)); + + Assert.Equal($"Error reading '' : The version cannot be null or empty.", exception.Message); + Assert.IsType(exception.InnerException); + Assert.Null(exception.InnerException.InnerException); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksCentralPackageVersionsIsValid_ReturnsCentralPackageVersions() + { + const string expectedPackageId = "b"; + VersionRange expectedVersionRange = VersionRange.Parse("[1.2.3,4.5.6)"); + var expectedCentralPackageVersion = new CentralPackageVersion(expectedPackageId, expectedVersionRange); + var json = $"{{\"frameworks\":{{\"a\":{{\"centralPackageVersions\":{{\"{expectedPackageId}\":\"{expectedVersionRange.ToShortString()}\"}}}}}}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + + Assert.Collection( + framework.CentralPackageVersions, + actualResult => + { + Assert.Equal(expectedPackageId, actualResult.Key); + Assert.Equal(expectedCentralPackageVersion, actualResult.Value); + }); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksCentralPackageVersionsHasDuplicateKey_LastOneWins() + { + const string expectedPackageId = "b"; + VersionRange unexpectedVersionRange = VersionRange.Parse("1.2.3"); + VersionRange expectedVersionRange = VersionRange.Parse("4.5.6"); + var expectedCentralPackageVersion = new CentralPackageVersion(expectedPackageId, expectedVersionRange); + var json = $"{{\"frameworks\":{{\"a\":{{\"centralPackageVersions\":{{\"{expectedPackageId}\":\"{unexpectedVersionRange.ToShortString()}\"," + + $"\"{expectedPackageId}\":\"{expectedVersionRange.ToShortString()}\"}}}}}}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + + Assert.Collection( + framework.CentralPackageVersions, + actualResult => + { + Assert.Equal(expectedPackageId, actualResult.Key); + Assert.Equal(expectedCentralPackageVersion, actualResult.Value); + }); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksDependenciesPropertyIsAbsent_ReturnsEmptyDependencies() + { + TargetFrameworkInformation framework = GetFramework("{\"frameworks\":{\"a\":{}}}"); + + Assert.Empty(framework.Dependencies); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksDependenciesValueIsNull_ReturnsEmptyDependencies() + { + TargetFrameworkInformation framework = GetFramework("{\"frameworks\":{\"a\":{\"dependencies\":null}}}"); + + Assert.Empty(framework.Dependencies); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksDependenciesDependencyNameIsEmptyString_Throws() + { + const string json = "{\"frameworks\":{\"a\":{\"dependencies\":{\"\":{}}}}}"; + + FileFormatException exception = Assert.Throws(() => GetPackageSpec(json)); + + Assert.Equal("Unable to resolve dependency ''.", exception.Message); + Assert.IsType(exception.InnerException); + Assert.Null(exception.InnerException.InnerException); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksDependenciesDependencyValueIsVersionString_ReturnsDependencyVersionRange() + { + var expectedResult = new LibraryRange( + name: "b", + new VersionRange(new NuGetVersion("1.2.3")), + LibraryDependencyTarget.All & ~LibraryDependencyTarget.Reference); + var json = $"{{\"frameworks\":{{\"a\":{{\"dependencies\":{{\"{expectedResult.Name}\":\"{expectedResult.VersionRange.ToShortString()}\"}}}}}}}}"; + + LibraryDependency dependency = GetFrameworksDependency(json); + + Assert.Equal(expectedResult, dependency.LibraryRange); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksDependenciesDependencyValueIsVersionRangeString_ReturnsDependencyVersionRange() + { + var expectedResult = new LibraryRange( + name: "b", + new VersionRange(new NuGetVersion("1.2.3"), includeMinVersion: true, new NuGetVersion("4.5.6"), includeMaxVersion: false), + LibraryDependencyTarget.All & ~LibraryDependencyTarget.Reference); + var json = $"{{\"frameworks\":{{\"a\":{{\"dependencies\":{{\"{expectedResult.Name}\":\"{expectedResult.VersionRange}\"}}}}}}}}"; + + LibraryDependency dependency = GetFrameworksDependency(json); + + Assert.Equal(expectedResult, dependency.LibraryRange); + } + + [Theory] + [InlineData(LibraryDependencyTarget.None)] + [InlineData(LibraryDependencyTarget.Assembly)] + [InlineData(LibraryDependencyTarget.Reference)] + [InlineData(LibraryDependencyTarget.WinMD)] + [InlineData(LibraryDependencyTarget.All)] + [InlineData(LibraryDependencyTarget.PackageProjectExternal)] + public void GetPackageSpec_WhenFrameworksDependenciesDependencyTargetValueIsUnsupported_Throws(LibraryDependencyTarget target) + { + var json = $"{{\"frameworks\":{{\"a\":{{\"dependencies\":{{\"b\":{{\"version\":\"1.2.3\",\"target\":\"{target}\"}}}}}}}}}}"; + + FileFormatException exception = Assert.Throws(() => GetPackageSpec(json)); + + Assert.Equal($"Error reading '' : Invalid dependency target value '{target}'.", exception.Message); + Assert.IsType(exception.InnerException); + Assert.Null(exception.InnerException.InnerException); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksDependenciesDependencyAutoreferencedPropertyIsAbsent_ReturnsFalseAutoreferenced() + { + const string json = "{\"frameworks\":{\"a\":{\"dependencies\":{\"b\":{\"target\":\"Project\"}}}}}"; + + LibraryDependency dependency = GetFrameworksDependency(json); + + Assert.False(dependency.AutoReferenced); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void GetPackageSpec_WhenFrameworksDependenciesDependencyAutoreferencedValueIsBool_ReturnsBoolAutoreferenced(bool expectedValue) + { + var json = $"{{\"frameworks\":{{\"a\":{{\"dependencies\":{{\"b\":{{\"autoReferenced\":{expectedValue.ToString().ToLower()},\"target\":\"Project\"}}}}}}}}}}"; + + LibraryDependency dependency = GetFrameworksDependency(json); + + Assert.Equal(expectedValue, dependency.AutoReferenced); + } + + [Theory] + [InlineData("exclude")] + [InlineData("include")] + [InlineData("suppressParent")] + public void GetPackageSpec_WhenFrameworksDependenciesDependencyValueIsArray_Throws(string propertyName) + { + var json = $"{{\"frameworks\":{{\"a\":{{\"dependencies\":{{\"b\":{{\"{propertyName}\":[\"c\"]}}}}}}}}}}"; + + // The exception messages will not be the same because the innermost exception in the baseline + // is a Newtonsoft.Json exception, while it's a .NET exception in the improved. + FileFormatException exception = Assert.Throws(() => GetPackageSpec(json)); + + Assert.Equal($"Error reading '' : Specified cast is not valid.", exception.Message); + Assert.IsType(exception.InnerException); + Assert.IsType(exception.InnerException.InnerException); + Assert.Null(exception.InnerException.InnerException.InnerException); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksDependenciesDependencyIncludeAndExcludePropertiesAreAbsent_ReturnsAllIncludeType() + { + const string json = "{\"frameworks\":{\"a\":{\"dependencies\":{\"b\":{\"version\":\"1.0.0\"}}}}}"; + + LibraryDependency dependency = GetFrameworksDependency(json); + + Assert.Equal(LibraryIncludeFlags.All, dependency.IncludeType); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksDependenciesDependencyExcludeValueIsValid_ReturnsIncludeType() + { + const string json = "{\"frameworks\":{\"a\":{\"dependencies\":{\"b\":{\"exclude\":\"Native\",\"version\":\"1.0.0\"}}}}}"; + + LibraryDependency dependency = GetFrameworksDependency(json); + + Assert.Equal(LibraryIncludeFlags.All & ~LibraryIncludeFlags.Native, dependency.IncludeType); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksDependenciesDependencyIncludeValueIsValid_ReturnsIncludeType() + { + const string json = "{\"frameworks\":{\"a\":{\"dependencies\":{\"b\":{\"include\":\"ContentFiles\",\"version\":\"1.0.0\"}}}}}"; + + LibraryDependency dependency = GetFrameworksDependency(json); + + Assert.Equal(LibraryIncludeFlags.ContentFiles, dependency.IncludeType); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksDependenciesDependencyIncludeValueOverridesTypeValue_ReturnsIncludeType() + { + const string json = "{\"frameworks\":{\"a\":{\"dependencies\":{\"b\":{\"include\":\"ContentFiles\",\"type\":\"BecomesNupkgDependency, SharedFramework\",\"version\":\"1.0.0\"}}}}}"; + + LibraryDependency dependency = GetFrameworksDependency(json); + + Assert.Equal(LibraryIncludeFlags.ContentFiles, dependency.IncludeType); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksDependenciesDependencySuppressParentValueOverridesTypeValue_ReturnsSuppressParent() + { + const string json = "{\"frameworks\":{\"a\":{\"dependencies\":{\"b\":{\"suppressParent\":\"ContentFiles\",\"type\":\"SharedFramework\",\"version\":\"1.0.0\"}}}}}"; + + LibraryDependency dependency = GetFrameworksDependency(json); + + Assert.Equal(LibraryIncludeFlags.ContentFiles, dependency.SuppressParent); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksDependenciesDependencySuppressParentPropertyIsAbsent_ReturnsSuppressParent() + { + const string json = "{\"frameworks\":{\"a\":{\"dependencies\":{\"b\":{\"version\":\"1.0.0\"}}}}}"; + + LibraryDependency dependency = GetFrameworksDependency(json); + + Assert.Equal(LibraryIncludeFlagUtils.DefaultSuppressParent, dependency.SuppressParent); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksDependenciesDependencySuppressParentValueIsValid_ReturnsSuppressParent() + { + const string json = "{\"frameworks\":{\"a\":{\"dependencies\":{\"b\":{\"suppressParent\":\"Compile\",\"version\":\"1.0.0\"}}}}}"; + + LibraryDependency dependency = GetFrameworksDependency(json); + + Assert.Equal(LibraryIncludeFlags.Compile, dependency.SuppressParent); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksDependenciesDependencyVersionValueIsInvalid_Throws() + { + const string json = "{\"frameworks\":{\"a\":{\"dependencies\":{\"b\":{\"version\":\"c\"}}}}}"; + + FileFormatException exception = Assert.Throws(() => GetPackageSpec(json)); + + Assert.Equal("Error reading '' : 'c' is not a valid version string.", exception.Message); + Assert.IsType(exception.InnerException); + Assert.IsType(exception.InnerException.InnerException); + Assert.Null(exception.InnerException.InnerException.InnerException); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksDependenciesDependencyTargetPropertyIsAbsent_ReturnsTarget() + { + const string json = "{\"frameworks\":{\"a\":{\"dependencies\":{\"b\":{\"version\":\"1.0.0\"}}}}}"; + + LibraryDependency dependency = GetFrameworksDependency(json); + + Assert.Equal( + LibraryDependencyTarget.All & ~LibraryDependencyTarget.Reference, + dependency.LibraryRange.TypeConstraint); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksDependenciesDependencyTargetValueIsPackageAndVersionPropertyIsAbsent_Throws() + { + const string json = "{\"frameworks\":{\"a\":{\"dependencies\":{\"b\":{\"target\":\"Package\"}}}}}"; + + FileFormatException exception = Assert.Throws(() => GetPackageSpec(json)); + + Assert.Equal("Error reading '' : Package dependencies must specify a version range.", exception.Message); + Assert.IsType(exception.InnerException); + Assert.IsType(exception.InnerException.InnerException); + Assert.Null(exception.InnerException.InnerException.InnerException); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksDependenciesDependencyTargetValueIsProjectAndVersionPropertyIsAbsent_ReturnsAllVersionRange() + { + const string json = "{\"frameworks\":{\"a\":{\"dependencies\":{\"b\":{\"target\":\"Project\"}}}}}"; + + LibraryDependency dependency = GetFrameworksDependency(json); + + Assert.Equal(VersionRange.All, dependency.LibraryRange.VersionRange); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksDependenciesDependencyNoWarnPropertyIsAbsent_ReturnsEmptyNoWarns() + { + const string json = "{\"frameworks\":{\"a\":{\"dependencies\":{\"b\":{\"version\":\"1.0.0\"}}}}}"; + + LibraryDependency dependency = GetFrameworksDependency(json); + + Assert.Empty(dependency.NoWarn); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksDependenciesDependencyNoWarnValueIsValid_ReturnsNoWarns() + { + NuGetLogCode[] expectedResults = { NuGetLogCode.NU1000, NuGetLogCode.NU3000 }; + var json = $"{{\"frameworks\":{{\"a\":{{\"dependencies\":{{\"b\":{{\"noWarn\":[\"{expectedResults[0].ToString()}\",\"{expectedResults[1].ToString()}\"],\"version\":\"1.0.0\"}}}}}}}}}}"; + + LibraryDependency dependency = GetFrameworksDependency(json); + + Assert.Collection( + dependency.NoWarn, + noWarn => Assert.Equal(expectedResults[0], noWarn), + noWarn => Assert.Equal(expectedResults[1], noWarn)); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksDependenciesDependencyGeneratePathPropertyPropertyIsAbsent_ReturnsFalseGeneratePathProperty() + { + const string json = "{\"frameworks\":{\"a\":{\"dependencies\":{\"b\":{\"version\":\"1.0.0\"}}}}}"; + + LibraryDependency dependency = GetFrameworksDependency(json); + + Assert.False(dependency.GeneratePathProperty); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void GetPackageSpec_WhenFrameworksDependenciesDependencyGeneratePathPropertyValueIsValid_ReturnsGeneratePathProperty(bool expectedResult) + { + var json = $"{{\"frameworks\":{{\"a\":{{\"dependencies\":{{\"b\":{{\"generatePathProperty\":{expectedResult.ToString().ToLowerInvariant()},\"version\":\"1.0.0\"}}}}}}}}}}"; + + LibraryDependency dependency = GetFrameworksDependency(json); + + Assert.Equal(expectedResult, dependency.GeneratePathProperty); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksDependenciesDependencyTypePropertyIsAbsent_ReturnsDefaultTypeConstraint() + { + const string json = "{\"frameworks\":{\"a\":{\"dependencies\":{\"b\":{\"version\":\"1.0.0\"}}}}}"; + + LibraryDependency dependency = GetFrameworksDependency(json); + + Assert.Equal( + LibraryDependencyTarget.All & ~LibraryDependencyTarget.Reference, + dependency.LibraryRange.TypeConstraint); + } + + + [Fact] + public void GetPackageSpec_WhenFrameworksDependenciesDependencyVersionCentrallyManagedPropertyIsAbsent_ReturnsFalseVersionCentrallyManaged() + { + const string json = "{\"frameworks\":{\"a\":{\"dependencies\":{\"b\":{\"target\":\"Package\",\"version\":\"1.0.0\"}}}}}"; + + LibraryDependency dependency = GetFrameworksDependency(json); + + Assert.False(dependency.VersionCentrallyManaged); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void GetPackageSpec_WhenFrameworksDependenciesDependencyVersionCentrallyManagedValueIsBool_ReturnsBoolVersionCentrallyManaged(bool expectedValue) + { + var json = $"{{\"frameworks\":{{\"a\":{{\"dependencies\":{{\"b\":{{\"versionCentrallyManaged\":{expectedValue.ToString().ToLower()},\"target\":\"Package\",\"version\":\"1.0.0\"}}}}}}}}}}"; + + LibraryDependency dependency = GetFrameworksDependency(json); + + Assert.Equal(expectedValue, dependency.VersionCentrallyManaged); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksDownloadDependenciesPropertyIsAbsent_ReturnsEmptyDownloadDependencies() + { + const string json = "{\"frameworks\":{\"a\":{}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + + Assert.Empty(framework.DownloadDependencies); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksDownloadDependenciesValueIsNull_ReturnsEmptyDownloadDependencies() + { + const string json = "{\"frameworks\":{\"a\":{\"downloadDependencies\":null}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + + Assert.Empty(framework.DownloadDependencies); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksDownloadDependenciesValueIsNotArray_ReturnsEmptyDownloadDependencies() + { + const string json = "{\"frameworks\":{\"a\":{\"downloadDependencies\":\"b\"}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + + Assert.Empty(framework.DownloadDependencies); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksDownloadDependenciesValueIsEmptyArray_ReturnsEmptyDownloadDependencies() + { + const string json = "{\"frameworks\":{\"a\":{\"downloadDependencies\":[]}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + + Assert.Empty(framework.DownloadDependencies); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksDownloadDependenciesDependencyNameIsAbsent_Throws() + { + const string json = "{\"frameworks\":{\"a\":{\"downloadDependencies\":[{\"version\":\"1.2.3\"}]}}}"; + + FileFormatException exception = Assert.Throws(() => GetPackageSpec(json)); + + Assert.Equal("Error reading '' : Unable to resolve downloadDependency ''.", exception.Message); + Assert.IsType(exception.InnerException); + Assert.Null(exception.InnerException.InnerException); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksDownloadDependenciesDependencyNameIsNull_ReturnsDownloadDependencies() + { + var expectedResult = new DownloadDependency(name: null, new VersionRange(new NuGetVersion("1.2.3"))); + var json = $"{{\"frameworks\":{{\"a\":{{\"downloadDependencies\":[{{\"name\":null,\"version\":\"{expectedResult.VersionRange.ToShortString()}\"}}]}}}}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + + DownloadDependency actualResult = framework.DownloadDependencies.Single(); + + Assert.Equal(expectedResult.Name, actualResult.Name); + Assert.Equal(expectedResult.VersionRange, actualResult.VersionRange); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksDownloadDependenciesDependencyVersionIsAbsent_Throws() + { + const string json = "{\"frameworks\":{\"a\":{\"downloadDependencies\":[{\"name\":\"b\"}]}}}"; + + FileFormatException exception = Assert.Throws(() => GetPackageSpec(json)); + + Assert.Equal("Error reading '' : The version cannot be null or empty", exception.Message); + Assert.IsType(exception.InnerException); + Assert.Null(exception.InnerException.InnerException); + } + + [Theory] + [InlineData("null")] + [InlineData("c")] + public void GetPackageSpec_WhenFrameworksDownloadDependenciesDependencyVersionIsInvalid_Throws(string version) + { + var json = $"{{\"frameworks\":{{\"a\":{{\"downloadDependencies\":[{{\"name\":\"b\",\"version\":\"{version}\"}}]}}}}}}"; + + FileFormatException exception = Assert.Throws(() => GetPackageSpec(json)); + + Assert.Equal($"Error reading '' : '{version}' is not a valid version string.", exception.Message); + Assert.IsType(exception.InnerException); + Assert.IsType(exception.InnerException.InnerException); + Assert.Null(exception.InnerException.InnerException.InnerException); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksDownloadDependenciesValueIsValid_ReturnsDownloadDependencies() + { + var expectedResult = new DownloadDependency(name: "b", new VersionRange(new NuGetVersion("1.2.3"))); + var json = $"{{\"frameworks\":{{\"a\":{{\"downloadDependencies\":[{{\"name\":\"{expectedResult.Name}\",\"version\":\"{expectedResult.VersionRange.ToShortString()}\"}}]}}}}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + + Assert.Equal(expectedResult, framework.DownloadDependencies.Single()); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksDownloadDependenciesValueHasDuplicates_PrefersFirstByName() + { + var expectedResult = new DownloadDependency(name: "b", new VersionRange(new NuGetVersion("1.2.3"))); + var unexpectedResult = new DownloadDependency(name: "b", new VersionRange(new NuGetVersion("4.5.6"))); + var json = "{\"frameworks\":{\"a\":{\"downloadDependencies\":[" + + $"{{\"name\":\"{expectedResult.Name}\",\"version\":\"{expectedResult.VersionRange.ToShortString()}\"}}," + + $"{{\"name\":\"{unexpectedResult.Name}\",\"version\":\"{unexpectedResult.VersionRange.ToShortString()}\"}}" + + "]}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + + Assert.Equal(expectedResult, framework.DownloadDependencies.Single()); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksFrameworkAssembliesPropertyIsAbsent_ReturnsEmptyDependencies() + { + const string json = "{\"frameworks\":{\"a\":{}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + + Assert.Empty(framework.Dependencies); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksFrameworkAssembliesValueIsNull_ReturnsEmptyDependencies() + { + const string json = "{\"frameworks\":{\"a\":{\"frameworkAssemblies\":null}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + + Assert.Empty(framework.Dependencies); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksFrameworkAssembliesValueIsEmptyObject_ReturnsEmptyDependencies() + { + const string json = "{\"frameworks\":{\"a\":{\"frameworkAssemblies\":{}}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + + Assert.Empty(framework.Dependencies); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksFrameworkAssembliesDependencyTargetPropertyIsAbsent_ReturnsTarget() + { + const string json = "{\"frameworks\":{\"a\":{\"frameworkAssemblies\":{\"b\":{\"version\":\"1.0.0\"}}}}}"; + + LibraryDependency dependency = GetFrameworksDependency(json); + + Assert.Equal(LibraryDependencyTarget.Reference, dependency.LibraryRange.TypeConstraint); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksFrameworkAssembliesDependencyTargetValueIsPackageAndVersionPropertyIsAbsent_Throws() + { + const string json = "{\"frameworks\":{\"a\":{\"frameworkAssemblies\":{\"b\":{\"target\":\"Package\"}}}}}"; + + FileFormatException exception = Assert.Throws(() => GetPackageSpec(json)); + + Assert.Equal("Error reading '' : Package dependencies must specify a version range.", exception.Message); + Assert.IsType(exception.InnerException); + Assert.IsType(exception.InnerException.InnerException); + Assert.Null(exception.InnerException.InnerException.InnerException); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksFrameworkAssembliesDependencyTargetValueIsProjectAndVersionPropertyIsAbsent_ReturnsAllVersionRange() + { + const string json = "{\"frameworks\":{\"a\":{\"frameworkAssemblies\":{\"b\":{\"target\":\"Project\"}}}}}"; + + LibraryDependency dependency = GetFrameworksDependency(json); + + Assert.Equal(VersionRange.All, dependency.LibraryRange.VersionRange); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksFrameworkReferencesPropertyIsAbsent_ReturnsEmptyFrameworkReferences() + { + const string json = "{\"frameworks\":{\"a\":{}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + + Assert.Empty(framework.FrameworkReferences); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksFrameworkReferencesValueIsNull_ReturnsEmptyFrameworkReferences() + { + const string json = "{\"frameworks\":{\"a\":{\"frameworkReferences\":null}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + + Assert.Empty(framework.FrameworkReferences); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksFrameworkReferencesValueIsEmptyObject_ReturnsEmptyFrameworkReferences() + { + const string json = "{\"frameworks\":{\"a\":{\"frameworkReferences\":{}}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + + Assert.Empty(framework.FrameworkReferences); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksFrameworkReferencesFrameworkNameIsEmptyString_Throws() + { + const string json = "{\"frameworks\":{\"a\":{\"frameworkReferences\":{\"\":{}}}}}"; + + FileFormatException exception = Assert.Throws(() => GetPackageSpec(json)); + + Assert.Equal("Error reading '' : Unable to resolve frameworkReference.", exception.Message); + Assert.IsType(exception.InnerException); + Assert.Null(exception.InnerException.InnerException); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksFrameworkReferencesPrivateAssetsPropertyIsAbsent_ReturnsNonePrivateAssets() + { + var expectedResult = new FrameworkDependency(name: "b", FrameworkDependencyFlags.None); + var json = $"{{\"frameworks\":{{\"a\":{{\"frameworkReferences\":{{\"{expectedResult.Name}\":{{}}}}}}}}}}"; + + FrameworkDependency dependency = GetFrameworksFrameworkReference(json); + + Assert.Equal(expectedResult, dependency); + } + + [Theory] + [InlineData("\"null\"")] + [InlineData("\"\"")] + [InlineData("\"c\"")] + public void GetPackageSpec_WhenFrameworksFrameworkReferencesPrivateAssetsValueIsInvalidValue_ReturnsNonePrivateAssets(string privateAssets) + { + var expectedResult = new FrameworkDependency(name: "b", FrameworkDependencyFlags.None); + var json = $"{{\"frameworks\":{{\"a\":{{\"frameworkReferences\":{{\"{expectedResult.Name}\":{{\"privateAssets\":{privateAssets}}}}}}}}}}}"; + + FrameworkDependency dependency = GetFrameworksFrameworkReference(json); + + Assert.Equal(expectedResult, dependency); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksFrameworkReferencesPrivateAssetsValueIsValidString_ReturnsPrivateAssets() + { + var expectedResult = new FrameworkDependency(name: "b", FrameworkDependencyFlags.All); + var json = $"{{\"frameworks\":{{\"a\":{{\"frameworkReferences\":{{\"{expectedResult.Name}\":{{\"privateAssets\":\"{expectedResult.PrivateAssets.ToString().ToLowerInvariant()}\"}}}}}}}}}}"; + + FrameworkDependency dependency = GetFrameworksFrameworkReference(json); + + Assert.Equal(expectedResult, dependency); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksFrameworkReferencesPrivateAssetsValueIsValidDelimitedString_ReturnsPrivateAssets() + { + var expectedResult = new FrameworkDependency(name: "b", FrameworkDependencyFlags.All); + var json = $"{{\"frameworks\":{{\"a\":{{\"frameworkReferences\":{{\"{expectedResult.Name}\":{{\"privateAssets\":\"none,all\"}}}}}}}}}}"; + + FrameworkDependency dependency = GetFrameworksFrameworkReference(json); + + Assert.Equal(expectedResult, dependency); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksImportsPropertyIsAbsent_ReturnsEmptyImports() + { + const string json = "{\"frameworks\":{\"a\":{}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + + Assert.Empty(framework.Imports); + } + + [Theory] + [InlineData("null")] + [InlineData("\"\"")] + public void GetPackageSpec_WhenFrameworksImportsValueIsArrayOfNullOrEmptyString_ImportIsSkipped(string import) + { + var json = $"{{\"frameworks\":{{\"a\":{{\"imports\":[{import}]}}}}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + + Assert.Empty(framework.Imports); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksImportsValueIsNull_ReturnsEmptyList() + { + const string json = "{\"frameworks\":{\"a\":{\"imports\":null}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + + Assert.Empty(framework.Imports); + } + + [Theory] + [InlineData("true")] + [InlineData("-2")] + [InlineData("3.14")] + [InlineData("{}")] + public void GetPackageSpec_WhenFrameworksImportsValueIsInvalidValue_ReturnsEmptyList(string value) + { + var json = $"{{\"frameworks\":{{\"a\":{{\"imports\":{value}}}}}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + + Assert.Empty(framework.Imports); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksImportsValueContainsInvalidValue_Throws() + { + const string expectedImport = "b"; + + var json = $"{{\"frameworks\":{{\"a\":{{\"imports\":[\"{expectedImport}\"]}}}}}}"; + + FileFormatException exception = Assert.Throws(() => GetPackageSpec(json)); + + Assert.Equal( + $"Error reading '' : Imports contains an invalid framework: '{expectedImport}' in 'project.json'.", + exception.Message); + Assert.IsType(exception.InnerException); + Assert.Null(exception.InnerException.InnerException); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksImportsValueIsString_ReturnsImport() + { + NuGetFramework expectedResult = NuGetFramework.Parse("net48"); + var json = $"{{\"frameworks\":{{\"a\":{{\"imports\":\"{expectedResult.GetShortFolderName()}\"}}}}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + + Assert.Collection( + framework.Imports, + actualResult => Assert.Equal(expectedResult, actualResult)); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksImportsValueIsArrayOfStrings_ReturnsImports() + { + NuGetFramework[] expectedResults = { NuGetFramework.Parse("net472"), NuGetFramework.Parse("net48") }; + var json = $"{{\"frameworks\":{{\"a\":{{\"imports\":[\"{expectedResults[0].GetShortFolderName()}\",\"{expectedResults[1].GetShortFolderName()}\"]}}}}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + + Assert.Collection( + framework.Imports, + actualResult => Assert.Equal(expectedResults[0], actualResult), + actualResult => Assert.Equal(expectedResults[1], actualResult)); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksRuntimeIdentifierGraphPathPropertyIsAbsent_ReturnsRuntimeIdentifierGraphPath() + { + const string json = "{\"frameworks\":{\"a\":{}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + + Assert.Null(framework.RuntimeIdentifierGraphPath); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData("b")] + public void GetPackageSpec_WhenFrameworksRuntimeIdentifierGraphPathValueIsString_ReturnsRuntimeIdentifierGraphPath(string expectedResult) + { + string runtimeIdentifierGraphPath = expectedResult == null ? "null" : $"\"{expectedResult}\""; + var json = $"{{\"frameworks\":{{\"a\":{{\"runtimeIdentifierGraphPath\":{runtimeIdentifierGraphPath}}}}}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + + Assert.Equal(expectedResult, framework.RuntimeIdentifierGraphPath); + } + + [Fact] + public void GetPackageSpec_WhenFrameworksWarnPropertyIsAbsent_ReturnsWarn() + { + const string json = "{\"frameworks\":{\"a\":{}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + + Assert.False(framework.Warn); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void GetPackageSpec_WhenFrameworksWarnValueIsValid_ReturnsWarn(bool expectedResult) + { + var json = $"{{\"frameworks\":{{\"a\":{{\"warn\":{expectedResult.ToString().ToLowerInvariant()}}}}}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + + Assert.Equal(expectedResult, framework.Warn); + } + +#pragma warning disable CS0612 // Type or member is obsolete + [Fact] + public void GetPackageSpec_WhenPackIncludePropertyIsAbsent_ReturnsEmptyPackInclude() + { + PackageSpec packageSpec = GetPackageSpec("{}"); + + Assert.Empty(packageSpec.PackInclude); + } + + [Fact] + public void GetPackageSpec_WhenPackIncludePropertyIsValid_ReturnsPackInclude() + { + var expectedResults = new List>() { new KeyValuePair("a", "b"), new KeyValuePair("c", "d") }; + var json = $"{{\"packInclude\":{{\"{expectedResults[0].Key}\":\"{expectedResults[0].Value}\",\"{expectedResults[1].Key}\":\"{expectedResults[1].Value}\"}}}}"; + + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Collection( + packageSpec.PackInclude, + actualResult => Assert.Equal(expectedResults[0], actualResult), + actualResult => Assert.Equal(expectedResults[1], actualResult)); + } + + [Theory] + [InlineData("{}")] + [InlineData("{\"packOptions\":null}")] + public void GetPackageSpec_WhenPackOptionsPropertyIsAbsentOrValueIsNull_ReturnsPackOptions(string json) + { + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.NotNull(packageSpec.PackOptions); + Assert.Null(packageSpec.PackOptions.IncludeExcludeFiles); + Assert.Empty(packageSpec.PackOptions.Mappings); + Assert.Empty(packageSpec.PackOptions.PackageType); + + Assert.Null(packageSpec.IconUrl); + Assert.Null(packageSpec.LicenseUrl); + Assert.Empty(packageSpec.Owners); + Assert.Null(packageSpec.ProjectUrl); + Assert.Null(packageSpec.ReleaseNotes); + Assert.False(packageSpec.RequireLicenseAcceptance); + Assert.Null(packageSpec.Summary); + Assert.Empty(packageSpec.Tags); + } + + [Fact] + public void GetPackageSpec_WhenPackOptionsPropertyIsAbsent_OwnersAndTagsAreEmpty() + { + const string json = "{\"owners\":[\"a\"],\"tags\":[\"b\"]}"; + + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Empty(packageSpec.Owners); + Assert.Empty(packageSpec.Tags); + } + + [Fact] + public void GetPackageSpec_WhenPackOptionsPropertyIsEmptyObject_ReturnsPackOptions() + { + string json = "{\"packOptions\":{}}"; + + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.NotNull(packageSpec.PackOptions); + Assert.Null(packageSpec.PackOptions.IncludeExcludeFiles); + Assert.Null(packageSpec.PackOptions.Mappings); + Assert.Empty(packageSpec.PackOptions.PackageType); + + Assert.Null(packageSpec.IconUrl); + Assert.Null(packageSpec.LicenseUrl); + Assert.Empty(packageSpec.Owners); + Assert.Null(packageSpec.ProjectUrl); + Assert.Null(packageSpec.ReleaseNotes); + Assert.False(packageSpec.RequireLicenseAcceptance); + Assert.Null(packageSpec.Summary); + Assert.Empty(packageSpec.Tags); + } + + [Fact] + public void GetPackageSpec_WhenPackOptionsValueIsValid_ReturnsPackOptions() + { + const string iconUrl = "a"; + const string licenseUrl = "b"; + string[] owners = { "c", "d" }; + const string projectUrl = "e"; + const string releaseNotes = "f"; + const bool requireLicenseAcceptance = true; + const string summary = "g"; + string[] tags = { "h", "i" }; + + var json = $"{{\"packOptions\":{{\"iconUrl\":\"{iconUrl}\",\"licenseUrl\":\"{licenseUrl}\",\"owners\":[{string.Join(",", owners.Select(owner => $"\"{owner}\""))}]," + + $"\"projectUrl\":\"{projectUrl}\",\"releaseNotes\":\"{releaseNotes}\",\"requireLicenseAcceptance\":{requireLicenseAcceptance.ToString().ToLowerInvariant()}," + + $"\"summary\":\"{summary}\",\"tags\":[{string.Join(",", tags.Select(tag => $"\"{tag}\""))}]}}}}"; + + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.NotNull(packageSpec.PackOptions); + Assert.Null(packageSpec.PackOptions.IncludeExcludeFiles); + Assert.Null(packageSpec.PackOptions.Mappings); + Assert.Empty(packageSpec.PackOptions.PackageType); + Assert.Equal(iconUrl, packageSpec.IconUrl); + Assert.Equal(licenseUrl, packageSpec.LicenseUrl); + Assert.Equal(owners, packageSpec.Owners); + Assert.Equal(projectUrl, packageSpec.ProjectUrl); + Assert.Equal(releaseNotes, packageSpec.ReleaseNotes); + Assert.Equal(requireLicenseAcceptance, packageSpec.RequireLicenseAcceptance); + Assert.Equal(summary, packageSpec.Summary); + Assert.Equal(tags, packageSpec.Tags); + } + + [Fact] + public void GetPackageSpec_WhenPackOptionsPackageTypeValueIsNull_ReturnsEmptyPackageTypes() + { + const string json = "{\"packOptions\":{\"packageType\":null}}"; + + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Empty(packageSpec.PackOptions.PackageType); + } + + [Theory] + [InlineData("true")] + [InlineData("-2")] + [InlineData("3.14")] + [InlineData("{}")] + [InlineData("[true]")] + [InlineData("[-2]")] + [InlineData("[3.14]")] + [InlineData("[null]")] + [InlineData("[{}]")] + [InlineData("[[]]")] + public void GetPackageSpec_WhenPackOptionsPackageTypeIsInvalid_Throws(string value) + { + var json = $"{{\"packOptions\":{{\"packageType\":{value}}}}}"; + + FileFormatException exception = Assert.Throws(() => GetPackageSpec(json)); + + Assert.Equal("The pack options package type must be a string or array of strings in 'project.json'.", exception.Message); + Assert.IsType(exception.InnerException); + Assert.Null(exception.InnerException.InnerException); + } + + [Theory] + [InlineData("\"a\"", "a")] + [InlineData("\"a,b\"", "a,b")] + [InlineData("[\"a\"]", "a")] + [InlineData("[\"a b\"]", "a b")] + public void GetPackageSpec_WhenPackOptionsPackageTypeValueIsValid_ReturnsPackageTypes(string value, string expectedName) + { + var expectedResult = new PackageType(expectedName, PackageType.EmptyVersion); + var json = $"{{\"packOptions\":{{\"packageType\":{value}}}}}"; + + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Collection( + packageSpec.PackOptions.PackageType, + actualResult => Assert.Equal(expectedResult, actualResult)); + } + + [Fact] + public void GetPackageSpec_WhenPackOptionsFilesValueIsNull_ReturnsNullInclude() + { + const string json = "{\"packOptions\":{\"files\":null}}"; + + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Null(packageSpec.PackOptions.IncludeExcludeFiles); + } + + [Fact] + public void GetPackageSpec_WhenPackOptionsFilesValueIsEmptyObject_ReturnsNullInclude() + { + const string json = "{\"packOptions\":{\"files\":{}}}"; + + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Null(packageSpec.PackOptions.IncludeExcludeFiles); + } + + [Fact] + public void GetPackageSpec_WhenPackOptionsFilesIncludeValueIsNull_ReturnsNullIncludeExcludeFiles() + { + const string json = "{\"packOptions\":{\"files\":{\"include\":null}}}"; + + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Null(packageSpec.PackOptions.IncludeExcludeFiles); + } + + [Fact] + public void GetPackageSpec_WhenPackOptionsFilesIncludeValueIsEmptyArray_ReturnsEmptyInclude() + { + const string json = "{\"packOptions\":{\"files\":{\"include\":[]}}}"; + + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Empty(packageSpec.PackOptions.IncludeExcludeFiles.Include); + } + + [Theory] + [InlineData("\"a\"", "a")] + [InlineData("\"a, b\"", "a, b")] + [InlineData("[null]", null)] + [InlineData("[\"\"]", "")] + [InlineData("[\"a\"]", "a")] + [InlineData("[\"a, b\"]", "a, b")] + [InlineData("[\"a\", \"b\"]", "a", "b")] + public void GetPackageSpec_WhenPackOptionsFilesIncludeValueIsValid_ReturnsInclude(string value, params string[] expectedResults) + { + expectedResults = expectedResults ?? new string[] { null }; + + var json = $"{{\"packOptions\":{{\"files\":{{\"include\":{value}}}}}}}"; + + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedResults, packageSpec.PackOptions.IncludeExcludeFiles.Include); + } + + [Fact] + public void GetPackageSpec_WhenPackOptionsFilesIncludeFilesValueIsNull_ReturnsNullIncludeExcludeFiles() + { + const string json = "{\"packOptions\":{\"files\":{\"includeFiles\":null}}}"; + + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Null(packageSpec.PackOptions.IncludeExcludeFiles); + } + + [Fact] + public void GetPackageSpec_WhenPackOptionsFilesIncludeFilesValueIsEmptyArray_ReturnsEmptyIncludeFiles() + { + const string json = "{\"packOptions\":{\"files\":{\"includeFiles\":[]}}}"; + + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Empty(packageSpec.PackOptions.IncludeExcludeFiles.IncludeFiles); + } + + [Theory] + [InlineData("\"a\"", "a")] + [InlineData("\"a, b\"", "a, b")] + [InlineData("[null]", null)] + [InlineData("[\"\"]", "")] + [InlineData("[\"a\"]", "a")] + [InlineData("[\"a, b\"]", "a, b")] + [InlineData("[\"a\", \"b\"]", "a", "b")] + public void GetPackageSpec_WhenPackOptionsFilesIncludeFilesValueIsValid_ReturnsIncludeFiles(string value, params string[] expectedResults) + { + expectedResults = expectedResults ?? new string[] { null }; + + var json = $"{{\"packOptions\":{{\"files\":{{\"includeFiles\":{value}}}}}}}"; + + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedResults, packageSpec.PackOptions.IncludeExcludeFiles.IncludeFiles); + } + + [Fact] + public void GetPackageSpec_WhenPackOptionsFilesExcludeValueIsNull_ReturnsNullIncludeExcludeFiles() + { + const string json = "{\"packOptions\":{\"files\":{\"exclude\":null}}}"; + + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Null(packageSpec.PackOptions.IncludeExcludeFiles); + } + + [Fact] + public void GetPackageSpec_WhenPackOptionsFilesExcludeValueIsEmptyArray_ReturnsEmptyExclude() + { + const string json = "{\"packOptions\":{\"files\":{\"exclude\":[]}}}"; + + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Empty(packageSpec.PackOptions.IncludeExcludeFiles.Exclude); + } + + [Theory] + [InlineData("\"a\"", "a")] + [InlineData("\"a, b\"", "a, b")] + [InlineData("[null]", null)] + [InlineData("[\"\"]", "")] + [InlineData("[\"a\"]", "a")] + [InlineData("[\"a, b\"]", "a, b")] + [InlineData("[\"a\", \"b\"]", "a", "b")] + public void GetPackageSpec_WhenPackOptionsFilesExcludeValueIsValid_ReturnsExclude(string value, params string[] expectedResults) + { + expectedResults = expectedResults ?? new string[] { null }; + + var json = $"{{\"packOptions\":{{\"files\":{{\"exclude\":{value}}}}}}}"; + + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedResults, packageSpec.PackOptions.IncludeExcludeFiles.Exclude); + } + + [Fact] + public void GetPackageSpec_WhenPackOptionsFilesExcludeFilesValueIsNull_ReturnsNullIncludeExcludeFiles() + { + const string json = "{\"packOptions\":{\"files\":{\"excludeFiles\":null}}}"; + + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Null(packageSpec.PackOptions.IncludeExcludeFiles); + } + + [Fact] + public void GetPackageSpec_WhenPackOptionsFilesExcludeFilesValueIsEmptyArray_ReturnsEmptyExcludeFiles() + { + const string json = "{\"packOptions\":{\"files\":{\"excludeFiles\":[]}}}"; + + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Empty(packageSpec.PackOptions.IncludeExcludeFiles.ExcludeFiles); + } + + [Theory] + [InlineData("\"a\"", "a")] + [InlineData("\"a, b\"", "a, b")] + [InlineData("[null]", null)] + [InlineData("[\"\"]", "")] + [InlineData("[\"a\"]", "a")] + [InlineData("[\"a, b\"]", "a, b")] + [InlineData("[\"a\", \"b\"]", "a", "b")] + public void GetPackageSpec_WhenPackOptionsFilesExcludeFilesValueIsValid_ReturnsExcludeFiles(string value, params string[] expectedResults) + { + expectedResults = expectedResults ?? new string[] { null }; + + var json = $"{{\"packOptions\":{{\"files\":{{\"excludeFiles\":{value}}}}}}}"; + + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedResults, packageSpec.PackOptions.IncludeExcludeFiles.ExcludeFiles); + } + + [Fact] + public void GetPackageSpec_WhenPackOptionsFilesMappingsPropertyIsAbsent_ReturnsNullMappings() + { + const string json = "{\"packOptions\":{\"files\":{}}}"; + + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Null(packageSpec.PackOptions.Mappings); + } + + [Fact] + public void GetPackageSpec_WhenPackOptionsFilesMappingsValueIsNull_ReturnsNullMappings() + { + const string json = "{\"packOptions\":{\"files\":{\"mappings\":null}}}"; + + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Null(packageSpec.PackOptions.Mappings); + } + + [Theory] + [InlineData("\"b\"", "b")] + [InlineData("\"b,c\"", "b,c")] + [InlineData("[\"b\", \"c\"]", "b", "c")] + public void GetPackageSpec_WhenPackOptionsFilesMappingsValueIsValid_ReturnsMappings(string value, params string[] expectedIncludes) + { + var expectedResults = new Dictionary() + { + { "a", new IncludeExcludeFiles() { Include = expectedIncludes } } + }; + var json = $"{{\"packOptions\":{{\"files\":{{\"mappings\":{{\"a\":{value}}}}}}}}}"; + + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedResults, packageSpec.PackOptions.Mappings); + } + + [Fact] + public void GetPackageSpec_WhenPackOptionsFilesMappingsValueHasMultipleMappings_ReturnsMappings() + { + var expectedResults = new Dictionary() + { + { "a", new IncludeExcludeFiles() { Include = new[] { "b" } } }, + { "c", new IncludeExcludeFiles() { Include = new[] { "d", "e" } } } + }; + const string json = "{\"packOptions\":{\"files\":{\"mappings\":{\"a\":\"b\",\"c\":[\"d\", \"e\"]}}}}"; + + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedResults, packageSpec.PackOptions.Mappings); + } + + [Fact] + public void GetPackageSpec_WhenPackOptionsFilesMappingsValueHasFiles_ReturnsMappings() + { + var expectedResults = new Dictionary() + { + { + "a", + new IncludeExcludeFiles() + { + Include = new [] { "b" }, + IncludeFiles = new [] { "c" }, + Exclude = new [] { "d" }, + ExcludeFiles = new [] { "e" } + } + } + }; + const string json = "{\"packOptions\":{\"files\":{\"mappings\":{\"a\":{\"include\":\"b\",\"includeFiles\":\"c\",\"exclude\":\"d\",\"excludeFiles\":\"e\"}}}}}"; + + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedResults, packageSpec.PackOptions.Mappings); + } +#pragma warning restore CS0612 // Type or member is obsolete + + [Fact] + public void GetPackageSpec_WhenRestorePropertyIsAbsent_ReturnsNullRestoreMetadata() + { + const string json = "{}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Null(packageSpec.RestoreMetadata); + } + + [Fact] + public void GetPackageSpec_WhenRestoreValueIsEmptyObject_ReturnsRestoreMetadata() + { + const string json = "{\"restore\":{}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.NotNull(packageSpec.RestoreMetadata); + } + + [Theory] + [InlineData("null")] + [InlineData("\"\"")] + [InlineData("\"a\"")] + public void GetPackageSpec_WhenRestoreProjectStyleValueIsInvalid_ReturnsProjectStyle(string value) + { + var json = $"{{\"restore\":{{\"projectStyle\":{value}}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(ProjectStyle.Unknown, packageSpec.RestoreMetadata.ProjectStyle); + } + + [Fact] + public void GetPackageSpec_WhenRestoreProjectStyleValueIsValid_ReturnsProjectStyle() + { + const ProjectStyle expectedResult = ProjectStyle.PackageReference; + + var json = $"{{\"restore\":{{\"projectStyle\":\"{expectedResult.ToString().ToLowerInvariant()}\"}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedResult, packageSpec.RestoreMetadata.ProjectStyle); + } + + [Theory] + [InlineData("null", null)] + [InlineData("\"\"", "")] + [InlineData("\"a\"", "a")] + public void GetPackageSpec_WhenRestoreProjectUniqueNameValueIsValid_ReturnsProjectUniqueName( + string value, + string expectedValue) + { + var json = $"{{\"restore\":{{\"projectUniqueName\":{value}}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedValue, packageSpec.RestoreMetadata.ProjectUniqueName); + } + + [Theory] + [InlineData("null", null)] + [InlineData("\"\"", "")] + [InlineData("\"a\"", "a")] + public void GetPackageSpec_WhenRestoreOutputPathValueIsValid_ReturnsOutputPath( + string value, + string expectedValue) + { + var json = $"{{\"restore\":{{\"outputPath\":{value}}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedValue, packageSpec.RestoreMetadata.OutputPath); + } + + [Theory] + [InlineData("null", null)] + [InlineData("\"\"", "")] + [InlineData("\"a\"", "a")] + public void GetPackageSpec_WhenRestorePackagesPathValueIsValid_ReturnsPackagesPath( + string value, + string expectedValue) + { + var json = $"{{\"restore\":{{\"packagesPath\":{value}}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedValue, packageSpec.RestoreMetadata.PackagesPath); + } + + [Theory] + [InlineData("null", null)] + [InlineData("\"\"", "")] + [InlineData("\"a\"", "a")] + public void GetPackageSpec_WhenRestoreProjectJsonPathValueIsValid_ReturnsProjectJsonPath( + string value, + string expectedValue) + { + var json = $"{{\"restore\":{{\"projectJsonPath\":{value}}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedValue, packageSpec.RestoreMetadata.ProjectJsonPath); + } + + [Theory] + [InlineData("null", null)] + [InlineData("\"\"", "")] + [InlineData("\"a\"", "a")] + public void GetPackageSpec_WhenRestoreProjectNameValueIsValid_ReturnsProjectName( + string value, + string expectedValue) + { + var json = $"{{\"restore\":{{\"projectName\":{value}}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedValue, packageSpec.RestoreMetadata.ProjectName); + } + + [Theory] + [InlineData("null", null)] + [InlineData("\"\"", "")] + [InlineData("\"a\"", "a")] + public void GetPackageSpec_WhenRestoreProjectPathValueIsValid_ReturnsProjectPath( + string value, + string expectedValue) + { + var json = $"{{\"restore\":{{\"projectPath\":{value}}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedValue, packageSpec.RestoreMetadata.ProjectPath); + } + + [Theory] + [InlineData(null, false)] + [InlineData(true, true)] + [InlineData(false, false)] + public void GetPackageSpec_WhenCrossTargetingValueIsValid_ReturnsCrossTargeting( + bool? value, + bool expectedValue) + { + var json = $"{{\"restore\":{{\"crossTargeting\":{(value.HasValue ? value.ToString().ToLowerInvariant() : "null")}}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedValue, packageSpec.RestoreMetadata.CrossTargeting); + } + + [Theory] + [InlineData(null, false)] + [InlineData(true, true)] + [InlineData(false, false)] + public void GetPackageSpec_WhenLegacyPackagesDirectoryValueIsValid_ReturnsLegacyPackagesDirectory( + bool? value, + bool expectedValue) + { + var json = $"{{\"restore\":{{\"legacyPackagesDirectory\":{(value.HasValue ? value.ToString().ToLowerInvariant() : "null")}}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedValue, packageSpec.RestoreMetadata.LegacyPackagesDirectory); + } + + [Theory] + [InlineData(null, false)] + [InlineData(true, true)] + [InlineData(false, false)] + public void GetPackageSpec_WhenValidateRuntimeAssetsValueIsValid_ReturnsValidateRuntimeAssets( + bool? value, + bool expectedValue) + { + var json = $"{{\"restore\":{{\"validateRuntimeAssets\":{(value.HasValue ? value.ToString().ToLowerInvariant() : "null")}}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedValue, packageSpec.RestoreMetadata.ValidateRuntimeAssets); + } + + [Theory] + [InlineData(null, false)] + [InlineData(true, true)] + [InlineData(false, false)] + public void GetPackageSpec_WhenSkipContentFileWriteValueIsValid_ReturnsSkipContentFileWrite( + bool? value, + bool expectedValue) + { + var json = $"{{\"restore\":{{\"skipContentFileWrite\":{(value.HasValue ? value.ToString().ToLowerInvariant() : "null")}}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedValue, packageSpec.RestoreMetadata.SkipContentFileWrite); + } + + [Theory] + [InlineData(null, false)] + [InlineData(true, true)] + [InlineData(false, false)] + public void GetPackageSpec_WhenCentralPackageVersionsManagementEnabledValueIsValid_ReturnsCentralPackageVersionsManagementEnabled( + bool? value, + bool expectedValue) + { + var json = $"{{\"restore\":{{\"centralPackageVersionsManagementEnabled\":{(value.HasValue ? value.ToString().ToLowerInvariant() : "null")}}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedValue, packageSpec.RestoreMetadata.CentralPackageVersionsEnabled); + } + + [Theory] + [InlineData(null, false)] + [InlineData(true, true)] + [InlineData(false, false)] + public void GetPackageSpec_WhenCentralPackageVersionOverrideDisabledValueIsValid_ReturnsCentralPackageVersionOverrideDisabled( + bool? value, + bool expectedValue) + { + var json = $"{{\"restore\":{{\"centralPackageVersionOverrideDisabled\":{(value.HasValue ? value.ToString().ToLowerInvariant() : "null")}}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedValue, packageSpec.RestoreMetadata.CentralPackageVersionOverrideDisabled); + } + + [Theory] + [InlineData(null, false)] + [InlineData(true, true)] + [InlineData(false, false)] + public void GetPackageSpec_WhenCentralPackageTransitivePinningEnabledValueIsValid_ReturnsCentralPackageTransitivePinningEnabled( + bool? value, + bool expectedValue) + { + var json = $"{{\"restore\":{{\"CentralPackageTransitivePinningEnabled\":{(value.HasValue ? value.ToString().ToLowerInvariant() : "null")}}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedValue, packageSpec.RestoreMetadata.CentralPackageTransitivePinningEnabled); + } + + [Fact] + public void GetPackageSpec_WhenSourcesValueIsEmptyObject_ReturnsEmptySources() + { + const string json = "{\"restore\":{\"sources\":{}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Empty(packageSpec.RestoreMetadata.Sources); + } + + [Fact] + public void GetPackageSpec_WhenSourcesValueIsValid_ReturnsSources() + { + PackageSource[] expectedResults = { new PackageSource(source: "a"), new PackageSource(source: "b") }; + string values = string.Join(",", expectedResults.Select(expectedResult => $"\"{expectedResult.Name}\":{{}}")); + var json = $"{{\"restore\":{{\"sources\":{{{values}}}}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedResults, packageSpec.RestoreMetadata.Sources); + } + + [Fact] + public void GetPackageSpec_WhenFilesValueIsEmptyObject_ReturnsEmptyFiles() + { + const string json = "{\"restore\":{\"files\":{}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Empty(packageSpec.RestoreMetadata.Files); + } + + [Fact] + public void GetPackageSpec_WhenFilesValueIsValid_ReturnsFiles() + { + ProjectRestoreMetadataFile[] expectedResults = + { + new ProjectRestoreMetadataFile(packagePath: "a", absolutePath: "b"), + new ProjectRestoreMetadataFile(packagePath: "c", absolutePath:"d") + }; + string values = string.Join(",", expectedResults.Select(expectedResult => $"\"{expectedResult.PackagePath}\":\"{expectedResult.AbsolutePath}\"")); + var json = $"{{\"restore\":{{\"files\":{{{values}}}}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedResults, packageSpec.RestoreMetadata.Files); + } + + [Fact] + public void GetPackageSpec_WhenRestoreFrameworksValueIsEmptyObject_ReturnsEmptyFrameworks() + { + const string json = "{\"restore\":{\"frameworks\":{}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Empty(packageSpec.RestoreMetadata.TargetFrameworks); + } + + [Fact] + public void GetPackageSpec_WhenRestoreFrameworksFrameworkNameValueIsValid_ReturnsFrameworks() + { + var expectedResult = new ProjectRestoreMetadataFrameworkInfo(NuGetFramework.ParseFolder("net472")); + var json = $"{{\"restore\":{{\"frameworks\":{{\"{expectedResult.FrameworkName.GetShortFolderName()}\":{{}}}}}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Collection( + packageSpec.RestoreMetadata.TargetFrameworks, + actualResult => Assert.Equal(expectedResult, actualResult)); + } + + [Fact] + public void GetPackageSpec_WhenRestoreFrameworksFrameworkValueHasProjectReferenceWithoutAssets_ReturnsFrameworks() + { + var projectReference = new ProjectRestoreReference() + { + ProjectUniqueName = "a", + ProjectPath = "b" + }; + var expectedResult = new ProjectRestoreMetadataFrameworkInfo(NuGetFramework.ParseFolder("net472")); + + expectedResult.ProjectReferences.Add(projectReference); + + var json = $"{{\"restore\":{{\"frameworks\":{{\"{expectedResult.FrameworkName.GetShortFolderName()}\":{{\"projectReferences\":{{" + + $"\"{projectReference.ProjectUniqueName}\":{{\"projectPath\":\"{projectReference.ProjectPath}\"}}}}}}}}}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Collection( + packageSpec.RestoreMetadata.TargetFrameworks, + actualResult => Assert.Equal(expectedResult, actualResult)); + } + + [Fact] + public void GetPackageSpec_WhenRestoreFrameworksFrameworkValueHasProjectReferenceWithAssets_ReturnsFrameworks() + { + var projectReference = new ProjectRestoreReference() + { + ProjectUniqueName = "a", + ProjectPath = "b", + IncludeAssets = LibraryIncludeFlags.Analyzers, + ExcludeAssets = LibraryIncludeFlags.Native, + PrivateAssets = LibraryIncludeFlags.Runtime + }; + var expectedResult = new ProjectRestoreMetadataFrameworkInfo(NuGetFramework.ParseFolder("net472")); + + expectedResult.ProjectReferences.Add(projectReference); + + var json = $"{{\"restore\":{{\"frameworks\":{{\"{expectedResult.FrameworkName.GetShortFolderName()}\":{{\"projectReferences\":{{" + + $"\"{projectReference.ProjectUniqueName}\":{{\"projectPath\":\"{projectReference.ProjectPath}\"," + + $"\"includeAssets\":\"{projectReference.IncludeAssets}\",\"excludeAssets\":\"{projectReference.ExcludeAssets}\"," + + $"\"privateAssets\":\"{projectReference.PrivateAssets}\"}}}}}}}}}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Collection( + packageSpec.RestoreMetadata.TargetFrameworks, + actualResult => Assert.Equal(expectedResult, actualResult)); + } + + [Fact] + public void GetPackageSpec_WhenRestoreConfigFilePathsValueIsEmptyArray_ReturnsEmptyConfigFilePaths() + { + const string json = "{\"restore\":{\"configFilePaths\":[]}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Empty(packageSpec.RestoreMetadata.ConfigFilePaths); + } + + [Fact] + public void GetPackageSpec_WhenRestoreConfigFilePathsValueIsValid_ReturnsConfigFilePaths() + { + string[] expectedResults = { "a", "b" }; + string values = string.Join(",", expectedResults.Select(expectedResult => $"\"{expectedResult}\"")); + var json = $"{{\"restore\":{{\"configFilePaths\":[{values}]}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedResults, packageSpec.RestoreMetadata.ConfigFilePaths); + } + + [Fact] + public void GetPackageSpec_WhenRestoreFallbackFoldersValueIsEmptyArray_ReturnsEmptyFallbackFolders() + { + const string json = "{\"restore\":{\"fallbackFolders\":[]}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Empty(packageSpec.RestoreMetadata.FallbackFolders); + } + + [Fact] + public void GetPackageSpec_WhenRestoreFallbackFoldersValueIsValid_ReturnsConfigFilePaths() + { + string[] expectedResults = { "a", "b" }; + string values = string.Join(",", expectedResults.Select(expectedResult => $"\"{expectedResult}\"")); + var json = $"{{\"restore\":{{\"fallbackFolders\":[{values}]}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedResults, packageSpec.RestoreMetadata.FallbackFolders); + } + + [Fact] + public void GetPackageSpec_WhenRestoreOriginalTargetFrameworksValueIsEmptyArray_ReturnsEmptyOriginalTargetFrameworks() + { + const string json = "{\"restore\":{\"originalTargetFrameworks\":[]}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Empty(packageSpec.RestoreMetadata.OriginalTargetFrameworks); + } + + [Fact] + public void GetPackageSpec_WhenRestoreOriginalTargetFrameworksValueIsValid_ReturnsOriginalTargetFrameworks() + { + string[] expectedResults = { "a", "b" }; + string values = string.Join(",", expectedResults.Select(expectedResult => $"\"{expectedResult}\"")); + var json = $"{{\"restore\":{{\"originalTargetFrameworks\":[{values}]}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedResults, packageSpec.RestoreMetadata.OriginalTargetFrameworks); + } + + [Fact] + public void GetPackageSpec_WhenRestoreWarningPropertiesValueIsEmptyObject_ReturnsWarningProperties() + { + var expectedResult = new WarningProperties(); + const string json = "{\"restore\":{\"warningProperties\":{}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedResult, packageSpec.RestoreMetadata.ProjectWideWarningProperties); + } + + [Fact] + public void GetPackageSpec_WhenRestoreWarningPropertiesValueIsValid_ReturnsWarningProperties() + { + var expectedResult = new WarningProperties( + new HashSet() { NuGetLogCode.NU3000 }, + new HashSet() { NuGetLogCode.NU3001 }, + allWarningsAsErrors: true, + new HashSet()); + var json = $"{{\"restore\":{{\"warningProperties\":{{\"allWarningsAsErrors\":{expectedResult.AllWarningsAsErrors.ToString().ToLowerInvariant()}," + + $"\"warnAsError\":[\"{expectedResult.WarningsAsErrors.Single()}\"],\"noWarn\":[\"{expectedResult.NoWarn.Single()}\"]}}}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedResult, packageSpec.RestoreMetadata.ProjectWideWarningProperties); + } + + [Fact] + public void GetPackageSpec_WhenRestoreRestoreLockPropertiesValueIsEmptyObject_ReturnsRestoreLockProperties() + { + var expectedResult = new RestoreLockProperties(); + const string json = "{\"restore\":{\"restoreLockProperties\":{}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedResult, packageSpec.RestoreMetadata.RestoreLockProperties); + } + + [Fact] + public void GetPackageSpec_WhenRestoreRestoreLockPropertiesValueIsValid_ReturnsRestoreLockProperties() + { + var expectedResult = new RestoreLockProperties( + restorePackagesWithLockFile: "a", + nuGetLockFilePath: "b", + restoreLockedMode: true); ; + var json = $"{{\"restore\":{{\"restoreLockProperties\":{{\"restoreLockedMode\":{expectedResult.RestoreLockedMode.ToString().ToLowerInvariant()}," + + $"\"restorePackagesWithLockFile\":\"{expectedResult.RestorePackagesWithLockFile}\"," + + $"\"nuGetLockFilePath\":\"{expectedResult.NuGetLockFilePath}\"}}}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedResult, packageSpec.RestoreMetadata.RestoreLockProperties); + } + + [Theory] + [InlineData("null")] + [InlineData("\"\"")] + [InlineData("\"a\"")] + public void GetPackageSpec_WhenRestorePackagesConfigPathValueIsValidAndProjectStyleValueIsNotPackagesConfig_DoesNotReturnPackagesConfigPath( + string value) + { + var json = $"{{\"restore\":{{\"projectStyle\":\"PackageReference\",\"packagesConfigPath\":{value}}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.IsNotType(packageSpec.RestoreMetadata); + } + + [Theory] + [InlineData("null", null)] + [InlineData("\"\"", "")] + [InlineData("\"a\"", "a")] + public void GetPackageSpec_WhenRestorePackagesConfigPathValueIsValidAndProjectStyleValueIsPackagesConfig_ReturnsPackagesConfigPath( + string value, + string expectedValue) + { + var json = $"{{\"restore\":{{\"projectStyle\":\"PackagesConfig\",\"packagesConfigPath\":{value}}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.IsType(packageSpec.RestoreMetadata); + Assert.Equal(expectedValue, ((PackagesConfigProjectRestoreMetadata)packageSpec.RestoreMetadata).PackagesConfigPath); + } + + [Fact] + public void GetPackageSpec_WhenRestoreSettingsValueIsEmptyObject_ReturnsRestoreSettings() + { + var expectedResult = new ProjectRestoreSettings(); + const string json = "{\"restoreSettings\":{}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedResult, packageSpec.RestoreSettings); + } + + [Fact] + public void GetPackageSpec_WhenRuntimesValueIsEmptyObject_ReturnsRuntimes() + { + var expectedResult = RuntimeGraph.Empty; + const string json = "{\"runtimes\":{}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedResult, packageSpec.RuntimeGraph); + } + + [Fact] + public void GetPackageSpec_WhenRuntimesValueIsValidWithImports_ReturnsRuntimes() + { + var runtimeDescription = new RuntimeDescription( + runtimeIdentifier: "a", + inheritedRuntimes: new[] { "b", "c" }, + Enumerable.Empty()); + var expectedResult = new RuntimeGraph(new[] { runtimeDescription }); + var json = $"{{\"runtimes\":{{\"{runtimeDescription.RuntimeIdentifier}\":{{\"#import\":[" + + $"{string.Join(",", runtimeDescription.InheritedRuntimes.Select(runtime => $"\"{runtime}\""))}]}}}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedResult, packageSpec.RuntimeGraph); + } + + [Fact] + public void GetPackageSpec_WhenRuntimesValueIsValidWithDependencySet_ReturnsRuntimes() + { + var dependencySet = new RuntimeDependencySet(id: "b"); + var runtimeDescription = new RuntimeDescription( + runtimeIdentifier: "a", + inheritedRuntimes: Enumerable.Empty(), + runtimeDependencySets: new[] { dependencySet }); + var expectedResult = new RuntimeGraph(new[] { runtimeDescription }); + var json = $"{{\"runtimes\":{{\"{runtimeDescription.RuntimeIdentifier}\":{{\"{dependencySet.Id}\":{{}}}}}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedResult, packageSpec.RuntimeGraph); + } + + [Fact] + public void GetPackageSpec_WhenRuntimesValueIsValidWithDependencySetWithDependency_ReturnsRuntimes() + { + var dependency = new RuntimePackageDependency("c", VersionRange.Parse("[1.2.3,4.5.6)")); + var dependencySet = new RuntimeDependencySet(id: "b", new[] { dependency }); + var runtimeDescription = new RuntimeDescription( + runtimeIdentifier: "a", + inheritedRuntimes: Enumerable.Empty(), + runtimeDependencySets: new[] { dependencySet }); + var expectedResult = new RuntimeGraph(new[] { runtimeDescription }); + var json = $"{{\"runtimes\":{{\"{runtimeDescription.RuntimeIdentifier}\":{{\"{dependencySet.Id}\":{{" + + $"\"{dependency.Id}\":\"{dependency.VersionRange.ToLegacyString()}\"}}}}}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedResult, packageSpec.RuntimeGraph); + } + + [Fact] + public void GetPackageSpec_WhenSupportsValueIsEmptyObject_ReturnsSupports() + { + var expectedResult = RuntimeGraph.Empty; + const string json = "{\"supports\":{}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedResult, packageSpec.RuntimeGraph); + } + + [Fact] + public void GetPackageSpec_WhenSupportsValueIsValidWithCompatibilityProfiles_ReturnsSupports() + { + var profile = new CompatibilityProfile(name: "a"); + var expectedResult = new RuntimeGraph(new[] { profile }); + var json = $"{{\"supports\":{{\"{profile.Name}\":{{}}}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedResult, packageSpec.RuntimeGraph); + } + + [Fact] + public void GetPackageSpec_WhenSupportsValueIsValidWithCompatibilityProfilesAndFrameworkRuntimePairs_ReturnsSupports() + { + FrameworkRuntimePair[] restoreContexts = new[] + { + new FrameworkRuntimePair(NuGetFramework.Parse("net472"), "b"), + new FrameworkRuntimePair(NuGetFramework.Parse("net48"), "c") + }; + var profile = new CompatibilityProfile(name: "a", restoreContexts); + var expectedResult = new RuntimeGraph(new[] { profile }); + var json = $"{{\"supports\":{{\"{profile.Name}\":{{" + + $"\"{restoreContexts[0].Framework.GetShortFolderName()}\":\"{restoreContexts[0].RuntimeIdentifier}\"," + + $"\"{restoreContexts[1].Framework.GetShortFolderName()}\":[\"{restoreContexts[1].RuntimeIdentifier}\"]}}}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedResult, packageSpec.RuntimeGraph); + } + +#pragma warning disable CS0612 // Type or member is obsolete + [Fact] + public void GetPackageSpec_WhenScriptsValueIsEmptyObject_ReturnsScripts() + { + const string json = "{\"scripts\":{}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Empty(packageSpec.Scripts); + } + + [Fact] + public void GetPackageSpec_WhenScriptsValueIsInvalid_Throws() + { + var json = "{\"scripts\":{\"a\":0}}"; + + FileFormatException exception = Assert.Throws(() => GetPackageSpec(json)); + + Assert.Equal("The value of a script in 'project.json' can only be a string or an array of strings", exception.Message); + Assert.IsType(exception.InnerException); + Assert.Null(exception.InnerException.InnerException); + + } + + [Fact] + public void GetPackageSpec_WhenScriptsValueIsValid_ReturnsScripts() + { + const string name0 = "a"; + const string name1 = "b"; + const string script0 = "c"; + const string script1 = "d"; + const string script2 = "e"; + + var json = $"{{\"scripts\":{{\"{name0}\":\"{script0}\",\"{name1}\":[\"{script1}\",\"{script2}\"]}}}}"; + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Collection( + packageSpec.Scripts, + actualResult => + { + Assert.Equal(name0, actualResult.Key); + Assert.Collection( + actualResult.Value, + actualScript => Assert.Equal(script0, actualScript)); + }, + actualResult => + { + Assert.Equal(name1, actualResult.Key); + Assert.Collection( + actualResult.Value, + actualScript => Assert.Equal(script1, actualScript), + actualScript => Assert.Equal(script2, actualScript)); + }); + } +#pragma warning restore CS0612 // Type or member is obsolete + + [Theory] + [InlineData("null", null)] + [InlineData("\"\"", "")] + [InlineData("\"a\"", "a")] + public void GetPackageSpec_WhenTitleValueIsValid_ReturnsTitle(string value, string expectedResult) + { + var json = $"{{\"title\":{value}}}"; + + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedResult, packageSpec.Title); + } + + [Fact] + public void GetPackageSpec_WhenNameIsNull_RestoreMetadataProvidesFallbackName() + { + const string expectedResult = "a"; + var json = $"{{\"restore\":{{\"projectName\":\"{expectedResult}\"}}}}"; + + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedResult, packageSpec.Name); + } + + [Theory] + [InlineData("{\"restore\":{\"projectJsonPath\":\"a\"}}")] + [InlineData("{\"restore\":{\"projectPath\":\"a\"}}")] + [InlineData("{\"restore\":{\"projectJsonPath\":\"a\",\"projectPath\":\"b\"}}")] + public void GetPackageSpec_WhenFilePathIsNull_RestoreMetadataProvidesFallbackFilePath(string json) + { + const string expectedResult = "a"; + + PackageSpec packageSpec = GetPackageSpec(json); + + Assert.Equal(expectedResult, packageSpec.FilePath); + } + + [Fact] + public void GetTargetFrameworkInformation_WithAnAlias() + { + TargetFrameworkInformation framework = GetFramework("{\"frameworks\":{\"net46\":{ \"targetAlias\" : \"alias\"}}}"); + + Assert.Equal("alias", framework.TargetAlias); + } + + [Fact] + public void PackageSpecReader_ReadsRestoreMetadataWithAliases() + { + // Arrange + var json = @"{ + ""restore"": { + ""projectUniqueName"": ""projectUniqueName"", + ""projectName"": ""projectName"", + ""projectPath"": ""projectPath"", + ""projectJsonPath"": ""projectJsonPath"", + ""packagesPath"": ""packagesPath"", + ""outputPath"": ""outputPath"", + ""projectStyle"": ""PackageReference"", + ""crossTargeting"": true, + ""frameworks"": { + ""frameworkidentifier123-frameworkprofile"": { + ""targetAlias"" : ""alias"", + ""projectReferences"": {} + } + }, + ""warningProperties"": { + } + } +}"; + + var actual = Utf8JsonStreamPackageSpecReader.GetPackageSpec(json, "TestProject", "project.json"); + + // Assert + var metadata = actual.RestoreMetadata; + var warningProperties = actual.RestoreMetadata.ProjectWideWarningProperties; + + Assert.NotNull(metadata); + Assert.Equal("alias", metadata.TargetFrameworks.Single().TargetAlias); + } + + [Fact] + public void PackageSpecReader_Read() + { + // Arrange + var json = @"{ + ""centralTransitiveDependencyGroups"": { + "".NETCoreApp,Version=v3.1"": { + ""Foo"": { + ""exclude"": ""Native"", + ""include"": ""Build"", + ""suppressParent"": ""All"", + ""version"": ""1.0.0"" + } + }, + "".NETCoreApp,Version=v3.0"": { + ""Bar"": { + ""exclude"": ""Native"", + ""include"": ""Build"", + ""suppressParent"": ""All"", + ""version"": ""2.0.0"" + } + } + } + }"; + + // Act + var results = new List(); + using Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(json)); + var reader = new Utf8JsonStreamReader(stream); + + if (reader.TokenType == JsonTokenType.StartObject) + { + while (reader.Read() && reader.TokenType == JsonTokenType.PropertyName) + { + if (reader.Read() && reader.TokenType == JsonTokenType.StartObject) + { + while (reader.Read() && reader.TokenType == JsonTokenType.PropertyName) + { + var frameworkPropertyName = reader.GetString(); + NuGetFramework framework = NuGetFramework.Parse(frameworkPropertyName); + var dependencies = new List(); + + Utf8JsonStreamPackageSpecReader.ReadCentralTransitiveDependencyGroup( + jsonReader: ref reader, + results: dependencies, + packageSpecPath: "SomePath"); + results.Add(new CentralTransitiveDependencyGroup(framework, dependencies)); + } + } + } + } + + // Assert + Assert.Equal(2, results.Count); + Assert.Equal(".NETCoreApp,Version=v3.1", results.ElementAt(0).FrameworkName); + var firstGroup = results.ElementAt(0); + Assert.Equal(1, firstGroup.TransitiveDependencies.Count()); + Assert.Equal("Build", firstGroup.TransitiveDependencies.First().IncludeType.ToString()); + Assert.Equal("All", firstGroup.TransitiveDependencies.First().SuppressParent.ToString()); + Assert.Equal("[1.0.0, )", firstGroup.TransitiveDependencies.First().LibraryRange.VersionRange.ToNormalizedString()); + Assert.True(firstGroup.TransitiveDependencies.First().VersionCentrallyManaged); + + var secondGroup = results.ElementAt(1); + Assert.Equal(1, secondGroup.TransitiveDependencies.Count()); + Assert.Equal("Build", secondGroup.TransitiveDependencies.First().IncludeType.ToString()); + Assert.Equal("All", secondGroup.TransitiveDependencies.First().SuppressParent.ToString()); + Assert.Equal("[2.0.0, )", secondGroup.TransitiveDependencies.First().LibraryRange.VersionRange.ToNormalizedString()); + Assert.True(secondGroup.TransitiveDependencies.First().VersionCentrallyManaged); + } + + [Fact] + public void GetPackageSpec_WithSecondaryFrameworks_ReturnsTargetFrameworkInformationWithDualCompatibilityFramework() + { + var json = $"{{\"frameworks\":{{\"net5.0\":{{\"secondaryFramework\": \"native\"}}}}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + framework.FrameworkName.Should().BeOfType(); + var dualCompatibilityFramework = framework.FrameworkName as DualCompatibilityFramework; + dualCompatibilityFramework.RootFramework.Should().Be(FrameworkConstants.CommonFrameworks.Net50); + dualCompatibilityFramework.SecondaryFramework.Should().Be(FrameworkConstants.CommonFrameworks.Native); + } + + [Fact] + public void GetPackageSpec_WithAssetTargetFallbackAndWithSecondaryFrameworks_ReturnsTargetFrameworkInformationWithDualCompatibilityFramework() + { + var json = $"{{\"frameworks\":{{\"net5.0\":{{\"assetTargetFallback\": true, \"imports\": [\"net472\", \"net471\"], \"secondaryFramework\": \"native\" }}}}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + framework.FrameworkName.Should().BeOfType(); + framework.AssetTargetFallback.Should().BeTrue(); + var assetTargetFallbackFramework = framework.FrameworkName as AssetTargetFallbackFramework; + assetTargetFallbackFramework.RootFramework.Should().BeOfType(); + var dualCompatibilityFramework = assetTargetFallbackFramework.RootFramework as DualCompatibilityFramework; + dualCompatibilityFramework.RootFramework.Should().Be(FrameworkConstants.CommonFrameworks.Net50); + dualCompatibilityFramework.SecondaryFramework.Should().Be(FrameworkConstants.CommonFrameworks.Native); + assetTargetFallbackFramework.Fallback.Should().HaveCount(2); + assetTargetFallbackFramework.Fallback.First().Should().Be(FrameworkConstants.CommonFrameworks.Net472); + assetTargetFallbackFramework.Fallback.Last().Should().Be(FrameworkConstants.CommonFrameworks.Net471); + } + + [Fact] + public void GetPackageSpec_WithRestoreAuditProperties_ReturnsRestoreAuditProperties() + { + // Arrange + var json = $"{{\"restore\":{{\"restoreAuditProperties\":{{\"enableAudit\": \"a\", \"auditLevel\": \"b\", \"auditMode\": \"c\"}}}}}}"; + + // Act + PackageSpec packageSpec = GetPackageSpec(json); + + // Assert + packageSpec.RestoreMetadata.RestoreAuditProperties.EnableAudit.Should().Be("a"); + packageSpec.RestoreMetadata.RestoreAuditProperties.AuditLevel.Should().Be("b"); + packageSpec.RestoreMetadata.RestoreAuditProperties.AuditMode.Should().Be("c"); + } + + private static PackageSpec GetPackageSpec(string json) + { + using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(json))) + { + return Utf8JsonStreamPackageSpecReader.GetPackageSpec(stream, name: null, packageSpecPath: null, snapshotValue: null); + } + } + + private static LibraryDependency GetDependency(string json) + { + PackageSpec packageSpec = GetPackageSpec(json); + + return packageSpec.Dependencies.Single(); + } + + private static TargetFrameworkInformation GetFramework(string json) + { + PackageSpec packageSpec = GetPackageSpec(json); + + return packageSpec.TargetFrameworks.Single(); + } + + private static LibraryDependency GetFrameworksDependency(string json) + { + TargetFrameworkInformation framework = GetFramework(json); + + return framework.Dependencies.Single(); + } + + private static FrameworkDependency GetFrameworksFrameworkReference(string json) + { + TargetFrameworkInformation framework = GetFramework(json); + + return framework.FrameworkReferences.Single(); + } + } +} diff --git a/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/Utf8JsonStreamReaderTests.cs b/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/Utf8JsonStreamReaderTests.cs new file mode 100644 index 00000000000..8e0b755fc26 --- /dev/null +++ b/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/Utf8JsonStreamReaderTests.cs @@ -0,0 +1,704 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.Json; +using Xunit; + +namespace NuGet.ProjectModel.Test +{ + [UseCulture("")] // Fix tests failing on systems with non-English locales + public class Utf8JsonStreamReaderTests + { + private static readonly string JsonWithOverflowObject = "{\"object1\":{\"a\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"b\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"c\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"d\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"e\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"f\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"g\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"h\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"i\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"j\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"k\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"l\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"m\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"n\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"o\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"p\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"q\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"r\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"s\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"t\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"u\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\"},\"object2\":{\"a\":\"abcdefghijklmnopqrstuvwxyz\",\"b\":\"abcdefghijklmnopqrstuvwxyz\",\"c\":\"abcdefghijklmnopqrstuvwxyz\",\"d\":\"abcdefghijklmnopqrstuvwxyz\",\"e\":\"abcdefghijklmnopqrstuvwxyz\",\"f\":\"abcdefghijklmnopqrstuvwxyz\",\"g\":\"abcdefghijklmnopqrstuvwxyz\",\"h\":\"abcdefghijklmnopqrstuvwxyz\",\"i\":\"abcdefghijklmnopqrstuvwxyz\",\"j\":\"abcdefghijklmnopqrstuvwxyz\",\"k\":\"abcdefghijklmnopqrstuvwxyz\",\"l\":\"abcdefghijklmnopqrstuvwxyz\",\"m\":\"abcdefghijklmnopqrstuvwxyz\",\"n\":\"abcdefghijklmnopqrstuvwxyz\",\"o\":\"abcdefghijklmnopqrstuvwxyz\",\"p\":\"abcdefghijklmnopqrstuvwxyz\",\"q\":\"abcdefghijklmnopqrstuvwxyz\",\"r\":\"abcdefghijklmnopqrstuvwxyz\",\"s\":\"abcdefghijklmnopqrstuvwxyz\",\"t\":\"abcdefghijklmnopqrstuvwxyz\",\"u\":\"abcdefghijklmnopqrstuvwxyz\"}}"; + private static readonly string JsonWithoutOverflow = "{\"object1\":{\"a\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"b\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"c\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"d\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"e\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"f\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"g\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"h\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"i\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"j\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"k\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"l\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"m\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"n\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"o\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"p\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"q\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"r\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"s\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"t\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"u\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\"}}"; + private static readonly string JsonWithOverflow = "{\"object1\":{\"a\":\"abcdefghijklmnopqrstuvwxyz\",\"b\":\"abcdefghijklmnopqrstuvwxyz\",\"c\":\"abcdefghijklmnopqrstuvwxyz\",\"d\":\"abcdefghijklmnopqrstuvwxyz\",\"e\":\"abcdefghijklmnopqrstuvwxyz\",\"f\":\"abcdefghijklmnopqrstuvwxyz\",\"g\":\"abcdefghijklmnopqrstuvwxyz\",\"h\":\"abcdefghijklmnopqrstuvwxyz\",\"i\":\"abcdefghijklmnopqrstuvwxyz\",\"j\":\"abcdefghijklmnopqrstuvwxyz\",\"k\":\"abcdefghijklmnopqrstuvwxyz\",\"l\":\"abcdefghijklmnopqrstuvwxyz\",\"m\":\"abcdefghijklmnopqrstuvwxyz\",\"n\":\"abcdefghijklmnopqrstuvwxyz\",\"o\":\"abcdefghijklmnopqrstuvwxyz\",\"p\":\"abcdefghijklmnopqrstuvwxyz\",\"q\":\"abcdefghijklmnopqrstuvwxyz\",\"r\":\"abcdefghijklmnopqrstuvwxyz\",\"s\":\"abcdefghijklmnopqrstuvwxyz\",\"t\":\"abcdefghijklmnopqrstuvwxyz\",\"u\":\"abcdefghijklmnopqrstuvwxyz\"}, \"object2\": {\"a\":\"abcdefghijklmnopqrstuvwxyz\",\"b\":\"abcdefghijklmnopqrstuvwxyz\",\"c\":\"abcdefghijklmnopqrstuvwxyz\",\"d\":\"abcdefghijklmnopqrstuvwxyz\",\"e\":\"abcdefghijklmnopqrstuvwxyz\",\"f\":\"abcdefghijklmnopqrstuvwxyz\",\"g\":\"abcdefghijklmnopqrstuvwxyz\",\"h\":\"abcdefghijklmnopqrstuvwxyz\",\"i\":\"abcdefghijklmnopqrstuvwxyz\",\"j\":\"abcdefghijklmnopqrstuvwxyz\",\"k\":\"abcdefghijklmnopqrstuvwxyz\",\"l\":\"abcdefghijklmnopqrstuvwxyz\",\"m\":\"abcdefghijklmnopqrstuvwxyz\",\"n\":\"abcdefghijklmnopqrstuvwxyz\",\"o\":\"abcdefghijklmnopqrstuvwxyz\",\"p\":\"abcdefghijklmnopqrstuvwxyz\",\"q\":\"abcdefghijklmnopqrstuvwxyz\",\"r\":\"abcdefghijklmnopqrstuvwxyz\",\"s\":\"abcdefghijklmnopqrstuvwxyz\",\"t\":\"abcdefghijklmnopqrstuvwxyz\",\"u\":\"abcdefghijklmnopqrstuvwxyz\"}, \"object3\":{\"a\":\"abcdefghijklmnopqrstuvwxyz\",\"b\":\"abcdefghijklmnopqrstuvwxyz\",\"c\":\"abcdefghijklmnopqrstuvwxyz\",\"d\":\"abcdefghijklmnopqrstuvwxyz\",\"e\":\"abcdefghijklmnopqrstuvwxyz\",\"f\":\"abcdefghijklmnopqrstuvwxyz\",\"g\":\"abcdefghijklmnopqrstuvwxyz\",\"h\":\"abcdefghijklmnopqrstuvwxyz\",\"i\":\"abcdefghijklmnopqrstuvwxyz\",\"j\":\"abcdefghijklmnopqrstuvwxyz\",\"k\":\"abcdefghijklmnopqrstuvwxyz\",\"l\":\"abcdefghijklmnopqrstuvwxyz\",\"m\":\"abcdefghijklmnopqrstuvwxyz\",\"n\":\"abcdefghijklmnopqrstuvwxyz\",\"o\":\"abcdefghijklmnopqrstuvwxyz\",\"p\":\"abcdefghijklmnopqrstuvwxyz\",\"q\":\"abcdefghijklmnopqrstuvwxyz\",\"r\":\"abcdefghijklmnopqrstuvwxyz\",\"s\":\"abcdefghijklmnopqrstuvwxyz\",\"t\":\"abcdefghijklmnopqrstuvwxyz\",\"u\":\"abcdefghijklmnopqrstuvwxyz\"}}"; + + [Fact] + public void Utf8JsonStreamReaderCtr_WhenStreamIsNull_Throws() + { + Assert.Throws(() => new Utf8JsonStreamReader(null)); + } + + [Fact] + public void Utf8JsonStreamReaderCtr_WhenStreamStartsWithUtf8Bom_SkipThem() + { + var json = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble()) + "{}"; + + using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(json))) + { + var reader = new Utf8JsonStreamReader(stream); + Assert.Equal(5, stream.Position); + Assert.Equal(JsonTokenType.StartObject, reader.TokenType); + } + } + + [Fact] + public void Utf8JsonStreamReaderCtr_WhenStreamStartsWithoutUtf8Bom_ReadFromStart() + { + var json = "{}"; + + using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(json))) + { + var reader = new Utf8JsonStreamReader(stream); + Assert.Equal(2, stream.Position); + Assert.Equal(JsonTokenType.StartObject, reader.TokenType); + } + } + + [Fact] + public void Utf8JsonStreamReaderCtr_WhenReadingWithOverflow_BufferHasFirst1024Bytes() + { + var json = Encoding.UTF8.GetBytes(JsonWithOverflowObject); + var firstBytes = json.Take(1024).ToArray(); + + using (var stream = new MemoryStream(json)) + { + var reader = new Utf8JsonStreamReader(stream); + var bufferString = reader.GetCurrentBufferAsString(); + Assert.Equal(Encoding.UTF8.GetString(firstBytes), bufferString); + } + } + + [Fact] + public void Read_WhenReadingWithoutOverflow_Read() + { + var json = Encoding.UTF8.GetBytes(JsonWithoutOverflow); + + using (var stream = new MemoryStream(json)) + { + var reader = new Utf8JsonStreamReader(stream); + Assert.Equal(JsonTokenType.StartObject, reader.TokenType); + reader.Read(); + Assert.Equal(JsonTokenType.PropertyName, reader.TokenType); + } + } + + [Fact] + public void Read_WhenReadingWithOverflow_ReadNextBuffer() + { + var json = Encoding.UTF8.GetBytes(JsonWithOverflowObject); + + using (var stream = new MemoryStream(json)) + { + var reader = new Utf8JsonStreamReader(stream); + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.PropertyName && reader.GetString() == "r") + { + break; + } + } + reader.Read(); + + Assert.True(reader.IsFinalBlock); + Assert.Equal(1024, reader.BufferSize); + Assert.Equal(JsonTokenType.String, reader.TokenType); + Assert.Equal("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", reader.GetString()); + reader.Read(); + Assert.Equal(JsonTokenType.PropertyName, reader.TokenType); + Assert.Equal("s", reader.GetString()); + } + } + + [Fact] + public void Read_WhenReadingWithLargeToken_ResizeBuffer() + { + var json = Encoding.UTF8.GetBytes("{\"largeToken\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\",\"smallToken\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\"}"); + + using (var stream = new MemoryStream(json)) + { + var reader = new Utf8JsonStreamReader(stream); + + reader.Read(); + reader.Read(); + + Assert.Equal(JsonTokenType.String, reader.TokenType); + Assert.Equal("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", + reader.GetString()); + Assert.Equal(2048, reader.BufferSize); + } + } + + [Fact] + public void Read_WhenReadingWithOverflowToBufferSize_LoadNextBuffer() + { + var json = Encoding.UTF8.GetBytes("{\"largeToken\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrst\",\"smallToken\":\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\"}"); + + using (var stream = new MemoryStream(json)) + { + var reader = new Utf8JsonStreamReader(stream); + + reader.Read(); + reader.Read(); + reader.Read(); + + Assert.True(reader.IsFinalBlock); + Assert.Equal(JsonTokenType.PropertyName, reader.TokenType); + Assert.Equal("smallToken", reader.GetString()); + Assert.True(reader.Read()); + Assert.Equal(JsonTokenType.String, reader.TokenType); + Assert.Equal("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", reader.GetString()); + } + } + + [Fact] + public void TrySkip_WhenReadingWithoutOverflow_SkipObject() + { + var json = Encoding.UTF8.GetBytes(JsonWithoutOverflow); + + using (var stream = new MemoryStream(json)) + { + var reader = new Utf8JsonStreamReader(stream); + Assert.Equal(JsonTokenType.StartObject, reader.TokenType); + reader.TrySkip(); + Assert.Equal(JsonTokenType.EndObject, reader.TokenType); + } + } + + [Fact] + public void TrySkip_WhenReadingWithOverflow_Skip() + { + var json = Encoding.UTF8.GetBytes(JsonWithOverflow); + + using (var stream = new MemoryStream(json)) + { + var reader = new Utf8JsonStreamReader(stream); + reader.Read(); + reader.TrySkip(); + reader.Read(); + reader.TrySkip(); + Assert.Equal(JsonTokenType.EndObject, reader.TokenType); + reader.Read(); + Assert.Equal("object3", reader.GetString()); + Assert.Equal(1024, reader.BufferSize); + + } + } + + [Fact] + public void TrySkip_WhenReadingWithOverflowObject_ResizeBuffer() + { + var json = Encoding.UTF8.GetBytes(JsonWithOverflowObject); + + using (var stream = new MemoryStream(json)) + { + var reader = new Utf8JsonStreamReader(stream); + + reader.Read(); + reader.TrySkip(); + reader.Read(); + Assert.Equal(JsonTokenType.PropertyName, reader.TokenType); + Assert.Equal("object2", reader.GetString()); + Assert.Equal(2048, reader.BufferSize); + } + } + + [Fact] + public void ReadNextTokenAsString_WhenCalled_AdvanceToken() + { + var json = Encoding.UTF8.GetBytes("{\"token\":\"value\"}"); + + using (var stream = new MemoryStream(json)) + { + var reader = new Utf8JsonStreamReader(stream); + reader.Read(); + Assert.Equal(JsonTokenType.PropertyName, reader.TokenType); + reader.ReadNextTokenAsString(); + Assert.Equal(JsonTokenType.String, reader.TokenType); + } + } + + [Theory] + [InlineData("null")] + [InlineData("\"b\"")] + [InlineData("{}")] + public void ReadStringArrayAsIList_WhenValueIsNotArray_ReturnsNull(string value) + { + var json = $"{{\"a\":{value}}}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + using (var stream = new MemoryStream(encodedBytes)) + { + var reader = new Utf8JsonStreamReader(stream); + reader.Read(); + reader.Read(); + Assert.NotEqual(JsonTokenType.PropertyName, reader.TokenType); + IList actualValues = reader.ReadStringArrayAsIList(); + Assert.Null(actualValues); + } + } + + [Fact] + public void ReadStringArrayAsIList_WhenValueIsEmptyArray_ReturnsNull() + { + var encodedBytes = Encoding.UTF8.GetBytes("{\"a\":[]}"); + using (var stream = new MemoryStream(encodedBytes)) + { + var reader = new Utf8JsonStreamReader(stream); + reader.Read(); + reader.Read(); + Assert.NotEqual(JsonTokenType.PropertyName, reader.TokenType); + IList actualValues = reader.ReadStringArrayAsIList(); + Assert.Null(actualValues); + } + } + + [Fact] + public void ReadStringArrayAsIList_WithSupportedTypes_ReturnsStringArray() + { + var encodedBytes = Encoding.UTF8.GetBytes("[\"a\",-2,3.14,true,null]"); + using (var stream = new MemoryStream(encodedBytes)) + { + var reader = new Utf8JsonStreamReader(stream); + IList actualValues = reader.ReadStringArrayAsIList(); + + Assert.Collection( + actualValues, + actualValue => Assert.Equal("a", actualValue), + actualValue => Assert.Equal("-2", actualValue), + actualValue => Assert.Equal("3.14", actualValue), + actualValue => Assert.Equal("True", actualValue), + actualValue => Assert.Equal(null, actualValue)); + Assert.Equal(JsonTokenType.EndArray, reader.TokenType); + } + } + + [Theory] + [InlineData("[]")] + [InlineData("{}")] + public void ReadStringArrayAsIList_WithUnsupportedTypes_Throws(string element) + { + var encodedBytes = Encoding.UTF8.GetBytes($"[{element}]"); + using (var stream = new MemoryStream(encodedBytes)) + { + var reader = new Utf8JsonStreamReader(stream); + Exception exceptionThrown = null; + try + { + reader.ReadStringArrayAsIList(); + } + catch (Exception ex) + { + exceptionThrown = ex; + } + Assert.IsType(typeof(InvalidCastException), exceptionThrown); + } + } + + + [Theory] + [InlineData("true", JsonTokenType.True)] + [InlineData("false", JsonTokenType.False)] + [InlineData("-2", JsonTokenType.Number)] + [InlineData("3.14", JsonTokenType.Number)] + [InlineData("{}", JsonTokenType.StartObject)] + [InlineData("[]", JsonTokenType.StartArray)] + [InlineData("[true]", JsonTokenType.StartArray)] + [InlineData("[-2]", JsonTokenType.StartArray)] + [InlineData("[3.14]", JsonTokenType.StartArray)] + [InlineData("[\"a\", \"b\"]", JsonTokenType.StartArray)] + + public void ReadDelimitedString_WhenValueIsNotString_Throws(string value, JsonTokenType expectedTokenType) + { + var json = $"{{\"a\":{value}}}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + using (var stream = new MemoryStream(encodedBytes)) + { + var reader = new Utf8JsonStreamReader(stream); + reader.Read(); + + Exception exceptionThrown = null; + try + { + reader.ReadDelimitedString(); + } + catch (Exception ex) + { + exceptionThrown = ex; + } + + Assert.NotNull(exceptionThrown); + Assert.IsType(typeof(JsonException), exceptionThrown); + Assert.NotNull(exceptionThrown.InnerException); + Assert.IsType(typeof(InvalidCastException), exceptionThrown.InnerException); + Assert.Equal(expectedTokenType, reader.TokenType); + } + } + + [Fact] + public void ReadDelimitedString_WhenValueIsString_ReturnsValue() + { + const string expectedResult = "b"; + var json = $"{{\"a\":\"{expectedResult}\"}}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + using (var stream = new MemoryStream(encodedBytes)) + { + var reader = new Utf8JsonStreamReader(stream); + reader.Read(); + IEnumerable actualResults = reader.ReadDelimitedString(); + Assert.Collection(actualResults, actualResult => Assert.Equal(expectedResult, actualResult)); + Assert.Equal(JsonTokenType.String, reader.TokenType); + } + } + + [Theory] + [InlineData("b,c,d")] + [InlineData("b c d")] + public void ReadDelimitedString_WhenValueIsDelimitedString_ReturnsValues(string value) + { + string[] expectedResults = value.Split(new[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries); + var json = $"{{\"a\":\"{value}\"}}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + using (var stream = new MemoryStream(encodedBytes)) + { + var reader = new Utf8JsonStreamReader(stream); + reader.Read(); + IEnumerable actualResults = reader.ReadDelimitedString(); + Assert.Equal(expectedResults, actualResults); + Assert.Equal(JsonTokenType.String, reader.TokenType); + } + } + + [Theory] + [InlineData("null")] + [InlineData("\"b\"")] + [InlineData("{}")] + public void ReadStringArrayAsList_WhenValueIsNotArray_ReturnsNull(string value) + { + var json = $"{{\"a\":{value}}}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + using (var stream = new MemoryStream(encodedBytes)) + { + var reader = new Utf8JsonStreamReader(stream); + reader.Read(); + reader.Read(); + Assert.NotEqual(JsonTokenType.PropertyName, reader.TokenType); + List actualValues = reader.ReadStringArrayAsList(); + Assert.Null(actualValues); + } + } + + [Fact] + public void ReadStringArrayAsList_WhenValueIsEmptyArray_ReturnsNull() + { + var encodedBytes = Encoding.UTF8.GetBytes("{\"a\":[]}"); + using (var stream = new MemoryStream(encodedBytes)) + { + var reader = new Utf8JsonStreamReader(stream); + reader.Read(); + reader.Read(); + Assert.NotEqual(JsonTokenType.PropertyName, reader.TokenType); + List actualValues = reader.ReadStringArrayAsList(); + Assert.Null(actualValues); + } + } + + [Fact] + public void ReadStringArrayAsList_WithSupportedTypes_ReturnsStringArray() + { + var encodedBytes = Encoding.UTF8.GetBytes("[\"a\",-2,3.14,true,null]"); + using (var stream = new MemoryStream(encodedBytes)) + { + var reader = new Utf8JsonStreamReader(stream); + + List actualValues = reader.ReadStringArrayAsList(); + + Assert.Collection( + actualValues, + actualValue => Assert.Equal("a", actualValue), + actualValue => Assert.Equal("-2", actualValue), + actualValue => Assert.Equal("3.14", actualValue), + actualValue => Assert.Equal("True", actualValue), + actualValue => Assert.Equal(null, actualValue)); + Assert.Equal(JsonTokenType.EndArray, reader.TokenType); + } + } + + [Theory] + [InlineData("[]")] + [InlineData("{}")] + public void ReadStringArrayAsList_WithUnsupportedTypes_Throws(string element) + { + var encodedBytes = Encoding.UTF8.GetBytes($"[{element}]"); + using (var stream = new MemoryStream(encodedBytes)) + { + var reader = new Utf8JsonStreamReader(stream); + + Exception exceptionThrown = null; + try + { + reader.ReadStringArrayAsList(); + } + catch (Exception ex) + { + exceptionThrown = ex; + } + Assert.IsType(typeof(InvalidCastException), exceptionThrown); + } + } + + + [Theory] + [InlineData("true", true)] + [InlineData("false", false)] + public void ReadNextTokenAsBoolOrFalse_WithValidValues_ReturnsBoolean(string value, bool expectedResult) + { + var json = $"{{\"a\":{value}}}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + using (var stream = new MemoryStream(encodedBytes)) + { + var reader = new Utf8JsonStreamReader(stream); + reader.Read(); + bool actualResult = reader.ReadNextTokenAsBoolOrFalse(); + Assert.Equal(expectedResult, actualResult); + } + } + + [Theory] + [InlineData("\"words\"")] + [InlineData("-3")] + [InlineData("3.3")] + [InlineData("[]")] + [InlineData("{}")] + public void ReadNextTokenAsBoolOrFalse_WithInvalidValues_ReturnsFalse(string value) + { + var json = $"{{\"a\":{value}}}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + using (var stream = new MemoryStream(encodedBytes)) + { + var reader = new Utf8JsonStreamReader(stream); + reader.Read(); + bool actualResult = reader.ReadNextTokenAsBoolOrFalse(); + Assert.False(actualResult); + } + } + + [Fact] + public void ReadNextStringOrArrayOfStringsAsReadOnlyList_WhenValueIsNull_ReturnsNull() + { + const string json = "{\"a\":null}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + using (var stream = new MemoryStream(encodedBytes)) + { + var reader = new Utf8JsonStreamReader(stream); + reader.Read(); + IEnumerable actualResults = reader.ReadNextStringOrArrayOfStringsAsReadOnlyList(); + Assert.Null(actualResults); + Assert.Equal(JsonTokenType.Null, reader.TokenType); + } + } + + [Theory] + [InlineData("true", JsonTokenType.True)] + [InlineData("false", JsonTokenType.False)] + [InlineData("-2", JsonTokenType.Number)] + [InlineData("3.14", JsonTokenType.Number)] + [InlineData("{}", JsonTokenType.StartObject)] + public void ReadNextStringOrArrayOfStringsAsReadOnlyList_WhenValueIsNotString_ReturnsNull( + string value, + JsonTokenType expectedTokenType) + { + var json = $"{{\"a\":{value}}}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + using (var stream = new MemoryStream(encodedBytes)) + { + var reader = new Utf8JsonStreamReader(stream); + reader.Read(); + Assert.Equal(JsonTokenType.PropertyName, reader.TokenType); + + IEnumerable actualResults = reader.ReadNextStringOrArrayOfStringsAsReadOnlyList(); + + Assert.Null(actualResults); + Assert.Equal(expectedTokenType, reader.TokenType); + } + } + + [Fact] + public void ReadNextStringOrArrayOfStringsAsReadOnlyList_WhenValueIsString_ReturnsValue() + { + const string expectedResult = "b"; + var json = $"{{\"a\":\"{expectedResult}\"}}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + using (var stream = new MemoryStream(encodedBytes)) + { + var reader = new Utf8JsonStreamReader(stream); + reader.Read(); + Assert.Equal(JsonTokenType.PropertyName, reader.TokenType); + + IEnumerable actualResults = reader.ReadNextStringOrArrayOfStringsAsReadOnlyList(); + + Assert.Collection(actualResults, actualResult => Assert.Equal(expectedResult, actualResult)); + Assert.Equal(JsonTokenType.String, reader.TokenType); + } + } + + [Theory] + [InlineData("b,c,d")] + [InlineData("b c d")] + public void ReadNextStringOrArrayOfStringsAsReadOnlyList_WhenValueIsDelimitedString_ReturnsValue(string expectedResult) + { + var json = $"{{\"a\":\"{expectedResult}\"}}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + using (var stream = new MemoryStream(encodedBytes)) + { + var reader = new Utf8JsonStreamReader(stream); + reader.Read(); + Assert.Equal(JsonTokenType.PropertyName, reader.TokenType); + + IEnumerable actualResults = reader.ReadNextStringOrArrayOfStringsAsReadOnlyList(); + + Assert.Collection(actualResults, actualResult => Assert.Equal(expectedResult, actualResult)); + Assert.Equal(JsonTokenType.String, reader.TokenType); + } + } + + [Fact] + public void ReadNextStringOrArrayOfStringsAsReadOnlyList_WhenValueIsEmptyArray_ReturnsEmptyList() + { + const string json = "{\"a\":[]}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + using (var stream = new MemoryStream(encodedBytes)) + { + var reader = new Utf8JsonStreamReader(stream); + reader.Read(); + Assert.Equal(JsonTokenType.PropertyName, reader.TokenType); + + IReadOnlyList actualResults = reader.ReadNextStringOrArrayOfStringsAsReadOnlyList(); + + Assert.Empty(actualResults); + Assert.Equal(JsonTokenType.EndArray, reader.TokenType); + } + } + + [Theory] + [InlineData("null", null)] + [InlineData("true", "True")] + [InlineData("-2", "-2")] + [InlineData("3.14", "3.14")] + [InlineData("\"b\"", "b")] + public void ReadNextStringOrArrayOfStringsAsReadOnlyList_WhenValueIsConvertibleToString_ReturnsValueAsString( + string value, + string expectedResult) + { + var json = $"{{\"a\":[{value}]}}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + using (var stream = new MemoryStream(encodedBytes)) + { + var reader = new Utf8JsonStreamReader(stream); + reader.Read(); + + Assert.Equal(JsonTokenType.PropertyName, reader.TokenType); + + IEnumerable actualResults = reader.ReadNextStringOrArrayOfStringsAsReadOnlyList(); + + Assert.Collection(actualResults, actualResult => Assert.Equal(expectedResult, actualResult)); + Assert.Equal(JsonTokenType.EndArray, reader.TokenType); + } + } + + [Theory] + [InlineData("[]", JsonTokenType.StartArray)] + [InlineData("{}", JsonTokenType.StartObject)] + public void ReadNextStringOrArrayOfStringsAsReadOnlyList_WhenValueIsNotConvertibleToString_ReturnsValueAsString( + string value, + JsonTokenType expectedToken) + { + var json = $"{{\"a\":[{value}]}}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + using (var stream = new MemoryStream(encodedBytes)) + { + var reader = new Utf8JsonStreamReader(stream); + reader.Read(); + Assert.Equal(JsonTokenType.PropertyName, reader.TokenType); + + Exception exceptionThrown = null; + try + { + reader.ReadNextStringOrArrayOfStringsAsReadOnlyList(); + } + catch (Exception ex) + { + exceptionThrown = ex; + } + Assert.NotNull(exceptionThrown); + Assert.IsType(typeof(InvalidCastException), exceptionThrown); + Assert.Equal(expectedToken, reader.TokenType); + } + } + + [Fact] + public void ReadNextStringOrArrayOfStringsAsReadOnlyList_WhenValueIsArrayOfStrings_ReturnsValues() + { + string[] expectedResults = { "b", "c" }; + var json = $"{{\"a\":[{string.Join(",", expectedResults.Select(expectedResult => $"\"{expectedResult}\""))}]}}"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + using (var stream = new MemoryStream(encodedBytes)) + { + var reader = new Utf8JsonStreamReader(stream); + reader.Read(); + Assert.Equal(JsonTokenType.PropertyName, reader.TokenType); + + IEnumerable actualResults = reader.ReadNextStringOrArrayOfStringsAsReadOnlyList(); + + Assert.Equal(expectedResults, actualResults); + Assert.Equal(JsonTokenType.EndArray, reader.TokenType); + } + } + + [Fact] + public void ReadStringArrayAsReadOnlyListFromArrayStart_WhenValuesAreConvertibleToString_ReturnsReadOnlyList() + { + const string json = "[null, true, -2, 3.14, \"a\"]"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + using (var stream = new MemoryStream(encodedBytes)) + { + var reader = new Utf8JsonStreamReader(stream); + Assert.Equal(JsonTokenType.StartArray, reader.TokenType); + + IEnumerable actualResults = reader.ReadStringArrayAsReadOnlyListFromArrayStart(); + + Assert.Collection( + actualResults, + actualResult => Assert.Equal(null, actualResult), + actualResult => Assert.Equal("True", actualResult), + actualResult => Assert.Equal("-2", actualResult), + actualResult => Assert.Equal("3.14", actualResult), + actualResult => Assert.Equal("a", actualResult)); + Assert.Equal(JsonTokenType.EndArray, reader.TokenType); + } + } + + [Theory] + [InlineData("[]", JsonTokenType.StartArray)] + [InlineData("{}", JsonTokenType.StartObject)] + public void ReadStringArrayAsReadOnlyListFromArrayStart_WhenValuesAreNotConvertibleToString_Throws( + string value, + JsonTokenType expectedToken) + { + var json = $"[{value}]"; + var encodedBytes = Encoding.UTF8.GetBytes(json); + using (var stream = new MemoryStream(encodedBytes)) + { + var reader = new Utf8JsonStreamReader(stream); + Assert.Equal(JsonTokenType.StartArray, reader.TokenType); + + Exception exceptionThrown = null; + try + { + reader.ReadStringArrayAsReadOnlyListFromArrayStart(); + } + catch (Exception ex) + { + exceptionThrown = ex; + } + Assert.NotNull(exceptionThrown); + Assert.IsType(typeof(InvalidCastException), exceptionThrown); + Assert.Equal(expectedToken, reader.TokenType); + } + } + } +} diff --git a/test/NuGet.Tests.Apex/NuGet.Tests.Apex/Utility/CommonUtility.cs b/test/NuGet.Tests.Apex/NuGet.Tests.Apex/Utility/CommonUtility.cs index 30a63b1e5a4..72057ebd9a7 100644 --- a/test/NuGet.Tests.Apex/NuGet.Tests.Apex/Utility/CommonUtility.cs +++ b/test/NuGet.Tests.Apex/NuGet.Tests.Apex/Utility/CommonUtility.cs @@ -400,7 +400,9 @@ private static LockFile GetAssetsFileWithRetry(string path) { content = File.ReadAllText(path); var format = new LockFileFormat(); +#pragma warning disable CS0612 // Type or member is obsolete return format.Parse(content, path); +#pragma warning restore CS0612 // Type or member is obsolete } catch {