1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/tools/profile_reset/jtl_compiler.h" 6 7 #include <string> 8 9 #include "base/values.h" 10 #include "chrome/browser/profile_resetter/jtl_foundation.h" 11 #include "chrome/browser/profile_resetter/jtl_instructions.h" 12 #include "testing/gmock/include/gmock/gmock.h" 13 #include "testing/gtest/include/gtest/gtest.h" 14 15 namespace { 16 17 const char kTestHashSeed[] = "test-hash-seed"; 18 19 // Helpers ------------------------------------------------------------------- 20 21 std::string GetHash(const std::string& input) { 22 return jtl_foundation::Hasher(kTestHashSeed).GetHash(input); 23 } 24 25 static std::string EncodeUint32(uint32 value) { 26 std::string bytecode; 27 for (int i = 0; i < 4; ++i) { 28 bytecode.push_back(static_cast<char>(value & 0xFFu)); 29 value >>= 8; 30 } 31 return bytecode; 32 } 33 34 // Tests --------------------------------------------------------------------- 35 36 // Note: Parsing and parsing-related errors are unit-tested separately in more 37 // detail in "jtl_parser_unittest.cc". Here, most of the time, we assume that 38 // creating the parse tree works. 39 40 TEST(JtlCompiler, CompileSingleInstructions) { 41 struct TestCase { 42 std::string source_code; 43 std::string expected_bytecode; 44 } cases[] = { 45 {"go(\"foo\").", OP_NAVIGATE(GetHash("foo"))}, 46 {"go(\"has whitespace\t\").", OP_NAVIGATE(GetHash("has whitespace\t"))}, 47 {"any.", OP_NAVIGATE_ANY}, 48 {"back.", OP_NAVIGATE_BACK}, 49 {"store_bool(\"name\", true).", 50 OP_STORE_BOOL(GetHash("name"), VALUE_TRUE)}, 51 {"compare_stored_bool(\"name\", true, false).", 52 OP_COMPARE_STORED_BOOL(GetHash("name"), VALUE_TRUE, VALUE_FALSE)}, 53 {"store_hash(\"name\", \"" + GetHash("value") + "\").", 54 OP_STORE_HASH(GetHash("name"), GetHash("value"))}, 55 {"store_hashed(\"name\", \"value\").", 56 OP_STORE_HASH(GetHash("name"), GetHash("value"))}, 57 {"store_node_bool(\"name\").", OP_STORE_NODE_BOOL(GetHash("name"))}, 58 {"store_node_hash(\"name\").", OP_STORE_NODE_HASH(GetHash("name"))}, 59 {"store_node_effective_sld_hash(\"name\").", 60 OP_STORE_NODE_EFFECTIVE_SLD_HASH(GetHash("name"))}, 61 {"compare_stored_hashed(\"name\", \"value\", \"default\").", 62 OP_COMPARE_STORED_HASH( 63 GetHash("name"), GetHash("value"), GetHash("default"))}, 64 {"compare_bool(false).", OP_COMPARE_NODE_BOOL(VALUE_FALSE)}, 65 {"compare_bool(true).", OP_COMPARE_NODE_BOOL(VALUE_TRUE)}, 66 {"compare_hashed(\"foo\").", OP_COMPARE_NODE_HASH(GetHash("foo"))}, 67 {"compare_hashed_not(\"foo\").", 68 OP_COMPARE_NODE_HASH_NOT(GetHash("foo"))}, 69 {"compare_to_stored_bool(\"name\").", 70 OP_COMPARE_NODE_TO_STORED_BOOL(GetHash("name"))}, 71 {"compare_to_stored_hash(\"name\").", 72 OP_COMPARE_NODE_TO_STORED_HASH(GetHash("name"))}, 73 {"compare_substring_hashed(\"pattern\").", 74 OP_COMPARE_NODE_SUBSTRING( 75 GetHash("pattern"), EncodeUint32(7), EncodeUint32(766))}, 76 {"break.", OP_STOP_EXECUTING_SENTENCE}, 77 {"break;", OP_STOP_EXECUTING_SENTENCE + OP_END_OF_SENTENCE}}; 78 79 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { 80 SCOPED_TRACE(cases[i].source_code); 81 std::string bytecode; 82 EXPECT_TRUE(JtlCompiler::Compile( 83 cases[i].source_code, kTestHashSeed, &bytecode, NULL)); 84 EXPECT_EQ(cases[i].expected_bytecode, bytecode); 85 } 86 } 87 88 TEST(JtlCompiler, CompileEntireProgram) { 89 const char kSourceCode[] = 90 "// Store \"x\"=true if path is found.\n" 91 "go(\"foo\").go(\"bar\").store_bool(\"x\", true);\n" 92 "// ...\n" 93 "// Store \"y\"=\"1\" if above value is set.\n" 94 "compare_stored_bool(\"x\", true, false).store_hashed(\"y\", \"1\");\n"; 95 96 std::string expected_bytecode = 97 OP_NAVIGATE(GetHash("foo")) + 98 OP_NAVIGATE(GetHash("bar")) + 99 OP_STORE_BOOL(GetHash("x"), VALUE_TRUE) + OP_END_OF_SENTENCE + 100 OP_COMPARE_STORED_BOOL(GetHash("x"), VALUE_TRUE, VALUE_FALSE) + 101 OP_STORE_HASH(GetHash("y"), GetHash("1")) + OP_END_OF_SENTENCE; 102 103 std::string bytecode; 104 EXPECT_TRUE( 105 JtlCompiler::Compile(kSourceCode, kTestHashSeed, &bytecode, NULL)); 106 EXPECT_EQ(expected_bytecode, bytecode); 107 } 108 109 TEST(JtlCompiler, InvalidOperationName) { 110 const char kSourceCode[] = "any()\n.\nnon_existent_instruction\n(\n)\n;\n"; 111 112 std::string bytecode; 113 JtlCompiler::CompileError error; 114 EXPECT_FALSE( 115 JtlCompiler::Compile(kSourceCode, kTestHashSeed, &bytecode, &error)); 116 EXPECT_THAT(error.context, testing::StartsWith("non_existent_instruction")); 117 EXPECT_EQ(2u, error.line_number); 118 EXPECT_EQ(JtlCompiler::CompileError::INVALID_OPERATION_NAME, 119 error.error_code); 120 } 121 122 TEST(JtlCompiler, InvalidArgumentsCount) { 123 const char* kSourceCodes[] = { 124 "any().\nstore_bool(\"name\", true, \"superfluous argument\");\n", 125 "any().\nstore_bool(\"name\");"}; // missing argument 126 127 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kSourceCodes); ++i) { 128 SCOPED_TRACE(kSourceCodes[i]); 129 std::string bytecode; 130 JtlCompiler::CompileError error; 131 EXPECT_FALSE(JtlCompiler::Compile( 132 kSourceCodes[i], kTestHashSeed, &bytecode, &error)); 133 EXPECT_THAT(error.context, testing::StartsWith("store_bool")); 134 EXPECT_EQ(1u, error.line_number); 135 EXPECT_EQ(JtlCompiler::CompileError::INVALID_ARGUMENT_COUNT, 136 error.error_code); 137 } 138 } 139 140 TEST(JtlCompiler, InvalidArgumentType) { 141 struct TestCase { 142 std::string expected_context_prefix; 143 std::string source_code; 144 } cases[] = { 145 {"compare_bool", "any()\n.\ncompare_bool(\"foo\");"}, 146 {"compare_bool", 147 "any()\n.\ncompare_bool(\"01234567890123456789012345678901\");"}, 148 {"compare_hashed", "any()\n.\ncompare_hashed(false);"}, 149 {"store_hash", "any()\n.\nstore_hash(\"name\", false);"}, 150 {"store_hash", "any()\n.\nstore_hash(\"name\", \"foo\");"}, 151 {"compare_stored_bool", 152 "any()\n.\ncompare_stored_bool(\"name\", \"need a bool\", false);"}, 153 {"compare_stored_bool", 154 "any()\n.\ncompare_stored_bool(\"name\", false, \"need a bool\");"}, 155 {"compare_substring_hashed", 156 "any()\n.\ncompare_substring_hashed(true);"}}; 157 158 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { 159 SCOPED_TRACE(cases[i].source_code); 160 std::string bytecode; 161 JtlCompiler::CompileError error; 162 EXPECT_FALSE(JtlCompiler::Compile( 163 cases[i].source_code, kTestHashSeed, &bytecode, &error)); 164 EXPECT_THAT(error.context, 165 testing::StartsWith(cases[i].expected_context_prefix)); 166 EXPECT_EQ(2u, error.line_number); 167 EXPECT_EQ(JtlCompiler::CompileError::INVALID_ARGUMENT_TYPE, 168 error.error_code); 169 } 170 } 171 172 TEST(JtlCompiler, InvalidArgumentValue) { 173 struct TestCase { 174 std::string expected_context_prefix; 175 std::string source_code; 176 } cases[] = { 177 {"compare_substring_hashed", "compare_substring_hashed(\"\");"}}; 178 179 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { 180 SCOPED_TRACE(cases[i].source_code); 181 std::string bytecode; 182 JtlCompiler::CompileError error; 183 EXPECT_FALSE(JtlCompiler::Compile( 184 cases[i].source_code, kTestHashSeed, &bytecode, &error)); 185 EXPECT_THAT(error.context, 186 testing::StartsWith(cases[i].expected_context_prefix)); 187 EXPECT_EQ(0u, error.line_number); 188 EXPECT_EQ(JtlCompiler::CompileError::INVALID_ARGUMENT_VALUE, 189 error.error_code); 190 } 191 } 192 193 TEST(JtlCompiler, MistmatchedDoubleQuotes) { 194 const char kSourceCode[] = "any().\ngo(\"ok\", \"stray quote).break();"; 195 196 std::string bytecode; 197 JtlCompiler::CompileError error; 198 EXPECT_FALSE( 199 JtlCompiler::Compile(kSourceCode, kTestHashSeed, &bytecode, &error)); 200 EXPECT_EQ(1u, error.line_number); 201 EXPECT_EQ(JtlCompiler::CompileError::MISMATCHED_DOUBLE_QUOTES, 202 error.error_code); 203 } 204 205 TEST(JtlCompiler, ParsingError) { 206 const char kSourceCode[] = "any().\ngo()missing_separator();"; 207 208 std::string bytecode; 209 JtlCompiler::CompileError error; 210 EXPECT_FALSE( 211 JtlCompiler::Compile(kSourceCode, kTestHashSeed, &bytecode, &error)); 212 EXPECT_THAT(error.context, testing::StartsWith("go")); 213 EXPECT_EQ(1u, error.line_number); 214 EXPECT_EQ(JtlCompiler::CompileError::PARSING_ERROR, error.error_code); 215 } 216 217 } // namespace 218