Skip to content
Permalink
Browse files

Merge pull request #35740 from dotnet/merges/release/dev16.1-to-relea…

…se/dev16.1-vs-deps

Merge release/dev16.1 to release/dev16.1-vs-deps
  • Loading branch information...
jasonmalinowski committed May 16, 2019
2 parents e38a60d + 1e993cf commit 9d80dea7fe1b14043b9b2ac4d0b59ed26f508742
@@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
@@ -40,10 +41,10 @@ public bool TryGetDocumentOption(OptionKey option, OptionSet underlyingOptions,
// Dictionary<string, object> from the old system.
//
// We cache this with a conditional weak table so we're able to maintain the assumptions in EditorConfigNamingStyleParser
// that the instance doesn't regularly change and thus can be used for further caching
// that the instance doesn't regularly change and thus can be used for further caching.
var allRawConventions = s_convertedDictionaryCache.GetValue(
_codingConventionSnapshot.AllRawConventions,
d => ImmutableDictionary.CreateRange(d.Select(c => KeyValuePairUtil.Create(c.Key, c.Value.ToString()))));
d => new StringConvertingDictionary(d));

try
{
@@ -57,6 +58,57 @@ public bool TryGetDocumentOption(OptionKey option, OptionSet underlyingOptions,
return false;
}
}

/// <summary>
/// A class that implements <see cref="IReadOnlyDictionary{String, String}" /> atop a <see cref="IReadOnlyDictionary{String, Object}" />
/// where we just convert the values to strings with ToString(). Ordering of the underlying dictionary is preserved, so that way
/// code that relies on the underlying ordering of the underlying dictionary isn't affected.
/// </summary>
private class StringConvertingDictionary : IReadOnlyDictionary<string, string>
{
private readonly IReadOnlyDictionary<string, object> _underlyingDictionary;

public StringConvertingDictionary(IReadOnlyDictionary<string, object> underlyingDictionary)
{
_underlyingDictionary = underlyingDictionary ?? throw new ArgumentNullException(nameof(underlyingDictionary));
}

public string this[string key] => _underlyingDictionary[key]?.ToString();

public IEnumerable<string> Keys => _underlyingDictionary.Keys;
public IEnumerable<string> Values => _underlyingDictionary.Values.Select(s => s?.ToString());

public int Count => _underlyingDictionary.Count;

public bool ContainsKey(string key) => _underlyingDictionary.ContainsKey(key);

public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
foreach (var pair in _underlyingDictionary)
{
yield return new KeyValuePair<string, string>(pair.Key, pair.Value?.ToString());
}
}

public bool TryGetValue(string key, out string value)
{
if (_underlyingDictionary.TryGetValue(key, out object objectValue))
{
value = objectValue?.ToString();
return true;
}
else
{
value = null;
return false;
}
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
}
}
@@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
using System.Composition;
using System.IO;
using System.Linq;
using System.Threading;
using System.Xml.Linq;
using Microsoft.CodeAnalysis.Editor.Options;
using Microsoft.CodeAnalysis.Editor.UnitTests;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.VisualStudio.CodingConventions;
using Microsoft.VisualStudio.Composition;
using Xunit;

