@@ -16,6 +16,8 @@
#include "type/type.h"
#include "uri/uri.h"
#include <tl/expected.hpp>
#include <algorithm>
#include <map>
#include <memory>
@@ -31,15 +33,19 @@ using etest::require;
using protocol::ErrorCode;
using protocol::Response;
using Responses = std::map<std::string, tl::expected<Response, protocol::Error>>;
namespace {
class FakeProtocolHandler final : public protocol::IProtocolHandler {
public:
explicit FakeProtocolHandler(std::map<std::string, Response> responses) : responses_{std::move(responses)} {}
[[nodiscard]] Response handle(uri::Uri const &uri) override { return responses_.at(uri.uri); }
explicit FakeProtocolHandler(Responses responses) : responses_{std::move(responses)} {}
[[nodiscard]] tl::expected<Response, protocol::Error> handle(uri::Uri const &uri) override {
return responses_.at(uri.uri);
}
private:
std::map<std::string, Response> responses_;
Responses responses_;
};
bool contains(std::vector<css::Rule> const &stylesheet, css::Rule const &rule) {
@@ -50,10 +56,9 @@ bool contains(std::vector<css::Rule> const &stylesheet, css::Rule const &rule) {
int main() {
etest::test("css in <head><style>", [] {
std::map<std::string, Response> responses{{
Responses responses{{
"hax://example.com"s,
Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 200},
.body{"<html><head><style>p { font-size: 123em; }</style></head></html>"},
},
@@ -68,8 +73,8 @@ int main() {
});
etest::test("navigation failure", [] {
engine::Engine e{std::make_unique<FakeProtocolHandler>(std::map{
std::pair{"hax://example.com"s, Response{.err = ErrorCode::Unresolved}},
engine::Engine e{std::make_unique<FakeProtocolHandler>(Responses{
std::pair{"hax://example.com"s, tl::unexpected{protocol::Error{ErrorCode::Unresolved}}},
})};
auto page = e.navigate(uri::Uri::parse("hax://example.com").value());
@@ -77,8 +82,8 @@ int main() {
});
etest::test("page load", [] {
engine::Engine e{std::make_unique<FakeProtocolHandler>(std::map{
std::pair{"hax://example.com"s, Response{.err = ErrorCode::Ok}},
engine::Engine e{std::make_unique<FakeProtocolHandler>(Responses{
std::pair{"hax://example.com"s, Response{}},
})};
auto page = e.navigate(uri::Uri::parse("hax://example.com").value());
@@ -86,8 +91,8 @@ int main() {
});
etest::test("layout update", [] {
engine::Engine e{std::make_unique<FakeProtocolHandler>(std::map{
std::pair{"hax://example.com"s, Response{.err = ErrorCode::Ok}},
engine::Engine e{std::make_unique<FakeProtocolHandler>(Responses{
std::pair{"hax://example.com"s, Response{}},
})};
e.set_layout_width(123);
@@ -99,10 +104,9 @@ int main() {
});
etest::test("css in <head><style> takes priority over browser built-in css", [] {
std::map<std::string, Response> responses{{
Responses responses{{
"hax://example.com"s,
Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 200},
.body{"<html></html>"},
},
@@ -113,10 +117,9 @@ int main() {
require(page->layout.has_value());
expect_eq(page->layout->get_property<css::PropertyId::Display>(), style::DisplayValue::Block);
responses = std::map<std::string, Response>{{
responses = Responses{{
"hax://example.com"s,
Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 200},
.body{"<html><head><style>html { display: inline; }</style></head></html>"},
},
@@ -132,10 +135,9 @@ int main() {
});
etest::test("multiple inline <head><style> elements are allowed", [] {
std::map<std::string, Response> responses{{
Responses responses{{
"hax://example.com"s,
Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 200},
.body{"<html><head><style>"
"a { color: red; } "
@@ -156,9 +158,8 @@ int main() {
});
etest::test("stylesheet link, parallel download", [] {
std::map<std::string, Response> responses;
Responses responses;
responses["hax://example.com"s] = Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 200},
.body{"<html><head>"
"<link rel=stylesheet href=one.css />"
@@ -166,12 +167,10 @@ int main() {
"</head></html>"},
};
responses["hax://example.com/one.css"s] = Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 200},
.body{"p { font-size: 123em; }"},
};
responses["hax://example.com/two.css"s] = Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 200},
.body{"p { color: green; }"},
};
@@ -183,14 +182,12 @@ int main() {
});
etest::test("stylesheet link, unsupported Content-Encoding", [] {
std::map<std::string, Response> responses;
Responses responses;
responses["hax://example.com"s] = Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 200},
.body{"<html><head><link rel=stylesheet href=lol.css /></head></html>"},
};
responses["hax://example.com/lol.css"s] = Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 200},
.headers{{"Content-Encoding", "really-borked-content-type"}},
.body{"p { font-size: 123em; }"},
@@ -209,14 +206,12 @@ int main() {
"\x78\x5e\x2b\x50\xa8\x56\x48\xcb\xcf\x2b\xd1\x2d\xce\xac\x4a\xb5\x52\x30\x34\x32\x4e\xcd\xb5\x56\xa8\xe5\x02\x00\x63\xc3\x07\x6f"s;
etest::test("stylesheet link, gzip Content-Encoding", [gzipped_css]() mutable {
std::map<std::string, Response> responses;
Responses responses;
responses["hax://example.com"s] = Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 200},
.body{"<html><head><link rel=stylesheet href=lol.css /></head></html>"},
};
responses["hax://example.com/lol.css"s] = Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 200},
.headers{{"Content-Encoding", "gzip"}},
.body{gzipped_css},
@@ -232,7 +227,6 @@ int main() {
// And again, but with x-gzip instead.
responses["hax://example.com/lol.css"s] = Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 200},
.headers{{"Content-Encoding", "x-gzip"}},
.body{std::move(gzipped_css)},
@@ -248,16 +242,14 @@ int main() {
});
etest::test("stylesheet link, gzip Content-Encoding, bad header", [gzipped_css]() mutable {
std::map<std::string, Response> responses;
Responses responses;
responses["hax://example.com"s] = Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 200},
.body{"<html><head><link rel=stylesheet href=lol.css /></head></html>"},
};
// Ruin the gzip header.
gzipped_css[1] += 1;
responses["hax://example.com/lol.css"s] = Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 200},
.headers{{"Content-Encoding", "gzip"}},
.body{std::move(gzipped_css)},
@@ -273,16 +265,14 @@ int main() {
});
etest::test("stylesheet link, gzip Content-Encoding, crc32 mismatch", [gzipped_css]() mutable {
std::map<std::string, Response> responses;
Responses responses;
responses["hax://example.com"s] = Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 200},
.body{"<html><head><link rel=stylesheet href=lol.css /></head></html>"},
};
// Ruin the content.
gzipped_css[20] += 1;
responses["hax://example.com/lol.css"s] = Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 200},
.headers{{"Content-Encoding", "gzip"}},
.body{std::move(gzipped_css)},
@@ -298,14 +288,12 @@ int main() {
});
etest::test("stylesheet link, gzip Content-Encoding, served zlib", [zlibbed_css] {
std::map<std::string, Response> responses;
Responses responses;
responses["hax://example.com"s] = Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 200},
.body{"<html><head><link rel=stylesheet href=lol.css /></head></html>"},
};
responses["hax://example.com/lol.css"s] = Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 200},
.headers{{"Content-Encoding", "gzip"}},
.body{zlibbed_css},
@@ -321,14 +309,12 @@ int main() {
});
etest::test("stylesheet link, deflate Content-Encoding", [zlibbed_css] {
std::map<std::string, Response> responses;
Responses responses;
responses["hax://example.com"s] = Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 200},
.body{"<html><head><link rel=stylesheet href=lol.css /></head></html>"},
};
responses["hax://example.com/lol.css"s] = Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 200},
.headers{{"Content-Encoding", "deflate"}},
.body{zlibbed_css},
@@ -344,20 +330,17 @@ int main() {
});
etest::test("redirect", [] {
std::map<std::string, Response> responses;
Responses responses;
responses["hax://example.com"s] = Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 301},
.headers = {{"Location", "hax://example.com/redirected"}},
};
responses["hax://example.com/redirected"s] = Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 200},
.body{"<html><body>hello!</body></html>"},
};
engine::Engine e{std::make_unique<FakeProtocolHandler>(std::move(responses))};
auto page = e.navigate(uri::Uri::parse("hax://example.com").value()).value();
expect_eq(page->response.err, protocol::ErrorCode::Ok);
expect_eq(page->uri.uri, "hax://example.com/redirected");
auto const &body = std::get<dom::Element>(page->dom.html().children.at(1));
@@ -365,9 +348,8 @@ int main() {
});
etest::test("redirect not providing Location header", [] {
std::map<std::string, Response> responses;
Responses responses;
responses["hax://example.com"s] = Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 301},
};
engine::Engine e{std::make_unique<FakeProtocolHandler>(std::move(responses))};
@@ -376,34 +358,29 @@ int main() {
});
etest::test("redirect, style", [] {
std::map<std::string, Response> responses;
Responses responses;
responses["hax://example.com"s] = Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 200},
.body{"<html><head>"
"<link rel=stylesheet href=hello.css />"
"</head></html>"},
};
responses["hax://example.com/hello.css"s] = Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 301},
.headers = {{"Location", "hax://example.com/redirected.css"}},
};
responses["hax://example.com/redirected.css"s] = Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 200},
.body{"p { color: green; }"},
};
engine::Engine e{std::make_unique<FakeProtocolHandler>(std::move(responses))};
auto page = e.navigate(uri::Uri::parse("hax://example.com").value()).value();
expect_eq(page->response.err, protocol::ErrorCode::Ok);
expect(contains(page->stylesheet.rules, {.selectors{"p"}, .declarations{{css::PropertyId::Color, "green"}}}));
});
etest::test("redirect loop", [] {
std::map<std::string, Response> responses;
Responses responses;
responses["hax://example.com"s] = Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 301},
.headers = {{"Location", "hax://example.com"}},
};
@@ -413,14 +390,12 @@ int main() {
});
etest::test("load", [] {
std::map<std::string, Response> responses;
Responses responses;
responses["hax://example.com"s] = Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 301},
.headers = {{"Location", "hax://example.com/redirected"}},
};
responses["hax://example.com/redirected"s] = Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 200},
.body{"<html><body>hello!</body></html>"},
};
@@ -433,14 +408,13 @@ int main() {
etest::test("IType accessor, you get what you give", [] {
auto naive = std::make_unique<type::NaiveType>();
type::IType const *saved = naive.get();
engine::Engine e{std::make_unique<FakeProtocolHandler>(std::map<std::string, Response>{}), std::move(naive)};
engine::Engine e{std::make_unique<FakeProtocolHandler>(Responses{}), std::move(naive)};
expect_eq(&e.font_system(), saved);
});
etest::test("bad uri in redirect", [] {
std::map<std::string, Response> responses;
Responses responses;
responses["hax://example.com"s] = Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 301},
.headers = {{"Location", ""}},
};
@@ -450,18 +424,17 @@ int main() {
});
etest::test("bad uri in style href", [] {
std::map<std::string, Response> responses;
Responses responses;
std::string body = "<html><head><link rel=stylesheet href=";
body += std::string(1025, 'a');
body += " /></head></html>";
responses["hax://example.com"s] = Response{
.err = ErrorCode::Ok,
.status_line = {.status_code = 200},
.body{std::move(body)},
};
engine::Engine e{std::make_unique<FakeProtocolHandler>(std::move(responses))};
auto page = e.navigate(uri::Uri::parse("hax://example.com").value()).value();
expect_eq(page->response.err, protocol::ErrorCode::Ok);
auto page = e.navigate(uri::Uri::parse("hax://example.com").value());
expect(page.has_value());
});
return etest::run_all_tests();