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 [Ignore ("Bug #75223")]
122 public void CreateCompiler ()
124 // Prepare the compilation
125 ICodeCompiler codeCompiler = _codeProvider.CreateCompiler ();
126 Assert.IsNotNull (codeCompiler, "#JW30 - CreateCompiler");
128 CompilerParameters options = new CompilerParameters ();
129 options.GenerateExecutable = true;
130 options.IncludeDebugInformation = true;
131 options.TreatWarningsAsErrors = true;
133 // process compilation
134 CompilerResults compilerResults = codeCompiler.CompileAssemblyFromSource (options,
135 "public class TestModule" + Environment.NewLine + "public shared sub Main()"
136 + Environment.NewLine + "System.Console.Write(\"Hello world!\")"
137 + Environment.NewLine + "End Sub" + Environment.NewLine + "End Class");
139 // Analyse the compilation success/messages
140 StringCollection MyOutput = compilerResults.Output;
141 string MyOutStr = "";
142 foreach (string MyStr in MyOutput) {
143 MyOutStr += MyStr + Environment.NewLine + Environment.NewLine;
146 if (compilerResults.Errors.Count != 0) {
147 Assert.Fail ("#JW31 - Hello world compilation: " + MyOutStr);
151 Assembly MyAss = compilerResults.CompiledAssembly;
152 } catch (Exception ex) {
153 Assert.Fail ("#JW32 - compilerResults.CompiledAssembly hasn't been an expected object" +
154 Environment.NewLine + ex.Message + Environment.NewLine + ex.StackTrace);
157 // Execute the test app
158 ProcessStartInfo NewProcInfo = new ProcessStartInfo ();
160 NewProcInfo.FileName = compilerResults.CompiledAssembly.Location;
162 NewProcInfo.FileName = "mono";
163 NewProcInfo.Arguments = compilerResults.CompiledAssembly.Location;
165 NewProcInfo.RedirectStandardOutput = true;
166 NewProcInfo.UseShellExecute = false;
167 NewProcInfo.CreateNoWindow = true;
168 string TestAppOutput = "";
170 Process MyProc = Process.Start (NewProcInfo);
171 MyProc.WaitForExit ();
172 TestAppOutput = MyProc.StandardOutput.ReadToEnd ();
175 } catch (Exception ex) {
176 Assert.Fail ("#JW34 - " + ex.Message + Environment.NewLine + ex.StackTrace);
178 Assert.AreEqual ("Hello world!", TestAppOutput, "#JW33 - Application output");
182 File.Delete (NewProcInfo.FileName);
187 public void CreateGenerator ()
189 ICodeGenerator MyVBCodeGen;
190 MyVBCodeGen = _codeProvider.CreateGenerator ();
191 Assert.IsNotNull (MyVBCodeGen, "#JW40 - CreateGenerator");
192 Assert.IsTrue (MyVBCodeGen.Supports (GeneratorSupport.DeclareEnums), "#JW41");
197 // there's not yet an mbas for the 2.0 profile
198 [Category ("NotWorking")]
200 public void CompileFromFile_InMemory ()
202 // create vb source file
203 string sourceFile = Path.Combine (_tempDir, "file." + _codeProvider.FileExtension);
204 using (FileStream f = new FileStream (sourceFile, FileMode.Create)) {
205 using (StreamWriter s = new StreamWriter (f)) {
206 s.Write (_sourceTest1);
212 CompilerParameters options = new CompilerParameters ();
213 options.GenerateExecutable = false;
214 options.GenerateInMemory = true;
215 options.TempFiles = new TempFileCollection (_tempDir);
217 ICodeCompiler compiler = _codeProvider.CreateCompiler ();
218 CompilerResults results = compiler.CompileAssemblyFromFile (options,
221 // verify compilation was successful
222 AssertCompileResults (results, true);
224 Assert.AreEqual (string.Empty, results.CompiledAssembly.Location, "#1");
225 Assert.IsNull (results.PathToAssembly, "#2");
226 Assert.IsNotNull (results.CompiledAssembly.GetType ("Test1"), "#3");
228 // verify we don't cleanup files in temp directory too agressively
229 string[] tempFiles = Directory.GetFiles (_tempDir);
230 Assert.AreEqual (1, tempFiles.Length, "#4");
231 Assert.AreEqual (sourceFile, tempFiles[0], "#5");
236 // there's not yet an mbas for the 2.0 profile
237 [Category ("NotWorking")]
239 public void CompileFromFileBatch_InMemory ()
241 // create vb source file
242 string sourceFile1 = Path.Combine (_tempDir, "file." + _codeProvider.FileExtension);
243 using (FileStream f = new FileStream (sourceFile1, FileMode.Create)) {
244 using (StreamWriter s = new StreamWriter (f)) {
245 s.Write (_sourceTest1);
251 string sourceFile2 = Path.Combine (_tempDir, "file2." + _codeProvider.FileExtension);
252 using (FileStream f = new FileStream (sourceFile2, FileMode.Create)) {
253 using (StreamWriter s = new StreamWriter (f)) {
254 s.Write (_sourceTest2);
260 CompilerParameters options = new CompilerParameters ();
261 options.GenerateExecutable = false;
262 options.GenerateInMemory = true;
263 options.TempFiles = new TempFileCollection (_tempDir);
265 ICodeCompiler compiler = _codeProvider.CreateCompiler ();
266 CompilerResults results = compiler.CompileAssemblyFromFileBatch (options,
267 new string[] { sourceFile1, sourceFile2 });
269 // verify compilation was successful
270 AssertCompileResults (results, true);
272 Assert.AreEqual (string.Empty, results.CompiledAssembly.Location, "#1");
273 Assert.IsNull (results.PathToAssembly, "#2");
275 Assert.IsNotNull (results.CompiledAssembly.GetType ("Test1"), "#3");
276 Assert.IsNotNull (results.CompiledAssembly.GetType ("Test2"), "#4");
278 // verify we don't cleanup files in temp directory too agressively
279 string[] tempFiles = Directory.GetFiles (_tempDir);
280 Assert.AreEqual (2, tempFiles.Length, "#5");
281 Assert.IsTrue (File.Exists (sourceFile1), "#6");
282 Assert.IsTrue (File.Exists (sourceFile2), "#7");
287 // there's not yet an mbas for the 2.0 profile
288 [Category ("NotWorking")]
290 public void CompileFromSource_InMemory ()
292 // create a file in temp directory to ensure that compiler is not removing
293 // too much (temporary) files
294 string tempFile = Path.Combine (_tempDir, "file." + _codeProvider.FileExtension);
295 using (FileStream fs = File.Create (tempFile)) {
299 CompilerParameters options = new CompilerParameters ();
300 options.GenerateExecutable = false;
301 options.GenerateInMemory = true;
302 options.TempFiles = new TempFileCollection (_tempDir);
304 ICodeCompiler compiler = _codeProvider.CreateCompiler ();
305 CompilerResults results = compiler.CompileAssemblyFromSource (options,
308 // verify compilation was successful
309 AssertCompileResults (results, true);
311 Assert.AreEqual (string.Empty, results.CompiledAssembly.Location, "#1");
312 Assert.IsNull (results.PathToAssembly, "#2");
313 Assert.IsNotNull (results.CompiledAssembly.GetType ("Test1"), "#3");
315 // verify we don't cleanup files in temp directory too agressively
316 string[] tempFiles = Directory.GetFiles (_tempDir);
317 Assert.AreEqual (1, tempFiles.Length, "#4");
318 Assert.AreEqual (tempFile, tempFiles[0], "#5");
323 // there's not yet an mbas for the 2.0 profile
324 [Category ("NotWorking")]
326 public void CompileFromSourceBatch_InMemory ()
328 // create a file in temp directory to ensure that compiler is not removing
329 // too much (temporary) files
330 string tempFile = Path.Combine (_tempDir, "file." + _codeProvider.FileExtension);
331 using (FileStream fs = File.Create (tempFile)) {
335 CompilerParameters options = new CompilerParameters ();
336 options.GenerateExecutable = false;
337 options.GenerateInMemory = true;
338 options.TempFiles = new TempFileCollection (_tempDir);
340 ICodeCompiler compiler = _codeProvider.CreateCompiler ();
341 CompilerResults results = compiler.CompileAssemblyFromSourceBatch (options,
342 new string[] { _sourceTest1, _sourceTest2 });
344 // verify compilation was successful
345 AssertCompileResults (results, true);
347 Assert.AreEqual (string.Empty, results.CompiledAssembly.Location, "#1");
348 Assert.IsNull (results.PathToAssembly, "#2");
350 Assert.IsNotNull (results.CompiledAssembly.GetType ("Test1"), "#3");
351 Assert.IsNotNull (results.CompiledAssembly.GetType ("Test2"), "#4");
353 // verify we don't cleanup files in temp directory too agressively
354 string[] tempFiles = Directory.GetFiles (_tempDir);
355 Assert.AreEqual (1, tempFiles.Length, "#5");
356 Assert.AreEqual (tempFile, tempFiles[0], "#6");
361 // there's not yet an mbas for the 2.0 profile
362 [Category ("NotWorking")]
364 public void CompileFromDom_NotInMemory ()
366 // create a file in temp directory to ensure that compiler is not removing
367 // too much (temporary) files
368 string tempFile = Path.Combine (_tempDir, "file." + _codeProvider.FileExtension);
369 using (FileStream fs = File.Create (tempFile)) {
373 // compile and verify result in separate appdomain to avoid file locks
374 AppDomain testDomain = CreateTestDomain ();
375 CrossDomainTester compileTester = CreateCrossDomainTester (testDomain);
377 string outputAssembly = null;
380 outputAssembly = compileTester.CompileAssemblyFromDom (_tempDir);
382 AppDomain.Unload (testDomain);
385 // there should be two files in temp dir: temp file and output assembly
386 string[] tempFiles = Directory.GetFiles (_tempDir);
387 Assert.AreEqual (2, tempFiles.Length, "#1");
388 Assert.IsTrue (File.Exists (outputAssembly), "#2");
389 Assert.IsTrue (File.Exists (tempFile), "#3");
394 // there's not yet an mbas for the 2.0 profile
395 [Category ("NotWorking")]
397 public void CompileFromDomBatch_NotInMemory ()
399 // create a file in temp directory to ensure that compiler is not removing
400 // too much (temporary) files
401 string tempFile = Path.Combine (_tempDir, "file." + _codeProvider.FileExtension);
402 using (FileStream fs = File.Create (tempFile)) {
406 // compile and verify result in separate appdomain to avoid file locks
407 AppDomain testDomain = CreateTestDomain ();
408 CrossDomainTester compileTester = CreateCrossDomainTester (testDomain);
410 string outputAssembly = null;
413 outputAssembly = compileTester.CompileAssemblyFromDomBatch (_tempDir);
415 AppDomain.Unload (testDomain);
418 // there should be two files in temp dir: temp file and output assembly
419 string[] tempFiles = Directory.GetFiles (_tempDir);
420 Assert.AreEqual (2, tempFiles.Length, "#1");
421 Assert.IsTrue (File.Exists (outputAssembly), "#2");
422 Assert.IsTrue (File.Exists (tempFile), "#3");
427 // there's not yet an mbas for the 2.0 profile
428 [Category ("NotWorking")]
430 public void CompileFromDom_InMemory ()
432 // create a file in temp directory to ensure that compiler is not removing
433 // too much (temporary) files
434 string tempFile = Path.Combine (_tempDir, "file." + _codeProvider.FileExtension);
435 using (FileStream fs = File.Create (tempFile)) {
439 CompilerParameters options = new CompilerParameters ();
440 options.GenerateExecutable = false;
441 options.GenerateInMemory = true;
442 options.TempFiles = new TempFileCollection (_tempDir);
444 ICodeCompiler compiler = _codeProvider.CreateCompiler ();
445 CompilerResults results = compiler.CompileAssemblyFromDom (options, new CodeCompileUnit ());
447 // verify compilation was successful
448 AssertCompileResults (results, true);
450 Assert.AreEqual (string.Empty, results.CompiledAssembly.Location, "#1");
451 Assert.IsNull (results.PathToAssembly, "#2");
453 // verify we don't cleanup files in temp directory too agressively
454 string[] tempFiles = Directory.GetFiles (_tempDir);
455 Assert.AreEqual (1, tempFiles.Length, "#3");
456 Assert.AreEqual (tempFile, tempFiles[0], "#4");
461 // there's not yet an mbas for the 2.0 profile
462 [Category ("NotWorking")]
464 public void CompileFromDomBatch_InMemory ()
466 // create a file in temp directory to ensure that compiler is not removing
467 // too much (temporary) files
468 string tempFile = Path.Combine (_tempDir, "file." + _codeProvider.FileExtension);
469 using (FileStream fs = File.Create (tempFile)) {
473 CompilerParameters options = new CompilerParameters ();
474 options.GenerateExecutable = false;
475 options.GenerateInMemory = true;
476 options.TempFiles = new TempFileCollection (_tempDir);
478 ICodeCompiler compiler = _codeProvider.CreateCompiler ();
479 CompilerResults results = compiler.CompileAssemblyFromDomBatch (options,
480 new CodeCompileUnit[] { new CodeCompileUnit (), new CodeCompileUnit () });
482 // verify compilation was successful
483 AssertCompileResults (results, true);
485 Assert.AreEqual (string.Empty, results.CompiledAssembly.Location, "#1");
486 Assert.IsNull (results.PathToAssembly, "#2");
488 // verify we don't cleanup files in temp directory too agressively
489 string[] tempFiles = Directory.GetFiles (_tempDir);
490 Assert.AreEqual (1, tempFiles.Length, "#3");
491 Assert.AreEqual (tempFile, tempFiles[0], "#4");
495 public void CreateParser ()
497 //System.CodeDom.Compiler.ICodeParser CreateParser()
501 public void CreateObjRef ()
503 //System.Runtime.Remoting.ObjRef CreateObjRef(System.Type requestedType)
509 return OS == OsType.Windows;
516 return OS == OsType.Unix;
523 return OS == OsType.Mac;
527 private static string CreateTempDirectory ()
529 // create a uniquely named zero-byte file
530 string tempFile = Path.GetTempFileName ();
531 // remove the temporary file
532 File.Delete (tempFile);
533 // create a directory named after the unique temporary file
534 Directory.CreateDirectory (tempFile);
535 // return the path to the temporary directory
539 private static void RemoveDirectory (string path)
542 if (Directory.Exists (path)) {
543 string[] directoryNames = Directory.GetDirectories (path);
544 foreach (string directoryName in directoryNames) {
545 RemoveDirectory (directoryName);
547 string[] fileNames = Directory.GetFiles (path);
548 foreach (string fileName in fileNames) {
549 File.Delete (fileName);
551 Directory.Delete (path, true);
553 } catch (Exception ex) {
554 throw new AssertionException ("Unable to cleanup '" + path + "'.", ex);
558 private static void AssertCompileResults (CompilerResults results, bool allowWarnings)
560 foreach (CompilerError compilerError in results.Errors) {
561 if (allowWarnings && compilerError.IsWarning) {
565 Assert.Fail (compilerError.ToString ());
569 private static AppDomain CreateTestDomain ()
571 return AppDomain.CreateDomain ("CompileFromDom", AppDomain.CurrentDomain.Evidence,
572 AppDomain.CurrentDomain.SetupInformation);
575 private static CrossDomainTester CreateCrossDomainTester (AppDomain domain)
577 Type testerType = typeof (CrossDomainTester);
579 return (CrossDomainTester) domain.CreateInstanceAndUnwrap (
580 testerType.Assembly.FullName, testerType.FullName, false,
581 BindingFlags.Public | BindingFlags.Instance, null, new object[0],
582 CultureInfo.InvariantCulture, new object[0], domain.Evidence);
585 private class CrossDomainTester : MarshalByRefObject
587 public string CompileAssemblyFromDom (string tempDir)
589 CompilerParameters options = new CompilerParameters ();
590 options.GenerateExecutable = false;
591 options.GenerateInMemory = false;
592 options.TempFiles = new TempFileCollection (tempDir);
594 VBCodeProvider codeProvider = new VBCodeProvider ();
595 ICodeCompiler compiler = codeProvider.CreateCompiler ();
596 CompilerResults results = compiler.CompileAssemblyFromDom (options, new CodeCompileUnit ());
598 // verify compilation was successful
599 AssertCompileResults (results, true);
601 Assert.IsTrue (results.CompiledAssembly.Location.Length != 0,
602 "Location should not be empty string");
603 Assert.IsNotNull (results.PathToAssembly, "PathToAssembly should not be null");
605 return results.PathToAssembly;
608 public string CompileAssemblyFromDomBatch (string tempDir)
610 CompilerParameters options = new CompilerParameters ();
611 options.GenerateExecutable = false;
612 options.GenerateInMemory = false;
613 options.TempFiles = new TempFileCollection (tempDir);
615 VBCodeProvider codeProvider = new VBCodeProvider ();
616 ICodeCompiler compiler = codeProvider.CreateCompiler ();
617 CompilerResults results = compiler.CompileAssemblyFromDomBatch (options, new CodeCompileUnit[] { new CodeCompileUnit (), new CodeCompileUnit () });
619 // verify compilation was successful
620 AssertCompileResults (results, true);
622 Assert.IsTrue (results.CompiledAssembly.Location.Length != 0,
623 "Location should not be empty string");
624 Assert.IsNotNull (results.PathToAssembly, "PathToAssembly should not be null");
626 return results.PathToAssembly;