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 ICodeCompiler compiler = _codeProvider.CreateCompiler ();
216 CompilerResults results = compiler.CompileAssemblyFromFile (options,
219 // verify compilation was successful
220 AssertCompileResults (results, true);
222 Assert.AreEqual (string.Empty, results.CompiledAssembly.Location, "#1");
223 Assert.IsNull (results.PathToAssembly, "#2");
224 Assert.IsNotNull (results.CompiledAssembly.GetType ("Test1"), "#3");
226 // verify we don't cleanup files in temp directory too agressively
227 string[] tempFiles = Directory.GetFiles (_tempDir);
228 Assert.AreEqual (1, tempFiles.Length, "#4");
229 Assert.AreEqual (sourceFile, tempFiles[0], "#5");
234 // there's not yet an mbas for the 2.0 profile
235 [Category ("NotWorking")]
237 public void CompileFromFileBatch_InMemory ()
239 // create vb source file
240 string sourceFile1 = Path.Combine (_tempDir, "file." + _codeProvider.FileExtension);
241 using (FileStream f = new FileStream (sourceFile1, FileMode.Create)) {
242 using (StreamWriter s = new StreamWriter (f)) {
243 s.Write (_sourceTest1);
249 string sourceFile2 = Path.Combine (_tempDir, "file2." + _codeProvider.FileExtension);
250 using (FileStream f = new FileStream (sourceFile2, FileMode.Create)) {
251 using (StreamWriter s = new StreamWriter (f)) {
252 s.Write (_sourceTest2);
258 CompilerParameters options = new CompilerParameters ();
259 options.GenerateExecutable = false;
260 options.GenerateInMemory = true;
261 options.TempFiles = new TempFileCollection (_tempDir);
263 ICodeCompiler compiler = _codeProvider.CreateCompiler ();
264 CompilerResults results = compiler.CompileAssemblyFromFileBatch (options,
265 new string[] { sourceFile1, sourceFile2 });
267 // verify compilation was successful
268 AssertCompileResults (results, true);
270 Assert.AreEqual (string.Empty, results.CompiledAssembly.Location, "#1");
271 Assert.IsNull (results.PathToAssembly, "#2");
273 Assert.IsNotNull (results.CompiledAssembly.GetType ("Test1"), "#3");
274 Assert.IsNotNull (results.CompiledAssembly.GetType ("Test2"), "#4");
276 // verify we don't cleanup files in temp directory too agressively
277 string[] tempFiles = Directory.GetFiles (_tempDir);
278 Assert.AreEqual (2, tempFiles.Length, "#5");
279 Assert.IsTrue (File.Exists (sourceFile1), "#6");
280 Assert.IsTrue (File.Exists (sourceFile2), "#7");
285 // there's not yet an mbas for the 2.0 profile
286 [Category ("NotWorking")]
288 public void CompileFromSource_InMemory ()
290 // create a file in temp directory to ensure that compiler is not removing
291 // too much (temporary) files
292 string tempFile = Path.Combine (_tempDir, "file." + _codeProvider.FileExtension);
293 using (FileStream fs = File.Create (tempFile)) {
297 CompilerParameters options = new CompilerParameters ();
298 options.GenerateExecutable = false;
299 options.GenerateInMemory = true;
300 options.TempFiles = new TempFileCollection (_tempDir);
302 ICodeCompiler compiler = _codeProvider.CreateCompiler ();
303 CompilerResults results = compiler.CompileAssemblyFromSource (options,
306 // verify compilation was successful
307 AssertCompileResults (results, true);
309 Assert.AreEqual (string.Empty, results.CompiledAssembly.Location, "#1");
310 Assert.IsNull (results.PathToAssembly, "#2");
311 Assert.IsNotNull (results.CompiledAssembly.GetType ("Test1"), "#3");
313 // verify we don't cleanup files in temp directory too agressively
314 string[] tempFiles = Directory.GetFiles (_tempDir);
315 Assert.AreEqual (1, tempFiles.Length, "#4");
316 Assert.AreEqual (tempFile, tempFiles[0], "#5");
321 // there's not yet an mbas for the 2.0 profile
322 [Category ("NotWorking")]
324 public void CompileFromSourceBatch_InMemory ()
326 // create a file in temp directory to ensure that compiler is not removing
327 // too much (temporary) files
328 string tempFile = Path.Combine (_tempDir, "file." + _codeProvider.FileExtension);
329 using (FileStream fs = File.Create (tempFile)) {
333 CompilerParameters options = new CompilerParameters ();
334 options.GenerateExecutable = false;
335 options.GenerateInMemory = true;
336 options.TempFiles = new TempFileCollection (_tempDir);
338 ICodeCompiler compiler = _codeProvider.CreateCompiler ();
339 CompilerResults results = compiler.CompileAssemblyFromSourceBatch (options,
340 new string[] { _sourceTest1, _sourceTest2 });
342 // verify compilation was successful
343 AssertCompileResults (results, true);
345 Assert.AreEqual (string.Empty, results.CompiledAssembly.Location, "#1");
346 Assert.IsNull (results.PathToAssembly, "#2");
348 Assert.IsNotNull (results.CompiledAssembly.GetType ("Test1"), "#3");
349 Assert.IsNotNull (results.CompiledAssembly.GetType ("Test2"), "#4");
351 // verify we don't cleanup files in temp directory too agressively
352 string[] tempFiles = Directory.GetFiles (_tempDir);
353 Assert.AreEqual (1, tempFiles.Length, "#5");
354 Assert.AreEqual (tempFile, tempFiles[0], "#6");
359 // there's not yet an mbas for the 2.0 profile
360 [Category ("NotWorking")]
362 public void CompileFromDom_NotInMemory ()
364 // create a file in temp directory to ensure that compiler is not removing
365 // too much (temporary) files
366 string tempFile = Path.Combine (_tempDir, "file." + _codeProvider.FileExtension);
367 using (FileStream fs = File.Create (tempFile)) {
371 // compile and verify result in separate appdomain to avoid file locks
372 AppDomain testDomain = CreateTestDomain ();
373 CrossDomainTester compileTester = CreateCrossDomainTester (testDomain);
375 string outputAssembly = null;
378 outputAssembly = compileTester.CompileAssemblyFromDom (_tempDir);
380 AppDomain.Unload (testDomain);
383 // there should be two files in temp dir: temp file and output assembly
384 string[] tempFiles = Directory.GetFiles (_tempDir);
385 Assert.AreEqual (2, tempFiles.Length, "#1");
386 Assert.IsTrue (File.Exists (outputAssembly), "#2");
387 Assert.IsTrue (File.Exists (tempFile), "#3");
392 // there's not yet an mbas for the 2.0 profile
393 [Category ("NotWorking")]
395 public void CompileFromDomBatch_NotInMemory ()
397 // create a file in temp directory to ensure that compiler is not removing
398 // too much (temporary) files
399 string tempFile = Path.Combine (_tempDir, "file." + _codeProvider.FileExtension);
400 using (FileStream fs = File.Create (tempFile)) {
404 // compile and verify result in separate appdomain to avoid file locks
405 AppDomain testDomain = CreateTestDomain ();
406 CrossDomainTester compileTester = CreateCrossDomainTester (testDomain);
408 string outputAssembly = null;
411 outputAssembly = compileTester.CompileAssemblyFromDomBatch (_tempDir);
413 AppDomain.Unload (testDomain);
416 // there should be two files in temp dir: temp file and output assembly
417 string[] tempFiles = Directory.GetFiles (_tempDir);
418 Assert.AreEqual (2, tempFiles.Length, "#1");
419 Assert.IsTrue (File.Exists (outputAssembly), "#2");
420 Assert.IsTrue (File.Exists (tempFile), "#3");
425 // there's not yet an mbas for the 2.0 profile
426 [Category ("NotWorking")]
428 public void CompileFromDom_InMemory ()
430 // create a file in temp directory to ensure that compiler is not removing
431 // too much (temporary) files
432 string tempFile = Path.Combine (_tempDir, "file." + _codeProvider.FileExtension);
433 using (FileStream fs = File.Create (tempFile)) {
437 CompilerParameters options = new CompilerParameters ();
438 options.GenerateExecutable = false;
439 options.GenerateInMemory = true;
440 options.TempFiles = new TempFileCollection (_tempDir);
442 ICodeCompiler compiler = _codeProvider.CreateCompiler ();
443 CompilerResults results = compiler.CompileAssemblyFromDom (options, new CodeCompileUnit ());
445 // verify compilation was successful
446 AssertCompileResults (results, true);
448 Assert.AreEqual (string.Empty, results.CompiledAssembly.Location, "#1");
449 Assert.IsNull (results.PathToAssembly, "#2");
451 // verify we don't cleanup files in temp directory too agressively
452 string[] tempFiles = Directory.GetFiles (_tempDir);
453 Assert.AreEqual (1, tempFiles.Length, "#3");
454 Assert.AreEqual (tempFile, tempFiles[0], "#4");
459 // there's not yet an mbas for the 2.0 profile
460 [Category ("NotWorking")]
462 public void CompileFromDomBatch_InMemory ()
464 // create a file in temp directory to ensure that compiler is not removing
465 // too much (temporary) files
466 string tempFile = Path.Combine (_tempDir, "file." + _codeProvider.FileExtension);
467 using (FileStream fs = File.Create (tempFile)) {
471 CompilerParameters options = new CompilerParameters ();
472 options.GenerateExecutable = false;
473 options.GenerateInMemory = true;
474 options.TempFiles = new TempFileCollection (_tempDir);
476 ICodeCompiler compiler = _codeProvider.CreateCompiler ();
477 CompilerResults results = compiler.CompileAssemblyFromDomBatch (options,
478 new CodeCompileUnit[] { new CodeCompileUnit (), new CodeCompileUnit () });
480 // verify compilation was successful
481 AssertCompileResults (results, true);
483 Assert.AreEqual (string.Empty, results.CompiledAssembly.Location, "#1");
484 Assert.IsNull (results.PathToAssembly, "#2");
486 // verify we don't cleanup files in temp directory too agressively
487 string[] tempFiles = Directory.GetFiles (_tempDir);
488 Assert.AreEqual (1, tempFiles.Length, "#3");
489 Assert.AreEqual (tempFile, tempFiles[0], "#4");
493 public void CreateParser ()
495 //System.CodeDom.Compiler.ICodeParser CreateParser()
499 public void CreateObjRef ()
501 //System.Runtime.Remoting.ObjRef CreateObjRef(System.Type requestedType)
507 return OS == OsType.Windows;
514 return OS == OsType.Unix;
521 return OS == OsType.Mac;
525 private static string CreateTempDirectory ()
527 // create a uniquely named zero-byte file
528 string tempFile = Path.GetTempFileName ();
529 // remove the temporary file
530 File.Delete (tempFile);
531 // create a directory named after the unique temporary file
532 Directory.CreateDirectory (tempFile);
533 // return the path to the temporary directory
537 private static void RemoveDirectory (string path)
540 if (Directory.Exists (path)) {
541 string[] directoryNames = Directory.GetDirectories (path);
542 foreach (string directoryName in directoryNames) {
543 RemoveDirectory (directoryName);
545 string[] fileNames = Directory.GetFiles (path);
546 foreach (string fileName in fileNames) {
547 File.Delete (fileName);
549 Directory.Delete (path, true);
551 } catch (Exception ex) {
552 throw new AssertionException ("Unable to cleanup '" + path + "'.", ex);
556 private static void AssertCompileResults (CompilerResults results, bool allowWarnings)
558 foreach (CompilerError compilerError in results.Errors) {
559 if (allowWarnings && compilerError.IsWarning) {
563 Assert.Fail (compilerError.ToString ());
567 private static AppDomain CreateTestDomain ()
569 return AppDomain.CreateDomain ("CompileFromDom", AppDomain.CurrentDomain.Evidence,
570 AppDomain.CurrentDomain.SetupInformation);
573 private static CrossDomainTester CreateCrossDomainTester (AppDomain domain)
575 Type testerType = typeof (CrossDomainTester);
577 return (CrossDomainTester) domain.CreateInstanceAndUnwrap (
578 testerType.Assembly.FullName, testerType.FullName, false,
579 BindingFlags.Public | BindingFlags.Instance, null, new object[0],
580 CultureInfo.InvariantCulture, new object[0], domain.Evidence);
583 private class CrossDomainTester : MarshalByRefObject
585 public string CompileAssemblyFromDom (string tempDir)
587 CompilerParameters options = new CompilerParameters ();
588 options.GenerateExecutable = false;
589 options.GenerateInMemory = false;
590 options.TempFiles = new TempFileCollection (tempDir);
592 VBCodeProvider codeProvider = new VBCodeProvider ();
593 ICodeCompiler compiler = codeProvider.CreateCompiler ();
594 CompilerResults results = compiler.CompileAssemblyFromDom (options, new CodeCompileUnit ());
596 // verify compilation was successful
597 AssertCompileResults (results, true);
599 Assert.IsTrue (results.CompiledAssembly.Location.Length != 0,
600 "Location should not be empty string");
601 Assert.IsNotNull (results.PathToAssembly, "PathToAssembly should not be null");
603 return results.PathToAssembly;
606 public string CompileAssemblyFromDomBatch (string tempDir)
608 CompilerParameters options = new CompilerParameters ();
609 options.GenerateExecutable = false;
610 options.GenerateInMemory = false;
611 options.TempFiles = new TempFileCollection (tempDir);
613 VBCodeProvider codeProvider = new VBCodeProvider ();
614 ICodeCompiler compiler = codeProvider.CreateCompiler ();
615 CompilerResults results = compiler.CompileAssemblyFromDomBatch (options, new CodeCompileUnit[] { new CodeCompileUnit (), new CodeCompileUnit () });
617 // verify compilation was successful
618 AssertCompileResults (results, true);
620 Assert.IsTrue (results.CompiledAssembly.Location.Length != 0,
621 "Location should not be empty string");
622 Assert.IsNotNull (results.PathToAssembly, "PathToAssembly should not be null");
624 return results.PathToAssembly;