mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Add a separate target for Dart coverter on FML types. (#14011)
The converters are still in a separate target that must be included manually. This allows targets that depend on FML but not Dart runtime not have to depend on the runtime. Adds a test that includes this target and tests image decompression from assets. There is also a test for the standalone DartConvertor in shell_unittests but not in fml_unittests be cause FML uni-tests cannot yet launch a VM. I will work on adding fixtures for those.
This commit is contained in:
parent
27fa60793b
commit
ca68af252c
@ -98,6 +98,8 @@ FILE: ../../../flutter/fml/command_line_unittest.cc
|
||||
FILE: ../../../flutter/fml/compiler_specific.h
|
||||
FILE: ../../../flutter/fml/concurrent_message_loop.cc
|
||||
FILE: ../../../flutter/fml/concurrent_message_loop.h
|
||||
FILE: ../../../flutter/fml/dart/dart_converter.cc
|
||||
FILE: ../../../flutter/fml/dart/dart_converter.h
|
||||
FILE: ../../../flutter/fml/delayed_task.cc
|
||||
FILE: ../../../flutter/fml/delayed_task.h
|
||||
FILE: ../../../flutter/fml/eintr_wrapper.h
|
||||
|
||||
@ -249,8 +249,9 @@ executable("fml_unittests") {
|
||||
deps = [
|
||||
":fml_fixtures",
|
||||
"$flutter_root/fml",
|
||||
"$flutter_root/fml/dart",
|
||||
"$flutter_root/runtime:libdart",
|
||||
"$flutter_root/testing",
|
||||
"//third_party/dart/runtime:libdart_jit",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
19
fml/dart/BUILD.gn
Normal file
19
fml/dart/BUILD.gn
Normal file
@ -0,0 +1,19 @@
|
||||
# Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
# Utilities for working with FML types with the Dart API. Targets that use FML
|
||||
# as well as Dart must explicitly include this target as FML itself cannot
|
||||
# depend on Dart.
|
||||
source_set("dart") {
|
||||
sources = [
|
||||
"dart_converter.cc",
|
||||
"dart_converter.h",
|
||||
]
|
||||
|
||||
public_deps = [
|
||||
"$flutter_root/fml",
|
||||
"$flutter_root/runtime:libdart",
|
||||
"//third_party/tonic",
|
||||
]
|
||||
}
|
||||
11
fml/dart/dart_converter.cc
Normal file
11
fml/dart/dart_converter.cc
Normal file
@ -0,0 +1,11 @@
|
||||
// Copyright 2013 The Flutter 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 "flutter/fml/dart/dart_converter.h"
|
||||
|
||||
namespace fml {
|
||||
|
||||
//
|
||||
|
||||
} // namespace fml
|
||||
123
fml/dart/dart_converter.h
Normal file
123
fml/dart/dart_converter.h
Normal file
@ -0,0 +1,123 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef FLUTTER_FML_DART_DART_CONVERTER_H_
|
||||
#define FLUTTER_FML_DART_DART_CONVERTER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "flutter/fml/mapping.h"
|
||||
#include "third_party/dart/runtime/include/dart_api.h"
|
||||
#include "third_party/tonic/converter/dart_converter.h"
|
||||
|
||||
namespace tonic {
|
||||
|
||||
using DartConverterMapping = std::unique_ptr<fml::Mapping>;
|
||||
|
||||
template <>
|
||||
struct DartConverter<DartConverterMapping> {
|
||||
static Dart_Handle ToDart(const DartConverterMapping& val) {
|
||||
if (!val) {
|
||||
return Dart_Null();
|
||||
}
|
||||
|
||||
auto dart_list_handle = Dart_NewListOf(Dart_CoreType_Int, val->GetSize());
|
||||
|
||||
if (Dart_IsError(dart_list_handle)) {
|
||||
FML_LOG(ERROR) << "Error while attempting to allocate a list: "
|
||||
<< Dart_GetError(dart_list_handle);
|
||||
return dart_list_handle;
|
||||
}
|
||||
|
||||
if (val->GetSize() == 0) {
|
||||
// Nothing to copy. Just return the zero sized list.
|
||||
return dart_list_handle;
|
||||
}
|
||||
|
||||
auto result = Dart_ListSetAsBytes(dart_list_handle, // list
|
||||
0, // offset
|
||||
val->GetMapping(), // native array,
|
||||
val->GetSize() // length
|
||||
);
|
||||
|
||||
if (Dart_IsError(result)) {
|
||||
FML_LOG(ERROR) << "Error while attempting to create a Dart list: "
|
||||
<< Dart_GetError(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
return dart_list_handle;
|
||||
}
|
||||
|
||||
static void SetReturnValue(Dart_NativeArguments args,
|
||||
const DartConverterMapping& val) {
|
||||
Dart_SetReturnValue(args, ToDart(val));
|
||||
}
|
||||
|
||||
static DartConverterMapping FromDart(Dart_Handle dart_list) {
|
||||
if (Dart_IsNull(dart_list)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (Dart_IsError(dart_list)) {
|
||||
FML_LOG(ERROR) << "Cannot convert an error handle to a list: "
|
||||
<< Dart_GetError(dart_list);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!Dart_IsList(dart_list)) {
|
||||
FML_LOG(ERROR) << "Dart handle was not a list.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
intptr_t length = 0;
|
||||
auto handle = Dart_ListLength(dart_list, &length);
|
||||
|
||||
if (Dart_IsError(handle)) {
|
||||
FML_LOG(ERROR) << "Could not get the length of the Dart list: "
|
||||
<< Dart_GetError(handle);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (length == 0) {
|
||||
// Return a valid zero sized mapping.
|
||||
return std::make_unique<fml::NonOwnedMapping>(nullptr, 0);
|
||||
}
|
||||
|
||||
auto mapping_buffer = ::malloc(length);
|
||||
|
||||
if (!mapping_buffer) {
|
||||
FML_LOG(ERROR)
|
||||
<< "Out of memory while attempting to allocate a mapping of size: "
|
||||
<< length;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto mapping = std::make_unique<fml::NonOwnedMapping>(
|
||||
static_cast<const uint8_t*>(mapping_buffer), length,
|
||||
[](const uint8_t* data, size_t size) {
|
||||
::free(const_cast<uint8_t*>(data));
|
||||
});
|
||||
|
||||
handle = Dart_ListGetAsBytes(
|
||||
dart_list, // list
|
||||
0, // offset
|
||||
static_cast<uint8_t*>(mapping_buffer), // native array
|
||||
length // length
|
||||
);
|
||||
|
||||
if (Dart_IsError(handle)) {
|
||||
FML_LOG(ERROR) << "Could not copy Dart list to native buffer: "
|
||||
<< Dart_GetError(handle);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return mapping;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace tonic
|
||||
|
||||
#endif // FLUTTER_FML_DART_DART_CONVERTER_H_
|
||||
@ -173,6 +173,7 @@ if (current_toolchain == host_toolchain) {
|
||||
":shell_unittests_gpu_configuration",
|
||||
"$flutter_root/common",
|
||||
"$flutter_root/flow",
|
||||
"$flutter_root/fml/dart",
|
||||
"$flutter_root/lib/ui:ui",
|
||||
"$flutter_root/shell",
|
||||
"$flutter_root/testing:dart",
|
||||
|
||||
@ -121,3 +121,20 @@ void canAccessIsolateLaunchData() {
|
||||
}
|
||||
|
||||
void notifyMessage(String string) native 'NotifyMessage';
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
void canConvertMappings() {
|
||||
sendFixtureMapping(getFixtureMapping());
|
||||
}
|
||||
|
||||
List<int> getFixtureMapping() native 'GetFixtureMapping';
|
||||
void sendFixtureMapping(List<int> list) native 'SendFixtureMapping';
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
void canDecompressImageFromAsset() {
|
||||
decodeImageFromList(Uint8List.fromList(getFixtureImage()), (Image result) {
|
||||
notifyWidthHeight(result.width, result.height);
|
||||
});
|
||||
}
|
||||
|
||||
List<int> getFixtureImage() native 'GetFixtureImage';
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
#include "flutter/flow/layers/picture_layer.h"
|
||||
#include "flutter/flow/layers/transform_layer.h"
|
||||
#include "flutter/fml/command_line.h"
|
||||
#include "flutter/fml/dart/dart_converter.h"
|
||||
#include "flutter/fml/make_copyable.h"
|
||||
#include "flutter/fml/message_loop.h"
|
||||
#include "flutter/fml/synchronization/count_down_latch.h"
|
||||
@ -1014,5 +1015,143 @@ TEST_F(ShellTest, Screenshot) {
|
||||
DestroyShell(std::move(shell));
|
||||
}
|
||||
|
||||
enum class MemsetPatternOp {
|
||||
kMemsetPatternOpSetBuffer,
|
||||
kMemsetPatternOpCheckBuffer,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// @brief Depending on the operation, either scribbles a known pattern
|
||||
/// into the buffer or checks if that pattern is present in an
|
||||
/// existing buffer. This is a portable variant of the
|
||||
/// memset_pattern class of methods that also happen to do assert
|
||||
/// that the same pattern exists.
|
||||
///
|
||||
/// @param buffer The buffer
|
||||
/// @param[in] size The size
|
||||
/// @param[in] op The operation
|
||||
///
|
||||
/// @return If the result of the operation was a success.
|
||||
///
|
||||
static bool MemsetPatternSetOrCheck(uint8_t* buffer,
|
||||
size_t size,
|
||||
MemsetPatternOp op) {
|
||||
if (buffer == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto pattern = reinterpret_cast<const uint8_t*>("dErP");
|
||||
constexpr auto pattern_length = 4;
|
||||
|
||||
uint8_t* start = buffer;
|
||||
uint8_t* p = buffer;
|
||||
|
||||
while ((start + size) - p >= pattern_length) {
|
||||
switch (op) {
|
||||
case MemsetPatternOp::kMemsetPatternOpSetBuffer:
|
||||
memmove(p, pattern, pattern_length);
|
||||
break;
|
||||
case MemsetPatternOp::kMemsetPatternOpCheckBuffer:
|
||||
if (memcmp(pattern, p, pattern_length) != 0) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
};
|
||||
p += pattern_length;
|
||||
}
|
||||
|
||||
if ((start + size) - p != 0) {
|
||||
switch (op) {
|
||||
case MemsetPatternOp::kMemsetPatternOpSetBuffer:
|
||||
memmove(p, pattern, (start + size) - p);
|
||||
break;
|
||||
case MemsetPatternOp::kMemsetPatternOpCheckBuffer:
|
||||
if (memcmp(pattern, p, (start + size) - p) != 0) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST_F(ShellTest, CanConvertToAndFromMappings) {
|
||||
const size_t buffer_size = 2 << 20;
|
||||
|
||||
uint8_t* buffer = static_cast<uint8_t*>(::malloc(buffer_size));
|
||||
ASSERT_NE(buffer, nullptr);
|
||||
ASSERT_TRUE(MemsetPatternSetOrCheck(
|
||||
buffer, buffer_size, MemsetPatternOp::kMemsetPatternOpSetBuffer));
|
||||
|
||||
std::unique_ptr<fml::Mapping> mapping =
|
||||
std::make_unique<fml::NonOwnedMapping>(
|
||||
buffer, buffer_size, [](const uint8_t* buffer, size_t size) {
|
||||
::free(const_cast<uint8_t*>(buffer));
|
||||
});
|
||||
|
||||
ASSERT_EQ(mapping->GetSize(), buffer_size);
|
||||
|
||||
fml::AutoResetWaitableEvent latch;
|
||||
AddNativeCallback(
|
||||
"SendFixtureMapping", CREATE_NATIVE_ENTRY([&](auto args) {
|
||||
auto mapping_from_dart =
|
||||
tonic::DartConverter<std::unique_ptr<fml::Mapping>>::FromDart(
|
||||
Dart_GetNativeArgument(args, 0));
|
||||
ASSERT_NE(mapping_from_dart, nullptr);
|
||||
ASSERT_EQ(mapping_from_dart->GetSize(), buffer_size);
|
||||
ASSERT_TRUE(MemsetPatternSetOrCheck(
|
||||
const_cast<uint8_t*>(mapping_from_dart->GetMapping()), // buffer
|
||||
mapping_from_dart->GetSize(), // size
|
||||
MemsetPatternOp::kMemsetPatternOpCheckBuffer // op
|
||||
));
|
||||
latch.Signal();
|
||||
}));
|
||||
|
||||
AddNativeCallback(
|
||||
"GetFixtureMapping", CREATE_NATIVE_ENTRY([&](auto args) {
|
||||
tonic::DartConverter<tonic::DartConverterMapping>::SetReturnValue(
|
||||
args, mapping);
|
||||
}));
|
||||
|
||||
auto settings = CreateSettingsForFixture();
|
||||
auto configuration = RunConfiguration::InferFromSettings(settings);
|
||||
configuration.SetEntrypoint("canConvertMappings");
|
||||
std::unique_ptr<Shell> shell = CreateShell(settings);
|
||||
ASSERT_NE(shell.get(), nullptr);
|
||||
RunEngine(shell.get(), std::move(configuration));
|
||||
latch.Wait();
|
||||
DestroyShell(std::move(shell));
|
||||
}
|
||||
|
||||
TEST_F(ShellTest, CanDecompressImageFromAsset) {
|
||||
fml::AutoResetWaitableEvent latch;
|
||||
AddNativeCallback("NotifyWidthHeight", CREATE_NATIVE_ENTRY([&](auto args) {
|
||||
auto width = tonic::DartConverter<int>::FromDart(
|
||||
Dart_GetNativeArgument(args, 0));
|
||||
auto height = tonic::DartConverter<int>::FromDart(
|
||||
Dart_GetNativeArgument(args, 1));
|
||||
ASSERT_EQ(width, 100);
|
||||
ASSERT_EQ(height, 100);
|
||||
latch.Signal();
|
||||
}));
|
||||
|
||||
AddNativeCallback(
|
||||
"GetFixtureImage", CREATE_NATIVE_ENTRY([](auto args) {
|
||||
auto fixture = OpenFixtureAsMapping("shelltest_screenshot.png");
|
||||
tonic::DartConverter<tonic::DartConverterMapping>::SetReturnValue(
|
||||
args, fixture);
|
||||
}));
|
||||
|
||||
auto settings = CreateSettingsForFixture();
|
||||
auto configuration = RunConfiguration::InferFromSettings(settings);
|
||||
configuration.SetEntrypoint("canDecompressImageFromAsset");
|
||||
std::unique_ptr<Shell> shell = CreateShell(settings);
|
||||
ASSERT_NE(shell.get(), nullptr);
|
||||
RunEngine(shell.get(), std::move(configuration));
|
||||
latch.Wait();
|
||||
DestroyShell(std::move(shell));
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
@ -49,5 +49,9 @@ fml::UniqueFD OpenFixture(std::string fixture_name) {
|
||||
return fixture_fd;
|
||||
}
|
||||
|
||||
std::unique_ptr<fml::Mapping> OpenFixtureAsMapping(std::string fixture_name) {
|
||||
return fml::FileMapping::CreateReadOnly(OpenFixture(fixture_name));
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
@ -8,21 +8,58 @@
|
||||
#include <string>
|
||||
|
||||
#include "flutter/fml/file.h"
|
||||
#include "flutter/fml/mapping.h"
|
||||
#include "flutter/testing/assertions.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace flutter {
|
||||
namespace testing {
|
||||
|
||||
// Returns the directory containing the test fixture for the target if this
|
||||
// target has fixtures configured. If there are no fixtures, this is a link
|
||||
// error.
|
||||
//------------------------------------------------------------------------------
|
||||
/// @brief Returns the directory containing the test fixture for the target
|
||||
/// if this target has fixtures configured. If there are no
|
||||
/// fixtures, this is a link error. If you see a linker error on
|
||||
/// this symbol, the unit-test target needs to depend on a
|
||||
/// `test_fixtures` target.
|
||||
///
|
||||
/// @return The fixtures path.
|
||||
///
|
||||
const char* GetFixturesPath();
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// @brief Opens the fixtures directory for the unit-test harness.
|
||||
///
|
||||
/// @return The file descriptor of the fixtures directory.
|
||||
///
|
||||
fml::UniqueFD OpenFixturesDirectory();
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// @brief Opens a fixture of the given file name.
|
||||
///
|
||||
/// @param[in] fixture_name The fixture name
|
||||
///
|
||||
/// @return The file descriptor of the given fixture. An invalid file
|
||||
/// descriptor is returned in case the fixture is not found.
|
||||
///
|
||||
fml::UniqueFD OpenFixture(std::string fixture_name);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// @brief Opens a fixture of the given file name and returns a mapping to
|
||||
/// its contents.
|
||||
///
|
||||
/// @param[in] fixture_name The fixture name
|
||||
///
|
||||
/// @return A mapping to the contents of fixture or null if the fixture does
|
||||
/// not exist or its contents cannot be mapped in.
|
||||
///
|
||||
std::unique_ptr<fml::Mapping> OpenFixtureAsMapping(std::string fixture_name);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// @brief Gets the name of the currently running test. This is useful in
|
||||
/// generating logs or assets based on test name.
|
||||
///
|
||||
/// @return The current test name.
|
||||
///
|
||||
std::string GetCurrentTestName();
|
||||
|
||||
} // namespace testing
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user