MetadataShadowCopyProviderTests.cs 11.8 KB
Newer Older
1
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.
C
CyrusNajmabadi 已提交
2

3 4 5 6 7 8 9
using System;
using System.Linq;
using System.IO;
using Roslyn.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;
using Microsoft.CodeAnalysis;
T
Tomas Matousek 已提交
10 11 12
using System.Collections.Immutable;
using Roslyn.Utilities;
using System.Runtime.InteropServices;
13
using System.Globalization;
14

15
namespace Microsoft.CodeAnalysis.Scripting.Hosting.UnitTests
16
{
T
Tomas Matousek 已提交
17 18
    // TODO: clean up and move to portable tests

19 20 21 22
    public class MetadataShadowCopyProviderTests : TestBase, IDisposable
    {
        private readonly MetadataShadowCopyProvider _provider;

T
Tomas Matousek 已提交
23 24 25 26 27 28
        private static readonly ImmutableArray<string> s_systemNoShadowCopyDirectories = ImmutableArray.Create(
                FileUtilities.NormalizeDirectoryPath(Environment.GetFolderPath(Environment.SpecialFolder.Windows)),
                FileUtilities.NormalizeDirectoryPath(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles)),
                FileUtilities.NormalizeDirectoryPath(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86)),
                FileUtilities.NormalizeDirectoryPath(RuntimeEnvironment.GetRuntimeDirectory()));

