From 6960649bf1d84a88e8908725e86e92544f66fe6b Mon Sep 17 00:00:00 2001 From: Takeshi Yoneda Date: Mon, 26 Apr 2021 16:28:58 +0900 Subject: [PATCH 1/3] wasi: support monotonic clock on clock_time_get. Signed-off-by: Takeshi Yoneda --- src/exports.cc | 21 ++++++++-- test/BUILD | 1 + test/exports_test.cc | 53 +++++++++++++++--------- test/runtime_test.cc | 89 +++++++++++++++++++---------------------- test/test_data/BUILD | 6 +++ test/test_data/clock.rs | 21 ++++++++++ test/utility.h | 32 ++++++++++----- 7 files changed, 142 insertions(+), 81 deletions(-) create mode 100644 test/test_data/clock.rs diff --git a/src/exports.cc b/src/exports.cc index 97bbab06..8513988c 100644 --- a/src/exports.cc +++ b/src/exports.cc @@ -833,16 +833,31 @@ Word wasi_unstable_args_sizes_get(void *raw_context, Word argc_ptr, Word argv_bu Word wasi_unstable_clock_time_get(void *raw_context, Word clock_id, uint64_t precision, Word result_time_uint64_ptr) { - if (clock_id != 0 /* realtime */) { +#if !defined(_MSC_VER) + clockid_t id = CLOCK_REALTIME; + switch (clock_id) { + case 0 /* realtime */: + break; + case 1 /* monotonic */: + id = CLOCK_MONOTONIC; + break; + default: + // process_cputime_id and thread_cputime_id are not supported yet. return 58; // __WASI_ENOTSUP } - + struct timespec tpe; + clock_gettime(id, &tpe); + uint64_t result = tpe.tv_sec; + result *= 1000000000; + result += tpe.tv_nsec; auto context = WASM_CONTEXT(raw_context); - uint64_t result = context->getCurrentTimeNanoseconds(); if (!context->wasm()->setDatatype(result_time_uint64_ptr, result)) { return 21; // __WASI_EFAULT } return 0; // __WASI_ESUCCESS +#else + return 58; // __WASI_ENOTSUP; +#endif } // __wasi_errno_t __wasi_random_get(uint8_t *buf, size_t buf_len); diff --git a/test/BUILD b/test/BUILD index 0fad5ceb..60ff362a 100644 --- a/test/BUILD +++ b/test/BUILD @@ -37,6 +37,7 @@ cc_test( srcs = ["exports_test.cc"], copts = COPTS, data = [ + "//test/test_data:clock.wasm", "//test/test_data:env.wasm", ], linkopts = LINKOPTS, diff --git a/test/exports_test.cc b/test/exports_test.cc index 74e23c08..9c61c59a 100644 --- a/test/exports_test.cc +++ b/test/exports_test.cc @@ -48,47 +48,60 @@ class TestContext : public ContextBase { }; TEST_P(TestVM, Environment) { + // Initialize VM. std::unordered_map envs = {{"KEY1", "VALUE1"}, {"KEY2", "VALUE2"}}; - initialize("env.wasm"); + initialize(readTestWasmFile("env.wasm"), "vm_id", "", "", envs); - auto wasm_base = WasmBase(std::move(vm_), "vm_id", "", "", envs, {}); - ASSERT_TRUE(wasm_base.wasm_vm()->load(source_, false)); - - TestContext context(&wasm_base); + // Initialize the context. + TestContext context(wasmBase()); current_context_ = &context; - wasm_base.registerCallbacks(); - - ASSERT_TRUE(wasm_base.wasm_vm()->link("")); - + // Call the exported function. WasmCallVoid<0> run; - wasm_base.wasm_vm()->getFunction("run", &run); - + wasmBase()->wasm_vm()->getFunction("run", &run); run(current_context_); + // Check logs. auto msg = context.log_msg(); EXPECT_NE(std::string::npos, msg.find("KEY1: VALUE1\n")) << msg; EXPECT_NE(std::string::npos, msg.find("KEY2: VALUE2\n")) << msg; } TEST_P(TestVM, WithoutEnvironment) { - initialize("env.wasm"); - auto wasm_base = WasmBase(std::move(vm_), "vm_id", "", "", {}, {}); - ASSERT_TRUE(wasm_base.wasm_vm()->load(source_, false)); + // Initialize VM. + initialize(readTestWasmFile("env.wasm")); - TestContext context(&wasm_base); + // Initialize the context. + TestContext context(wasmBase()); current_context_ = &context; - wasm_base.registerCallbacks(); + // Call the exported function. + WasmCallVoid<0> run; + wasmBase()->wasm_vm()->getFunction("run", &run); + run(current_context_); + + // Check logs. + EXPECT_EQ(context.log_msg(), ""); +} - ASSERT_TRUE(wasm_base.wasm_vm()->link("")); +TEST_P(TestVM, Clock) { + // Initialize VM. + initialize(readTestWasmFile("clock.wasm")); - WasmCallVoid<0> run; - wasm_base.wasm_vm()->getFunction("run", &run); + // Initialize the context. + TestContext context(wasmBase()); + current_context_ = &context; + // Call the exported function. + WasmCallVoid<0> run; + wasmBase()->wasm_vm()->getFunction("run", &run); + ASSERT_TRUE(run); run(current_context_); - EXPECT_EQ(context.log_msg(), ""); + // Check logs. + auto msg = context.log_msg(); + EXPECT_NE(std::string::npos, msg.find("monotonic: ")) << msg; + EXPECT_NE(std::string::npos, msg.find("realtime: ")) << msg; } } // namespace diff --git a/test/runtime_test.cc b/test/runtime_test.cc index 7128d8c7..30e33e7a 100644 --- a/test/runtime_test.cc +++ b/test/runtime_test.cc @@ -34,44 +34,39 @@ auto test_values = testing::ValuesIn(getRuntimes()); INSTANTIATE_TEST_SUITE_P(Runtimes, TestVM, test_values); TEST_P(TestVM, Basic) { - EXPECT_EQ(vm_->cloneable(), proxy_wasm::Cloneable::CompiledBytecode); - EXPECT_EQ(vm_->runtime(), runtime_); + EXPECT_EQ(wasmVm()->cloneable(), proxy_wasm::Cloneable::CompiledBytecode); + EXPECT_EQ(wasmVm()->runtime(), runtime()); } TEST_P(TestVM, ABIVersion) { - initialize("abi_export.wasm"); - ASSERT_TRUE(vm_->load(source_, false)); - ASSERT_EQ(vm_->getAbiVersion(), AbiVersion::ProxyWasm_0_2_0); + initialize(readTestWasmFile("abi_export.wasm")); + ASSERT_EQ(wasmVm()->getAbiVersion(), AbiVersion::ProxyWasm_0_2_0); } TEST_P(TestVM, Memory) { - initialize("abi_export.wasm"); - ASSERT_TRUE(vm_->load(source_, false)); - ASSERT_TRUE(vm_->link("")); + initialize(readTestWasmFile("abi_export.wasm")); Word word; - ASSERT_TRUE(vm_->setWord(0x2000, Word(100))); - ASSERT_TRUE(vm_->getWord(0x2000, &word)); + ASSERT_TRUE(wasmVm()->setWord(0x2000, Word(100))); + ASSERT_TRUE(wasmVm()->getWord(0x2000, &word)); ASSERT_EQ(100, word.u64_); int32_t data[2] = {-1, 200}; - ASSERT_TRUE(vm_->setMemory(0x200, sizeof(int32_t) * 2, static_cast(data))); - ASSERT_TRUE(vm_->getWord(0x200, &word)); + ASSERT_TRUE(wasmVm()->setMemory(0x200, sizeof(int32_t) * 2, static_cast(data))); + ASSERT_TRUE(wasmVm()->getWord(0x200, &word)); ASSERT_EQ(-1, static_cast(word.u64_)); - ASSERT_TRUE(vm_->getWord(0x204, &word)); + ASSERT_TRUE(wasmVm()->getWord(0x204, &word)); ASSERT_EQ(200, static_cast(word.u64_)); } TEST_P(TestVM, Clone) { - initialize("abi_export.wasm"); - ASSERT_TRUE(vm_->load(source_, false)); - ASSERT_TRUE(vm_->link("")); + initialize(readTestWasmFile("abi_export.wasm")); const auto address = 0x2000; Word word; { - auto clone = vm_->clone(); + auto clone = wasmVm()->clone(); ASSERT_TRUE(clone != nullptr); - ASSERT_NE(vm_, clone); + ASSERT_NE(wasmVm(), clone.get()); ASSERT_TRUE(clone->link("")); ASSERT_TRUE(clone->setWord(address, Word(100))); @@ -80,7 +75,7 @@ TEST_P(TestVM, Clone) { } // check memory arrays are not overrapped - ASSERT_TRUE(vm_->getWord(address, &word)); + ASSERT_TRUE(wasmVm()->getWord(address, &word)); ASSERT_NE(100, word.u64_); } @@ -101,47 +96,46 @@ void callback(void *raw_context) { Word callback2(void *raw_context, Word val) { return val + 100; } TEST_P(TestVM, StraceLogLevel) { - initialize("callback.wasm"); - ASSERT_TRUE(vm_->load(source_, false)); - vm_->registerCallback("env", "callback", &nopCallback, - &ConvertFunctionWordToUint32::convertFunctionWordToUint32); - vm_->registerCallback( + ASSERT_TRUE(wasmVm()->load(readTestWasmFile("callback.wasm"), false)); + wasmVm()->registerCallback( + "env", "callback", &nopCallback, + &ConvertFunctionWordToUint32::convertFunctionWordToUint32); + wasmVm()->registerCallback( "env", "callback2", &callback2, &ConvertFunctionWordToUint32::convertFunctionWordToUint32); - ASSERT_TRUE(vm_->link("")); + ASSERT_TRUE(wasmVm()->link("")); WasmCallVoid<0> run; - vm_->getFunction("run", &run); + wasmVm()->getFunction("run", &run); run(nullptr); // no trace message found since DummyIntegration's log_level_ defaults to LogLevel::info - EXPECT_EQ(integration_->trace_message_, ""); + EXPECT_EQ(integration()->trace_message_, ""); - integration_->log_level_ = LogLevel::trace; + integration()->log_level_ = LogLevel::trace; run(nullptr); - EXPECT_NE(integration_->trace_message_, ""); + EXPECT_NE(integration()->trace_message_, ""); } TEST_P(TestVM, Callback) { - initialize("callback.wasm"); - ASSERT_TRUE(vm_->load(source_, false)); + ASSERT_TRUE(wasmVm()->load(readTestWasmFile("callback.wasm"), false)); TestContext context; current_context_ = &context; - vm_->registerCallback( + wasmVm()->registerCallback( "env", "callback", &callback, &ConvertFunctionWordToUint32::convertFunctionWordToUint32); - vm_->registerCallback( + wasmVm()->registerCallback( "env", "callback2", &callback2, &ConvertFunctionWordToUint32::convertFunctionWordToUint32); - ASSERT_TRUE(vm_->link("")); + ASSERT_TRUE(wasmVm()->link("")); WasmCallVoid<0> run; - vm_->getFunction("run", &run); + wasmVm()->getFunction("run", &run); EXPECT_TRUE(run != nullptr); for (auto i = 0; i < 100; i++) { run(current_context_); @@ -149,33 +143,32 @@ TEST_P(TestVM, Callback) { ASSERT_EQ(context.counter, 100); WasmCallWord<1> run2; - vm_->getFunction("run2", &run2); + wasmVm()->getFunction("run2", &run2); Word res = run2(current_context_, Word{0}); ASSERT_EQ(res.u32(), 100100); // 10000 (global) + 100(in callback) } TEST_P(TestVM, Trap) { - initialize("trap.wasm"); - ASSERT_TRUE(vm_->load(source_, false)); - ASSERT_TRUE(vm_->link("")); + ASSERT_TRUE(wasmVm()->load(readTestWasmFile("trap.wasm"), false)); + ASSERT_TRUE(wasmVm()->link("")); WasmCallVoid<0> trigger; - vm_->getFunction("trigger", &trigger); + wasmVm()->getFunction("trigger", &trigger); EXPECT_TRUE(trigger != nullptr); trigger(current_context_); std::string exp_message = "Function: trigger failed"; - ASSERT_TRUE(integration_->error_message_.find(exp_message) != std::string::npos); + ASSERT_TRUE(integration()->error_message_.find(exp_message) != std::string::npos); WasmCallWord<1> trigger2; - vm_->getFunction("trigger2", &trigger2); + wasmVm()->getFunction("trigger2", &trigger2); EXPECT_TRUE(trigger2 != nullptr); trigger2(current_context_, 0); exp_message = "Function: trigger2 failed:"; - ASSERT_TRUE(integration_->error_message_.find(exp_message) != std::string::npos); + ASSERT_TRUE(integration()->error_message_.find(exp_message) != std::string::npos); } TEST_P(TestVM, WithPrecompiledSection) { // Verify that stripping precompile_* custom section works. - initialize("abi_export.wasm"); + auto source = readTestWasmFile("abi_export.wasm"); // Append precompiled_test section std::vector custom_section = {// custom section id 0x00, @@ -189,9 +182,9 @@ TEST_P(TestVM, WithPrecompiledSection) { // content 0x01, 0x01}; - source_.append(custom_section.data(), custom_section.size()); - ASSERT_TRUE(vm_->load(source_, false)); - ASSERT_EQ(vm_->getAbiVersion(), AbiVersion::ProxyWasm_0_2_0); + source.append(custom_section.data(), custom_section.size()); + ASSERT_TRUE(wasmVm()->load(source, false)); + ASSERT_EQ(wasmVm()->getAbiVersion(), AbiVersion::ProxyWasm_0_2_0); } } // namespace diff --git a/test/test_data/BUILD b/test/test_data/BUILD index 715ed92e..3d99c103 100644 --- a/test/test_data/BUILD +++ b/test/test_data/BUILD @@ -22,3 +22,9 @@ wasm_rust_binary( srcs = ["env.rs"], wasi = True, ) + +wasm_rust_binary( + name = "clock.wasm", + srcs = ["clock.rs"], + wasi = True, +) diff --git a/test/test_data/clock.rs b/test/test_data/clock.rs new file mode 100644 index 00000000..480a2b1b --- /dev/null +++ b/test/test_data/clock.rs @@ -0,0 +1,21 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::time::{Instant, SystemTime}; + +#[no_mangle] +pub extern "C" fn run() { + println!("monotonic: {:?}", Instant::now()); + println!("realtime: {:?}", SystemTime::now()); +} diff --git a/test/utility.h b/test/utility.h index 22a86f1f..739885c8 100644 --- a/test/utility.h +++ b/test/utility.h @@ -62,32 +62,44 @@ struct DummyIntegration : public WasmVmIntegration { class TestVM : public testing::TestWithParam { public: - std::unique_ptr vm_; - - TestVM() : integration_(new DummyIntegration{}) { - runtime_ = GetParam(); + TestVM() : integration_(new DummyIntegration{}), runtime_(GetParam()) { if (runtime_ == "") { EXPECT_TRUE(false) << "runtime must not be empty"; #if defined(WASM_V8) } else if (runtime_ == "v8") { - vm_ = proxy_wasm::createV8Vm(); + wasm_vm_ = proxy_wasm::createV8Vm(); #endif #if defined(WASM_WAVM) } else if (runtime_ == "wavm") { - vm_ = proxy_wasm::createWavmVm(); + wasm_vm_ = proxy_wasm::createWavmVm(); #endif #if defined(WASM_WASMTIME) } else if (runtime_ == "wasmtime") { - vm_ = proxy_wasm::createWasmtimeVm(); + wasm_vm_ = proxy_wasm::createWasmtimeVm(); #endif } - vm_->integration().reset(integration_); + wasm_vm_->integration().reset(integration_); } - void initialize(std::string filename) { source_ = readTestWasmFile(filename); } + void initialize(const std::string source, std::string_view vm_id = "", + std::string_view vm_configuration = "", std::string_view vm_key = "", + std::unordered_map envs = {}, + AllowedCapabilitiesMap allowed_capabilities = {}) { + wasm_base_ = std::make_unique(std::move(wasm_vm_), vm_id, vm_key, vm_configuration, + envs, allowed_capabilities); + ASSERT_TRUE(wasm_base_->wasm_vm()->load(source, false)); + wasm_base_->registerCallbacks(); + ASSERT_TRUE(wasm_base_->wasm_vm()->link("")); + } + std::string runtime() { return runtime_; } + proxy_wasm::WasmBase *wasmBase() { return wasm_base_.get(); } + proxy_wasm::WasmVm *wasmVm() { return wasm_vm_ ? wasm_vm_.get() : wasm_base_->wasm_vm(); } + DummyIntegration *integration() { return integration_; } +private: DummyIntegration *integration_; - std::string source_; std::string runtime_; + std::unique_ptr wasm_base_; + std::unique_ptr wasm_vm_; }; } // namespace proxy_wasm From dff90443349b3cb513a292f7a90c5b901cf9e111 Mon Sep 17 00:00:00 2001 From: Takeshi Yoneda Date: Mon, 26 Apr 2021 16:53:52 +0900 Subject: [PATCH 2/3] review: add getMonotonicTimeNanoseconds. Signed-off-by: Takeshi Yoneda --- include/proxy-wasm/context.h | 13 +++++++++++++ include/proxy-wasm/context_interface.h | 3 +++ src/exports.cc | 16 ++++------------ 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/include/proxy-wasm/context.h b/include/proxy-wasm/context.h index bf770a7b..6f4e7925 100644 --- a/include/proxy-wasm/context.h +++ b/include/proxy-wasm/context.h @@ -245,6 +245,19 @@ class ContextBase : public RootInterface, #else unimplemented(); return 0; +#endif + } + uint64_t getMonotonicTimeNanoseconds() override { +#if !defined(_MSC_VER) + struct timespec tpe; + clock_gettime(CLOCK_MONOTONIC, &tpe); + uint64_t t = tpe.tv_sec; + t *= 1000000000; + t += tpe.tv_nsec; + return t; +#else + unimplemented(); + return 0; #endif } std::string_view getConfiguration() override { diff --git a/include/proxy-wasm/context_interface.h b/include/proxy-wasm/context_interface.h index 1a6f738d..841dd0e1 100644 --- a/include/proxy-wasm/context_interface.h +++ b/include/proxy-wasm/context_interface.h @@ -557,6 +557,9 @@ struct GeneralInterface { // Provides the current time in nanoseconds since the Unix epoch. virtual uint64_t getCurrentTimeNanoseconds() = 0; + // Provides the monotonic time in nanoseconds. + virtual uint64_t getMonotonicTimeNanoseconds() = 0; + // Returns plugin configuration. virtual std::string_view getConfiguration() = 0; diff --git a/src/exports.cc b/src/exports.cc index 8513988c..9a6431ce 100644 --- a/src/exports.cc +++ b/src/exports.cc @@ -833,31 +833,23 @@ Word wasi_unstable_args_sizes_get(void *raw_context, Word argc_ptr, Word argv_bu Word wasi_unstable_clock_time_get(void *raw_context, Word clock_id, uint64_t precision, Word result_time_uint64_ptr) { -#if !defined(_MSC_VER) - clockid_t id = CLOCK_REALTIME; + uint64_t result = 0; + auto context = WASM_CONTEXT(raw_context); switch (clock_id) { case 0 /* realtime */: + result = context->getCurrentTimeNanoseconds(); break; case 1 /* monotonic */: - id = CLOCK_MONOTONIC; + result = context->getMonotonicTimeNanoseconds(); break; default: // process_cputime_id and thread_cputime_id are not supported yet. return 58; // __WASI_ENOTSUP } - struct timespec tpe; - clock_gettime(id, &tpe); - uint64_t result = tpe.tv_sec; - result *= 1000000000; - result += tpe.tv_nsec; - auto context = WASM_CONTEXT(raw_context); if (!context->wasm()->setDatatype(result_time_uint64_ptr, result)) { return 21; // __WASI_EFAULT } return 0; // __WASI_ESUCCESS -#else - return 58; // __WASI_ENOTSUP; -#endif } // __wasi_errno_t __wasi_random_get(uint8_t *buf, size_t buf_len); From d6a86c3004c8b9592b9c45d121e456aac9627e0c Mon Sep 17 00:00:00 2001 From: Takeshi Yoneda Date: Tue, 27 Apr 2021 10:09:34 +0900 Subject: [PATCH 3/3] review: revert test refactoring. Signed-off-by: Takeshi Yoneda --- test/exports_test.cc | 50 +++++++++++++++---------- test/runtime_test.cc | 89 ++++++++++++++++++++++++-------------------- test/utility.h | 32 +++++----------- 3 files changed, 88 insertions(+), 83 deletions(-) diff --git a/test/exports_test.cc b/test/exports_test.cc index 9c61c59a..c35ea02d 100644 --- a/test/exports_test.cc +++ b/test/exports_test.cc @@ -48,53 +48,63 @@ class TestContext : public ContextBase { }; TEST_P(TestVM, Environment) { - // Initialize VM. std::unordered_map envs = {{"KEY1", "VALUE1"}, {"KEY2", "VALUE2"}}; - initialize(readTestWasmFile("env.wasm"), "vm_id", "", "", envs); + initialize("env.wasm"); - // Initialize the context. - TestContext context(wasmBase()); + auto wasm_base = WasmBase(std::move(vm_), "vm_id", "", "", envs, {}); + ASSERT_TRUE(wasm_base.wasm_vm()->load(source_, false)); + + TestContext context(&wasm_base); current_context_ = &context; - // Call the exported function. + wasm_base.registerCallbacks(); + + ASSERT_TRUE(wasm_base.wasm_vm()->link("")); + WasmCallVoid<0> run; - wasmBase()->wasm_vm()->getFunction("run", &run); + wasm_base.wasm_vm()->getFunction("run", &run); + run(current_context_); - // Check logs. auto msg = context.log_msg(); EXPECT_NE(std::string::npos, msg.find("KEY1: VALUE1\n")) << msg; EXPECT_NE(std::string::npos, msg.find("KEY2: VALUE2\n")) << msg; } TEST_P(TestVM, WithoutEnvironment) { - // Initialize VM. - initialize(readTestWasmFile("env.wasm")); + initialize("env.wasm"); + auto wasm_base = WasmBase(std::move(vm_), "vm_id", "", "", {}, {}); + ASSERT_TRUE(wasm_base.wasm_vm()->load(source_, false)); - // Initialize the context. - TestContext context(wasmBase()); + TestContext context(&wasm_base); current_context_ = &context; - // Call the exported function. + wasm_base.registerCallbacks(); + + ASSERT_TRUE(wasm_base.wasm_vm()->link("")); + WasmCallVoid<0> run; - wasmBase()->wasm_vm()->getFunction("run", &run); + wasm_base.wasm_vm()->getFunction("run", &run); + run(current_context_); - // Check logs. EXPECT_EQ(context.log_msg(), ""); } TEST_P(TestVM, Clock) { - // Initialize VM. - initialize(readTestWasmFile("clock.wasm")); + initialize("clock.wasm"); + auto wasm_base = WasmBase(std::move(vm_), "vm_id", "", "", {}, {}); + ASSERT_TRUE(wasm_base.wasm_vm()->load(source_, false)); - // Initialize the context. - TestContext context(wasmBase()); + TestContext context(&wasm_base); current_context_ = &context; - // Call the exported function. + wasm_base.registerCallbacks(); + + ASSERT_TRUE(wasm_base.wasm_vm()->link("")); + WasmCallVoid<0> run; - wasmBase()->wasm_vm()->getFunction("run", &run); + wasm_base.wasm_vm()->getFunction("run", &run); ASSERT_TRUE(run); run(current_context_); diff --git a/test/runtime_test.cc b/test/runtime_test.cc index 30e33e7a..7128d8c7 100644 --- a/test/runtime_test.cc +++ b/test/runtime_test.cc @@ -34,39 +34,44 @@ auto test_values = testing::ValuesIn(getRuntimes()); INSTANTIATE_TEST_SUITE_P(Runtimes, TestVM, test_values); TEST_P(TestVM, Basic) { - EXPECT_EQ(wasmVm()->cloneable(), proxy_wasm::Cloneable::CompiledBytecode); - EXPECT_EQ(wasmVm()->runtime(), runtime()); + EXPECT_EQ(vm_->cloneable(), proxy_wasm::Cloneable::CompiledBytecode); + EXPECT_EQ(vm_->runtime(), runtime_); } TEST_P(TestVM, ABIVersion) { - initialize(readTestWasmFile("abi_export.wasm")); - ASSERT_EQ(wasmVm()->getAbiVersion(), AbiVersion::ProxyWasm_0_2_0); + initialize("abi_export.wasm"); + ASSERT_TRUE(vm_->load(source_, false)); + ASSERT_EQ(vm_->getAbiVersion(), AbiVersion::ProxyWasm_0_2_0); } TEST_P(TestVM, Memory) { - initialize(readTestWasmFile("abi_export.wasm")); + initialize("abi_export.wasm"); + ASSERT_TRUE(vm_->load(source_, false)); + ASSERT_TRUE(vm_->link("")); Word word; - ASSERT_TRUE(wasmVm()->setWord(0x2000, Word(100))); - ASSERT_TRUE(wasmVm()->getWord(0x2000, &word)); + ASSERT_TRUE(vm_->setWord(0x2000, Word(100))); + ASSERT_TRUE(vm_->getWord(0x2000, &word)); ASSERT_EQ(100, word.u64_); int32_t data[2] = {-1, 200}; - ASSERT_TRUE(wasmVm()->setMemory(0x200, sizeof(int32_t) * 2, static_cast(data))); - ASSERT_TRUE(wasmVm()->getWord(0x200, &word)); + ASSERT_TRUE(vm_->setMemory(0x200, sizeof(int32_t) * 2, static_cast(data))); + ASSERT_TRUE(vm_->getWord(0x200, &word)); ASSERT_EQ(-1, static_cast(word.u64_)); - ASSERT_TRUE(wasmVm()->getWord(0x204, &word)); + ASSERT_TRUE(vm_->getWord(0x204, &word)); ASSERT_EQ(200, static_cast(word.u64_)); } TEST_P(TestVM, Clone) { - initialize(readTestWasmFile("abi_export.wasm")); + initialize("abi_export.wasm"); + ASSERT_TRUE(vm_->load(source_, false)); + ASSERT_TRUE(vm_->link("")); const auto address = 0x2000; Word word; { - auto clone = wasmVm()->clone(); + auto clone = vm_->clone(); ASSERT_TRUE(clone != nullptr); - ASSERT_NE(wasmVm(), clone.get()); + ASSERT_NE(vm_, clone); ASSERT_TRUE(clone->link("")); ASSERT_TRUE(clone->setWord(address, Word(100))); @@ -75,7 +80,7 @@ TEST_P(TestVM, Clone) { } // check memory arrays are not overrapped - ASSERT_TRUE(wasmVm()->getWord(address, &word)); + ASSERT_TRUE(vm_->getWord(address, &word)); ASSERT_NE(100, word.u64_); } @@ -96,46 +101,47 @@ void callback(void *raw_context) { Word callback2(void *raw_context, Word val) { return val + 100; } TEST_P(TestVM, StraceLogLevel) { - ASSERT_TRUE(wasmVm()->load(readTestWasmFile("callback.wasm"), false)); - wasmVm()->registerCallback( - "env", "callback", &nopCallback, - &ConvertFunctionWordToUint32::convertFunctionWordToUint32); - wasmVm()->registerCallback( + initialize("callback.wasm"); + ASSERT_TRUE(vm_->load(source_, false)); + vm_->registerCallback("env", "callback", &nopCallback, + &ConvertFunctionWordToUint32::convertFunctionWordToUint32); + vm_->registerCallback( "env", "callback2", &callback2, &ConvertFunctionWordToUint32::convertFunctionWordToUint32); - ASSERT_TRUE(wasmVm()->link("")); + ASSERT_TRUE(vm_->link("")); WasmCallVoid<0> run; - wasmVm()->getFunction("run", &run); + vm_->getFunction("run", &run); run(nullptr); // no trace message found since DummyIntegration's log_level_ defaults to LogLevel::info - EXPECT_EQ(integration()->trace_message_, ""); + EXPECT_EQ(integration_->trace_message_, ""); - integration()->log_level_ = LogLevel::trace; + integration_->log_level_ = LogLevel::trace; run(nullptr); - EXPECT_NE(integration()->trace_message_, ""); + EXPECT_NE(integration_->trace_message_, ""); } TEST_P(TestVM, Callback) { - ASSERT_TRUE(wasmVm()->load(readTestWasmFile("callback.wasm"), false)); + initialize("callback.wasm"); + ASSERT_TRUE(vm_->load(source_, false)); TestContext context; current_context_ = &context; - wasmVm()->registerCallback( + vm_->registerCallback( "env", "callback", &callback, &ConvertFunctionWordToUint32::convertFunctionWordToUint32); - wasmVm()->registerCallback( + vm_->registerCallback( "env", "callback2", &callback2, &ConvertFunctionWordToUint32::convertFunctionWordToUint32); - ASSERT_TRUE(wasmVm()->link("")); + ASSERT_TRUE(vm_->link("")); WasmCallVoid<0> run; - wasmVm()->getFunction("run", &run); + vm_->getFunction("run", &run); EXPECT_TRUE(run != nullptr); for (auto i = 0; i < 100; i++) { run(current_context_); @@ -143,32 +149,33 @@ TEST_P(TestVM, Callback) { ASSERT_EQ(context.counter, 100); WasmCallWord<1> run2; - wasmVm()->getFunction("run2", &run2); + vm_->getFunction("run2", &run2); Word res = run2(current_context_, Word{0}); ASSERT_EQ(res.u32(), 100100); // 10000 (global) + 100(in callback) } TEST_P(TestVM, Trap) { - ASSERT_TRUE(wasmVm()->load(readTestWasmFile("trap.wasm"), false)); - ASSERT_TRUE(wasmVm()->link("")); + initialize("trap.wasm"); + ASSERT_TRUE(vm_->load(source_, false)); + ASSERT_TRUE(vm_->link("")); WasmCallVoid<0> trigger; - wasmVm()->getFunction("trigger", &trigger); + vm_->getFunction("trigger", &trigger); EXPECT_TRUE(trigger != nullptr); trigger(current_context_); std::string exp_message = "Function: trigger failed"; - ASSERT_TRUE(integration()->error_message_.find(exp_message) != std::string::npos); + ASSERT_TRUE(integration_->error_message_.find(exp_message) != std::string::npos); WasmCallWord<1> trigger2; - wasmVm()->getFunction("trigger2", &trigger2); + vm_->getFunction("trigger2", &trigger2); EXPECT_TRUE(trigger2 != nullptr); trigger2(current_context_, 0); exp_message = "Function: trigger2 failed:"; - ASSERT_TRUE(integration()->error_message_.find(exp_message) != std::string::npos); + ASSERT_TRUE(integration_->error_message_.find(exp_message) != std::string::npos); } TEST_P(TestVM, WithPrecompiledSection) { // Verify that stripping precompile_* custom section works. - auto source = readTestWasmFile("abi_export.wasm"); + initialize("abi_export.wasm"); // Append precompiled_test section std::vector custom_section = {// custom section id 0x00, @@ -182,9 +189,9 @@ TEST_P(TestVM, WithPrecompiledSection) { // content 0x01, 0x01}; - source.append(custom_section.data(), custom_section.size()); - ASSERT_TRUE(wasmVm()->load(source, false)); - ASSERT_EQ(wasmVm()->getAbiVersion(), AbiVersion::ProxyWasm_0_2_0); + source_.append(custom_section.data(), custom_section.size()); + ASSERT_TRUE(vm_->load(source_, false)); + ASSERT_EQ(vm_->getAbiVersion(), AbiVersion::ProxyWasm_0_2_0); } } // namespace diff --git a/test/utility.h b/test/utility.h index 739885c8..22a86f1f 100644 --- a/test/utility.h +++ b/test/utility.h @@ -62,44 +62,32 @@ struct DummyIntegration : public WasmVmIntegration { class TestVM : public testing::TestWithParam { public: - TestVM() : integration_(new DummyIntegration{}), runtime_(GetParam()) { + std::unique_ptr vm_; + + TestVM() : integration_(new DummyIntegration{}) { + runtime_ = GetParam(); if (runtime_ == "") { EXPECT_TRUE(false) << "runtime must not be empty"; #if defined(WASM_V8) } else if (runtime_ == "v8") { - wasm_vm_ = proxy_wasm::createV8Vm(); + vm_ = proxy_wasm::createV8Vm(); #endif #if defined(WASM_WAVM) } else if (runtime_ == "wavm") { - wasm_vm_ = proxy_wasm::createWavmVm(); + vm_ = proxy_wasm::createWavmVm(); #endif #if defined(WASM_WASMTIME) } else if (runtime_ == "wasmtime") { - wasm_vm_ = proxy_wasm::createWasmtimeVm(); + vm_ = proxy_wasm::createWasmtimeVm(); #endif } - wasm_vm_->integration().reset(integration_); + vm_->integration().reset(integration_); } - void initialize(const std::string source, std::string_view vm_id = "", - std::string_view vm_configuration = "", std::string_view vm_key = "", - std::unordered_map envs = {}, - AllowedCapabilitiesMap allowed_capabilities = {}) { - wasm_base_ = std::make_unique(std::move(wasm_vm_), vm_id, vm_key, vm_configuration, - envs, allowed_capabilities); - ASSERT_TRUE(wasm_base_->wasm_vm()->load(source, false)); - wasm_base_->registerCallbacks(); - ASSERT_TRUE(wasm_base_->wasm_vm()->link("")); - } - std::string runtime() { return runtime_; } - proxy_wasm::WasmBase *wasmBase() { return wasm_base_.get(); } - proxy_wasm::WasmVm *wasmVm() { return wasm_vm_ ? wasm_vm_.get() : wasm_base_->wasm_vm(); } - DummyIntegration *integration() { return integration_; } + void initialize(std::string filename) { source_ = readTestWasmFile(filename); } -private: DummyIntegration *integration_; + std::string source_; std::string runtime_; - std::unique_ptr wasm_base_; - std::unique_ptr wasm_vm_; }; } // namespace proxy_wasm