2 // Microsoft.VisualBasic.VBCodeProvider.cs
5 // Jochen Wezel (jwezel@compumaster.de)
7 // (C) 2003 Jochen Wezel (CompuMaster GmbH)
10 // 2003-12-10 JW: publishing of this file
15 using System.CodeDom.Compiler;
16 using System.Collections.Specialized;
17 using System.Globalization;
18 using System.Reflection;
19 using System.Diagnostics;
22 using Microsoft.VisualBasic;
23 using NUnit.Framework;
25 namespace MonoTests.Microsoft.VisualBasic
35 public class VBCodeProviderTest
37 private string _tempDir;
38 private CodeDomProvider _codeProvider;
39 private static OsType OS;
40 private static char DSC = Path.DirectorySeparatorChar;
42 private static readonly string _sourceTest1 = "Public Class Test1" +
43 Environment.NewLine + "End Class";
44 private static readonly string _sourceTest2 = "Public Class Test2" +
45 Environment.NewLine + "End Class";
48 public void GetReady ()
52 } else if ('\\' == DSC) {
58 _codeProvider = new VBCodeProvider ();
59 _tempDir = CreateTempDirectory ();
63 public void TearDown ()
65 RemoveDirectory (_tempDir);
69 public void FileExtension ()
71 Assert.AreEqual ("vb", _codeProvider.FileExtension, "#JW10");
75 public void LanguageOptionsTest ()
77 Assert.AreEqual (LanguageOptions.CaseInsensitive, _codeProvider.LanguageOptions, "#JW20");
81 public void GeneratorSupports ()
83 ICodeGenerator codeGenerator = _codeProvider.CreateGenerator ();
84 Assert.IsTrue (codeGenerator.Supports (GeneratorSupport.DeclareEnums), "#1");
85 Assert.IsTrue (codeGenerator.Supports (GeneratorSupport.ArraysOfArrays), "#2");
86 Assert.IsTrue (codeGenerator.Supports (GeneratorSupport.AssemblyAttributes), "#3");
87 Assert.IsTrue (codeGenerator.Supports (GeneratorSupport.ChainedConstructorArguments), "#4");
88 Assert.IsTrue (codeGenerator.Supports (GeneratorSupport.ComplexExpressions), "#5");
89 Assert.IsTrue (codeGenerator.Supports (GeneratorSupport.DeclareDelegates), "#6");
90 Assert.IsTrue (codeGenerator.Supports (GeneratorSupport.DeclareEnums), "#7");
91 Assert.IsTrue (codeGenerator.Supports (GeneratorSupport.DeclareEvents), "#8");
92 Assert.IsTrue (codeGenerator.Supports (GeneratorSupport.DeclareInterfaces), "#9");
93 Assert.IsTrue (codeGenerator.Supports (GeneratorSupport.DeclareValueTypes), "#10");
94 Assert.IsTrue (codeGenerator.Supports (GeneratorSupport.EntryPointMethod), "#11");
95 Assert.IsTrue (codeGenerator.Supports (GeneratorSupport.GotoStatements), "#12");
96 Assert.IsTrue (codeGenerator.Supports (GeneratorSupport.MultidimensionalArrays), "#13");
97 Assert.IsTrue (codeGenerator.Supports (GeneratorSupport.MultipleInterfaceMembers), "#14");
98 Assert.IsTrue (codeGenerator.Supports (GeneratorSupport.NestedTypes), "#15");
99 Assert.IsTrue (codeGenerator.Supports (GeneratorSupport.ParameterAttributes), "#16");
100 Assert.IsTrue (codeGenerator.Supports (GeneratorSupport.PublicStaticMembers), "#17");
101 Assert.IsTrue (codeGenerator.Supports (GeneratorSupport.ReferenceParameters), "#18");
102 Assert.IsTrue (codeGenerator.Supports (GeneratorSupport.ReturnTypeAttributes), "#19");
103 Assert.IsTrue (codeGenerator.Supports (GeneratorSupport.StaticConstructors), "#20");
104 Assert.IsTrue (codeGenerator.Supports (GeneratorSupport.TryCatchStatements), "#21");
105 Assert.IsTrue (codeGenerator.Supports (GeneratorSupport.Win32Resources), "#22");
107 Assert.IsTrue (codeGenerator.Supports (GeneratorSupport.DeclareIndexerProperties), "#23");
108 Assert.IsTrue (codeGenerator.Supports (GeneratorSupport.GenericTypeDeclaration), "#24");
109 Assert.IsTrue (codeGenerator.Supports (GeneratorSupport.GenericTypeReference), "#25");
110 Assert.IsTrue (codeGenerator.Supports (GeneratorSupport.PartialTypes), "#26");
111 Assert.IsTrue (codeGenerator.Supports (GeneratorSupport.Resources), "#27");
117 // there's not yet an mbas for the 2.0 profile
118 [Category ("NotWorking")]
120 public void CreateCompiler ()
122 // Prepare the compilation
123 ICodeCompiler codeCompiler = _codeProvider.CreateCompiler ();
124 Assert.IsNotNull (codeCompiler, "#JW30 - CreateCompiler");
126 CompilerParameters options = new CompilerParameters ();
127 options.GenerateExecutable = true;
128 options.IncludeDebugInformation = true;
129 options.TreatWarningsAsErrors = true;
131 // process compilation
132 CompilerResults compilerResults = codeCompiler.CompileAssemblyFromSource (options,
133 "public class TestModule" + Environment.NewLine + "public shared sub Main()"
134 + Environment.NewLine + "System.Console.Write(\"Hello world!\")"
135 + Environment.NewLine + "End Sub" + Environment.NewLine + "End Class");
137 // Analyse the compilation success/messages
138 StringCollection MyOutput = compilerResults.Output;
139 string MyOutStr = "";
140 foreach (string MyStr in MyOutput) {
141 MyOutStr += MyStr + Environment.NewLine + Environment.NewLine;
144 if (compilerResults.Errors.Count != 0) {
145 Assert.Fail ("#JW31 - Hello world compilation: " + MyOutStr);
149 Assembly MyAss = compilerResults.CompiledAssembly;
150 } catch (Exception ex) {
151 Assert.Fail ("#JW32 - compilerResults.CompiledAssembly hasn't been an expected object" +
152 Environment.NewLine + ex.Message + Environment.NewLine + ex.StackTrace);
155 // Execute the test app
156 ProcessStartInfo NewProcInfo = new ProcessStartInfo ();
158 NewProcInfo.FileName = compilerResults.CompiledAssembly.Location;
160 NewProcInfo.FileName = "mono";
161 NewProcInfo.Arguments = compilerResults.CompiledAssembly.Location;
163 NewProcInfo.RedirectStandardOutput = true;
164 NewProcInfo.UseShellExecute = false;
165 NewProcInfo.CreateNoWindow = true;
166 string TestAppOutput = "";
168 Process MyProc = Process.Start (NewProcInfo);
169 MyProc.WaitForExit ();
170 TestAppOutput = MyProc.StandardOutput.ReadToEnd ();
173 } catch (Exception ex) {
174 Assert.Fail ("#JW34 - " + ex.Message + Environment.NewLine + ex.StackTrace);
176 Assert.AreEqual ("Hello world!", TestAppOutput, "#JW33 - Application output");
180 File.Delete (NewProcInfo.FileName);
185 public void CreateGenerator ()
187 ICodeGenerator MyVBCodeGen;
188 MyVBCodeGen = _codeProvider.CreateGenerator ();
189 Assert.IsNotNull (MyVBCodeGen, "#JW40 - CreateGenerator");
190 Assert.IsTrue (MyVBCodeGen.Supports (GeneratorSupport.DeclareEnums), "#JW41");
195 // there's not yet an mbas for the 2.0 profile
196 [Category ("NotWorking")]
198 public void CompileFromFile_InMemory ()
200 // create vb source file
201 string sourceFile = Path.Combine (_tempDir, "file." + _codeProvider.FileExtension);
202 using (FileStream f = new FileStream (sourceFile, FileMode.Create)) {
203 using (StreamWriter s = new StreamWriter (f)) {
204 s.Write (_sourceTest1);
210 CompilerParameters options = new CompilerParameters ();
211 options.GenerateExecutable = false;
212 options.GenerateInMemory = true;
213 options.TempFiles = new TempFileCollection (_tempDir);
215 options.EmbeddedResources.Add (sourceFile);
218 ICodeCompiler compiler = _codeProvider.CreateCompiler ();
219 CompilerResults results = compiler.CompileAssemblyFromFile (options,
222 // verify compilation was successful
223 AssertCompileResults (results, true);
225 Assembly compiledAssembly = results.CompiledAssembly;
227 Assert.IsNotNull (compiledAssembly, "#1");
228 Assert.AreEqual (string.Empty, compiledAssembly.Location, "#2");
229 Assert.IsNull (results.PathToAssembly, "#3");
230 Assert.IsNotNull (compiledAssembly.GetType ("Test1"), "#4");
232 // verify we don't cleanup files in temp directory too agressively
233 string[] tempFiles = Directory.GetFiles (_tempDir);
234 Assert.AreEqual (1, tempFiles.Length, "#5");
235 Assert.AreEqual (sourceFile, tempFiles[0], "#6");
238 string[] resources = compiledAssembly.GetManifestResourceNames();
239 Assert.IsNotNull (resources, "#7");
240 Assert.AreEqual (1, resources.Length, "#8");
241 Assert.AreEqual ("file.vb", resources[0], "#9");
242 Assert.IsNull (compiledAssembly.GetFile ("file.vb"), "#10");
243 Assert.IsNotNull (compiledAssembly.GetManifestResourceStream ("file.vb"), "#11");
249 // there's not yet an mbas for the 2.0 profile
250 [Category ("NotWorking")]
252 public void CompileFromFileBatch_InMemory ()
254 // create vb source file
255 string sourceFile1 = Path.Combine (_tempDir, "file1." + _codeProvider.FileExtension);
256 using (FileStream f = new FileStream (sourceFile1, FileMode.Create)) {
257 using (StreamWriter s = new StreamWriter (f)) {
258 s.Write (_sourceTest1);
264 string sourceFile2 = Path.Combine (_tempDir, "file2." + _codeProvider.FileExtension);
265 using (FileStream f = new FileStream (sourceFile2, FileMode.Create)) {
266 using (StreamWriter s = new StreamWriter (f)) {
267 s.Write (_sourceTest2);
273 CompilerParameters options = new CompilerParameters ();
274 options.GenerateExecutable = false;
275 options.GenerateInMemory = true;
276 options.TempFiles = new TempFileCollection (_tempDir);
278 options.EmbeddedResources.Add (sourceFile1);
279 options.LinkedResources.Add (sourceFile2);
282 ICodeCompiler compiler = _codeProvider.CreateCompiler ();
283 CompilerResults results = compiler.CompileAssemblyFromFileBatch (options,
284 new string[] { sourceFile1, sourceFile2 });
286 // verify compilation was successful
287 AssertCompileResults (results, true);
289 Assembly compiledAssembly = results.CompiledAssembly;
291 Assert.IsNotNull (compiledAssembly, "#1");
292 Assert.AreEqual (string.Empty, compiledAssembly.Location, "#2");
293 Assert.IsNull (results.PathToAssembly, "#3");
295 Assert.IsNotNull (compiledAssembly.GetType ("Test1"), "#4");
296 Assert.IsNotNull (compiledAssembly.GetType ("Test2"), "#5");
298 // verify we don't cleanup files in temp directory too agressively
299 string[] tempFiles = Directory.GetFiles (_tempDir);
300 Assert.AreEqual (2, tempFiles.Length, "#6");
301 Assert.IsTrue (File.Exists (sourceFile1), "#7");
302 Assert.IsTrue (File.Exists (sourceFile2), "#8");
305 string[] resources = compiledAssembly.GetManifestResourceNames();
306 Assert.IsNotNull (resources, "#9");
307 Assert.AreEqual (2, resources.Length, "#10");
309 Assert.AreEqual ("file1.vb", resources[0], "#A1");
310 Assert.IsNull (compiledAssembly.GetFile ("file1.vb"), "#A2");
311 Assert.IsNotNull (compiledAssembly.GetManifestResourceStream ("file1.vb"), "#A3");
312 ManifestResourceInfo info = compiledAssembly.GetManifestResourceInfo ("file1.vb");
313 Assert.IsNotNull (info, "#A4");
314 Assert.IsNull (info.FileName, "#A5");
315 Assert.IsNull (info.ReferencedAssembly, "#A6");
316 Assert.AreEqual ((ResourceLocation.Embedded | ResourceLocation.ContainedInManifestFile), info.ResourceLocation, "#A7");
318 Assert.AreEqual ("file2.vb", resources[1], "#B1");
320 compiledAssembly.GetFile ("file2.vb");
322 } catch (FileNotFoundException) {
325 compiledAssembly.GetManifestResourceStream ("file2.vb");
327 } catch (FileNotFoundException) {
329 info = compiledAssembly.GetManifestResourceInfo ("file2.vb");
330 Assert.IsNotNull (info, "#B4");
331 Assert.IsNotNull (info.FileName, "#B5");
332 Assert.AreEqual ("file2.vb", info.FileName, "#N6");
333 Assert.IsNull (info.ReferencedAssembly, "#B7");
334 Assert.AreEqual ((ResourceLocation) 0, info.ResourceLocation, "#B8");
340 // there's not yet an mbas for the 2.0 profile
341 [Category ("NotWorking")]
343 public void CompileFromSource_InMemory ()
345 // create a file in temp directory to ensure that compiler is not removing
346 // too much (temporary) files
347 string tempFile = Path.Combine (_tempDir, "file." + _codeProvider.FileExtension);
348 using (FileStream fs = File.Create (tempFile)) {
352 CompilerParameters options = new CompilerParameters ();
353 options.GenerateExecutable = false;
354 options.GenerateInMemory = true;
355 options.TempFiles = new TempFileCollection (_tempDir);
357 ICodeCompiler compiler = _codeProvider.CreateCompiler ();
358 CompilerResults results = compiler.CompileAssemblyFromSource (options,
361 // verify compilation was successful
362 AssertCompileResults (results, true);
364 Assert.AreEqual (string.Empty, results.CompiledAssembly.Location, "#1");
365 Assert.IsNull (results.PathToAssembly, "#2");
366 Assert.IsNotNull (results.CompiledAssembly.GetType ("Test1"), "#3");
368 // verify we don't cleanup files in temp directory too agressively
369 string[] tempFiles = Directory.GetFiles (_tempDir);
370 Assert.AreEqual (1, tempFiles.Length, "#4");
371 Assert.AreEqual (tempFile, tempFiles[0], "#5");
376 // there's not yet an mbas for the 2.0 profile
377 [Category ("NotWorking")]
379 public void CompileFromSourceBatch_InMemory ()
381 // create a file in temp directory to ensure that compiler is not removing
382 // too much (temporary) files
383 string tempFile = Path.Combine (_tempDir, "file." + _codeProvider.FileExtension);
384 using (FileStream fs = File.Create (tempFile)) {
388 CompilerParameters options = new CompilerParameters ();
389 options.GenerateExecutable = false;
390 options.GenerateInMemory = true;
391 options.TempFiles = new TempFileCollection (_tempDir);
393 ICodeCompiler compiler = _codeProvider.CreateCompiler ();
394 CompilerResults results = compiler.CompileAssemblyFromSourceBatch (options,
395 new string[] { _sourceTest1, _sourceTest2 });
397 // verify compilation was successful
398 AssertCompileResults (results, true);
400 Assert.AreEqual (string.Empty, results.CompiledAssembly.Location, "#1");
401 Assert.IsNull (results.PathToAssembly, "#2");
403 Assert.IsNotNull (results.CompiledAssembly.GetType ("Test1"), "#3");
404 Assert.IsNotNull (results.CompiledAssembly.GetType ("Test2"), "#4");
406 // verify we don't cleanup files in temp directory too agressively
407 string[] tempFiles = Directory.GetFiles (_tempDir);
408 Assert.AreEqual (1, tempFiles.Length, "#5");
409 Assert.AreEqual (tempFile, tempFiles[0], "#6");
414 // there's not yet an mbas for the 2.0 profile
415 [Category ("NotWorking")]
417 public void CompileFromDom_NotInMemory ()
419 // create a file in temp directory to ensure that compiler is not removing
420 // too much (temporary) files
421 string tempFile = Path.Combine (_tempDir, "file." + _codeProvider.FileExtension);
422 using (FileStream fs = File.Create (tempFile)) {
426 // compile and verify result in separate appdomain to avoid file locks
427 AppDomain testDomain = CreateTestDomain ();
428 CrossDomainTester compileTester = CreateCrossDomainTester (testDomain);
430 string outputAssembly = null;
433 outputAssembly = compileTester.CompileAssemblyFromDom (_tempDir);
435 AppDomain.Unload (testDomain);
438 // there should be two files in temp dir: temp file and output assembly
439 string[] tempFiles = Directory.GetFiles (_tempDir);
440 Assert.AreEqual (2, tempFiles.Length, "#1");
441 Assert.IsTrue (File.Exists (outputAssembly), "#2");
442 Assert.IsTrue (File.Exists (tempFile), "#3");
447 // there's not yet an mbas for the 2.0 profile
448 [Category ("NotWorking")]
450 public void CompileFromDomBatch_NotInMemory ()
452 // create a file in temp directory to ensure that compiler is not removing
453 // too much (temporary) files
454 string tempFile = Path.Combine (_tempDir, "file." + _codeProvider.FileExtension);
455 using (FileStream fs = File.Create (tempFile)) {
459 // compile and verify result in separate appdomain to avoid file locks
460 AppDomain testDomain = CreateTestDomain ();
461 CrossDomainTester compileTester = CreateCrossDomainTester (testDomain);
463 string outputAssembly = null;
466 outputAssembly = compileTester.CompileAssemblyFromDomBatch (_tempDir);
468 AppDomain.Unload (testDomain);
471 // there should be two files in temp dir: temp file and output assembly
472 string[] tempFiles = Directory.GetFiles (_tempDir);
473 Assert.AreEqual (2, tempFiles.Length, "#1");
474 Assert.IsTrue (File.Exists (outputAssembly), "#2");
475 Assert.IsTrue (File.Exists (tempFile), "#3");
480 // there's not yet an mbas for the 2.0 profile
481 [Category ("NotWorking")]
483 public void CompileFromDom_InMemory ()
485 // create a file in temp directory to ensure that compiler is not removing
486 // too much (temporary) files
487 string tempFile = Path.Combine (_tempDir, "file." + _codeProvider.FileExtension);
488 using (FileStream fs = File.Create (tempFile)) {
492 CompilerParameters options = new CompilerParameters ();
493 options.GenerateExecutable = false;
494 options.GenerateInMemory = true;
495 options.TempFiles = new TempFileCollection (_tempDir);
497 ICodeCompiler compiler = _codeProvider.CreateCompiler ();
498 CompilerResults results = compiler.CompileAssemblyFromDom (options, new CodeCompileUnit ());
500 // verify compilation was successful
501 AssertCompileResults (results, true);
503 Assert.AreEqual (string.Empty, results.CompiledAssembly.Location, "#1");
504 Assert.IsNull (results.PathToAssembly, "#2");
506 // verify we don't cleanup files in temp directory too agressively
507 string[] tempFiles = Directory.GetFiles (_tempDir);
508 Assert.AreEqual (1, tempFiles.Length, "#3");
509 Assert.AreEqual (tempFile, tempFiles[0], "#4");
514 // there's not yet an mbas for the 2.0 profile
515 [Category ("NotWorking")]
517 public void CompileFromDomBatch_InMemory ()
519 // create a file in temp directory to ensure that compiler is not removing
520 // too much (temporary) files
521 string tempFile = Path.Combine (_tempDir, "file." + _codeProvider.FileExtension);
522 using (FileStream fs = File.Create (tempFile)) {
526 CompilerParameters options = new CompilerParameters ();
527 options.GenerateExecutable = false;
528 options.GenerateInMemory = true;
529 options.TempFiles = new TempFileCollection (_tempDir);
531 ICodeCompiler compiler = _codeProvider.CreateCompiler ();
532 CompilerResults results = compiler.CompileAssemblyFromDomBatch (options,
533 new CodeCompileUnit[] { new CodeCompileUnit (), new CodeCompileUnit () });
535 // verify compilation was successful
536 AssertCompileResults (results, true);
538 Assert.AreEqual (string.Empty, results.CompiledAssembly.Location, "#1");
539 Assert.IsNull (results.PathToAssembly, "#2");
541 // verify we don't cleanup files in temp directory too agressively
542 string[] tempFiles = Directory.GetFiles (_tempDir);
543 Assert.AreEqual (1, tempFiles.Length, "#3");
544 Assert.AreEqual (tempFile, tempFiles[0], "#4");
548 public void CreateParser ()
550 //System.CodeDom.Compiler.ICodeParser CreateParser()
554 public void CreateObjRef ()
556 //System.Runtime.Remoting.ObjRef CreateObjRef(System.Type requestedType)
562 return OS == OsType.Windows;
569 return OS == OsType.Unix;
576 return OS == OsType.Mac;
580 private static string CreateTempDirectory ()
582 // create a uniquely named zero-byte file
583 string tempFile = Path.GetTempFileName ();
584 // remove the temporary file
585 File.Delete (tempFile);
586 // create a directory named after the unique temporary file
587 Directory.CreateDirectory (tempFile);
588 // return the path to the temporary directory
592 private static void RemoveDirectory (string path)
595 if (Directory.Exists (path)) {
596 string[] directoryNames = Directory.GetDirectories (path);
597 foreach (string directoryName in directoryNames) {
598 RemoveDirectory (directoryName);
600 string[] fileNames = Directory.GetFiles (path);
601 foreach (string fileName in fileNames) {
602 File.Delete (fileName);
604 Directory.Delete (path, true);
606 } catch (Exception ex) {
607 throw new AssertionException ("Unable to cleanup '" + path + "'.", ex);
611 private static void AssertCompileResults (CompilerResults results, bool allowWarnings)
613 foreach (CompilerError compilerError in results.Errors) {
614 if (allowWarnings && compilerError.IsWarning) {
618 Assert.Fail (compilerError.ToString ());
622 private static AppDomain CreateTestDomain ()
624 return AppDomain.CreateDomain ("CompileFromDom", AppDomain.CurrentDomain.Evidence,
625 AppDomain.CurrentDomain.SetupInformation);
628 private static CrossDomainTester CreateCrossDomainTester (AppDomain domain)
630 Type testerType = typeof (CrossDomainTester);
632 return (CrossDomainTester) domain.CreateInstanceAndUnwrap (
633 testerType.Assembly.FullName, testerType.FullName, false,
634 BindingFlags.Public | BindingFlags.Instance, null, new object[0],
635 CultureInfo.InvariantCulture, new object[0], domain.Evidence);
638 private class CrossDomainTester : MarshalByRefObject
640 public string CompileAssemblyFromDom (string tempDir)
642 CompilerParameters options = new CompilerParameters ();
643 options.GenerateExecutable = false;
644 options.GenerateInMemory = false;
645 options.TempFiles = new TempFileCollection (tempDir);
647 VBCodeProvider codeProvider = new VBCodeProvider ();
648 ICodeCompiler compiler = codeProvider.CreateCompiler ();
649 CompilerResults results = compiler.CompileAssemblyFromDom (options, new CodeCompileUnit ());
651 // verify compilation was successful
652 AssertCompileResults (results, true);
654 Assert.IsTrue (results.CompiledAssembly.Location.Length != 0,
655 "Location should not be empty string");
656 Assert.IsNotNull (results.PathToAssembly, "PathToAssembly should not be null");
658 return results.PathToAssembly;
661 public string CompileAssemblyFromDomBatch (string tempDir)
663 CompilerParameters options = new CompilerParameters ();
664 options.GenerateExecutable = false;
665 options.GenerateInMemory = false;
666 options.TempFiles = new TempFileCollection (tempDir);
668 VBCodeProvider codeProvider = new VBCodeProvider ();
669 ICodeCompiler compiler = codeProvider.CreateCompiler ();
670 CompilerResults results = compiler.CompileAssemblyFromDomBatch (options, new CodeCompileUnit[] { new CodeCompileUnit (), new CodeCompileUnit () });
672 // verify compilation was successful
673 AssertCompileResults (results, true);
675 Assert.IsTrue (results.CompiledAssembly.Location.Length != 0,
676 "Location should not be empty string");
677 Assert.IsNotNull (results.PathToAssembly, "PathToAssembly should not be null");
679 return results.PathToAssembly;