29 30
        public MetadataShadowCopyProviderTests()
        {
31 32 33 34 35 36
            _provider = CreateProvider(CultureInfo.InvariantCulture);
        }

        private static MetadataShadowCopyProvider CreateProvider(CultureInfo culture)
        {
            return new MetadataShadowCopyProvider(TempRoot.Root, s_systemNoShadowCopyDirectories, culture);
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
        }

        public override void Dispose()
        {
            _provider.Dispose();
            Assert.False(Directory.Exists(_provider.ShadowCopyDirectory), "Shadow copy directory should have been deleted");
            base.Dispose();
        }

        [Fact]
        public void Errors()
        {
            Assert.Throws<ArgumentNullException>(() => _provider.NeedsShadowCopy(null));
            Assert.Throws<ArgumentException>(() => _provider.NeedsShadowCopy("c:foo.dll"));
            Assert.Throws<ArgumentException>(() => _provider.NeedsShadowCopy("bar.dll"));
            Assert.Throws<ArgumentException>(() => _provider.NeedsShadowCopy(@"\bar.dll"));
            Assert.Throws<ArgumentException>(() => _provider.NeedsShadowCopy(@"../bar.dll"));

            Assert.Throws<ArgumentNullException>(() => _provider.SuppressShadowCopy(null));
            Assert.Throws<ArgumentException>(() => _provider.SuppressShadowCopy("c:foo.dll"));
            Assert.Throws<ArgumentException>(() => _provider.SuppressShadowCopy("bar.dll"));
            Assert.Throws<ArgumentException>(() => _provider.SuppressShadowCopy(@"\bar.dll"));
            Assert.Throws<ArgumentException>(() => _provider.SuppressShadowCopy(@"../bar.dll"));

            Assert.Throws<ArgumentOutOfRangeException>(() => _provider.GetMetadataShadowCopy(@"c:\foo.dll", (MetadataImageKind)Byte.MaxValue));
            Assert.Throws<ArgumentNullException>(() => _provider.GetMetadataShadowCopy(null, MetadataImageKind.Assembly));
            Assert.Throws<ArgumentException>(() => _provider.GetMetadataShadowCopy("c:foo.dll", MetadataImageKind.Assembly));
            Assert.Throws<ArgumentException>(() => _provider.GetMetadataShadowCopy("bar.dll", MetadataImageKind.Assembly));
            Assert.Throws<ArgumentException>(() => _provider.GetMetadataShadowCopy(@"\bar.dll", MetadataImageKind.Assembly));
            Assert.Throws<ArgumentException>(() => _provider.GetMetadataShadowCopy(@"../bar.dll", MetadataImageKind.Assembly));

            Assert.Throws<ArgumentOutOfRangeException>(() => _provider.GetMetadata(@"c:\foo.dll", (MetadataImageKind)Byte.MaxValue));
            Assert.Throws<ArgumentNullException>(() => _provider.GetMetadata(null, MetadataImageKind.Assembly));
            Assert.Throws<ArgumentException>(() => _provider.GetMetadata("c:foo.dll", MetadataImageKind.Assembly));
        }

        [Fact]
        public void Copy()
        {
            var dir = Temp.CreateDirectory();
            var dll = dir.CreateFile("a.dll").WriteAllBytes(TestResources.MetadataTests.InterfaceAndClass.CSClasses01);
            var doc = dir.CreateFile("a.xml").WriteAllText("<hello>");

            var sc1 = _provider.GetMetadataShadowCopy(dll.Path, MetadataImageKind.Assembly);
            var sc2 = _provider.GetMetadataShadowCopy(dll.Path, MetadataImageKind.Assembly);
            Assert.Equal(sc2, sc1);
            Assert.Equal(dll.Path, sc1.PrimaryModule.OriginalPath);
            Assert.NotEqual(dll.Path, sc1.PrimaryModule.FullPath);

            Assert.False(sc1.Metadata.IsImageOwner, "Copy expected");

            Assert.Equal(File.ReadAllBytes(dll.Path), File.ReadAllBytes(sc1.PrimaryModule.FullPath));
            Assert.Equal(File.ReadAllBytes(doc.Path), File.ReadAllBytes(sc1.DocumentationFile.FullPath));
        }

        [Fact]
        public void SuppressCopy1()
        {
            var dll = Temp.CreateFile().WriteAllText("blah");

            _provider.SuppressShadowCopy(dll.Path);

            var sc1 = _provider.GetMetadataShadowCopy(dll.Path, MetadataImageKind.Assembly);
            Assert.Null(sc1);
        }

        [Fact]
        public void SuppressCopy_Framework()
        {
            // framework assemblies not copied:
            string mscorlib = typeof(object).Assembly.Location;
            var sc2 = _provider.GetMetadataShadowCopy(mscorlib, MetadataImageKind.Assembly);
            Assert.Null(sc2);
        }

        [Fact]
        public void SuppressCopy_ShadowCopyDirectory()
        {
            // shadow copies not copied:
            var dll = Temp.CreateFile("a.dll").WriteAllBytes(TestResources.MetadataTests.InterfaceAndClass.CSClasses01);

            // copy:
            var sc1 = _provider.GetMetadataShadowCopy(dll.Path, MetadataImageKind.Assembly);
            Assert.NotEqual(dll.Path, sc1.PrimaryModule.FullPath);

            // file not copied:
            var sc2 = _provider.GetMetadataShadowCopy(sc1.PrimaryModule.FullPath, MetadataImageKind.Assembly);
            Assert.Null(sc2);
        }

        [Fact]
        public void Modules()
        {
            // modules: { MultiModule.dll, mod2.netmodule, mod3.netmodule }
            var dir = Temp.CreateDirectory();
132
            string path0 = dir.CreateFile("MultiModule.dll").WriteAllBytes(TestResources.SymbolsTests.MultiModule.MultiModuleDll).Path;
133 134 135
            string path1 = dir.CreateFile("mod2.netmodule").WriteAllBytes(TestResources.SymbolsTests.MultiModule.mod2).Path;
            string path2 = dir.CreateFile("mod3.netmodule").WriteAllBytes(TestResources.SymbolsTests.MultiModule.mod3).Path;

136
            var metadata1 = _provider.GetMetadata(path0, MetadataImageKind.Assembly) as AssemblyMetadata;
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
            Assert.NotNull(metadata1);
            Assert.Equal(3, metadata1.GetModules().Length);

            var scDir = Directory.GetFileSystemEntries(_provider.ShadowCopyDirectory).Single();
            Assert.True(Directory.Exists(scDir));

            var scFiles = Directory.GetFileSystemEntries(scDir);
            AssertEx.SetEqual(new[] { "MultiModule.dll", "mod2.netmodule", "mod3.netmodule" }, scFiles.Select(p => Path.GetFileName(p)));

            foreach (var sc in scFiles)
            {
                Assert.True(_provider.IsShadowCopy(sc));

                // files should be locked:
                Assert.Throws<IOException>(() => File.Delete(sc));
            }

            // should get the same metadata:
155
            var metadata2 = _provider.GetMetadata(path0, MetadataImageKind.Assembly) as AssemblyMetadata;
156 157 158 159 160
            Assert.Same(metadata1, metadata2);

            // modify the file:
            File.SetLastWriteTimeUtc(path0, DateTime.Now + TimeSpan.FromHours(1));

161 162
            // we get an updated image if we ask again:
            var modifiedMetadata3 = _provider.GetMetadata(path0, MetadataImageKind.Assembly) as AssemblyMetadata;
163 164 165
            Assert.NotSame(modifiedMetadata3, metadata2);

            // the file has been modified - we get new metadata:
166
            for (int i = 0; i < metadata2.GetModules().Length; i++)
167
            {
168
                Assert.NotSame(metadata2.GetModules()[i], modifiedMetadata3.GetModules()[i]);
169 170 171 172 173 174 175
            }
        }

        [Fact]
        public unsafe void DisposalOnFailure()
        {
            var f0 = Temp.CreateFile().WriteAllText("bogus").Path;
176
            Assert.Throws<BadImageFormatException>(() => _provider.GetMetadata(f0, MetadataImageKind.Assembly));
177

178
            string f1 = Temp.CreateFile().WriteAllBytes(TestResources.SymbolsTests.MultiModule.MultiModuleDll).Path;
179
            Assert.Throws<FileNotFoundException>(() => _provider.GetMetadata(f1, MetadataImageKind.Assembly));
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
        }

        [Fact]
        public void GetMetadata()
        {
            var dir = Temp.CreateDirectory();
            var dll = dir.CreateFile("a.dll").WriteAllBytes(TestResources.MetadataTests.InterfaceAndClass.CSClasses01);
            var doc = dir.CreateFile("a.xml").WriteAllText("<hello>");

            var sc1 = _provider.GetMetadataShadowCopy(dll.Path, MetadataImageKind.Assembly);
            var sc2 = _provider.GetMetadataShadowCopy(dll.Path, MetadataImageKind.Assembly);

            var md1 = _provider.GetMetadata(dll.Path, MetadataImageKind.Assembly);
            Assert.NotNull(md1);
            Assert.Equal(MetadataImageKind.Assembly, md1.Kind);

            // This needs to be in different folder from referencesdir to cause the other code path 
            // to be triggered for NeedsShadowCopy method
198 199 200
            var dir2 = Path.GetTempPath();
            string dll2 = Path.Combine(dir2, "a2.dll");
            File.WriteAllBytes(dll2, TestResources.MetadataTests.InterfaceAndClass.CSClasses01);
201 202 203 204 205

            Assert.Equal(1, _provider.CacheSize);
            var sc3a = _provider.GetMetadataShadowCopy(dll2, MetadataImageKind.Module);
            Assert.Equal(2, _provider.CacheSize);
        }
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220

        [Fact]
        public void XmlDocComments_SpecificCulture()
        {
            var elGR = CultureInfo.GetCultureInfo("el-GR");
            var arMA = CultureInfo.GetCultureInfo("ar-MA");

            var dir = Temp.CreateDirectory();
            var dll = dir.CreateFile("a.dll").WriteAllBytes(TestResources.MetadataTests.InterfaceAndClass.CSClasses01);
            var docInvariant = dir.CreateFile("a.xml").WriteAllText("Invariant");
            var docGreek = dir.CreateDirectory(elGR.Name).CreateFile("a.xml").WriteAllText("Greek");

            // invariant culture
            var provider = CreateProvider(CultureInfo.InvariantCulture);
            var sc = provider.GetMetadataShadowCopy(dll.Path, MetadataImageKind.Assembly);
C
CyrusNajmabadi 已提交
221
            Assert.Equal(Path.Combine(Path.GetDirectoryName(sc.PrimaryModule.FullPath), @"a.xml"), sc.DocumentationFile.FullPath);
222 223
            Assert.Equal("Invariant", File.ReadAllText(sc.DocumentationFile.FullPath));

224
            // Greek culture
225 226 227 228 229
            provider = CreateProvider(elGR);
            sc = provider.GetMetadataShadowCopy(dll.Path, MetadataImageKind.Assembly);
            Assert.Equal(Path.Combine(Path.GetDirectoryName(sc.PrimaryModule.FullPath), @"el-GR\a.xml"), sc.DocumentationFile.FullPath);
            Assert.Equal("Greek", File.ReadAllText(sc.DocumentationFile.FullPath));

230
            // Arabic culture (culture specific docs not found, use invariant)
231 232 233 234 235 236 237 238 239 240
            provider = CreateProvider(arMA);
            sc = provider.GetMetadataShadowCopy(dll.Path, MetadataImageKind.Assembly);
            Assert.Equal(Path.Combine(Path.GetDirectoryName(sc.PrimaryModule.FullPath), @"a.xml"), sc.DocumentationFile.FullPath);
            Assert.Equal("Invariant", File.ReadAllText(sc.DocumentationFile.FullPath));

            // no culture:
            provider = CreateProvider(null);
            sc = provider.GetMetadataShadowCopy(dll.Path, MetadataImageKind.Assembly);
            Assert.Null(sc.DocumentationFile);
        }
241 242
    }
}