1 using System;
2 using System.IO;
3 using Microsoft.Build.Framework;
4 using Microsoft.Build.Tasks;
5 using Microsoft.Build.Utilities;
6 using Xunit;
7 
8 namespace Microsoft.Build.UnitTests.ResolveAssemblyReference_Tests.VersioningAndUnification.AppConfig
9 {
10     public sealed class SpecificVersionPrimary : ResolveAssemblyReferenceTestFixture
11     {
12         /// <summary>
13         /// In this case,
14         /// - A single primary version-strict reference was passed in to assembly version 1.0.0.0
15         /// - An app.config was passed in that promotes assembly version from 1.0.0.0 to 2.0.0.0
16         /// - Version 1.0.0.0 of the file exists.
17         /// - Version 2.0.0.0 of the file exists.
18         /// Expected:
19         /// - The resulting assembly returned should be 1.0.0.0.
20         /// Rationale:
21         /// Primary references are never unified. This is because:
22         /// (a) The user expects that a primary reference will be respected.
23         /// (b) When FindDependencies is false and AutoUnify is true, we'd have to find all
24         ///     dependencies anyway to make things work consistently. This would be a significant
25         ///     perf hit when loading large solutions.
26         /// </summary>
27         [Fact]
28         [PlatformSpecific(TestPlatforms.Windows)]
Exists()29         public void Exists()
30         {
31             // This WriteLine is a hack.  On a slow machine, the Tasks unittest fails because remoting
32             // times out the object used for remoting console writes.  Adding a write in the middle of
33             // keeps remoting from timing out the object.
34             Console.WriteLine("Performing VersioningAndUnification.Prerequisite.SpecificVersionPrimary.Exists() test");
35 
36             // Create the engine.
37             MockEngine engine = new MockEngine();
38 
39             ITaskItem[] assemblyNames = new TaskItem[]
40             {
41                 new TaskItem("UnifyMe, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
42             };
43             assemblyNames[0].SetMetadata("SpecificVersion", "true");
44 
45             // Construct the app.config.
46             string appConfigFile = WriteAppConfig
47                 (
48                     "        <dependentAssembly>\n" +
49                     "            <assemblyIdentity name='UnifyMe' PublicKeyToken='b77a5c561934e089' culture='neutral' />\n" +
50                     "            <bindingRedirect oldVersion='1.0.0.0' newVersion='2.0.0.0' />\n" +
51                     "        </dependentAssembly>\n"
52                 );
53 
54             // Now, pass feed resolved primary references into ResolveAssemblyReference.
55             ResolveAssemblyReference t = new ResolveAssemblyReference();
56 
57             t.BuildEngine = engine;
58             t.Assemblies = assemblyNames;
59             t.SearchPaths = DefaultPaths;
60             t.AppConfigFile = appConfigFile;
61 
62             bool succeeded = Execute(t);
63 
64             Assert.True(succeeded);
65             Assert.Equal(1, t.ResolvedFiles.Length);
66             AssertNoCase("UnifyMe, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, ProcessorArchitecture=MSIL", t.ResolvedFiles[0].GetMetadata("FusionName"));
67             AssertNoCase(@"{Registry:Software\Microsoft\.NetFramework,v2.0,AssemblyFoldersEx}", t.ResolvedFiles[0].GetMetadata("ResolvedFrom"));
68 
69             // Cleanup.
70             File.Delete(appConfigFile);
71         }
72 
73         /// <summary>
74         /// In this case,
75         /// - A single primary version-strict reference was passed in to assembly version 1.0.0.0
76         /// - An app.config was passed in that promotes a *different* assembly version name from
77         //    1.0.0.0 to 2.0.0.0
78         /// - Version 1.0.0.0 of the file exists.
79         /// - Version 2.0.0.0 of the file exists.
80         /// Expected:
81         /// -- The resulting assembly returned should be 1.0.0.0.
82         /// Rationale:
83         /// Primary references are never unified. This is because:
84         /// (a) The user expects that a primary reference will be respected.
85         /// (b) When FindDependencies is false and AutoUnify is true, we'd have to find all
86         ///     dependencies anyway to make things work consistently. This would be a significant
87         ///     perf hit when loading large solutions.
88         /// </summary>
89         [Fact]
90         [Trait("Category", "mono-osx-failing")]
ExistsDifferentName()91         public void ExistsDifferentName()
92         {
93             // Create the engine.
94             MockEngine engine = new MockEngine();
95 
96             ITaskItem[] assemblyNames = new TaskItem[]
97             {
98                 new TaskItem("UnifyMe, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
99             };
100             assemblyNames[0].SetMetadata("SpecificVersion", "true");
101 
102             // Construct the app.config.
103             string appConfigFile = WriteAppConfig
104                 (
105                     "        <dependentAssembly>\n" +
106                     "            <assemblyIdentity name='DontUnifyMe' PublicKeyToken='b77a5c561934e089' culture='neutral' />\n" +
107                     "            <bindingRedirect oldVersion='1.0.0.0' newVersion='2.0.0.0' />\n" +
108                     "        </dependentAssembly>\n"
109                 );
110 
111             // Now, pass feed resolved primary references into ResolveAssemblyReference.
112             ResolveAssemblyReference t = new ResolveAssemblyReference();
113 
114             t.BuildEngine = engine;
115             t.Assemblies = assemblyNames;
116             t.SearchPaths = DefaultPaths;
117             t.AppConfigFile = appConfigFile;
118 
119             bool succeeded = Execute(t);
120 
121             Assert.True(succeeded);
122             Assert.Equal(1, t.ResolvedFiles.Length);
123             AssertNoCase("UnifyMe, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, ProcessorArchitecture=MSIL", t.ResolvedFiles[0].GetMetadata("FusionName"));
124 
125             // Cleanup.
126             File.Delete(appConfigFile);
127         }
128 
129         /// <summary>
130         /// In this case,
131         /// - A single primary version-strict reference was passed in to assembly version 1.0.0.0
132         /// - An app.config was passed in that promotes assembly version from range 0.0.0.0-1.5.0.0 to 2.0.0.0
133         /// - Version 1.0.0.0 of the file exists.
134         /// - Version 2.0.0.0 of the file exists.
135         /// Expected:
136         /// -- The resulting assembly returned should be 1.0.0.0.
137         /// Rationale:
138         /// Primary references are never unified. This is because:
139         /// (a) The user expects that a primary reference will be respected.
140         /// (b) When FindDependencies is false and AutoUnify is true, we'd have to find all
141         ///     dependencies anyway to make things work consistently. This would be a significant
142         ///     perf hit when loading large solutions.
143         /// </summary>
144         [Fact]
145         [Trait("Category", "mono-osx-failing")]
ExistsOldVersionRange()146         public void ExistsOldVersionRange()
147         {
148             // Create the engine.
149             MockEngine engine = new MockEngine();
150 
151             ITaskItem[] assemblyNames = new TaskItem[]
152             {
153                 new TaskItem("UnifyMe, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
154             };
155             assemblyNames[0].SetMetadata("SpecificVersion", "true");
156 
157             // Construct the app.config.
158             string appConfigFile = WriteAppConfig
159                 (
160                     "        <dependentAssembly>\n" +
161                     "            <assemblyIdentity name='UnifyMe' PublicKeyToken='b77a5c561934e089' culture='neutral' />\n" +
162                     "            <bindingRedirect oldVersion='0.0.0.0-1.5.0.0' newVersion='2.0.0.0' />\n" +
163                     "        </dependentAssembly>\n"
164                 );
165 
166             // Now, pass feed resolved primary references into ResolveAssemblyReference.
167             ResolveAssemblyReference t = new ResolveAssemblyReference();
168 
169             t.BuildEngine = engine;
170             t.Assemblies = assemblyNames;
171             t.SearchPaths = DefaultPaths;
172             t.AppConfigFile = appConfigFile;
173 
174             bool succeeded = Execute(t);
175 
176             Assert.True(succeeded);
177             Assert.Equal(1, t.ResolvedFiles.Length);
178             AssertNoCase("UnifyMe, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, ProcessorArchitecture=MSIL", t.ResolvedFiles[0].GetMetadata("FusionName"));
179 
180             // Cleanup.
181             File.Delete(appConfigFile);
182         }
183 
184         /// <summary>
185         /// In this case,
186         /// - A single primary version-strict reference was passed in to assembly version 1.0.0.0
187         /// - An app.config was passed in that promotes assembly version from 1.0.0.0 to 4.0.0.0
188         /// - Version 1.0.0.0 of the file exists.
189         /// - Version 4.0.0.0 of the file *does not* exist.
190         /// Expected:
191         /// -- The resulting assembly returned should be 1.0.0.0.
192         /// Rationale:
193         /// Primary references are never unified. This is because:
194         /// (a) The user expects that a primary reference will be respected.
195         /// (b) When FindDependencies is false and AutoUnify is true, we'd have to find all
196         ///     dependencies anyway to make things work consistently. This would be a significant
197         ///     perf hit when loading large solutions.
198         /// </summary>
199         [Fact]
200         [Trait("Category", "mono-osx-failing")]
HighVersionDoesntExist()201         public void HighVersionDoesntExist()
202         {
203             // Create the engine.
204             MockEngine engine = new MockEngine();
205 
206             ITaskItem[] assemblyNames = new TaskItem[]
207             {
208                 new TaskItem("UnifyMe, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
209             };
210             assemblyNames[0].SetMetadata("SpecificVersion", "true");
211 
212             // Construct the app.config.
213             string appConfigFile = WriteAppConfig
214                 (
215                     "        <dependentAssembly>\n" +
216                     "            <assemblyIdentity name='UnifyMe' PublicKeyToken='b77a5c561934e089' culture='neutral' />\n" +
217                     "            <bindingRedirect oldVersion='1.0.0.0' newVersion='4.0.0.0' />\n" +
218                     "        </dependentAssembly>\n"
219                 );
220 
221             // Now, pass feed resolved primary references into ResolveAssemblyReference.
222             ResolveAssemblyReference t = new ResolveAssemblyReference();
223 
224             t.BuildEngine = engine;
225             t.Assemblies = assemblyNames;
226             t.SearchPaths = DefaultPaths;
227             t.AppConfigFile = appConfigFile;
228 
229             bool succeeded = Execute(t);
230 
231             Assert.True(succeeded);
232             Assert.Equal(1, t.ResolvedFiles.Length);
233             AssertNoCase("UnifyMe, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, ProcessorArchitecture=MSIL", t.ResolvedFiles[0].GetMetadata("FusionName"));
234 
235             // Cleanup.
236             File.Delete(appConfigFile);
237         }
238 
239         /// <summary>
240         /// In this case,
241         /// - A single primary version-strict reference was passed in to assembly version 0.5.0.0
242         /// - An app.config was passed in that promotes assembly version from 0.0.0.0-2.0.0.0 to 2.0.0.0
243         /// - Version 0.5.0.0 of the file *does not* exists.
244         /// - Version 2.0.0.0 of the file exists.
245         /// Expected:
246         /// - The reference is not resolved.
247         /// Rationale:
248         /// Primary references are never unified--even those that don't exist on disk. This is because:
249         /// (a) The user expects that a primary reference will be respected.
250         /// (b) When FindDependencies is false and AutoUnify is true, we'd have to find all
251         ///     dependencies anyway to make things work consistently. This would be a significant
252         ///     perf hit when loading large solutions.
253         /// </summary>
254         [Fact]
LowVersionDoesntExist()255         public void LowVersionDoesntExist()
256         {
257             // Create the engine.
258             MockEngine engine = new MockEngine();
259 
260             ITaskItem[] assemblyNames = new TaskItem[]
261             {
262                 new TaskItem("UnifyMe, Version=0.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
263             };
264             assemblyNames[0].SetMetadata("SpecificVersion", "true");
265 
266             // Construct the app.config.
267             string appConfigFile = WriteAppConfig
268                 (
269                     "        <dependentAssembly>\n" +
270                     "            <assemblyIdentity name='UnifyMe' PublicKeyToken='b77a5c561934e089' culture='neutral' />\n" +
271                     "            <bindingRedirect oldVersion='0.0.0.0-2.0.0.0' newVersion='2.0.0.0' />\n" +
272                     "        </dependentAssembly>\n"
273                 );
274 
275             // Now, pass feed resolved primary references into ResolveAssemblyReference.
276             ResolveAssemblyReference t = new ResolveAssemblyReference();
277 
278             t.BuildEngine = engine;
279             t.Assemblies = assemblyNames;
280             t.SearchPaths = DefaultPaths;
281             t.AppConfigFile = appConfigFile;
282 
283             bool succeeded = Execute(t);
284 
285             Assert.True(succeeded);
286             Assert.Equal(0, t.ResolvedFiles.Length);
287 
288             // Cleanup.
289             File.Delete(appConfigFile);
290         }
291     }
292 }