/* * Copyright (C) 2009 Julien Peeters * Copyright (C) 2017-2018 Michael Gratton * * 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 * * Author: * Julien Peeters * Michael Gratton */ public void assert_null(Object? actual, string? context = null) throws Error { if (actual != null) { print_assert(context ?? "Object is non-null", null); assert_not_reached(); } } public void assert_non_null(Object? actual, string? context = null) throws Error { if (actual == null) { print_assert(context ?? "Object is null", null); assert_not_reached(); } } public void assert_equal(Object expected, Object? actual, string? context = null) throws Error { if (expected != actual) { print_assert(context ?? "Objects are not equal", null); assert_not_reached(); } } public void assert_string(string expected, string? actual, string? context = null) throws Error { if (expected != actual) { string a = expected; if (a.length > 32) { a = a[0:32] + "…"; } string b = actual; if (b.length > 32) { b = b[0:32] + "…"; } print_assert("Expected: \"%s\", was: \"%s\"".printf(a, b), context); assert_not_reached(); } } public void assert_null_string(string? actual, string? context = null) throws Error { if (actual != null) { string a = actual; if (a.length > 70) { a = a[0:70] + "…"; } print_assert("Expected: null, was: \"%s\"".printf(a), context); assert_not_reached(); } } public void assert_int(int expected, int actual, string? context = null) throws Error { if (expected != actual) { print_assert("Expected: %d, was: %d".printf(expected, actual), context); assert_not_reached(); } } public void assert_true(bool condition, string? context = null) throws Error { if (!condition) { print_assert(context ?? "Expected true", null); assert_not_reached(); } } public void assert_false(bool condition, string? context = null) throws Error { if (condition) { print_assert(context ?? "Expected false", null); assert_not_reached(); } } public void assert_error(Error expected, Error? actual, string? context = null) { bool failed = false; if (actual == null) { print_assert( "Expected error: %s %i, was null".printf( expected.domain.to_string(), expected.code ), context ); failed = true; } else if (expected.domain != actual.domain || expected.code != actual.code) { print_assert( "Expected error: %s %i, was actually %s %i: %s".printf( expected.domain.to_string(), expected.code, actual.domain.to_string(), actual.code, actual.message ), context ); failed = true; } if (failed) { assert_not_reached(); } } // XXX this shadows GLib.assert_no_error since that doesn't work public void assert_no_error(Error? err, string? context = null) { if (err != null) { print_assert( "Unexpected error: %s %i: %s".printf( err.domain.to_string(), err.code, err.message ), context ); assert_not_reached(); } } private inline void print_assert(string message, string? context) { string output = message; if (context != null) { output = "%s: %s".printf(context, output); } GLib.stderr.puts(output); GLib.stderr.putc('\n'); } public abstract class TestCase : Object { /** GLib.File URI for resources in test/data. */ public const string RESOURCE_URI = "resource:///org/gnome/GearyTest"; private class SignalWaiter : Object { public bool was_fired = false; public void @callback(Object source) { was_fired = true; } } protected MainContext main_loop = MainContext.default(); private GLib.TestSuite suite; private Adaptor[] adaptors = new Adaptor[0]; private AsyncQueue async_results = new AsyncQueue(); public delegate void TestMethod() throws Error; public TestCase(string name) { this.suite = new GLib.TestSuite(name); } public void add_test(string name, owned TestMethod test) { var adaptor = new Adaptor(name, (owned) test, this); this.adaptors += adaptor; this.suite.add( new GLib.TestCase( adaptor.name, adaptor.set_up, adaptor.run, adaptor.tear_down ) ); } public virtual void set_up() throws Error { } public virtual void tear_down() throws Error { } public GLib.TestSuite get_suite() { return this.suite; } protected void async_complete(AsyncResult result) { this.async_results.push(result); // notify the loop so that if async_result() has already been // called, that method won't block this.main_loop.wakeup(); } protected AsyncResult async_result() { AsyncResult? result = null; while (result == null) { this.main_loop.iteration(true); result = this.async_results.try_pop(); } return result; } /** * Waits for a mock object's call to be completed. * * This method busy waits on the test's main loop until either * until {@link ExpectedCall.was_called} is true, or until the * given timeout in seconds has occurred. * * Returns //true// if the call was made, or //false// if the * timeout was reached. */ protected bool wait_for_call(ExpectedCall call, double timeout = 1.0) { GLib.Timer timer = new GLib.Timer(); timer.start(); while (!call.was_called && timer.elapsed() < timeout) { this.main_loop.iteration(false); } return call.was_called; } /** * Waits for an object's signal to be fired. * * This method busy waits on the test's main loop until either * until the object emits the named signal, or until the given * timeout in seconds has occurred. * * Returns //true// if the signal was fired, or //false// if the * timeout was reached. */ protected bool wait_for_signal(Object source, string name, double timeout = 0.5) { SignalWaiter handler = new SignalWaiter(); ulong id = GLib.Signal.connect_swapped( source, name, (GLib.Callback) handler.callback, handler ); GLib.Timer timer = new GLib.Timer(); timer.start(); while (!handler.was_fired && timer.elapsed() < timeout) { this.main_loop.iteration(false); } source.disconnect(id); return handler.was_fired; } private class Adaptor { public string name { get; private set; } private TestMethod test; private TestCase test_case; public Adaptor(string name, owned TestMethod test, TestCase test_case) { this.name = name; this.test = (owned) test; this.test_case = test_case; } public void set_up(void* fixture) { try { this.test_case.set_up(); } catch (Error err) { assert_no_error(err); } } public void run(void* fixture) { try { this.test(); } catch (Error err) { assert_no_error(err); } } public void tear_down(void* fixture) { try { this.test_case.tear_down(); } catch (Error err) { assert_no_error(err); } } } }