[geary/wip/794174-conversation-monitor-max-cpu: 3/4] Add a mock object mixin that can check call expectations on mocks.



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]