203 lines
7.3 KiB
C#
203 lines
7.3 KiB
C#
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Microsoft Research Singularity
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// File: mktests.cs
|
|
//
|
|
// Examine assembly metadata to compile a manifest of test code in a Singularity
|
|
// distribution.
|
|
//
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Xml;
|
|
|
|
using Bartok.MSIL;
|
|
|
|
public class mktests
|
|
{
|
|
const string ATTRIBUTE_PREFIX = "Microsoft.Singularity.UnitTest.";
|
|
|
|
private static void Usage()
|
|
{
|
|
Console.WriteLine("Usage:\n" +
|
|
" mktests /out:<test_manifest_file> [assemblies]\n");
|
|
}
|
|
|
|
public static int Main(string[] args)
|
|
{
|
|
if (args.Length == 0) {
|
|
Usage();
|
|
return 0;
|
|
}
|
|
DateTime timeBegin = DateTime.Now;
|
|
ArrayList infiles = new ArrayList();
|
|
string outfile = null;
|
|
for (int i = 0; i < args.Length; i++) {
|
|
string arg = args[i];
|
|
if (arg.StartsWith("-") || arg.StartsWith("/")) {
|
|
if (arg.StartsWith("-out:") || arg.StartsWith("/out:")) {
|
|
outfile = arg.Substring(arg.IndexOf(":") + 1);
|
|
}
|
|
else {
|
|
Console.WriteLine("Malformed argument: \"" + arg + "\"");
|
|
Usage();
|
|
return 1;
|
|
}
|
|
}
|
|
else {
|
|
// This is just an assembly name
|
|
infiles.Add(arg);
|
|
}
|
|
}
|
|
if (outfile == null) {
|
|
Console.WriteLine("You must specify the output file.");
|
|
Usage();
|
|
return 1;
|
|
}
|
|
if (infiles.Count == 0) {
|
|
Console.WriteLine("You must specify at least one input file.");
|
|
Usage();
|
|
return 1;
|
|
}
|
|
XmlTextWriter oo = new XmlTextWriter(outfile, null);
|
|
oo.Formatting = Formatting.Indented;
|
|
oo.Indentation = 4;
|
|
try {
|
|
oo.WriteStartDocument();
|
|
if (infiles.Count == 1) {
|
|
oo.WriteComment("Export of tests from: " + infiles[0]);
|
|
}
|
|
else {
|
|
oo.WriteComment("Export of tests from:");
|
|
foreach (string f in infiles) {
|
|
oo.WriteComment(" " + f);
|
|
}
|
|
}
|
|
oo.WriteStartElement("Profile");
|
|
oo.WriteAttributeString("Name", outfile);
|
|
ProcessAssemblies(oo, infiles);
|
|
oo.WriteEndElement();
|
|
oo.WriteEndDocument();
|
|
}
|
|
finally {
|
|
oo.Close();
|
|
}
|
|
|
|
TimeSpan elapsed = DateTime.Now - timeBegin;
|
|
Console.WriteLine("mktests: {0} seconds elapsed.", elapsed.TotalSeconds);
|
|
return 0;
|
|
}
|
|
|
|
private static void ProcessAssemblies(XmlTextWriter oo, ArrayList infiles)
|
|
{
|
|
MetaDataResolver resolver =
|
|
new MetaDataResolver(infiles, new ArrayList(), new DateTime(), false, false);
|
|
|
|
MetaDataResolver.ResolveCustomAttributes(new MetaDataResolver[] { resolver });
|
|
foreach (MetaData md in resolver.MetaDataList) {
|
|
// Assume that if we are processing an assembly, it will contain tests
|
|
string name = md.Name;
|
|
string pkg;
|
|
string assembly = Tail(name, '\\', out pkg); // extract filename
|
|
string module = assembly.Substring(0, assembly.IndexOf('.')); // trim extension
|
|
oo.WriteStartElement("Module");
|
|
oo.WriteAttributeString("Name", module);
|
|
ProcessAssembly(oo, md);
|
|
oo.WriteEndElement();
|
|
}
|
|
}
|
|
|
|
private static void ProcessAssembly(XmlTextWriter oo, MetaData md)
|
|
{
|
|
IDictionary<string, MetaDataObject> tests = new SortedDictionary<string, MetaDataObject>();
|
|
IDictionary<string, MetaDataObject> suites = new SortedDictionary<string, MetaDataObject>();
|
|
// Look for the annotation that tells us that this assembly is a stand-alone
|
|
// test app.
|
|
MetaDataAssembly mda = (MetaDataAssembly) md.Assemblies[0];
|
|
foreach (MetaDataCustomAttribute attrib in md.CustomAttributes) {
|
|
MetaDataObject parent = attrib.Parent;
|
|
//Console.WriteLine("Found: {0} in {1} {2}", attrib.Name, parent.FullName, parent.FullNameWithContext);
|
|
if (!attrib.Name.StartsWith(ATTRIBUTE_PREFIX)) {
|
|
continue;
|
|
}
|
|
string attribName = attrib.Name.Substring(ATTRIBUTE_PREFIX.Length);
|
|
switch (attribName) {
|
|
case "TestAppAttribute":
|
|
break;
|
|
case "TestClassAttribute":
|
|
suites.Add(attrib.Parent.FullName, attrib);
|
|
break;
|
|
case "TestMethodAttribute":
|
|
MetaDataObject m = attrib.Parent;
|
|
tests.Add(attrib.Parent.FullName, attrib);
|
|
break;
|
|
}
|
|
}
|
|
string prevSuite = "";
|
|
foreach (KeyValuePair<string, MetaDataObject> kvp in tests) {
|
|
string k = kvp.Key;
|
|
string className;
|
|
string testname = Tail(k, '.', out className);
|
|
if (!suites.ContainsKey(className)) {
|
|
Console.WriteLine("TestMethod declared outside of a TestClass: {0}", k);
|
|
continue;
|
|
}
|
|
if (!prevSuite.Equals(className)) {
|
|
if (! prevSuite.Equals("")) {
|
|
oo.WriteEndElement();
|
|
}
|
|
oo.WriteStartElement("Suite");
|
|
string ignored;
|
|
oo.WriteAttributeString("Name", Tail(className, '.', out ignored));
|
|
prevSuite = className;
|
|
}
|
|
oo.WriteStartElement("Test");
|
|
oo.WriteAttributeString("Name", testname);
|
|
#if EXTRACT_TIMEOUT
|
|
object timeout = psItem.Fields["Test Timeout"].Value;
|
|
if (timeout != null) {
|
|
oo.WriteAttributeString("Timeout", timeout.ToString());
|
|
}
|
|
object knownFailure = psItem.Fields["Test Known Failure"].Value;
|
|
if (knownFailure != null) {
|
|
oo.WriteAttributeString("KnownFailure", knownFailure.ToString());
|
|
}
|
|
#endif
|
|
oo.WriteEndElement();
|
|
}
|
|
if (! prevSuite.Equals("")) {
|
|
oo.WriteEndElement();
|
|
}
|
|
}
|
|
|
|
// Split a string at the last occcurence of a character, returning both
|
|
// the before and after. If the character is not present, then the tail
|
|
// is empty and before is the entire string.
|
|
private static string Tail(string it, char pattern, out string before)
|
|
{
|
|
int i = it.LastIndexOf(pattern);
|
|
if (i < 0) {
|
|
before = it;
|
|
return "";
|
|
}
|
|
else {
|
|
before = it.Substring(0, i);
|
|
return it.Substring(i + 1);
|
|
}
|
|
}
|
|
|
|
private static void Assert(bool cond, string trueText)
|
|
{
|
|
if (!cond) {
|
|
throw new Exception("Expected: " + trueText);
|
|
}
|
|
}
|
|
}
|