blob: ea17268c161efc0ba9ce6e2f295ce59f1c79ccbc [file] [log] [blame]
// Copyright 2015 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 <memory>
#include "cobalt/cssom/selector_tree.h"
#include "cobalt/base/version_compatibility.h"
#include "cobalt/css_parser/parser.h"
#include "cobalt/cssom/css_style_rule.h"
#include "cobalt/cssom/specificity.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cobalt {
namespace cssom {
TEST(SelectorTreeTest, RootShouldHaveNoChildrenAfterInitialization) {
SelectorTree selector_tree;
EXPECT_TRUE(
selector_tree.children(selector_tree.root_node(), kChildCombinator)
.empty());
EXPECT_TRUE(
selector_tree.children(selector_tree.root_node(), kDescendantCombinator)
.empty());
EXPECT_TRUE(
selector_tree.children(selector_tree.root_node(), kNextSiblingCombinator)
.empty());
EXPECT_TRUE(
selector_tree
.children(selector_tree.root_node(), kFollowingSiblingCombinator)
.empty());
}
TEST(SelectorTreeTest, AppendRuleShouldTakeOneRule) {
SelectorTree selector_tree;
// Selector Tree:
// root
// kDescendantCombinator -> node_1("div")
std::unique_ptr<css_parser::Parser> css_parser = css_parser::Parser::Create();
scoped_refptr<CSSStyleRule> css_style_rule_1 =
css_parser->ParseRule("div {}", base::SourceLocation(
"[object SelectorTreeTest]", 1, 1))
->AsCSSStyleRule();
selector_tree.AppendRule(css_style_rule_1);
// Verify that ValidateVersionCompatibility does not report a usage error
// when the minimum compatibility version is 1.
base::VersionCompatibility::GetInstance()->SetMinimumVersion(1);
EXPECT_TRUE(selector_tree.ValidateVersionCompatibility());
ASSERT_EQ(0,
selector_tree.children(selector_tree.root_node(), kChildCombinator)
.size());
ASSERT_EQ(1, selector_tree
.children(selector_tree.root_node(), kDescendantCombinator)
.size());
ASSERT_EQ(0, selector_tree
.children(selector_tree.root_node(), kNextSiblingCombinator)
.size());
ASSERT_EQ(
0, selector_tree
.children(selector_tree.root_node(), kFollowingSiblingCombinator)
.size());
const SelectorTree::Node* node_1 =
selector_tree.children(selector_tree.root_node(), kDescendantCombinator)
.begin()
->second;
ASSERT_EQ(1, node_1->rules().size());
EXPECT_EQ(css_style_rule_1, node_1->rules()[0]);
EXPECT_EQ(Specificity(0, 0, 1), node_1->cumulative_specificity());
}
TEST(SelectorTreeTest, AppendRuleShouldNormalizeCompoundSelector) {
SelectorTree selector_tree;
// Selector Tree:
// root
// kDescendantCombinator -> node_1(".class#id")
std::unique_ptr<css_parser::Parser> css_parser = css_parser::Parser::Create();
scoped_refptr<CSSStyleRule> css_style_rule_1 =
css_parser->ParseRule(
".class#id {}",
base::SourceLocation("[object SelectorTreeTest]", 1, 1))
->AsCSSStyleRule();
scoped_refptr<CSSStyleRule> css_style_rule_2 =
css_parser->ParseRule(
"#id.class {}",
base::SourceLocation("[object SelectorTreeTest]", 1, 1))
->AsCSSStyleRule();
selector_tree.AppendRule(css_style_rule_1);
selector_tree.AppendRule(css_style_rule_2);
// Verify that ValidateVersionCompatibility does not report a usage error
// when the minimum compatibility version is 1.
base::VersionCompatibility::GetInstance()->SetMinimumVersion(1);
EXPECT_TRUE(selector_tree.ValidateVersionCompatibility());
ASSERT_EQ(0,
selector_tree.children(selector_tree.root_node(), kChildCombinator)
.size());
ASSERT_EQ(1, selector_tree
.children(selector_tree.root_node(), kDescendantCombinator)
.size());
ASSERT_EQ(0, selector_tree
.children(selector_tree.root_node(), kNextSiblingCombinator)
.size());
ASSERT_EQ(
0, selector_tree
.children(selector_tree.root_node(), kFollowingSiblingCombinator)
.size());
}
TEST(SelectorTreeTest, AppendRuleSimpleShouldTakeTwoIdenticalRules) {
SelectorTree selector_tree;
// Selector Tree:
// root
// kDescendantCombinator -> node_1("div")
std::unique_ptr<css_parser::Parser> css_parser = css_parser::Parser::Create();
scoped_refptr<CSSStyleRule> css_style_rule_1 =
css_parser->ParseRule("div {}", base::SourceLocation(
"[object SelectorTreeTest]", 1, 1))
->AsCSSStyleRule();
scoped_refptr<CSSStyleRule> css_style_rule_2 =
css_parser->ParseRule("div {}", base::SourceLocation(
"[object SelectorTreeTest]", 1, 1))
->AsCSSStyleRule();
selector_tree.AppendRule(css_style_rule_1);
selector_tree.AppendRule(css_style_rule_2);
// Verify that ValidateVersionCompatibility does not report a usage error
// when the minimum compatibility version is 1.
base::VersionCompatibility::GetInstance()->SetMinimumVersion(1);
EXPECT_TRUE(selector_tree.ValidateVersionCompatibility());
ASSERT_EQ(0,
selector_tree.children(selector_tree.root_node(), kChildCombinator)
.size());
ASSERT_EQ(1, selector_tree
.children(selector_tree.root_node(), kDescendantCombinator)
.size());
ASSERT_EQ(0, selector_tree
.children(selector_tree.root_node(), kNextSiblingCombinator)
.size());
ASSERT_EQ(
0, selector_tree
.children(selector_tree.root_node(), kFollowingSiblingCombinator)
.size());
const SelectorTree::Node* node_1 =
selector_tree.children(selector_tree.root_node(), kDescendantCombinator)
.begin()
->second;
ASSERT_EQ(2, node_1->rules().size());
EXPECT_EQ(css_style_rule_1, node_1->rules()[0]);
EXPECT_EQ(css_style_rule_2, node_1->rules()[1]);
EXPECT_EQ(Specificity(0, 0, 1), node_1->cumulative_specificity());
}
TEST(SelectorTreeTest, AppendRuleSimpleShouldTakeTwoDesendantSelectors) {
SelectorTree selector_tree;
// Selector Tree:
// root
// kNone -> node_1("div")
// kChildCombinator -> node_2("span")
std::unique_ptr<css_parser::Parser> css_parser = css_parser::Parser::Create();
scoped_refptr<CSSStyleRule> css_style_rule_1 =
css_parser->ParseRule("div {}", base::SourceLocation(
"[object SelectorTreeTest]", 1, 1))
->AsCSSStyleRule();
scoped_refptr<CSSStyleRule> css_style_rule_2 =
css_parser->ParseRule(
"div span {}",
base::SourceLocation("[object SelectorTreeTest]", 1, 1))
->AsCSSStyleRule();
selector_tree.AppendRule(css_style_rule_1);
selector_tree.AppendRule(css_style_rule_2);
// Verify that ValidateVersionCompatibility does not report a usage error
// when the minimum compatibility version is 1.
base::VersionCompatibility::GetInstance()->SetMinimumVersion(1);
EXPECT_TRUE(selector_tree.ValidateVersionCompatibility());
ASSERT_EQ(0,
selector_tree.children(selector_tree.root_node(), kChildCombinator)
.size());
ASSERT_EQ(1, selector_tree
.children(selector_tree.root_node(), kDescendantCombinator)
.size());
ASSERT_EQ(0, selector_tree
.children(selector_tree.root_node(), kNextSiblingCombinator)
.size());
ASSERT_EQ(
0, selector_tree
.children(selector_tree.root_node(), kFollowingSiblingCombinator)
.size());
const SelectorTree::Node* node_1 =
selector_tree.children(selector_tree.root_node(), kDescendantCombinator)
.begin()
->second;
ASSERT_EQ(1, node_1->rules().size());
EXPECT_EQ(css_style_rule_1, node_1->rules()[0]);
EXPECT_EQ(Specificity(0, 0, 1), node_1->cumulative_specificity());
ASSERT_EQ(0, selector_tree.children(node_1, kChildCombinator).size());
ASSERT_EQ(1, selector_tree.children(node_1, kDescendantCombinator).size());
ASSERT_EQ(0, selector_tree.children(node_1, kNextSiblingCombinator).size());
ASSERT_EQ(0,
selector_tree.children(node_1, kFollowingSiblingCombinator).size());
const SelectorTree::Node* node_2 =
selector_tree.children(node_1, kDescendantCombinator).begin()->second;
ASSERT_EQ(1, node_2->rules().size());
EXPECT_EQ(css_style_rule_2, node_2->rules()[0]);
EXPECT_EQ(Specificity(0, 0, 2), node_2->cumulative_specificity());
}
TEST(SelectorTreeTest, AppendRuleTwoDifferentNotSelectorsForSameElement) {
SelectorTree selector_tree;
// Selector Tree:
// root
// kDescendantCombinator -> node_1("body:not(.class-1)")
// kDescendantCombinator -> node_2("body:not(.class-2)")
std::unique_ptr<css_parser::Parser> css_parser = css_parser::Parser::Create();
scoped_refptr<CSSStyleRule> css_style_rule_1 =
css_parser
->ParseRule("body:not(.class-1) {}",
base::SourceLocation("[object SelectorTreeTest]", 1, 1))
->AsCSSStyleRule();
scoped_refptr<CSSStyleRule> css_style_rule_2 =
css_parser
->ParseRule("body:not(.class-2) {}",
base::SourceLocation("[object SelectorTreeTest]", 1, 1))
->AsCSSStyleRule();
selector_tree.AppendRule(css_style_rule_1);
selector_tree.AppendRule(css_style_rule_2);
// Verify that ValidateVersionCompatibility does not report a usage error
// when the minimum compatibility version is 16.
base::VersionCompatibility::GetInstance()->SetMinimumVersion(16);
EXPECT_TRUE(selector_tree.ValidateVersionCompatibility());
// Verify that ValidateVersionCompatibility reports a usage error when the
// minimum compatibility version is 1.
base::VersionCompatibility::GetInstance()->SetMinimumVersion(1);
EXPECT_FALSE(selector_tree.ValidateVersionCompatibility());
ASSERT_EQ(0,
selector_tree.children(selector_tree.root_node(), kChildCombinator)
.size());
ASSERT_EQ(2, selector_tree
.children(selector_tree.root_node(), kDescendantCombinator)
.size());
ASSERT_EQ(0, selector_tree
.children(selector_tree.root_node(), kNextSiblingCombinator)
.size());
ASSERT_EQ(
0, selector_tree
.children(selector_tree.root_node(), kFollowingSiblingCombinator)
.size());
auto node_iter =
selector_tree.children(selector_tree.root_node(), kDescendantCombinator)
.begin();
const SelectorTree::Node* node_1 = node_iter->second;
ASSERT_EQ(1, node_1->rules().size());
EXPECT_EQ(css_style_rule_1, node_1->rules()[0]);
EXPECT_EQ(Specificity(0, 1, 1), node_1->cumulative_specificity());
const SelectorTree::Node* node_2 = (++node_iter)->second;
ASSERT_EQ(1, node_2->rules().size());
EXPECT_EQ(css_style_rule_2, node_2->rules()[0]);
EXPECT_EQ(Specificity(0, 1, 1), node_1->cumulative_specificity());
}
TEST(SelectorTreeTest, AppendRuleTwoNotSelectorsForDifferentElements) {
SelectorTree selector_tree;
// Selector Tree:
// root
// kDescendantCombinator -> node_1("body:not(.class-1)")
// kDescendantCombinator -> node_2("div:not(.class-1)")
std::unique_ptr<css_parser::Parser> css_parser = css_parser::Parser::Create();
scoped_refptr<CSSStyleRule> css_style_rule_1 =
css_parser
->ParseRule("body:not(.class-1) {}",
base::SourceLocation("[object SelectorTreeTest]", 1, 1))
->AsCSSStyleRule();
scoped_refptr<CSSStyleRule> css_style_rule_2 =
css_parser
->ParseRule("div:not(.class-1) {}",
base::SourceLocation("[object SelectorTreeTest]", 1, 1))
->AsCSSStyleRule();
selector_tree.AppendRule(css_style_rule_1);
selector_tree.AppendRule(css_style_rule_2);
// Verify that ValidateVersionCompatibility does not report a usage error
// when the minimum compatibility version is 1.
base::VersionCompatibility::GetInstance()->SetMinimumVersion(1);
EXPECT_TRUE(selector_tree.ValidateVersionCompatibility());
ASSERT_EQ(0,
selector_tree.children(selector_tree.root_node(), kChildCombinator)
.size());
ASSERT_EQ(2, selector_tree
.children(selector_tree.root_node(), kDescendantCombinator)
.size());
ASSERT_EQ(0, selector_tree
.children(selector_tree.root_node(), kNextSiblingCombinator)
.size());
ASSERT_EQ(
0, selector_tree
.children(selector_tree.root_node(), kFollowingSiblingCombinator)
.size());
auto node_iter =
selector_tree.children(selector_tree.root_node(), kDescendantCombinator)
.begin();
const SelectorTree::Node* node_1 = node_iter->second;
ASSERT_EQ(1, node_1->rules().size());
EXPECT_EQ(css_style_rule_1, node_1->rules()[0]);
EXPECT_EQ(Specificity(0, 1, 1), node_1->cumulative_specificity());
const SelectorTree::Node* node_2 = (++node_iter)->second;
ASSERT_EQ(1, node_2->rules().size());
EXPECT_EQ(css_style_rule_2, node_2->rules()[0]);
EXPECT_EQ(Specificity(0, 1, 1), node_1->cumulative_specificity());
}
TEST(SelectorTreeTest, AppendRuleTwoIdenticalNotSelectors) {
SelectorTree selector_tree;
// Selector Tree:
// root
// kDescendantCombinator -> node_1("body:not(.class-1")
std::unique_ptr<css_parser::Parser> css_parser = css_parser::Parser::Create();
scoped_refptr<CSSStyleRule> css_style_rule_1 =
css_parser
->ParseRule("body:not(.class-1) {}",
base::SourceLocation("[object SelectorTreeTest]", 1, 1))
->AsCSSStyleRule();
scoped_refptr<CSSStyleRule> css_style_rule_2 =
css_parser
->ParseRule("body:not(.class-1) {}",
base::SourceLocation("[object SelectorTreeTest]", 1, 1))
->AsCSSStyleRule();
selector_tree.AppendRule(css_style_rule_1);
selector_tree.AppendRule(css_style_rule_2);
// Verify that ValidateVersionCompatibility does not report a usage error
// when the minimum compatibility version is 1.
base::VersionCompatibility::GetInstance()->SetMinimumVersion(1);
EXPECT_TRUE(selector_tree.ValidateVersionCompatibility());
ASSERT_EQ(0,
selector_tree.children(selector_tree.root_node(), kChildCombinator)
.size());
ASSERT_EQ(1, selector_tree
.children(selector_tree.root_node(), kDescendantCombinator)
.size());
ASSERT_EQ(0, selector_tree
.children(selector_tree.root_node(), kNextSiblingCombinator)
.size());
ASSERT_EQ(
0, selector_tree
.children(selector_tree.root_node(), kFollowingSiblingCombinator)
.size());
const SelectorTree::Node* node_1 =
selector_tree.children(selector_tree.root_node(), kDescendantCombinator)
.begin()
->second;
ASSERT_EQ(2, node_1->rules().size());
EXPECT_EQ(css_style_rule_1, node_1->rules()[0]);
EXPECT_EQ(css_style_rule_2, node_1->rules()[1]);
EXPECT_EQ(Specificity(0, 1, 1), node_1->cumulative_specificity());
}
} // namespace cssom
} // namespace cobalt