1 //
2 // Copyright (c) 2010-2025 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
8 using System.Linq;
9 using Antmicro.Renode.Core;
10 using Antmicro.Renode.PlatformDescription;
11 using NUnit.Framework;
12 using Antmicro.Renode.UnitTests.Mocks;
13 using static Antmicro.Renode.PlatformDescription.UserInterface.PlatformDescriptionMachineExtensions;
14 
15 namespace Antmicro.Renode.UnitTests.PlatformDescription
16 {
17     [TestFixture]
18     public class UsingTests
19     {
20         [Test]
ShouldFindVariableFromUsing()21         public void ShouldFindVariableFromUsing()
22         {
23             var a = @"
24 cpu1: Antmicro.Renode.UnitTests.Mocks.MockCPU";
25 
26             var source = @"
27 using ""A""
28 cpu2: Antmicro.Renode.UnitTests.Mocks.MockCPU
29     OtherCpu: cpu1";
30 
31             ProcessSource(null, source, a);
32         }
33 
34         [Test]
ShouldNotFindLocalVariableFromUsing()35         public void ShouldNotFindLocalVariableFromUsing()
36         {
37             var a = @"
38 local cpu1: Antmicro.Renode.UnitTests.Mocks.MockCPU";
39 
40             var source = @"
41 using ""A""
42 cpu2: Antmicro.Renode.UnitTests.Mocks.MockCPU
43     OtherCpu: cpu1";
44 
45             var exception = Assert.Throws<ParsingException>(() => ProcessSource(null, source, a));
46             Assert.AreEqual(ParsingError.MissingReference, exception.Error);
47         }
48 
49         [Test]
ShouldFindVariableFromNestedUsing()50         public void ShouldFindVariableFromNestedUsing()
51         {
52             var source = @"
53 using ""A""
54 cpu3: Antmicro.Renode.UnitTests.Mocks.MockCPU
55     OtherCpu: cpu2";
56 
57             var a = @"
58 using ""B""
59 cpu2: Antmicro.Renode.UnitTests.Mocks.MockCPU
60     OtherCpu: cpu1";
61 
62             var b = @"
63 cpu1: Antmicro.Renode.UnitTests.Mocks.MockCPU";
64 
65             ProcessSource(null, source, a, b);
66         }
67 
68         [Test]
ShouldAllowReverseVariableDependency()69         public void ShouldAllowReverseVariableDependency()
70         {
71             var source = @"
72 using ""A""
73 otherCpu: Antmicro.Renode.UnitTests.Mocks.MockCPU";
74 
75             var a = @"
76 cpu: Antmicro.Renode.UnitTests.Mocks.MockCPU
77     OtherCpu: otherCpu";
78 
79             ProcessSource(null, source, a);
80         }
81 
82         [Test]
ShouldFindPrefixedVariable()83         public void ShouldFindPrefixedVariable()
84         {
85             var source = @"
86 using ""A"" prefixed ""a_""
87 cpu: Antmicro.Renode.UnitTests.Mocks.MockCPU
88     OtherCpu: a_cpu";
89 
90             var a = @"
91 cpu: Antmicro.Renode.UnitTests.Mocks.MockCPU";
92 
93             ProcessSource(null, source, a);
94         }
95 
96         [Test]
ShouldFindNestedPrefixedVariable()97         public void ShouldFindNestedPrefixedVariable()
98         {
99             var source = @"
100 using ""A"" prefixed ""a_""
101 using ""B""
102 someCpu: Antmicro.Renode.UnitTests.Mocks.MockCPU
103     OtherCpu: a_b_cpu
104 
105 oneMoreCpu: Antmicro.Renode.UnitTests.Mocks.MockCPU { OtherCpu: cpu }
106 ";
107 
108             var a = @"
109 using ""B"" prefixed ""b_""";
110 
111             var b = @"
112 cpu: Antmicro.Renode.UnitTests.Mocks.MockCPU";
113 
114             ProcessSource(null, source, a, b);
115         }
116 
117         [Test]
ShouldFollowAttributeOverrideOrder0()118         public void ShouldFollowAttributeOverrideOrder0()
119         {
120             var source = @"
121 using ""A""
122 using ""B""
123 cpu: Antmicro.Renode.UnitTests.Mocks.MockCPU @ sysbus 0x0
124     Placeholder: ""set by source first""
125 
126 cpu:
127     Placeholder: ""set by source second""
128 ";
129 
130             var a = @"
131 cpu:
132     Placeholder: ""set by A""
133 ";
134 
135             var b = @"
136 using ""C""
137 cpu:
138     Placeholder: ""set by B""
139 ";
140 
141             var c = @"
142 cpu:
143     Placeholder: ""set by C""
144 ";
145 
146             var machine = new Machine();
147             ProcessSource(machine, source, a, b, c);
148             MockCPU mock;
149             Assert.IsTrue(machine.TryGetByName("sysbus.cpu", out mock));
150             Assert.AreEqual("set by source second", mock.Placeholder);
151         }
152 
153         [Test]
ShouldFollowAttributeOverrideOrder1()154         public void ShouldFollowAttributeOverrideOrder1()
155         {
156             var source = @"
157 using ""A""
158 using ""B""
159 cpu: Antmicro.Renode.UnitTests.Mocks.MockCPU @ sysbus
160 ";
161 
162             var a = @"
163 cpu:
164     Placeholder: ""set by A first""
165 cpu:
166     Placeholder: ""set by A second""
167 ";
168 
169             var b = @"
170 using ""C""
171 cpu:
172     Placeholder: ""set by B first""
173 cpu:
174     Placeholder: ""set by B second""
175 ";
176 
177             var c = @"
178 cpu:
179     Placeholder: ""set by C first""
180 cpu:
181     Placeholder: ""set by C second""
182 ";
183 
184             var machine = new Machine();
185             ProcessSource(machine, source, a, b, c);
186             MockCPU mock;
187             Assert.IsTrue(machine.TryGetByName("sysbus.cpu", out mock));
188             Assert.AreEqual("set by B second", mock.Placeholder);
189         }
190 
191         [Test]
ShouldFollowAttributeOverrideOrder2()192         public void ShouldFollowAttributeOverrideOrder2()
193         {
194             var source = @"
195 using ""A""
196 cpu: Antmicro.Renode.UnitTests.Mocks.MockCPU @ sysbus 0x0
197 ";
198 
199             var a = @"
200 using ""B""
201 cpu:
202     Placeholder: ""set by A""
203 ";
204 
205             var b = @"
206 cpu:
207     Placeholder: ""set by B""
208 ";
209 
210             var machine = new Machine();
211             ProcessSource(machine, source, a, b);
212             MockCPU mock;
213             Assert.IsTrue(machine.TryGetByName("sysbus.cpu", out mock));
214             Assert.AreEqual("set by A", mock.Placeholder);
215         }
216 
217         [Test]
ShouldFollowAttributeOverrideOrder3()218         public void ShouldFollowAttributeOverrideOrder3()
219         {
220             var source = @"
221 using ""A""
222 using ""B""
223 using ""C""
224 cpu: Antmicro.Renode.UnitTests.Mocks.MockCPU @ sysbus
225 ";
226 
227             var a = @"
228 cpu:
229     Placeholder: ""set by A first""
230 cpu:
231     Placeholder: ""set by A second""
232 ";
233 
234             var b = @"
235 cpu:
236     Placeholder: ""set by B first""
237 cpu:
238     Placeholder: ""set by B second""
239 ";
240             var c = @"
241 using ""A"";
242 ";
243 
244             var machine = new Machine();
245             ProcessSource(machine, source, a, b, c);
246             MockCPU mock;
247             Assert.IsTrue(machine.TryGetByName("sysbus.cpu", out mock));
248             Assert.AreEqual("set by A second", mock.Placeholder);
249         }
250 
251         [Test]
ShouldFollowAttributeOverrideOrder4()252         public void ShouldFollowAttributeOverrideOrder4()
253         {
254             var source = @"
255 using ""A""
256 using ""B""
257 cpu: Antmicro.Renode.UnitTests.Mocks.MockCPU @ sysbus
258 ";
259 
260             var a = @"
261 cpu:
262     Placeholder: ""set by A first""
263 cpu:
264     Placeholder: ""set by A second""
265 ";
266 
267             var b = @"
268 using ""A""
269 cpu:
270     Placeholder: ""set by B first""
271 cpu:
272     Placeholder: ""set by B second""
273 ";
274 
275             var machine = new Machine();
276             ProcessSource(machine, source, a, b);
277             MockCPU mock;
278             Assert.IsTrue(machine.TryGetByName("sysbus.cpu", out mock));
279             Assert.AreEqual("set by B second", mock.Placeholder);
280         }
281 
282         [Test]
ShouldFailOnDuplicateUsingEntry()283         public void ShouldFailOnDuplicateUsingEntry()
284         {
285             var source = @"
286 using ""A""
287 using ""B""
288 using ""A""
289 cpu: Antmicro.Renode.UnitTests.Mocks.MockCPU @ sysbus
290 ";
291 
292             var a = @"
293 cpu:
294     Placeholder: ""set by A first""
295 cpu:
296     Placeholder: ""set by A second""
297 ";
298 
299             var b = @"
300 cpu:
301     Placeholder: ""set by B first""
302 cpu:
303     Placeholder: ""set by B second""
304 ";
305 
306             var exception = Assert.Throws<ParsingException>(() => ProcessSource(null, source, a, b));
307             Assert.AreEqual(ParsingError.DuplicateUsing, exception.Error);
308         }
309 
310         [Test]
ShouldHandleIrqDestinationOnPrefixing()311         public void ShouldHandleIrqDestinationOnPrefixing()
312         {
313             var a = @"
314 cpu: Antmicro.Renode.UnitTests.Mocks.MockReceiver
315 sender: Antmicro.Renode.UnitTests.Mocks.MockIrqSender
316     Irq -> cpu@0";
317 
318             var source = @"
319 using ""A"" prefixed ""sth_""";
320 
321             ProcessSource(null, source, a);
322         }
323 
324         [Test]
ShouldFailOnRecurringUsings()325         public void ShouldFailOnRecurringUsings()
326         {
327             var b = @"
328 using ""A""
329 p: Antmicro.Renode.UnitTests.Mocks.EmptyPeripheral";
330 
331             var a = @"
332 using ""B""
333 p2: Antmicro.Renode.UnitTests.Mocks.EmptyPeripheral";
334 
335             var source = @"
336 using ""B""
337 p3: Antmicro.Renode.UnitTests.Mocks.EmptyPeripheral";
338 
339             var exception = Assert.Throws<ParsingException>(() => ProcessSource(null, source, a, b));
340             Assert.AreEqual(ParsingError.RecurringUsing, exception.Error);
341         }
342 
343         [Test]
ShouldFailOnDirectlyRecurringUsings()344         public void ShouldFailOnDirectlyRecurringUsings()
345         {
346             var a = @"
347 using ""A""
348 p: Antmicro.Renode.UnitTests.Mocks.EmptyPeripheral";
349 
350             var source = @"
351 using ""A""
352 p2: Antmicro.Renode.UnitTests.Mocks.EmptyPeripheral";
353 
354             var exception = Assert.Throws<ParsingException>(() => ProcessSource(null, source, a));
355             Assert.AreEqual(ParsingError.RecurringUsing, exception.Error);
356         }
357 
358         [Test]
ShouldFailOnNonExistingUsingFile()359         public void ShouldFailOnNonExistingUsingFile()
360         {
361             var source = @"
362 using ""A""";
363 
364             var exception = Assert.Throws<ParsingException>(() => ProcessSource(null, source));
365             Assert.AreEqual(ParsingError.UsingFileNotFound, exception.Error);
366         }
367 
368 #if PLATFORM_WINDOWS
369         [Test]
ShouldHandleAbsolutePath()370         public void ShouldHandleAbsolutePath()
371         {
372             // On Windows, both \ and / should work as path component separators in the path to be resolved
373             // and the returned path should always use the preferred \ separator
374             Assert.AreEqual(@"C:\tmp\platform.repl", ResolvePath(@"C:\tmp\platform.repl", @"C:\tmp\includer.repl"));
375             Assert.AreEqual(@"C:\tmp\platform.repl", ResolvePath(@"C:/tmp/platform.repl", @"C:\tmp\includer.repl"));
376         }
377 
378         [Test]
ShouldHandleRelativePathInSameDirectory()379         public void ShouldHandleRelativePathInSameDirectory()
380         {
381             Assert.AreEqual(@"C:\tmp\platform.repl", ResolvePath(@".\platform.repl", @"C:\tmp\includer.repl"));
382             Assert.AreEqual(@"C:\tmp\platform.repl", ResolvePath(@"./platform.repl", @"C:\tmp\includer.repl"));
383         }
384 
385         [Test]
ShouldHandleRelativePathInParentDirectory()386         public void ShouldHandleRelativePathInParentDirectory()
387         {
388             Assert.AreEqual(@"C:\abc\platform.repl", ResolvePath(@"..\abc\platform.repl", @"C:\tmp\includer.repl"));
389             Assert.AreEqual(@"C:\abc\platform.repl", ResolvePath(@"../abc/platform.repl", @"C:\tmp\includer.repl"));
390         }
391 #else
392         [Test]
ShouldHandleAbsolutePath()393         public void ShouldHandleAbsolutePath()
394         {
395             Assert.AreEqual("/tmp/platform.repl", ResolvePath("/tmp/platform.repl", "/tmp/includer.repl"));
396         }
397 
398         [Test]
ShouldHandleRelativePathInSameDirectory()399         public void ShouldHandleRelativePathInSameDirectory()
400         {
401             Assert.AreEqual("/tmp/platform.repl", ResolvePath("./platform.repl", "/tmp/includer.repl"));
402         }
403 
404         [Test]
ShouldHandleRelativePathInParentDirectory()405         public void ShouldHandleRelativePathInParentDirectory()
406         {
407             Assert.AreEqual("/abc/platform.repl", ResolvePath("../abc/platform.repl", "/tmp/includer.repl"));
408         }
409 
410         [Test]
BackslashShouldNotBePathSeparatorOnUnix()411         public void BackslashShouldNotBePathSeparatorOnUnix()
412         {
413             // Backslashes are a valid filename character on Unix
414             Assert.AreEqual(@"/tmp/plat\form.repl", ResolvePath(@"./plat\form.repl", "/tmp/includer.repl"));
415         }
416 #endif
417 
418         [SetUp]
SetUp()419         public void SetUp()
420         {
421             resolver = new UsingResolver(Enumerable.Empty<string>());
422         }
423 
ProcessSource(Machine machine, params string[] sources)424         private static void ProcessSource(Machine machine, params string[] sources)
425         {
426             var letters = Enumerable.Range(0, sources.Length - 1).Select(x => (char)('A' + x)).ToArray();
427             var usingResolver = new FakeUsingResolver();
428             for(var i = 1; i < sources.Length; i++)
429             {
430                 usingResolver.With(letters[i - 1].ToString(), sources[i]);
431             }
432             var creationDriver = new CreationDriver(machine ?? new Machine(), usingResolver, new FakeInitHandler());
433         	creationDriver.ProcessDescription(sources[0]);
434         }
435 
ResolvePath(string path, string includingPath)436         private string ResolvePath(string path, string includingPath)
437         {
438             return resolver.Resolve(path, includingPath);
439         }
440 
441         private UsingResolver resolver;
442     }
443 }
444