Skip to content
File

Blob: src/rust/cxx-integration-test/cxx-rust-integration-test.c++

7.7 KB
1#include <workerd/rust/cxx-integration-test/lib.rs.h>
2#include <workerd/rust/cxx-integration/lib.rs.h>
3 
4#include <kj-rs/kj-rs.h>
5#include <rust/cxx.h>
6#include <signal.h>
7 
8#include <kj/async.h>
9#include <kj/test.h>
10 
11using namespace kj_rs;
12 
13// Test generic rust/c++ integration boundary.
14// See src/rust/cxx-integration-tests for rust backend.
15 
16namespace workerd::rust {
17 
18KJ_TEST("init cxx_integration") {
19 // this tests initializes cxx integration for the rest of the tests
20 rust::cxx_integration::init();
21}
22 
23KJ_TEST("panic results in abort") {
24 KJ_EXPECT_SIGNAL(SIGABRT, rust::cxx_integration::trigger_panic("foobar"));
25}
26 
27KJ_TEST("ok Result") {
28 KJ_EXPECT(42 == rust::test::result_ok());
29}
30 
31KJ_TEST("err Result") {
32 // if fn returns an error, it is translated into kj::Exception exception.
33 try {
34 rust::test::result_error();
35 KJ_FAIL_REQUIRE("exception is expected");
36 } catch (kj::Exception& e) {
37 // this is expected
38 KJ_EXPECT(e.getDescription() == "test error"_kj);
39 }
40}
41 
42KJ_TEST("err Result with getCaughtExceptionAsKj") {
43 // if fn returns an error, it is translated into kj::Exception exception that can be accessed
44 // using getCaughtExceptionAsKj as is very common in the source code.
45 try {
46 rust::test::result_error();
47 KJ_FAIL_REQUIRE("exception is expected");
48 } catch (...) {
49 // this is expected
50 KJ_EXPECT(kj::getCaughtExceptionAsKj().getDescription() == "test error"_kj);
51 }
52}
53 
54KJ_TEST("test callback") {
55 rust::test::TestCallback callback = [](size_t a, size_t b) { return a + b; };
56 auto result = rust::test::call_callback(callback, 40, 2);
57 KJ_EXPECT(result == 42);
58}
59 
60KJ_TEST("test crashing callback") {
61 rust::test::TestCallback callback = [](size_t a, size_t b) -> size_t {
62 KJ_FAIL_REQUIRE("expected to crash");
63 };
64 // std::terminate is called when c++ throws fatal except
65 KJ_EXPECT_SIGNAL(SIGABRT, rust::test::call_callback(callback, 40, 2));
66}
67 
68KJ_TEST("test recoverable exception callback") {
69 rust::test::TestCallback callback = [](size_t a, size_t b) -> size_t {
70 kj::throwRecoverableException(KJ_EXCEPTION(DISCONNECTED, "Premature EOF."));
71 KJ_UNREACHABLE;
72 };
73 // std::terminate is called when c++ throws unhandled recoverable exception
74 KJ_EXPECT_SIGNAL(SIGABRT, rust::test::call_callback(callback, 40, 2));
75}
76 
77KJ_TEST("shared structure") {
78 {
79 // rust structure arguments are passed by-value in c++
80 const auto s = rust::test::SharedStruct{.a = 20, .b = 22};
81 KJ_EXPECT(42 == rust::test::pass_shared_struct(s));
82 }
83 
84 {
85 // rust structure return values are return by-value in c++
86 const auto s = rust::test::return_shared_struct();
87 KJ_EXPECT(13 == s.a);
88 KJ_EXPECT(29 == s.b);
89 }
90 
91 {
92 // rust reference looks like const reference to c++;
93 const auto s = rust::test::SharedStruct{.a = 20, .b = 22};
94 KJ_EXPECT(42 == rust::test::pass_shared_struct_as_ref(s));
95 }
96 
97 {
98 // rust mutable reference looks like reference to c++
99 auto s = rust::test::SharedStruct{.a = 10, .b = 32};
100 rust::test::pass_shared_struct_as_mut_ref(s);
101 KJ_EXPECT(s.a == 42);
102 KJ_EXPECT(s.b == 0);
103 }
104 
105 {
106 // rust const pointer looks like const pointer to c++
107 const auto s = rust::test::SharedStruct{.a = 20, .b = 22};
108 KJ_EXPECT(42 == rust::test::pass_shared_struct_as_const_ptr(&s));
109 }
110 
111 {
112 // rust mut pointer looks like pointer to c++
113 auto s = rust::test::SharedStruct{.a = 10, .b = 32};
114 rust::test::pass_shared_struct_as_mut_ptr(&s);
115 KJ_EXPECT(s.a == 0);
116 KJ_EXPECT(s.b == 0);
117 }
118 
119 {
120 // rust Box<T> type is represented as special ::rust::Box<T> c++ type
121 // there are many ways to create a Box
122 
123 {
124 // box can be created by copying the value
125 auto box = ::rust::Box<rust::test::SharedStruct>(rust::test::SharedStruct{.a = 3, .b = 39});
126 // box is consumed by the call as expected
127 KJ_EXPECT(42 == rust::test::pass_shared_struct_as_box(kj::mv(box)));
128 }
129 
130 {
131 // box can be created by moving the value
132 auto s = rust::test::SharedStruct{.a = 3, .b = 39};
133 auto box = ::rust::Box<rust::test::SharedStruct>(kj::mv(s));
134 KJ_EXPECT(42 == rust::test::pass_shared_struct_as_box(kj::mv(box)));
135 }
136 
137 {
138 // box can be created from raw pointer.
139 // Memory needs to be allocated using malloc, since rust doesn't have
140 // access to delete.
141 auto mem = malloc(sizeof(rust::test::SharedStruct));
142 auto s = new (mem) rust::test::SharedStruct;
143 s->a = 4;
144 s->b = 38;
145 auto box = ::rust::Box<rust::test::SharedStruct>::from_raw(s);
146 KJ_EXPECT(42 == rust::test::pass_shared_struct_as_box(kj::mv(box)));
147 }
148 }
149 
150 {
151 // box can be returned from rust to c++ as well
152 auto box = rust::test::return_shared_struct_as_box();
153 KJ_EXPECT(1 == box->a);
154 KJ_EXPECT(41 == box->b);
155 }
156}
157 
158KJ_TEST("opaque rust type") {
159 // &str is represented as a special ::rust::Str type
160 // it supports variety of implicit constructors.
161 auto s = rust::test::rust_struct_new_box("test_name");
162 ::rust::Str name = s->get_name();
163 
164 // ::rust::Str is _not_ null-terminated so kj::StringPtr can't be created from
165 // it. need to allocate to create c++-string (or use it as ArrayPtr).
166 auto strName = std::string(name);
167 KJ_EXPECT("test_name"_kj == kj::StringPtr(strName.c_str()));
168 
169 s->set_name("another_name");
170 KJ_EXPECT("another_name"_kjc == kj::arrayPtr(s->get_name().data(), s->get_name().size()));
171}
172 
173KJ_TEST("rust::String test") {
174 auto s = rust::test::get_string();
175 auto expected = "rust_string"_kj;
176 KJ_EXPECT(expected == kj::str(s));
177 KJ_EXPECT(expected == kj::toCharSequence(s));
178 KJ_EXPECT(kj::hashCode(expected) == kj::hashCode(s));
179}
180 
181KJ_TEST("rust::str test") {
182 auto s = rust::test::get_str();
183 auto expected = "rust_str"_kj;
184 KJ_EXPECT(expected == kj::str(s));
185 KJ_EXPECT(expected == kj::toCharSequence(s));
186 KJ_EXPECT(kj::hashCode(expected) == kj::hashCode(s));
187}
188 
189KJ_TEST("test async immediate future") {
190 kj::EventLoop loop;
191 kj::WaitScope waitScope(loop);
192 
193 auto pair = kj::newPromiseAndCrossThreadFulfiller<size_t>();
194 
195 rust::test::UsizeCallback callback = [&](size_t a) { pair.fulfiller->fulfill(kj::mv(a)); };
196 rust::test::async_immediate(callback);
197 
198 auto result = pair.promise.wait(waitScope);
199 KJ_EXPECT(result == 42);
200}
201 
202KJ_TEST("test async delay") {
203 kj::EventLoop loop;
204 kj::WaitScope waitScope(loop);
205 
206 auto pair = kj::newPromiseAndCrossThreadFulfiller<size_t>();
207 
208 rust::test::UsizeCallback callback = [&](size_t a) { pair.fulfiller->fulfill(kj::mv(a)); };
209 rust::test::async_sleep(callback);
210 
211 auto result = pair.promise.wait(waitScope);
212 KJ_EXPECT(result == 42);
213}
214 
215KJ_TEST("array/slice convertions") {
216 // const arrayPtr -> const slice
217 {
218 kj::ArrayPtr<const kj::byte> a = "foo"_kjb;
219 ::rust::Slice<const kj::byte> s = a.as<Rust>();
220 KJ_EXPECT(s.length() == a.size());
221 }
222 
223 // mutable arrayPtr -> const slice
224 {
225 kj::Array<kj::byte> a = kj::heapArray<kj::byte>(20);
226 kj::ArrayPtr<kj::byte> p = a.asPtr();
227 ::rust::Slice<const kj::byte> s = p.as<Rust>();
228 KJ_EXPECT(s.length() == a.size());
229 }
230 
231 // mutable arrayPtr -> mutable slice
232 {
233 kj::Array<kj::byte> a = kj::heapArray<kj::byte>(20);
234 ::rust::Slice<kj::byte> s = a.asPtr().as<RustMutable>();
235 KJ_EXPECT(s.length() == a.size());
236 }
237 
238 // const array -> const slice
239 {
240 kj::Array<const kj::byte> a = kj::heapArray<kj::byte>(20);
241 ::rust::Slice<const kj::byte> s = a.as<Rust>();
242 KJ_EXPECT(s.length() == a.size());
243 }
244 
245 // mutable array -> const slice
246 {
247 kj::Array<kj::byte> a = kj::heapArray<kj::byte>(20);
248 ::rust::Slice<const kj::byte> s = a.as<Rust>();
249 KJ_EXPECT(s.length() == a.size());
250 }
251 
252 // mutable array -> mutable slice
253 {
254 kj::Array<kj::byte> a = kj::heapArray<kj::byte>(20);
255 ::rust::Slice<kj::byte> s = a.as<RustMutable>();
256 KJ_EXPECT(s.length() == a.size());
257 }
258}
259 
260} // namespace workerd::rust