Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
ffi_native_unittest.cc
Go to the documentation of this file.
1// Copyright 2021 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "flutter/testing/dart_isolate_runner.h"
6#include "flutter/testing/fixture_test.h"
7
8#include "tonic/dart_args.h"
10
11namespace flutter {
12namespace testing {
13
14class MyNativeClass : public RefCountedDartWrappable<MyNativeClass> {
15 DEFINE_WRAPPERTYPEINFO();
17
18 MyNativeClass(intptr_t value) : _value(value){};
19
20 public:
21 intptr_t _value = 0;
22
23 static void Create(Dart_Handle path_handle, intptr_t value) {
24 auto path = fml::MakeRefCounted<MyNativeClass>(value);
25 path->AssociateWithDartWrapper(path_handle);
26 }
27
28 // Dummy test functions:
29
30 static int32_t MyTestFunction(MyNativeClass* ptr,
31 int32_t x,
32 Dart_Handle handle) {
33 return ptr->_value + x;
34 }
35
37};
38
39IMPLEMENT_WRAPPERTYPEINFO("Lib", MyNativeClass)
40
41class FfiNativeTest : public FixtureTest {
42 public:
44 : settings_(CreateSettingsForFixture()),
46 thread_(CreateNewThread()),
48 thread_,
49 thread_,
50 thread_,
51 thread_) {}
52
53 ~FfiNativeTest() = default;
54
55 [[nodiscard]] bool RunWithEntrypoint(const std::string& entrypoint) {
56 if (running_isolate_) {
57 return false;
58 }
59 auto isolate =
60 RunDartCodeInIsolate(vm_, settings_, task_runners_, entrypoint, {},
62 if (!isolate || isolate->get()->GetPhase() != DartIsolate::Phase::Running) {
63 return false;
64 }
65
66 running_isolate_ = std::move(isolate);
67 return true;
68 }
69
70 template <typename C, typename Signature, Signature function>
71 void DoCallThroughTest(const char* testName, const char* testEntry) {
72 auto weak_persistent_value = tonic::DartWeakPersistentValue();
73
75
76 AddFfiNativeCallback(
77 testName, reinterpret_cast<void*>(
79
80 AddFfiNativeCallback(
81 "CreateNative",
82 reinterpret_cast<void*>(
84 &MyNativeClass::Create>::Call));
85
86 AddNativeCallback("SignalDone",
88 // Clear on initiative of Dart.
89 weak_persistent_value.Clear();
90
91 event.Signal();
92 }));
93
94 ASSERT_TRUE(RunWithEntrypoint(testEntry));
95 event.Wait();
96
97 running_isolate_->Shutdown();
98 }
99
100 template <typename C, typename Signature, Signature function>
101 void DoSerialiseTest(bool leaf,
102 const char* returnFfi,
103 const char* returnDart,
104 const char* argsFfi,
105 const char* argsDart) {
107
108 EXPECT_EQ(dispatcher.AllowedAsLeafCall(), leaf);
109 EXPECT_STREQ(dispatcher.GetReturnFfiRepresentation(), returnFfi);
110 EXPECT_STREQ(dispatcher.GetReturnDartRepresentation(), returnDart);
111
112 {
113 std::ostringstream stream_;
114 dispatcher.WriteFfiArguments(&stream_);
115 EXPECT_STREQ(stream_.str().c_str(), argsFfi);
116 }
117
118 {
119 std::ostringstream stream_;
120 dispatcher.WriteDartArguments(&stream_);
121 EXPECT_STREQ(stream_.str().c_str(), argsDart);
122 }
123 }
124
125 protected:
128 std::unique_ptr<AutoIsolateShutdown> running_isolate_;
132};
133
134//
135// Call bindings.
136//
137
138// Call and serialise a simple function through the Tonic FFI bindings.
139
140void Nop() {}
141
142TEST_F(FfiNativeTest, FfiBindingCallNop) {
143 DoCallThroughTest<void, decltype(&Nop), &Nop>("Nop", "callNop");
144}
145
146TEST_F(FfiNativeTest, SerialiseNop) {
147 DoSerialiseTest<void, decltype(&Nop), &Nop>(
148 /*leaf=*/true, "Void", "void", "", "");
149}
150
151// Call and serialise function with bool.
152
153bool EchoBool(bool arg) {
154 EXPECT_TRUE(arg);
155 return arg;
156}
157
158TEST_F(FfiNativeTest, FfiBindingCallEchoBool) {
159 DoCallThroughTest<void, decltype(&EchoBool), &EchoBool>("EchoBool",
160 "callEchoBool");
161}
162
163TEST_F(FfiNativeTest, SerialiseEchoBool) {
164 DoSerialiseTest<void, decltype(&EchoBool), &EchoBool>(
165 /*leaf=*/true, "Bool", "bool", "Bool", "bool");
166}
167
168// Call and serialise function with intptr_t.
169
170intptr_t EchoIntPtr(intptr_t arg) {
171 EXPECT_EQ(arg, 23);
172 return arg;
173}
174
175TEST_F(FfiNativeTest, FfiBindingCallEchoIntPtr) {
176 DoCallThroughTest<void, decltype(&EchoIntPtr), &EchoIntPtr>("EchoIntPtr",
177 "callEchoIntPtr");
178}
179
180TEST_F(FfiNativeTest, SerialiseEchoIntPtr) {
181 if (sizeof(intptr_t) == 8) {
182 DoSerialiseTest<void, decltype(&EchoIntPtr), &EchoIntPtr>(
183 /*leaf=*/true, "Int64", "int", "Int64", "int");
184 } else {
185 EXPECT_EQ(sizeof(intptr_t), 4ul);
186 DoSerialiseTest<void, decltype(&EchoIntPtr), &EchoIntPtr>(
187 /*leaf=*/true, "Int32", "int", "Int32", "int");
188 }
189}
190
191// Call and serialise function with double.
192
193double EchoDouble(double arg) {
194 EXPECT_EQ(arg, 23.0);
195 return arg;
196}
197
198TEST_F(FfiNativeTest, FfiBindingCallEchoDouble) {
199 DoCallThroughTest<void, decltype(&EchoDouble), &EchoDouble>("EchoDouble",
200 "callEchoDouble");
201}
202
203TEST_F(FfiNativeTest, SerialiseEchoDouble) {
204 DoSerialiseTest<void, decltype(&EchoDouble), &EchoDouble>(
205 /*leaf=*/true, "Double", "double", "Double", "double");
206}
207
208// Call and serialise function with Dart_Handle.
209
211 const char* c_str = nullptr;
212 Dart_StringToCString(str, &c_str);
213 EXPECT_STREQ(c_str, "Hello EchoHandle");
214 return str;
215}
216
217TEST_F(FfiNativeTest, FfiBindingCallEchoHandle) {
218 DoCallThroughTest<void, decltype(&EchoHandle), &EchoHandle>("EchoHandle",
219 "callEchoHandle");
220}
221
222TEST_F(FfiNativeTest, SerialiseEchoHandle) {
223 DoSerialiseTest<void, decltype(&EchoHandle), &EchoHandle>(
224 /*leaf=*/false, "Handle", "Object", "Handle", "Object");
225}
226
227// Call and serialise function with std::string.
228
229std::string EchoString(std::string arg) {
230 EXPECT_STREQ(arg.c_str(), "Hello EchoString");
231 return arg;
232}
233
234TEST_F(FfiNativeTest, FfiBindingCallEchoString) {
235 DoCallThroughTest<void, decltype(&EchoString), &EchoString>("EchoString",
236 "callEchoString");
237}
238
239TEST_F(FfiNativeTest, SerialiseEchoString) {
240 DoSerialiseTest<void, decltype(&EchoString), &EchoString>(
241 /*leaf=*/false, "Handle", "String", "Handle", "String");
242}
243
244// Call and serialise function with std::u16string.
245
246std::u16string EchoU16String(std::u16string arg) {
247 EXPECT_EQ(arg, u"Hello EchoU16String");
248 return arg;
249}
250
251TEST_F(FfiNativeTest, FfiBindingCallEchoU16String) {
252 DoCallThroughTest<void, decltype(&EchoU16String), &EchoU16String>(
253 "EchoU16String", "callEchoU16String");
254}
255
256TEST_F(FfiNativeTest, SerialiseEchoU16String) {
257 DoSerialiseTest<void, decltype(&EchoU16String), &EchoU16String>(
258 /*leaf=*/false, "Handle", "String", "Handle", "String");
259}
260
261// Call and serialise function with std::vector.
262
263std::vector<std::string> EchoVector(const std::vector<std::string>& arg) {
264 EXPECT_STREQ(arg[0].c_str(), "Hello EchoVector");
265 return arg;
266}
267
268TEST_F(FfiNativeTest, FfiBindingCallEchoVector) {
269 DoCallThroughTest<void, decltype(&EchoVector), &EchoVector>("EchoVector",
270 "callEchoVector");
271}
272
273TEST_F(FfiNativeTest, SerialiseEchoVector) {
274 DoSerialiseTest<void, decltype(&EchoVector), &EchoVector>(
275 /*leaf=*/false, "Handle", "List", "Handle", "List");
276}
277
278// Call and serialise function with DartWrappable.
279
281 EXPECT_EQ(arg->_value, 0x1234);
282 return arg->_value;
283}
284
285TEST_F(FfiNativeTest, FfiBindingCallEchoWrappable) {
286 DoCallThroughTest<void, decltype(&EchoWrappable), &EchoWrappable>(
287 "EchoWrappable", "callEchoWrappable");
288}
289
290TEST_F(FfiNativeTest, SerialiseEchoWrappable) {
291 if (sizeof(intptr_t) == 8) {
292 DoSerialiseTest<void, decltype(&EchoWrappable), &EchoWrappable>(
293 /*leaf=*/true, "Int64", "int", "Pointer", "Pointer");
294 } else {
295 EXPECT_EQ(sizeof(intptr_t), 4ul);
296 DoSerialiseTest<void, decltype(&EchoWrappable), &EchoWrappable>(
297 /*leaf=*/true, "Int32", "int", "Pointer", "Pointer");
298 }
299}
300
301// Call and serialise function with TypedList<..>.
302
303tonic::Float32List EchoTypedList(tonic::Float32List arg) {
304 EXPECT_NEAR(arg[1], 3.14, 0.01);
305 return arg;
306}
307
308TEST_F(FfiNativeTest, FfiBindingCallEchoTypedList) {
309 DoCallThroughTest<void, decltype(&EchoTypedList), &EchoTypedList>(
310 "EchoTypedList", "callEchoTypedList");
311}
312
313TEST_F(FfiNativeTest, SerialiseEchoTypedList) {
314 DoSerialiseTest<void, decltype(&EchoTypedList), &EchoTypedList>(
315 /*leaf=*/false, "Handle", "Object", "Handle", "Object");
316}
317
318// Call and serialise a static class member function.
319
320TEST_F(FfiNativeTest, FfiBindingCallClassMemberFunction) {
321 DoCallThroughTest<void, decltype(&MyNativeClass::MyTestFunction),
323 "MyNativeClass::MyTestFunction", "callMyTestFunction");
324}
325
326TEST_F(FfiNativeTest, SerialiseClassMemberFunction) {
327 DoSerialiseTest<void, decltype(&MyNativeClass::MyTestFunction),
329 /*leaf=*/false, "Int32", "int", "Pointer, Int32, Handle",
330 "Pointer, int, Object");
331}
332
333// Call and serialise an instance method.
334
335TEST_F(FfiNativeTest, FfiBindingCallClassMemberMethod) {
336 DoCallThroughTest<MyNativeClass, decltype(&MyNativeClass::MyTestMethod),
337 &MyNativeClass::MyTestMethod>("MyNativeClass::MyTestMethod",
338 "callMyTestMethod");
339}
340
341TEST_F(FfiNativeTest, SerialiseClassMemberMethod) {
342 DoSerialiseTest<MyNativeClass, decltype(&MyNativeClass::MyTestMethod),
344 /*leaf=*/false, "Handle", "Object", "Pointer, Int64", "Pointer, int");
345}
346
347} // namespace testing
348} // namespace flutter
static sk_sp< Effect > Create()
void DoCallThroughTest(const char *testName, const char *testEntry)
void DoSerialiseTest(bool leaf, const char *returnFfi, const char *returnDart, const char *argsFfi, const char *argsDart)
fml::RefPtr< fml::TaskRunner > thread_
bool RunWithEntrypoint(const std::string &entrypoint)
std::unique_ptr< AutoIsolateShutdown > running_isolate_
FML_DISALLOW_COPY_AND_ASSIGN(FfiNativeTest)
static void Create(Dart_Handle path_handle, intptr_t value)
static int32_t MyTestFunction(MyNativeClass *ptr, int32_t x, Dart_Handle handle)
struct _Dart_Handle * Dart_Handle
Definition dart_api.h:258
struct _Dart_NativeArguments * Dart_NativeArguments
Definition dart_api.h:3010
DART_EXPORT Dart_Handle Dart_StringToCString(Dart_Handle str, const char **cstr)
DART_EXPORT Dart_Handle Dart_NewInteger(int64_t value)
#define IMPLEMENT_WRAPPERTYPEINFO(LibraryName, ClassName)
Settings settings_
TaskRunners task_runners_
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
FlKeyEvent * event
uint8_t value
double x
std::string GetCurrentTestName()
Gets the name of the currently running test. This is useful in generating logs or assets based on tes...
Definition testing.cc:15
TEST_F(DisplayListTest, Defaults)
std::string GetDefaultKernelFilePath()
Returns the default path to kernel_blob.bin. This file is within the directory returned by GetFixture...
Definition testing.cc:19
intptr_t EchoWrappable(MyNativeClass *arg)
std::string EchoString(std::string arg)
tonic::Float32List EchoTypedList(tonic::Float32List arg)
std::unique_ptr< AutoIsolateShutdown > RunDartCodeInIsolate(DartVMRef &vm_ref, const Settings &settings, const TaskRunners &task_runners, std::string entrypoint, const std::vector< std::string > &args, const std::string &kernel_file_path, fml::WeakPtr< IOManager > io_manager, std::shared_ptr< VolatilePathTracker > volatile_path_tracker, std::unique_ptr< PlatformConfiguration > platform_configuration)
intptr_t EchoIntPtr(intptr_t arg)
std::u16string EchoU16String(std::u16string arg)
std::vector< std::string > EchoVector(const std::vector< std::string > &arg)
double EchoDouble(double arg)
Dart_Handle EchoHandle(Dart_Handle str)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition switches.h:57
#define FML_FRIEND_MAKE_REF_COUNTED(T)
#define CREATE_NATIVE_ENTRY(native_entry)
#define EXPECT_TRUE(handle)
Definition unit_test.h:685