summaryrefslogtreecommitdiff
path: root/valadate/testplan.vala
diff options
context:
space:
mode:
Diffstat (limited to 'valadate/testplan.vala')
-rw-r--r--valadate/testplan.vala283
1 files changed, 283 insertions, 0 deletions
diff --git a/valadate/testplan.vala b/valadate/testplan.vala
new file mode 100644
index 000000000..fc8928e51
--- /dev/null
+++ b/valadate/testplan.vala
@@ -0,0 +1,283 @@
+/*
+ * Valadate - Unit testing library for GObject-based libraries.
+ * Copyright (C) 2016 Chris Daley <chebizarro@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Chris Daley <chebizarro@gmail.com>
+ */
+
+public class Valadate.TestPlan : Vala.CodeVisitor {
+
+ [CCode (has_target = false)]
+ public delegate void TestMethod(TestCase self) throws Error;
+
+ public delegate void AsyncTestMethod(TestCase self, AsyncReadyCallback cb);
+ public delegate void AsyncTestMethodResult(TestCase self, AsyncResult res) throws Error;
+
+ public delegate Type GetType();
+
+ public File plan {get;set;}
+
+ public TestAssembly assembly {get;set;}
+
+ public TestOptions options {get;set;}
+
+ public TestConfig config {get;set;}
+
+ public TestResult result {get;set;}
+
+ public TestRunner runner {get;set;}
+
+ public TestSuite root {get;protected set;}
+
+ private Vala.CodeContext context;
+ private TestGatherer gatherer;
+ private delegate TestCase Constructor();
+ private TestSuite testsuite;
+ private TestCase testcase;
+
+ public TestPlan(TestAssembly assembly) throws Error {
+
+ this.assembly = assembly;
+ options = assembly.options;
+
+ var plan_name = Path.get_basename(assembly.binary.get_path());
+ if(plan_name.has_prefix("lt-"))
+ plan_name = plan_name.substring(3);
+
+ plan = assembly.srcdir.get_child(plan_name + ".vapi");
+ if(!plan.query_exists()) {
+ plan = assembly.builddir.get_child(plan_name + ".vapi");
+ if(!plan.query_exists()) {
+ throw new TestConfigError.TESTPLAN(
+ "Test Plan %s Not Found in %s or %s", plan_name, assembly.srcdir.get_path(), assembly.builddir.get_path());
+ }
+ }
+ config = new TestConfig(options);
+ runner = new TestRunner();
+ result = new TestResult(config);
+ testsuite = root = new TestSuite("/");
+ setup_context();
+ load_test_plan();
+ }
+
+ public int run() throws Error {
+ return runner.run_all(this);
+ }
+
+ private void setup_context() {
+ context = new Vala.CodeContext ();
+ Vala.CodeContext.push (context);
+ context.report.enable_warnings = false;
+ context.report.set_verbose_errors (false);
+ context.verbose_mode = false;
+ }
+
+ public void load_test_plan() throws Error {
+ context.add_source_file (new Vala.SourceFile (
+ context, Vala.SourceFileType.PACKAGE, plan.get_path()));
+ var parser = new Vala.Parser ();
+ parser.parse (context);
+ gatherer = new TestGatherer(assembly);
+ context.accept(gatherer);
+ context.accept(this);
+ }
+
+ public override void visit_namespace(Vala.Namespace ns) {
+ if (ns.name != null) {
+ var currpath = "/" + ns.get_full_name().replace(".","/");
+ if(config.in_subprocess)
+ if(!options.running_test.has_prefix(currpath))
+ return;
+
+ if(currpath.last_index_of("/") == 0)
+ testsuite = root;
+
+ var ts = new TestSuite(ns.name);
+ testsuite.add_test(ts);
+ testsuite = ts;
+ }
+ ns.accept_children(this);
+ }
+
+ public override void visit_class(Vala.Class cls) {
+
+ try {
+ if (is_subtype_of(cls, typeof(TestCase)) && !cls.is_abstract) {
+ unowned Constructor ctor = get_constructor(cls);
+ testcase = ctor();
+ testcase.name = cls.name;
+ testcase.label = "/%s".printf(cls.get_full_name().replace(".","/"));
+ testsuite.add_test(testcase);
+ visit_testcase(cls);
+
+ } else if (is_subtype_of(cls,typeof(TestSuite))) {
+ visit_testsuite(cls);
+ }
+ } catch (Error e) {
+ error(e.message);
+ }
+ cls.accept_children(this);
+ }
+
+ private bool is_subtype_of(Vala.Class cls, Type type) {
+ var t = Type.from_name(cls.get_full_name().replace(".",""));
+ if(t.is_a(type))
+ return true;
+ return false;
+ }
+
+ private unowned Constructor get_constructor(Vala.Class cls) throws Error {
+ var attr = new Vala.CCodeAttribute (cls.default_construction_method);
+ return (Constructor)assembly.get_method(attr.name);
+ }
+
+ public void visit_testcase(Vala.Class cls) {
+
+ var t = Type.from_name(cls.get_full_name().replace(".",""));
+ var p = t.parent();
+ if(p != typeof(TestCase)) {
+ var basecls = gatherer.classes.get(p);
+ if(basecls != null)
+ visit_testcase(basecls);
+ }
+
+ foreach(var method in cls.get_methods()) {
+
+ if(config.in_subprocess)
+ if (options.running_test != "%s/%s".printf(
+ testcase.label, method.name))
+ continue;
+
+ if(!is_test(method))
+ continue;
+
+ var added = false;
+ foreach(var test in testcase)
+ if(test.name == method.name)
+ added=true;
+ if(added)
+ continue;
+
+ var adapter = new TestAdapter(method.name, config.timeout);
+ annotate_label(adapter);
+ annotate(adapter, method);
+
+ if(config.in_subprocess && adapter.status != TestStatus.SKIPPED) {
+ var attr = new Vala.CCodeAttribute (method);
+
+ if(method.coroutine) {
+ try {
+ unowned TestPlan.AsyncTestMethod beginmethod =
+ (TestPlan.AsyncTestMethod)assembly.get_method(attr.name);
+ unowned TestPlan.AsyncTestMethodResult testmethod =
+ (TestPlan.AsyncTestMethodResult)assembly.get_method(attr.finish_real_name);
+ adapter.add_async_test(beginmethod, testmethod);
+ } catch (Error e) {
+ var message = e.message;
+ adapter.add_test_method(()=> {debug(message);});
+ }
+ } else {
+ try {
+ TestPlan.TestMethod testmethod =
+ (TestPlan.TestMethod)assembly.get_method(attr.name);
+ adapter.add_test((owned)testmethod);
+ } catch (Error e) {
+ var message = e.message;
+ adapter.add_test_method(()=> {debug(message);});
+ }
+ }
+ } else {
+ adapter.add_test_method(()=> {assert_not_reached();});
+ }
+
+ adapter.label = "%s/%s".printf(
+ testcase.label,
+ adapter.label);
+
+ testcase.add_test(adapter);
+ }
+
+ }
+
+ private void annotate_label(Test test) {
+ if(test.name.has_prefix("test_")) {
+ test.label = test.name.substring(5);
+ } else if(test.name.has_prefix("_test_")) {
+ test.label = test.name.substring(6);
+ test.status = TestStatus.SKIPPED;
+ } else if(test.name.has_prefix("todo_test_")) {
+ test.label = test.name.substring(10);
+ test.status = TestStatus.TODO;
+ } else {
+ test.label = test.name;
+ }
+ test.label = test.label.replace("_", " ");
+ }
+
+ private void annotate(TestAdapter adapter, Vala.Method method) {
+
+ foreach(var attr in method.attributes) {
+ if(attr.name == "Test") {
+ if(attr.has_argument("name"))
+ adapter.label = attr.get_string("name");
+ if(attr.has_argument("skip")) {
+ adapter.status = TestStatus.SKIPPED;
+ adapter.status_message = attr.get_string("skip");
+ } else if(attr.has_argument("todo")) {
+ adapter.status = TestStatus.SKIPPED;
+ adapter.status_message = attr.get_string("todo");
+ } else if(attr.has_argument("timeout")) {
+ adapter.timeout = int.parse(attr.get_string("timeout"));
+ }
+ }
+ }
+ }
+
+ private bool is_test(Vala.Method method) {
+ bool istest = false;
+
+ if(method.is_virtual)
+ foreach(var test in testcase)
+ if(test.name == method.name)
+ return false;
+
+ if (method.name.has_prefix("test_") ||
+ method.name.has_prefix("_test_") ||
+ method.name.has_prefix("todo_test_"))
+ istest = true;
+
+ foreach(var attr in method.attributes)
+ if(attr.name == "Test")
+ istest = true;
+
+ if(method.has_result)
+ istest = false;
+
+ if(method.get_parameters().size > 0)
+ istest = false;
+
+ return istest;
+ }
+
+ public TestSuite visit_testsuite(Vala.Class testclass) throws Error {
+ unowned Constructor meth = get_constructor(testclass);
+ var testcase_test = meth() as TestSuite;
+ testcase_test.name = testclass.name;
+ return testcase_test;
+ }
+}