namespace Microsoft.CodeAnalysis.UnitTests.EditorConfigStorageLocation
{
[UseExportProvider]
public class LegacyEditorConfigDocumentOptionsProviderTests
{
[Fact]
public void OrderingOfEditorConfigMaintained()
{
using var tempRoot = new TempRoot();
var tempDirectory = tempRoot.CreateDirectory();

// Write out an .editorconfig. We'll write out 100 random GUIDs
var expectedKeysInOrder = new List<string>();

using (var writer = new StreamWriter(tempDirectory.CreateFile(".editorconfig").Path))
{
writer.WriteLine("root = true");
writer.WriteLine("[*.cs]");

for (int i = 0; i < 100; i++)
{
var key = Guid.NewGuid().ToString();
expectedKeysInOrder.Add(key);
writer.WriteLine($"{key} = value");
}
}

// Create a workspace with a file in that path
var codingConventionsCatalog = ExportProviderCache.GetOrCreateAssemblyCatalog(typeof(ICodingConventionsManager).Assembly).WithPart(typeof(MockFileWatcher));
var exportProvider = ExportProviderCache.GetOrCreateExportProviderFactory(TestExportProvider.EntireAssemblyCatalogWithCSharpAndVisualBasic.WithParts(codingConventionsCatalog)).CreateExportProvider();

using var workspace = TestWorkspace.CreateWorkspace(
new XElement("Workspace",
new XElement("Project", new XAttribute("Language", "C#"),
new XElement("Document", new XAttribute("FilePath", tempDirectory.CreateFile("Test.cs").Path)))), exportProvider: exportProvider);

var document = workspace.CurrentSolution.Projects.Single().Documents.Single();

var providerFactory = workspace.ExportProvider.GetExportedValues<IDocumentOptionsProviderFactory>().OfType<LegacyEditorConfigDocumentOptionsProviderFactory>().Single();
var provider = providerFactory.TryCreate(workspace);

var option = new Option<List<string>>(nameof(LegacyEditorConfigDocumentOptionsProviderTests), nameof(OrderingOfEditorConfigMaintained), null, new[] { new KeysReturningStorageLocation() });
var optionKey = new OptionKey(option);

// Fetch the underlying option order with a "option" that returns the keys
provider.GetOptionsForDocumentAsync(document, CancellationToken.None).Result.TryGetDocumentOption(optionKey, workspace.Options, out object actualKeysInOrderObject);

var actualKeysInOrder = Assert.IsAssignableFrom<IEnumerable<string>>(actualKeysInOrderObject);

Assert.Equal(expectedKeysInOrder, actualKeysInOrder);
}

[PartNotDiscoverable]
[Export(typeof(IFileWatcher))]
[Shared]
private class MockFileWatcher : IFileWatcher
{
#pragma warning disable CS0067 // the event is unused

public event ConventionsFileChangedAsyncEventHandler ConventionFileChanged;
public event ContextFileMovedAsyncEventHandler ContextFileMoved;

#pragma warning restore CS0067

public void Dispose()
{
}

public void StartWatching(string fileName, string directoryPath)
{
}

public void StopWatching(string fileName, string directoryPath)
{
}
}

/// <summary>
/// An option storage location that returns as the value all the keys in the order they came from the underlying storage.
/// </summary>
private class KeysReturningStorageLocation : OptionStorageLocation, IEditorConfigStorageLocation
{
public bool TryGetOption(object underlyingOption, IReadOnlyDictionary<string, string> rawOptions, Type type, out object value)
{
value = rawOptions.Keys;

return true;
}
}
}
}
@@ -56,6 +56,7 @@
<Reference Include="System.Xml.Linq" />
<Reference Include="WindowsBase" />
<PackageReference Include="BasicUndo" Version="$(BasicUndoVersion)" />
<PackageReference Include="Microsoft.VisualStudio.CodingConventions" Version="$(MicrosoftVisualStudioCodingConventionsVersion)" />
<PackageReference Include="Microsoft.VisualStudio.Composition" Version="$(MicrosoftVisualStudioCompositionVersion)" />
<PackageReference Include="Microsoft.VisualStudio.Platform.VSEditor" Version="$(MicrosoftVisualStudioPlatformVSEditorVersion)" />
<PackageReference Include="Microsoft.VisualStudio.InteractiveWindow" Version="$(MicrosoftVisualStudioInteractiveWindowVersion)" />
@@ -11,7 +11,7 @@

namespace Microsoft.CodeAnalysis.UnitTests.EditorConfig.StorageLocation
{
public class EditorConfigStorageLocationTests
public class NamingStylePreferenceEditorConfigStorageLocationTests
{
[Fact]
public static void TestEmptyDictionaryReturnNoNamingStylePreferencesObjectReturnsFalse()

0 comments on commit 9d80dea

Please sign in to comment.
You can’t perform that action at this time.