From: Avery Pennarun Date: Fri, 1 May 2009 00:42:44 +0000 (-0400) Subject: Import latest wvtest for C#/.NET from versaplex project. X-Git-Url: http://rtime.felk.cvut.cz/gitweb/wvtest.git/commitdiff_plain/8ae6824b9142135abde1469d6b288140e5df79d4 Import latest wvtest for C#/.NET from versaplex project. Commit 8f939de8b03b0e0236a182543af0298bb6b97c52. --- diff --git a/dotnet/monorules.mk b/dotnet/monorules.mk new file mode 100644 index 0000000..e066ec9 --- /dev/null +++ b/dotnet/monorules.mk @@ -0,0 +1,83 @@ +default: all + +SHELL=/bin/bash + +# cc -E tries to guess by extension what to do with the file. +# And it does other weird things. cpp seems to Just Work(tm), so use that for +# our C# (.cs) files +CSCPP=cpp + +# Cygwin supports symlinks, but they aren't actually useful outside cygwin, +# so let's just copy instead. We also use Microsoft's .net compiler instead +# of mono. +ifeq ($(OS),Windows_NT) + CSC?=csc + SYMLINK=cp + MONORUN= +else + CSC?=gmcs -langversion:linq + SYMLINK=ln -sf + PKGS += /r:Mono.Posix + MONORUN=mono --debug +endif + +CSFLAGS=/warn:4 /debug +#CSFLAGS += /warnaserror + +TESTRUNNER=$(WVDOTNET)/wvtestrunner.pl + +# Rules for generating autodependencies on header files +$(patsubst %.cs.E,%.d,$(filter %.cs.E,$(FILES))): %.d: %.cs + @echo Generating dependency file $@ for $< + @set -e; set -o pipefail; rm -f $@; (\ + ($(CSCPP) -M -MM -MQ '$@' $(CPPFLAGS) $< && echo Makefile) \ + | paste -s -d ' ' - && \ + $(CSCPP) -M -MM -MQ '$<'.E $(CPPFLAGS) $< \ + ) > $@ \ + || (rm -f $@ && echo "Error generating dependency file." && exit 1) + +include $(patsubst %.cs.E,%.d,$(filter %.cs.E,$(FILES))) + +# Rule for actually preprocessing source files with headers +%.cs.E: %.cs + @rm -f $@ + set -o pipefail; $(CSCPP) $(CPPFLAGS) -C -dI $< \ + | expand -8 \ + | sed -e 's,^#include,//#include,' \ + | grep -v '^# [0-9]' \ + >$@ || (rm -f $@ && exit 1) + + +define csbuild + @for d in $(filter ../%.dll,$^); do \ + rm -f $$(basename $$d); \ + $(SYMLINK) -v $$d .; \ + done + $(CSC) $(CSFLAGS) /target:$1 /out:$@ \ + $(PKGS) \ + $(filter %.cs.E %.cs,$^) \ + $(patsubst %.dll,/r:%.dll,$(filter %.dll,$^)) +endef + + +%.dll: assemblyinfo.cs + $(call csbuild,library) + +# This must come before the %.cs rule, since %.cs.E files are better. +%.exe: %.cs.E + $(call csbuild,exe) + +%.exe: %.cs + $(call csbuild,exe) + +%: %.exe + rm -f $@ + $(SYMLINK) $< $@ + +%.pass: %.exe + rm -f $@ + $(TESTRUNNER) $(MONORUN) ./$^ + touch $@ + +clean:: + rm -f *~ *.E *.d *.exe *.dll *.mdb *.pdb diff --git a/dotnet/wvtest.cs b/dotnet/wvtest.cs new file mode 100644 index 0000000..de1cb7a --- /dev/null +++ b/dotnet/wvtest.cs @@ -0,0 +1,375 @@ +/* + * Versaplex: + * Copyright (C)2007-2008 Versabanq Innovations Inc. and contributors. + * See the included file named LICENSE for license information. + */ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.Serialization; +using Wv; + +// We put this in wvtest.cs since wvtest.cs should be able to compile all +// by itself, without relying on any other parts of wvdotnet. On the other +// hand, it's perfectly fine for wvdotnet to have wvtest.cs in it. +namespace Wv +{ + public static class WvReflection + { + public static IEnumerable find_types(Type attrtype) + { + foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies()) + { + foreach (Type t in a.GetTypes()) + { + if (!t.IsDefined(attrtype, false)) + continue; + + yield return t; + } + } + } + + public static IEnumerable find_methods(this Type t, + Type attrtype) + { + foreach (MethodInfo m in t.GetMethods()) + { + if (!m.IsDefined(attrtype, false)) + continue; + + yield return m; + } + } + } +} + +namespace Wv.Test +{ + public class WvTest + { + struct TestInfo + { + public string name; + public Action cb; + + public TestInfo(string name, Action cb) + { this.name = name; this.cb = cb; } + } + List tests = new List(); + + public int failures { get; private set; } + + public WvTest() + { + foreach (Type t in + WvReflection.find_types(typeof(TestFixtureAttribute))) + { + foreach (MethodInfo m in + t.find_methods(typeof(TestAttribute))) + { + // The new t2, m2 are needed so that each delegate gets + // its own copy of the variable. + Type t2 = t; + MethodInfo m2 = m; + RegisterTest(String.Format("{0}/{1}", + t.Name, m.Name), + delegate() { + try { + m2.Invoke(Activator.CreateInstance(t2), + null); + } catch (TargetInvocationException e) { + throw e.InnerException; + } + }); + } + } + } + + public void RegisterTest(string name, Action tc) + { + tests.Add(new TestInfo(name, tc)); + } + + public static void DoMain() + { + // Enough to run an entire test + Environment.Exit(new WvTest().Run()); + } + + public int Run() + { + string[] args = Environment.GetCommandLineArgs(); + + if (args.Length <= 1) + Console.WriteLine("WvTest: Running all tests"); + else + Console.WriteLine("WvTest: Running only selected tests"); + + foreach (TestInfo test in tests) + { + string[] parts = test.name.Split(new char[] { '/' }, 2); + + bool runthis = (args.Length <= 1); + foreach (string arg in args) + if (parts[0].StartsWith(arg) || parts[1].StartsWith(arg)) + runthis = true; + + if (!runthis) continue; + + Console.WriteLine("\nTesting \"{0}\" in {1}:", + parts[1], parts[0]); + + try { + test.cb(); + } catch (WvAssertionFailure) { + failures++; + } catch (Exception e) { + Console.WriteLine(e.ToString()); + Console.WriteLine("! WvTest Exception received FAILED"); + failures++; + } + } + + Console.Out.WriteLine("Result: {0} failures.", failures); + + // Return a safe unix exit code + return failures > 0 ? 1 : 0; + } + + public static bool booleanize(bool x) + { + return x; + } + + public static bool booleanize(long x) + { + return x != 0; + } + + public static bool booleanize(ulong x) + { + return x != 0; + } + + public static bool booleanize(string s) + { + return s != null && s != ""; + } + + public static bool booleanize(object o) + { + return o != null; + } + + static bool expect_fail = false; + public static void expect_next_failure() + { + expect_fail = true; + } + + public static bool test(bool ok, string file, int line, string s) + { + s = s.Replace("\n", "!"); + s = s.Replace("\r", "!"); + string suffix = ""; + if (expect_fail) + { + if (!ok) + suffix = " (expected) ok"; + else + suffix = " (expected fail!) FAILED"; + } + Console.WriteLine("! {0}:{1,-5} {2,-40} {3}{4}", + file, line, s, + ok ? "ok" : "FAILED", + suffix); + Console.Out.Flush(); + expect_fail = false; + + if (!ok) + throw new WvAssertionFailure(String.Format("{0}:{1} {2}", file, line, s)); + + return ok; + } + + public static void test_exception(string file, int line, string s) + { + Console.WriteLine("! {0}:{1,-5} {2,-40} {3}", + file, line, s, "EXCEPTION"); + Console.Out.Flush(); + } + + public static bool test_eq(bool cond1, bool cond2, + string file, int line, + string s1, string s2) + { + return test(cond1 == cond2, file, line, + String.Format("[{0}] == [{1}] ({{{2}}} == {{{3}}})", + cond1, cond2, s1, s2)); + } + + public static bool test_eq(long cond1, long cond2, + string file, int line, + string s1, string s2) + { + return test(cond1 == cond2, file, line, + String.Format("[{0}] == [{1}] ({{{2}}} == {{{3}}})", + cond1, cond2, s1, s2)); + } + + public static bool test_eq(ulong cond1, ulong cond2, + string file, int line, + string s1, string s2) + { + return test(cond1 == cond2, file, line, + String.Format("[{0}] == [{1}] ({{{2}}} == {{{3}}})", + cond1, cond2, s1, s2)); + } + + public static bool test_eq(double cond1, double cond2, + string file, int line, + string s1, string s2) + { + return test(cond1 == cond2, file, line, + String.Format("[{0}] == [{1}] ({{{2}}} == {{{3}}})", + cond1, cond2, s1, s2)); + } + + public static bool test_eq(decimal cond1, decimal cond2, + string file, int line, + string s1, string s2) + { + return test(cond1 == cond2, file, line, + String.Format("[{0}] == [{1}] ({{{2}}} == {{{3}}})", + cond1, cond2, s1, s2)); + } + + public static bool test_eq(string cond1, string cond2, + string file, int line, + string s1, string s2) + { + return test(cond1 == cond2, file, line, + String.Format("[{0}] == [{1}] ({{{2}}} == {{{3}}})", + cond1, cond2, s1, s2)); + } + + // some objects can compare themselves to 'null', which is helpful. + // for example, DateTime.MinValue == null, but only through + // IComparable, not through IObject. + public static bool test_eq(IComparable cond1, IComparable cond2, + string file, int line, + string s1, string s2) + { + return test(cond1.CompareTo(cond2) == 0, file, line, + String.Format("[{0}] == [{1}]", s1, s2)); + } + + public static bool test_eq(object cond1, object cond2, + string file, int line, + string s1, string s2) + { + return test(cond1 == cond2, file, line, + String.Format("[{0}] == [{1}]", s1, s2)); + } + + public static bool test_ne(bool cond1, bool cond2, + string file, int line, + string s1, string s2) + { + return test(cond1 != cond2, file, line, + String.Format("[{0}] != [{1}] ({{{2}}} != {{{3}}})", + cond1, cond2, s1, s2)); + } + + public static bool test_ne(long cond1, long cond2, + string file, int line, + string s1, string s2) + { + return test(cond1 != cond2, file, line, + String.Format("[{0}] != [{1}] ({{{2}}} != {{{3}}})", + cond1, cond2, s1, s2)); + } + + public static bool test_ne(ulong cond1, ulong cond2, + string file, int line, + string s1, string s2) + { + return test(cond1 != cond2, file, line, + String.Format("[{0}] != [{1}] ({{{2}}} != {{{3}}})", + cond1, cond2, s1, s2)); + } + + public static bool test_ne(double cond1, double cond2, + string file, int line, + string s1, string s2) + { + return test(cond1 != cond2, file, line, + String.Format("[{0}] != [{1}] ({{{2}}} != {{{3}}})", + cond1, cond2, s1, s2)); + } + + public static bool test_ne(decimal cond1, decimal cond2, + string file, int line, + string s1, string s2) + { + return test(cond1 != cond2, file, line, + String.Format("[{0}] != [{1}] ({{{2}}} != {{{3}}})", + cond1, cond2, s1, s2)); + } + + public static bool test_ne(string cond1, string cond2, + string file, int line, + string s1, string s2) + { + return test(cond1 != cond2, file, line, + String.Format("[{0}] != [{1}] ({{{2}}} != {{{3}}})", + cond1, cond2, s1, s2)); + } + + // See notes for test_eq(IComparable,IComparable) + public static bool test_ne(IComparable cond1, IComparable cond2, + string file, int line, + string s1, string s2) + { + return test(cond1.CompareTo(cond2) != 0, file, line, + String.Format("[{0}] != [{1}]", s1, s2)); + } + + public static bool test_ne(object cond1, object cond2, + string file, int line, + string s1, string s2) + { + return test(cond1 != cond2, file, line, + String.Format("[{0}] != [{1}]", s1, s2)); + } + } + + public class WvAssertionFailure : Exception + { + public WvAssertionFailure() + : base() + { + } + + public WvAssertionFailure(string msg) + : base(msg) + { + } + } + + // Placeholders for NUnit compatibility + public class TestFixtureAttribute : Attribute + { + } + public class TestAttribute : Attribute + { + } + [AttributeUsage(AttributeTargets.Method, AllowMultiple=true)] + public class CategoryAttribute : Attribute + { + public CategoryAttribute(string x) + { + } + } +} diff --git a/dotnet/wvtest.cs.h b/dotnet/wvtest.cs.h new file mode 100644 index 0000000..30ce532 --- /dev/null +++ b/dotnet/wvtest.cs.h @@ -0,0 +1,9 @@ +#ifndef __WVTEST_CS_H // Blank lines in this file mess up line numbering! +#define __WVTEST_CS_H +#define WVASSERT(x) try { WvTest.test(WvTest.booleanize(x), __FILE__, __LINE__, #x); } catch (Wv.Test.WvAssertionFailure) { throw; } catch (System.Exception) { WvTest.test_exception(__FILE__, __LINE__, #x); throw; } +#define WVPASS(x) WVASSERT(x) +#define WVFAIL(x) try { WvTest.test(!WvTest.booleanize(x), __FILE__, __LINE__, "NOT(" + #x + ")"); } catch (Wv.Test.WvAssertionFailure) { throw; } catch (System.Exception) { WvTest.test_exception(__FILE__, __LINE__, "NOT(" + #x + ")"); throw; } +#define WVEXCEPT(x) { System.Exception _wvex = null; try { x; } catch (System.Exception _wvasserte) { _wvex = _wvasserte; } WvTest.test(_wvex != null, __FILE__, __LINE__, "EXCEPT(" + #x + ")"); if (_wvex != null) throw _wvex; } +#define WVPASSEQ(x, y) try { WvTest.test_eq((x), (y), __FILE__, __LINE__, #x, #y); } catch (Wv.Test.WvAssertionFailure) { throw; } catch (System.Exception) { WvTest.test_exception(__FILE__, __LINE__, string.Format("[{0}] == [{1}]", #x, #y)); throw; } +#define WVPASSNE(x, y) try { WvTest.test_ne((x), (y), __FILE__, __LINE__, #x, #y); } catch (Wv.Test.WvAssertionFailure) { throw; } catch (System.Exception) { WvTest.test_exception(__FILE__, __LINE__, string.Format("[{0}] != [{1}]", #x, #y)); throw; } +#endif // __WVTEST_CS_H diff --git a/dotnet/wvtest.t.cs b/dotnet/wvtest.t.cs new file mode 100644 index 0000000..6fd3c46 --- /dev/null +++ b/dotnet/wvtest.t.cs @@ -0,0 +1,96 @@ +/* + * Versaplex: + * Copyright (C)2007-2008 Versabanq Innovations Inc. and contributors. + * See the included file named LICENSE for license information. + */ +#include "wvtest.cs.h" +using System; +using Wv.Test; + +[TestFixture] +public class WvTestTest +{ + [Test] public void test_wvtest() + { + WVPASS(1); + WVPASS("hello"); + WVPASS(new Object()); + WVPASS(0 != 1); + + WVFAIL(0); + WVFAIL(""); + WVFAIL(null); + + WVPASSEQ(7, 7); + WVPASSEQ("foo", "foo"); + WVPASSEQ("", ""); + Object obj = new Object(); + WVPASSEQ(obj, obj); + WVPASSEQ(null, null); + + WVPASSNE(7, 8); + WVPASSNE("foo", "blue"); + WVPASSNE("", "notempty"); + WVPASSNE(null, ""); + WVPASSNE(obj, null); + WVPASSNE(obj, new Object()); + WVPASSNE(new Object(), new Object()); + } + + // these are only public to get rid of the "not assigned to" warnings. + // we don't assign to them because that's the whole point of the test. + public DateTime null_date; + public TimeSpan null_span; + + [Test] public void test_dates_and_spans() + { + WVPASS(null_date == DateTime.MinValue); + WVPASSEQ(null_date, DateTime.MinValue); + WVPASS(null_span == TimeSpan.Zero); + WVPASSEQ(null_span, TimeSpan.Zero); + + TimeSpan t = TimeSpan.FromMinutes(60*24*7); + WVPASSEQ(t.ToString(), "7.00:00:00"); + WVPASSEQ(t.Ticks, 7*24*60*60*10000000L); + WVPASS(t.TotalMinutes == 7*24*60); + WVPASSEQ(t.TotalMinutes, 7*24*60); + WVPASSEQ(t.TotalSeconds, 7*24*60*60); + WVPASSEQ(t.Minutes, 0); + } + + void throw_exception() + { + throw new System.Exception("Exception thrown"); + } + void no_throw_exception() + { + return; + } + + [Test] public void test_exceptions() + { + bool caught = false; + + try { + WVEXCEPT(throw_exception()); + } catch (Wv.Test.WvAssertionFailure e) { + throw e; + } catch (System.Exception) { + caught = true; + } + + WVPASS(caught); + + caught = false; + + System.Console.WriteLine("Ignore next failure: it is expected"); + WvTest.expect_next_failure(); + try { + WVEXCEPT(no_throw_exception()); + } catch (Wv.Test.WvAssertionFailure) { + caught = true; + } + + WVPASS(caught); + } +}