blob: e076af479a3037f3afeec96da0f72d950144b67d [file] [log] [blame]
// Copyright 2022 The Cobalt Authors. All Rights Reserved.
//
// 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.
#include "cobalt/web/cache_utils.h"
#include <utility>
#include "base/message_loop/message_loop.h"
#include "cobalt/script/v8c/entry_scope.h"
#include "cobalt/web/agent.h"
#include "cobalt/web/context.h"
#include "cobalt/web/testing/test_with_javascript.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cobalt {
namespace web {
namespace {
v8::Local<v8::Value> ExpectState(
base::Optional<v8::Local<v8::Value>> promise_value,
v8::Promise::PromiseState expected) {
EXPECT_TRUE(promise_value.has_value());
EXPECT_TRUE(promise_value.value()->IsPromise());
auto promise = promise_value->As<v8::Promise>();
auto* isolate = promise->GetIsolate();
auto context = isolate->GetCurrentContext();
auto e = std::make_unique<base::WaitableEvent>();
auto fulfilled_promise = promise->Then(
context, v8::Function::New(
context,
[](const v8::FunctionCallbackInfo<v8::Value>& info) {
static_cast<base::WaitableEvent*>(
info.Data().As<v8::External>()->Value())
->Signal();
},
v8::External::New(isolate, e.get()))
.ToLocalChecked());
auto rejected_promise = promise->Catch(
context, v8::Function::New(
context,
[](const v8::FunctionCallbackInfo<v8::Value>& info) {
static_cast<base::WaitableEvent*>(
info.Data().As<v8::External>()->Value())
->Signal();
},
v8::External::New(isolate, e.get()))
.ToLocalChecked());
EXPECT_TRUE(!fulfilled_promise.IsEmpty());
EXPECT_TRUE(!rejected_promise.IsEmpty());
base::RunLoop run_loop;
run_loop.RunUntilIdle();
// Ensure promise is scheduled to be resolved.
isolate->PerformMicrotaskCheckpoint();
e->Wait();
EXPECT_TRUE(promise->State() == expected);
return promise->Result();
}
class GetGlobalScopeTypeIdWindow : public ::testing::Test {
public:
base::TypeId GetGlobalScopeTypeId() const {
return base::GetTypeId<dom::Window>();
}
};
class CacheUtilsTest
: public testing::TestWithJavaScriptBase<GetGlobalScopeTypeIdWindow> {};
} // namespace
TEST_F(CacheUtilsTest, Eval) {
auto* isolate = web_context()->global_environment()->isolate();
script::v8c::EntryScope entry_scope(isolate);
auto v8_this = v8::Object::New(isolate);
cache_utils::Set(v8_this, "a", v8::Number::New(isolate, 1.0));
EXPECT_TRUE(v8_this->IsObject());
EXPECT_DOUBLE_EQ(1.0,
cache_utils::Get(v8_this, "a")->As<v8::Number>()->Value());
auto context = isolate->GetCurrentContext();
auto global = context->Global();
cache_utils::Set(global, "obj", v8_this);
auto result =
cache_utils::Evaluate(isolate, "obj.a++; obj;")->As<v8::Object>();
EXPECT_DOUBLE_EQ(2.0,
cache_utils::Get(result, "a")->As<v8::Number>()->Value());
EXPECT_DOUBLE_EQ(2.0,
cache_utils::Get(v8_this, "a")->As<v8::Number>()->Value());
}
TEST_F(CacheUtilsTest, ResponseHeaders) {
auto* isolate = web_context()->global_environment()->isolate();
script::v8c::EntryScope entry_scope(isolate);
std::string body_string = "body";
auto* body_begin = reinterpret_cast<const uint8_t*>(body_string.data());
auto* body_end = body_begin + body_string.size();
std::vector<uint8_t> body(body_begin, body_end);
base::DictionaryValue initial_options;
initial_options.SetKey("status", base::Value(200));
initial_options.SetKey("statusText", base::Value("OK"));
base::ListValue initial_headers;
base::ListValue header_1;
header_1.GetList().emplace_back("a");
header_1.GetList().emplace_back("1");
initial_headers.GetList().push_back(std::move(header_1));
base::ListValue header_2;
header_2.GetList().emplace_back("b");
header_2.GetList().emplace_back("2");
initial_headers.GetList().push_back(std::move(header_2));
initial_options.SetKey("headers", std::move(initial_headers));
auto response =
cache_utils::CreateResponse(isolate, body, initial_options).value();
EXPECT_DOUBLE_EQ(200, cache_utils::FromNumber(
cache_utils::Get(response, "status").value()));
EXPECT_EQ("OK",
cache_utils::FromV8String(
isolate, cache_utils::Get(response, "statusText").value()));
base::Value options = cache_utils::ExtractResponseOptions(response).value();
EXPECT_DOUBLE_EQ(200, cache_utils::Get(options, "status")->GetDouble());
EXPECT_EQ("OK", cache_utils::Get(options, "statusText")->GetString());
EXPECT_EQ(2, cache_utils::Get(options, "headers")->GetList().size());
EXPECT_EQ("a", cache_utils::Get(options, "headers.0.0")->GetString());
EXPECT_EQ("1", cache_utils::Get(options, "headers.0.1")->GetString());
EXPECT_EQ("b", cache_utils::Get(options, "headers.1.0")->GetString());
EXPECT_EQ("2", cache_utils::Get(options, "headers.1.1")->GetString());
}
TEST_F(CacheUtilsTest, BaseToV8) {
auto* isolate = web_context()->global_environment()->isolate();
script::v8c::EntryScope entry_scope(isolate);
std::string json = R"~({
"options": {
"headers": [["a", "1"], ["b", "2"]],
"status": 200,
"statusText": "OK"
},
"url": "https://www.example.com/1"
})~";
base::Value base_value = cache_utils::Deserialize(json).value();
auto v8_value = cache_utils::BaseToV8(isolate, base_value).value();
EXPECT_TRUE(v8_value->IsObject());
EXPECT_EQ("https://www.example.com/1",
cache_utils::GetString(v8_value, "url"));
EXPECT_DOUBLE_EQ(200,
cache_utils::GetNumber(v8_value, "options.status").value());
EXPECT_EQ("OK", cache_utils::GetString(v8_value, "options.statusText"));
EXPECT_EQ("a", cache_utils::GetString(v8_value, "options.headers.0.0"));
EXPECT_EQ("1", cache_utils::GetString(v8_value, "options.headers.0.1"));
EXPECT_EQ("b", cache_utils::GetString(v8_value, "options.headers.1.0"));
EXPECT_EQ("2", cache_utils::GetString(v8_value, "options.headers.1.1"));
EXPECT_EQ(base_value, cache_utils::V8ToBase(isolate, v8_value));
}
TEST_F(CacheUtilsTest, ThenFulfilled) {
auto* isolate = web_context()->global_environment()->isolate();
script::v8c::EntryScope entry_scope(isolate);
auto promise =
cache_utils::Evaluate(isolate, "Promise.resolve('success')").value();
auto still_fulfilled_promise = cache_utils::Then(
promise, base::BindOnce([&](v8::Local<v8::Promise> p)
-> base::Optional<v8::Local<v8::Promise>> {
auto* isolate = p->GetIsolate();
std::string new_message =
"still " + cache_utils::FromV8String(isolate, p->Result());
return cache_utils::Evaluate(isolate,
base::StringPrintf("Promise.resolve('%s')",
new_message.c_str()))
->As<v8::Promise>();
}));
auto promise_result = ExpectState(still_fulfilled_promise,
v8::Promise::PromiseState::kFulfilled);
EXPECT_EQ("still success",
cache_utils::FromV8String(isolate, promise_result));
}
TEST_F(CacheUtilsTest, ThenFulfilledThenRejected) {
auto* isolate = web_context()->global_environment()->isolate();
script::v8c::EntryScope entry_scope(isolate);
auto promise =
cache_utils::Evaluate(isolate, "Promise.resolve('success')").value();
auto rejected_promise = cache_utils::Then(
promise, base::BindOnce([&](v8::Local<v8::Promise> p)
-> base::Optional<v8::Local<v8::Promise>> {
auto* isolate = p->GetIsolate();
std::string new_message =
"no longer " + cache_utils::FromV8String(isolate, p->Result());
return cache_utils::Evaluate(isolate,
base::StringPrintf("Promise.reject('%s')",
new_message.c_str()))
->As<v8::Promise>();
}));
auto promise_result =
ExpectState(rejected_promise, v8::Promise::PromiseState::kRejected);
EXPECT_EQ("no longer success",
cache_utils::FromV8String(isolate, promise_result));
}
TEST_F(CacheUtilsTest, ThenRejected) {
auto* isolate = web_context()->global_environment()->isolate();
script::v8c::EntryScope entry_scope(isolate);
auto promise =
cache_utils::Evaluate(isolate, "Promise.reject('fail')").value();
auto still_rejected_promise = cache_utils::Then(
promise,
base::BindOnce(
[](v8::Local<v8::Promise>) -> base::Optional<v8::Local<v8::Promise>> {
return base::nullopt;
}));
auto promise_result =
ExpectState(still_rejected_promise, v8::Promise::PromiseState::kRejected);
EXPECT_EQ("fail", cache_utils::FromV8String(isolate, promise_result));
}
} // namespace web
} // namespace cobalt