blob: eba04477f99c6e630c6e999c265065b1fb5732f4 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/accessibility/features/v8_utils.h"
#include <memory>
#include "base/logging.h"
#include "base/test/task_environment.h"
#include "gin/array_buffer.h"
#include "gin/public/isolate_holder.h"
#include "services/accessibility/features/bindings_isolate_holder.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "v8-array-buffer.h"
#include "v8-container.h"
#include "v8-isolate.h"
#include "v8-local-handle.h"
#include "v8-object.h"
#include "v8-primitive.h"
#include "v8-template.h"
namespace ax {
class V8ValueConverterTest : public testing::Test,
public BindingsIsolateHolder {
public:
V8ValueConverterTest() {
BindingsIsolateHolder::InitializeV8();
isolate_holder_ = std::make_unique<gin::IsolateHolder>(
base::SingleThreadTaskRunner::GetCurrentDefault(),
gin::IsolateHolder::kSingleThread,
gin::IsolateHolder::IsolateType::kTest);
isolate_ = isolate_holder_->isolate();
}
// BindingsIsolateHolder:
v8::Isolate* GetIsolate() const override { return isolate_; }
v8::Local<v8::Context> GetContext() const override {
v8::EscapableHandleScope handle_scope(isolate_);
v8::Local<v8::Context> context =
v8::Local<v8::Context>::New(isolate_, context_);
return handle_scope.Escape(context);
}
void HandleError(const std::string& message) override {
LOG(ERROR) << message;
}
protected:
void SetUp() override {
v8::HandleScope handle_scope(isolate_);
v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate_);
context_.Reset(isolate_, v8::Context::New(isolate_, nullptr, global));
}
void TearDown() override { context_.Reset(); }
base::test::TaskEnvironment task_environment_;
std::unique_ptr<gin::IsolateHolder> isolate_holder_;
raw_ptr<v8::Isolate> isolate_ = nullptr;
// Context for the JavaScript in the test.
v8::Persistent<v8::Context> context_;
};
// This test covers all data types that accessibility uses to dispatch events.
// They are all added to a dictionary (which is also a type used), and makes
// sure the final conversion has the right types.
TEST_F(V8ValueConverterTest, DictWithBasicTypes) {
base::Value::Dict dict;
dict.Set("boolean", true);
dict.Set("integer", 2);
dict.Set("double", 2.0f);
dict.Set("string", "hello world");
v8::HandleScope handle_scope(isolate_);
v8::Local<v8::Context> context = GetContext();
v8::Context::Scope context_scope(context);
v8::Local<v8::Object> v8_object = V8ValueConverter::GetInstance()
->ConvertToV8Value(dict, context)
.As<v8::Object>();
ASSERT_FALSE(v8_object.IsEmpty());
EXPECT_EQ(dict.size(),
v8_object->GetPropertyNames(context).ToLocalChecked()->Length());
EXPECT_TRUE(v8_object
->Get(context, v8::String::NewFromUtf8(
isolate_, "boolean",
v8::NewStringType::kInternalized)
.ToLocalChecked())
.ToLocalChecked()
->IsTrue());
EXPECT_TRUE(v8_object
->Get(context, v8::String::NewFromUtf8(
isolate_, "integer",
v8::NewStringType::kInternalized)
.ToLocalChecked())
.ToLocalChecked()
->IsInt32());
EXPECT_TRUE(v8_object
->Get(context, v8::String::NewFromUtf8(
isolate_, "double",
v8::NewStringType::kInternalized)
.ToLocalChecked())
.ToLocalChecked()
->IsNumber());
EXPECT_TRUE(v8_object
->Get(context, v8::String::NewFromUtf8(
isolate_, "string",
v8::NewStringType::kInternalized)
.ToLocalChecked())
.ToLocalChecked()
->IsString());
}
// Tests the conversion of the list type, used as the arguments list to function
// calls.
TEST_F(V8ValueConverterTest, ConvertsList) {
base::Value::List list;
list.Append(true);
list.Append(2);
v8::HandleScope handle_scope(isolate_);
v8::Local<v8::Context> context = GetContext();
v8::Context::Scope context_scope(context);
v8::Local<v8::Array> v8_array = V8ValueConverter::GetInstance()
->ConvertToV8Value(list, context)
.As<v8::Array>();
EXPECT_EQ(list.size(), v8_array->Length());
EXPECT_TRUE(v8_array->Get(context, v8::Integer::New(isolate_, 0))
.ToLocalChecked()
->IsTrue());
EXPECT_TRUE(v8_array->Get(context, v8::Integer::New(isolate_, 1))
.ToLocalChecked()
->IsInt32());
}
TEST_F(V8ValueConverterTest, ConvertsBlogStorage) {
base::Value::BlobStorage storage({1u, 2u});
v8::HandleScope handle_scope(isolate_);
v8::Local<v8::Context> context = GetContext();
v8::Context::Scope context_scope(context);
v8::Local<v8::ArrayBuffer> v8_array = V8ValueConverter::GetInstance()
->ConvertToV8Value(storage, context)
.As<v8::ArrayBuffer>();
EXPECT_EQ(storage.size(), v8_array->ByteLength());
}
} // namespace ax