[geary/wip/794174-conversation-monitor-max-cpu: 3/4] Add a mock object mixin that can check call expectations on mocks.
- From: Michael Gratton <mjog src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/wip/794174-conversation-monitor-max-cpu: 3/4] Add a mock object mixin that can check call expectations on mocks.
- Date: Sat, 24 Mar 2018 03:15:28 +0000 (UTC)
commit ace6cb87aae92e4251062d0ce7d2644fd960b206
Author: Michael James Gratton <mike vee net>
Date: Fri Mar 9 12:03:07 2018 +1100
Add a mock object mixin that can check call expectations on mocks.
* test/mock-object.vala: Add initial mock object implementation.
* test/test-case.vala; Add some useful high level assertion functions.
test/CMakeLists.txt | 1 +
test/meson.build | 1 +
test/mock-object.vala | 211 +++++++++++++++++++++++++++++++++++++++++++++++++
test/test-case.vala | 101 +++++++++++++++++++++++-
4 files changed, 311 insertions(+), 3 deletions(-)
---
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 40d161b..2d16ff0 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -3,6 +3,7 @@
# Copyright 2016 Michael Gratton <mike vee net>
set(TEST_LIB_SRC
+ mock-object.vala
test-case.vala
)
diff --git a/test/meson.build b/test/meson.build
index 86fde3c..7c44a8b 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -1,4 +1,5 @@
geary_test_lib_sources = [
+ 'mock-object.vala',
'test-case.vala',
]
diff --git a/test/mock-object.vala b/test/mock-object.vala
new file mode 100644
index 0000000..2902ce5
--- /dev/null
+++ b/test/mock-object.vala
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2018 Michael Gratton <mike vee net>
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later). See the COPYING file in this distribution.
+ */
+
+private interface Argument {
+
+ public abstract void assert(Object object) throws Error;
+
+}
+
+private class BoxArgument<T> : Object, Argument {
+
+ private T value;
+
+ internal BoxArgument(T value) {
+ this.value = value;
+ }
+
+ public new void assert(Object object) throws Error {
+ assert_true(
+ object is BoxArgument,
+ "Expected %s value".printf(this.get_type().name())
+ );
+ assert_true(this.value == ((BoxArgument<T>) object).value);
+ }
+
+ }
+
+private class IntArgument : Object, Argument {
+
+ private int value;
+
+ internal IntArgument(int value) {
+ this.value = value;
+ }
+
+ public new void assert(Object object) throws Error {
+ assert_true(object is IntArgument, "Expected int value");
+ assert_int(this.value, ((IntArgument) object).value);
+ }
+
+}
+
+
+public interface MockObject {
+
+
+ public static Object box_arg<T>(T value) {
+ return new BoxArgument<T>(value);
+ }
+
+ public static Object int_arg(int value) {
+ return new IntArgument(value);
+ }
+
+ public class ExpectedCall : Object {
+
+
+ public string name { get; private set; }
+ internal Object[]? args;
+ public Error? throw_error { get; private set; default = null; }
+ public Object? return_object { get; private set; default = null; }
+ public Variant? return_value { get; private set; default = null; }
+
+
+ internal ExpectedCall(string name, Object[]? args) {
+ this.name = name;
+ this.args = args;
+ }
+
+ public ExpectedCall returns_object(Object value) {
+ this.return_object = value;
+ return this;
+ }
+
+ public ExpectedCall returns_boolean(bool value) {
+ this.return_value = new GLib.Variant.boolean(value);
+ return this;
+ }
+
+ public ExpectedCall @throws(Error err) {
+ this.throw_error = err;
+ return this;
+ }
+
+ }
+
+
+ protected struct ActualCall {
+ string name;
+ Object[] args;
+ }
+
+
+ protected abstract Gee.List<ExpectedCall> expected { get; set; }
+ protected abstract Gee.List<ActualCall?> actual { get; set; }
+
+
+ public ExpectedCall expect_call(string name, Object[]? args = null) {
+ ExpectedCall expected = new ExpectedCall(name, args);
+ this.expected.add(expected);
+ return expected;
+ }
+
+ public void assert_expectations() throws Error{
+ int calls = 0;
+ foreach (ExpectedCall expected in this.expected) {
+ string context = "Call #%d, %s".printf(calls, expected.name);
+ ActualCall? actual = null;
+ if (calls < this.actual.size) {
+ actual = this.actual.get(calls++);
+ }
+
+ assert_true(actual != null, context + " not made");
+ assert_string(expected.name, actual.name, context);
+ if (expected.args != null) {
+ assert_args(expected.args, actual.args, context);
+ }
+ }
+
+ assert_int(this.expected.size, this.actual.size,
+ "Number of calls");
+ }
+
+ protected bool boolean_call(string name, Object[] args, bool default_return)
+ throws Error {
+ ExpectedCall? expected = get_next_expected();
+ this.actual.add({ name, args });
+
+ if (expected.throw_error != null) {
+ throw expected.throw_error;
+ }
+
+ bool return_value = default_return;
+ if (expected != null && expected.return_value != null) {
+ return_value = expected.return_value.get_boolean();
+ }
+ return return_value;
+ }
+
+ protected R object_call<R>(string name, Object[] args, R default_return)
+ throws Error {
+ ExpectedCall? expected = get_next_expected();
+ this.actual.add({ name, args });
+
+ if (expected.throw_error != null) {
+ throw expected.throw_error;
+ }
+
+ R? return_object = default_return;
+ if (expected != null && expected.return_object != null) {
+ return_object = (R) expected.return_object;
+ }
+ return return_object;
+ }
+
+ private inline ExpectedCall? get_next_expected() {
+ ExpectedCall? next = null;
+ if (this.expected.size > this.actual.size) {
+ next = this.expected.get(this.actual.size);
+ }
+ return next;
+ }
+
+ private void assert_args(Object[]? expected_args, Object[]? actual_args, string context)
+ throws Error {
+ int args = 0;
+ foreach (Object expected in expected_args) {
+ if (args >= actual_args.length) {
+ break;
+ }
+
+ Object actual = actual_args[args];
+ string arg_context = "%s, argument #%d".printf(context, args++);
+
+ if (expected is Argument) {
+ ((Argument) expected).assert(actual);
+ } else if (expected != null) {
+ assert_true(
+ actual != null,
+ "%s: Expected %s, actual is null".printf(
+ arg_context, expected.get_type().name()
+ )
+ );
+ assert_true(
+ expected.get_type() == actual.get_type(),
+ "%s: Expected %s, actual: %s".printf(
+ arg_context,
+ expected.get_type().name(),
+ actual.get_type().name()
+ )
+ );
+ assert_equal(
+ expected, actual,
+ "%s: object value".printf(arg_context)
+ );
+ } else {
+
+ }
+ }
+
+ assert_int(
+ expected_args.length, actual_args.length,
+ "%s: argument list length".printf(context)
+ );
+ }
+
+}
diff --git a/test/test-case.vala b/test/test-case.vala
index b17ce89..7c85a1f 100644
--- a/test/test-case.vala
+++ b/test/test-case.vala
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2009 Julien Peeters
- * Copyright (C) 2017 Michael Gratton
+ * Copyright (C) 2017-2018 Michael Gratton <mike vee net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -17,10 +17,105 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Author:
- * Julien Peeters <contact julienpeeters fr>
- * Michael Gratton <mike vee net>
+ * Julien Peeters <contact julienpeeters fr>
+ * Michael Gratton <mike vee net>
*/
+
+public void assert_equal(Object expected, Object actual, string? context = null)
+ throws Error {
+ if (expected != actual) {
+ print_assert(context ?? "Objects 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 > 20) {
+ a = a[0:15] + "…";
+ }
+ string b = actual;
+ if (b.length > 20) {
+ b = b[0:15] + "…";
+ }
+ print_assert("Expected: \"%s\", was: \"%s\"".printf(a, b), 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_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 {
protected MainContext main_loop = MainContext.default();
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]