[mock-function] Make mock-function usable with move-only types

And add tests.

Test: runtests -t mock-function-test
Test: runtests -t mtk-thermal-test
Test: runtests -t mt8167-i2c-test
Test: runtests -t as370-gpio-test
Test: runtests -t bt-hci-mediatek-test
Change-Id: I1de2ac4a0066dc7e1f754fd40dd922c3ede97df6
diff --git a/zircon/system/ulib/mock-function/include/lib/mock-function/mock-function.h b/zircon/system/ulib/mock-function/include/lib/mock-function/mock-function.h
index bdfc3a7..9990e78 100644
--- a/zircon/system/ulib/mock-function/include/lib/mock-function/mock-function.h
+++ b/zircon/system/ulib/mock-function/include/lib/mock-function/mock-function.h
@@ -46,7 +46,8 @@
 public:
     MockFunction& ExpectCall(R ret, Ts... args) {
         has_expectations_ = true;
-        expectations_.push_back(Expectation{ret, std::make_tuple(args...)});
+        std::tuple<Ts...> args_tuple = std::make_tuple(std::forward<Ts>(args)...);
+        expectations_.push_back(Expectation{std::move(ret), std::move(args_tuple)});
         return *this;
     }
 
@@ -57,7 +58,7 @@
 
     R Call(Ts... args) {
         R ret = {};
-        CallHelper(&ret, args...);
+        CallHelper(&ret, std::forward<Ts>(args)...);
         return ret;
     }
 
@@ -79,9 +80,9 @@
     void CallHelper(R* ret, Ts... args) {
         ASSERT_LT(expectation_index_, expectations_.size());
 
-        Expectation exp = expectations_[expectation_index_++];
-        EXPECT_TRUE(exp.args == std::make_tuple(args...));
-        *ret = exp.ret_value;
+        Expectation exp = std::move(expectations_[expectation_index_++]);
+        EXPECT_TRUE(exp.args == std::make_tuple(std::forward<Ts>(args)...));
+        *ret = std::move(exp.ret_value);
     }
 
     bool has_expectations_ = false;
@@ -94,7 +95,8 @@
 public:
     MockFunction& ExpectCall(Ts... args) {
         has_expectations_ = true;
-        expectations_.push_back(std::make_tuple(args...));
+        std::tuple<Ts...> args_tuple = std::make_tuple(std::forward<Ts>(args)...);
+        expectations_.push_back(std::move(args_tuple));
         return *this;
     }
 
@@ -103,7 +105,7 @@
         return *this;
     }
 
-    void Call(Ts... args) { CallHelper(args...); }
+    void Call(Ts... args) { CallHelper(std::forward<Ts>(args)...); }
 
     bool HasExpectations() const { return has_expectations_; }
 
@@ -117,7 +119,9 @@
 private:
     void CallHelper(Ts... args) {
         ASSERT_LT(expectation_index_, expectations_.size());
-        EXPECT_TRUE(expectations_[expectation_index_++] == std::make_tuple(args...));
+
+        std::tuple<Ts...> exp = std::move(expectations_[expectation_index_++]);
+        EXPECT_TRUE(exp == std::make_tuple(std::forward<Ts>(args)...));
     }
 
     bool has_expectations_ = false;
diff --git a/zircon/system/ulib/mock-function/test/BUILD.gn b/zircon/system/ulib/mock-function/test/BUILD.gn
new file mode 100644
index 0000000..6b5f4a1
--- /dev/null
+++ b/zircon/system/ulib/mock-function/test/BUILD.gn
@@ -0,0 +1,24 @@
+# Copyright 2019 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+group("test") {
+  testonly = true
+  deps = [
+    ":mock-function-test",
+  ]
+}
+
+test("mock-function-test") {
+  output_name = "mock-function-test"
+  sources = [
+    "mock-function-test.cpp",
+  ]
+  deps = [
+    "$zx/system/ulib/fbl",
+    "$zx/system/ulib/fdio",
+    "$zx/system/ulib/mock-function",
+    "$zx/system/ulib/zircon",
+    "$zx/system/ulib/zx",
+    "$zx/system/ulib/zxtest",
+  ]
+}
diff --git a/zircon/system/ulib/mock-function/test/mock-function-test.cpp b/zircon/system/ulib/mock-function/test/mock-function-test.cpp
new file mode 100644
index 0000000..601cc0c
--- /dev/null
+++ b/zircon/system/ulib/mock-function/test/mock-function-test.cpp
@@ -0,0 +1,67 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <lib/mock-function/mock-function.h>
+#include <zxtest/zxtest.h>
+
+namespace mock_function {
+
+class MoveOnlyClass {
+public:
+    DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(MoveOnlyClass);
+
+    MoveOnlyClass() : key_(0) {}
+    MoveOnlyClass(uint32_t key) : key_(key) {}
+
+    MoveOnlyClass(MoveOnlyClass&& other) : key_(other.key_) { other.key_ = 0; }
+    MoveOnlyClass& operator=(MoveOnlyClass&& other) {
+        key_ = other.key_;
+        other.key_ = 0;
+        return *this;
+    }
+
+    bool operator==(const MoveOnlyClass& lhs) const { return lhs.key_ == key_; }
+
+    uint32_t key() const { return key_; }
+
+private:
+    uint32_t key_;
+};
+
+TEST(MockFunction, MoveArgument) {
+    MockFunction<void, MoveOnlyClass, int> mock_function;
+    MoveOnlyClass arg1(10);
+    MoveOnlyClass arg2(10);
+
+    mock_function.ExpectCall(std::move(arg1), 25);
+    mock_function.Call(std::move(arg2), 25);
+
+    mock_function.VerifyAndClear();
+}
+
+TEST(MockFunction, MoveReturnValue) {
+    MockFunction<MoveOnlyClass, int, int> mock_function;
+    MoveOnlyClass arg1(50);
+
+    mock_function.ExpectCall(std::move(arg1), 100, 200);
+    MoveOnlyClass ret = mock_function.Call(100, 200);
+
+    mock_function.VerifyAndClear();
+    EXPECT_EQ(ret.key(), 50);
+}
+
+TEST(MockFunction, MoveTupleReturnValue) {
+    MockFunction<std::tuple<int, MoveOnlyClass>, int> mock_function;
+    MoveOnlyClass arg1(30);
+
+    mock_function.ExpectCall({80, std::move(arg1)}, 5000);
+    std::tuple<int, MoveOnlyClass> tup = mock_function.Call(5000);
+    MoveOnlyClass ret = std::move(std::get<1>(tup));
+
+    mock_function.VerifyAndClear();
+    EXPECT_EQ(std::get<0>(tup), 80);
+    EXPECT_EQ(ret.key(), 30);
+}
+
+}  // namespace mock_function
diff --git a/zircon/system/utest/BUILD.gn b/zircon/system/utest/BUILD.gn
index 1df80f0..f6ecff4 100644
--- a/zircon/system/utest/BUILD.gn
+++ b/zircon/system/utest/BUILD.gn
@@ -91,6 +91,7 @@
       "$zx/system/ulib/minfs/allocator/test",
       "$zx/system/ulib/minfs/test",
       "$zx/system/ulib/mipi-dsi:mipidsi",
+      "$zx/system/ulib/mock-function/test",
       "$zx/system/ulib/paver:paver-test",
       "$zx/system/ulib/pretty:pretty-test",
       "$zx/system/ulib/region-alloc/test",