3 // - If xunit can't laod a trait discoverer assembly, it silently ignores the error.
4 // - the RemoteTestExecutor code used by corefx only seems to work if
5 // the app is executed from the binary dir using dotnet ./<dllname>.
6 // If ran using dotnet run, it seems to invoke itself instead of
7 // RemoteExecutorConsoleApp.exe.
8 // If ran using dotnet bin/.../<dllname>, it fails with:
9 // No executable found matching command "dotnet-<dir>/RemoteExecutorConsoleApp.exe"
13 using System
.Runtime
.Loader
;
15 using System
.Threading
;
17 using System
.Reflection
;
18 using System
.Collections
;
19 using System
.Collections
.Generic
;
21 using System
.CodeDom
.Compiler
;
22 using Microsoft
.CSharp
;
25 using Xunit
.Abstractions
;
26 using Xunit
.ConsoleClient
;
28 class MsgSink
: IMessageSink
{
30 public bool OnMessage(IMessageSinkMessage message
) {
31 Console
.WriteLine (((Xunit
.Sdk
.DiagnosticMessage
)message
).Message
);
35 public bool OnMessageWithTypes(IMessageSinkMessage message
, HashSet
<string> messageTypes
) {
36 Console
.WriteLine ("m2");
43 static string GetTypeName (Type t
) {
45 return t
.DeclaringType
.FullName
+ "." + t
.Name
;
50 static void ComputeTraits (Assembly assembly
, XunitTestCase tc
) {
51 // Traits are not set because of some assembly loading problems i.e. Assembly.Load (new AssemblyName ("Microsoft.DotNet.XUnitExtensions")) fails
52 // So load them manually
53 foreach (ReflectionAttributeInfo attr
in ((ReflectionMethodInfo
)tc
.Method
).GetCustomAttributes (typeof (ITraitAttribute
))) {
54 var discovererAttr
= attr
.GetCustomAttributes (typeof (TraitDiscovererAttribute
)).FirstOrDefault();
55 if (discovererAttr
!= null) {
56 var discoverer_args
= discovererAttr
.GetConstructorArguments().Cast
<string>().ToList();
57 var disc_assembly
= Assembly
.LoadFrom (Path
.Combine (Path
.GetDirectoryName (assembly
.Location
), discoverer_args
[1]) + ".dll");
58 var disc_type
= disc_assembly
.GetType (discoverer_args
[0]);
59 var disc_obj
= (ITraitDiscoverer
)Activator
.CreateInstance (disc_type
);
60 foreach (var trait
in disc_obj
.GetTraits (attr
)) {
61 if (!tc
.Traits
.ContainsKey (trait
.Key
))
62 tc
.Traits
[trait
.Key
] = new List
<string> ();
63 tc
.Traits
[trait
.Key
].Add (trait
.Value
);
70 public object[] Values
;
73 static CodeExpression
EncodeValue (object val
)
75 if (val
is int || val
is long || val
is uint || val
is ulong || val
is byte || val
is sbyte || val
is short || val
is ushort || val
is bool || val
is string || val
is char || val
is float || val
is double)
76 return new CodePrimitiveExpression (val
);
78 return new CodeTypeOfExpression ((Type
)val
);
79 else if (val
is Enum
) {
80 TypeCode typeCode
= Convert
.GetTypeCode (val
);
82 if (typeCode
== TypeCode
.UInt64
)
83 o
= UInt64
.Parse (((Enum
)val
).ToString ("D"));
85 o
= Int64
.Parse (((Enum
)val
).ToString ("D"));
86 return new CodeCastExpression (new CodeTypeReference (val
.GetType ()), new CodePrimitiveExpression (o
));
87 } else if (val
is null) {
88 return new CodePrimitiveExpression (null);
89 } else if (val
is Array
) {
90 var arr
= (IEnumerable
)val
;
91 var arr_expr
= new CodeArrayCreateExpression (new CodeTypeReference (val
.GetType ()), new CodeExpression
[] {});
92 foreach (var v
in arr
)
93 arr_expr
.Initializers
.Add (EncodeValue (v
));
96 throw new Exception ("Unable to emit inline data: " + val
.GetType () + " " + val
);
100 static int Main (string[] args
)
102 if (args
.Length
< 3) {
103 Console
.WriteLine ("Usage: <outfile> <corefx dir> <test assembly filename> <xunit console options>");
107 var outfile_name
= args
[0];
108 var sdkdir
= args
[1] + "/artifacts/bin/runtime/netcoreapp-OSX-Debug-x64";
109 args
= args
.Skip (2).ToArray ();
111 // Despite a lot of effort, couldn't get dotnet to load these assemblies from the sdk dir, so copy them to our binary dir
112 File
.Copy ($"{sdkdir}/Microsoft.DotNet.PlatformAbstractions.dll", AppContext
.BaseDirectory
, true);
113 File
.Copy ($"{sdkdir}/CoreFx.Private.TestUtilities.dll", AppContext
.BaseDirectory
, true);
114 File
.Copy ($"{sdkdir}/Microsoft.DotNet.XUnitExtensions.dll", AppContext
.BaseDirectory
, true);
116 var cmdline
= CommandLine
.Parse (args
);
119 File
.Copy (cmdline
.Project
.Assemblies
.First ().AssemblyFilename
, AppContext
.BaseDirectory
, true);
121 var assembly
= Assembly
.LoadFrom (Path
.Combine (AppContext
.BaseDirectory
, Path
.GetFileName (cmdline
.Project
.Assemblies
.First ().AssemblyFilename
)));
123 var msg_sink
= new MsgSink ();
124 var xunit2
= new Xunit2Discoverer (AppDomainSupport
.Denied
, new NullSourceInformationProvider (), new ReflectionAssemblyInfo (assembly
), null, null, msg_sink
);
125 var sink
= new TestDiscoverySink ();
126 var config
= new TestAssemblyConfiguration () { DiagnosticMessages = true, InternalDiagnosticMessages = true, PreEnumerateTheories = false }
;
127 xunit2
.Find (false, sink
, TestFrameworkOptions
.ForDiscovery (config
));
128 sink
.Finished
.WaitOne ();
130 foreach (XunitTestCase tc
in sink
.TestCases
)
131 ComputeTraits (assembly
, tc
);
133 // Compute testcase data
134 var tc_data
= new Dictionary
<XunitTestCase
, List
<CaseData
>> ();
135 foreach (XunitTestCase tc
in sink
.TestCases
) {
136 var m
= ((ReflectionMethodInfo
)tc
.Method
).MethodInfo
;
137 var t
= m
.ReflectedType
;
139 var cases
= new List
<CaseData
> ();
141 if (m
.GetParameters ().Length
> 0) {
142 foreach (var cattr
in m
.GetCustomAttributes (true))
143 if (cattr
is InlineDataAttribute
) {
144 var data
= ((InlineDataAttribute
)cattr
).GetData (null).First ();
146 data
= new object [m
.GetParameters ().Length
];
147 if (data
.Length
!= m
.GetParameters ().Length
)
148 throw new Exception ();
150 bool unhandled
= false;
151 foreach (var val
in data
) {
152 if (val
is float || val
is double)
155 var type
= val
as Type
;
160 if (!val
.GetType ().IsPublic
)
165 cases
.Add (new CaseData () { Values = data }
);
168 cases
.Add (new CaseData ());
170 tc_data
[tc
] = cases
;
174 //w.WriteLine ($"\t\ttypeof({typename}).GetMethod (\"{m.Name}\", BindingFlags.Static|BindingFlags.NonPublic).Invoke(null, null);");
175 //w.WriteLine ($"\t\tusing (var o = new {typename} ()) {{");
176 //if (cmdline.StopOnFail)
177 //w.WriteLine ("\t\tif (nfailed > 0) return 1;");
178 //w.WriteLine ("\t\tConsole.WriteLine (\"RUN: \" + nrun + \", FAILED: \" + nfailed);");
181 var cu
= new CodeCompileUnit ();
182 var ns
= new CodeNamespace ("");
183 cu
.Namespaces
.Add (ns
);
184 ns
.Imports
.Add (new CodeNamespaceImport ("System"));
185 ns
.Imports
.Add (new CodeNamespaceImport ("System.Reflection"));
186 var code_class
= new CodeTypeDeclaration ("RunTests");
187 ns
.Types
.Add (code_class
);
189 var code_main
= new CodeEntryPointMethod ();
190 code_main
.ReturnType
= new CodeTypeReference ("System.Int32");
191 code_class
.Members
.Add (code_main
);
193 var statements
= code_main
.Statements
;
194 statements
.Add (new CodeVariableDeclarationStatement (typeof (int), "nrun", new CodePrimitiveExpression (0)));
195 statements
.Add (new CodeVariableDeclarationStatement (typeof (int), "nfailed", new CodePrimitiveExpression (0)));
197 var filters
= cmdline
.Project
.Filters
;
198 foreach (XunitTestCase tc
in sink
.TestCases
) {
199 var m
= ((ReflectionMethodInfo
)tc
.Method
).MethodInfo
;
200 //Console.WriteLine ("" + m.ReflectedType + " " + m + " " + (tc.TestMethodArguments == null));
201 var t
= m
.ReflectedType
;
204 if (!filters
.Filter (tc
))
207 var cases
= tc_data
[tc
];
210 foreach (var test
in cases
) {
211 string typename
= GetTypeName (t
);
214 msg
= $"{typename}:{m.Name}[{caseindex}]...";
216 msg
= $"{typename}:{m.Name}...";
218 statements
.Add (new CodeMethodInvokeExpression (new CodeTypeReferenceExpression ("Console"), "WriteLine", new CodeExpression
[] { new CodePrimitiveExpression (msg) }
));
219 statements
.Add (new CodeAssignStatement (new CodeVariableReferenceExpression ("nrun"), new CodeBinaryOperatorExpression (new CodeVariableReferenceExpression ("nrun"), CodeBinaryOperatorType
.Add
, new CodePrimitiveExpression (1))));
220 var try1
= new CodeTryCatchFinallyStatement();
221 statements
.Add (try1
);
224 try1
.TryStatements
.Add (new CodeVariableDeclarationStatement ("var", "o", new CodeObjectCreateExpression (t
, new CodeExpression
[] {})));
229 CodeMethodInvokeExpression call
;
232 call
= new CodeMethodInvokeExpression (new CodeTypeReferenceExpression (t
), m
.Name
, new CodeExpression
[] {});
234 call
= new CodeMethodInvokeExpression (new CodeVariableReferenceExpression ("o"), m
.Name
, new CodeExpression
[] {});
236 if (test
.Values
!= null) {
237 foreach (var val
in test
.Values
)
238 call
.Parameters
.Add (EncodeValue (val
));
240 try1
.TryStatements
.Add (call
);
242 var catch1
= new CodeCatchClause ("ex", new CodeTypeReference ("System.Exception"));
243 catch1
.Statements
.Add (new CodeAssignStatement (new CodeVariableReferenceExpression ("nfailed"), new CodeBinaryOperatorExpression (new CodeVariableReferenceExpression ("nfailed"), CodeBinaryOperatorType
.Add
, new CodePrimitiveExpression (1))));
244 catch1
.Statements
.Add (new CodeMethodInvokeExpression (new CodeTypeReferenceExpression ("Console"), "WriteLine", new CodeExpression
[] {
245 new CodeBinaryOperatorExpression (
246 new CodePrimitiveExpression ("FAILED: "),
247 CodeBinaryOperatorType
.Add
,
248 new CodeVariableReferenceExpression ("ex"))
250 try1
.CatchClauses
.Add (catch1
);
254 //w.WriteLine ("\t\tConsole.WriteLine (\"RUN: \" + nrun + \", FAILED: \" + nfailed);");
255 statements
.Add (new CodeMethodReturnStatement (new CodePrimitiveExpression (0)));
257 var provider
= new CSharpCodeProvider ();
258 using (var w2
= File
.CreateText (outfile_name
)) {
259 provider
.GenerateCodeFromCompileUnit (cu
, w2
, new CodeGeneratorOptions ());