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_parser.h" 6 7 #include "base/json/json_writer.h" 8 #include "base/memory/scoped_ptr.h" 9 #include "base/values.h" 10 #include "testing/gmock/include/gmock/gmock.h" 11 #include "testing/gtest/include/gtest/gtest.h" 12 13 namespace { 14 15 // Helpers ------------------------------------------------------------------- 16 17 void ExpectNextOperation(JtlParser* parser, 18 const char* expected_name, 19 const char* expected_args_json, 20 bool expected_ends_sentence) { 21 std::string actual_name; 22 base::ListValue actual_args; 23 std::string actual_args_json; 24 bool actual_ends_sentence; 25 26 EXPECT_FALSE(parser->HasFinished()); 27 EXPECT_TRUE(parser->ParseNextOperation( 28 &actual_name, &actual_args, &actual_ends_sentence)); 29 EXPECT_EQ(expected_name, actual_name); 30 base::JSONWriter::Write(&actual_args, &actual_args_json); 31 EXPECT_EQ(expected_args_json, actual_args_json); 32 EXPECT_EQ(expected_ends_sentence, actual_ends_sentence); 33 } 34 35 void ExpectNextOperationToFail(JtlParser* parser, 36 size_t expected_line_number, 37 const char* expected_context_prefix) { 38 std::string actual_name; 39 base::ListValue actual_args; 40 bool actual_ends_sentence; 41 42 EXPECT_FALSE(parser->HasFinished()); 43 EXPECT_FALSE(parser->ParseNextOperation( 44 &actual_name, &actual_args, &actual_ends_sentence)); 45 EXPECT_THAT(parser->GetLastContext(), 46 testing::StartsWith(expected_context_prefix)); 47 EXPECT_EQ(expected_line_number, parser->GetLastLineNumber()); 48 } 49 50 JtlParser* CreateParserFromVerboseText(const std::string& verbose_text) { 51 std::string compacted_source_code; 52 std::vector<size_t> newline_indices; 53 EXPECT_TRUE(JtlParser::RemoveCommentsAndAllWhitespace( 54 verbose_text, &compacted_source_code, &newline_indices, NULL)); 55 return new JtlParser(compacted_source_code, newline_indices); 56 } 57 58 // Tests --------------------------------------------------------------------- 59 60 TEST(JtlParser, CompactingEmpty) { 61 const char kSourceCode[] = ""; 62 const char kCompactedSourceCode[] = ""; 63 64 scoped_ptr<JtlParser> parser(CreateParserFromVerboseText(kSourceCode)); 65 EXPECT_EQ(kCompactedSourceCode, parser->compacted_source()); 66 } 67 68 TEST(JtlParser, CompactingTrivial) { 69 const char kSourceCode[] = "foo"; 70 const char kCompactedSourceCode[] = "foo"; 71 72 scoped_ptr<JtlParser> parser(CreateParserFromVerboseText(kSourceCode)); 73 EXPECT_EQ(kCompactedSourceCode, parser->compacted_source()); 74 } 75 76 TEST(JtlParser, CompactingOneLine) { 77 const char kSourceCode[] = " \r f\to o ( true ) "; 78 const char kCompactedSourceCode[] = "foo(true)"; 79 80 scoped_ptr<JtlParser> parser(CreateParserFromVerboseText(kSourceCode)); 81 EXPECT_EQ(kCompactedSourceCode, parser->compacted_source()); 82 for (size_t i = 0; i < arraysize(kCompactedSourceCode) - 1; ++i) { 83 SCOPED_TRACE(testing::Message("Position ") << i); 84 EXPECT_EQ(0u, parser->GetOriginalLineNumber(i)); 85 } 86 } 87 88 TEST(JtlParser, CompactingMultipleLines) { 89 const char kSourceCode[] = "a\nbb\n \nccc \n\n d( \n e \n )"; 90 const char kCompactedSourceCode[] = "abbcccd(e)"; 91 const size_t kLineNumbers[] = {0u, 1u, 1u, 3u, 3u, 3u, 5u, 5u, 6u, 7u}; 92 COMPILE_ASSERT(arraysize(kCompactedSourceCode) == arraysize(kLineNumbers) + 1, 93 mismatched_test_data); 94 95 scoped_ptr<JtlParser> parser(CreateParserFromVerboseText(kSourceCode)); 96 EXPECT_EQ(kCompactedSourceCode, parser->compacted_source()); 97 for (size_t i = 0; i < arraysize(kLineNumbers); ++i) { 98 SCOPED_TRACE(testing::Message("Position ") << i); 99 EXPECT_EQ(kLineNumbers[i], parser->GetOriginalLineNumber(i)); 100 } 101 } 102 103 TEST(JtlParser, CompactingMultipleLinesWithComments) { 104 const char kSourceCode[] = 105 "a/ /b//Comment \n" 106 "//\n" 107 "// Full line comment\n" 108 " cd //"; 109 const char kCompactedSourceCode[] = "a//bcd"; 110 const size_t kLineNumbers[] = {0u, 0u, 0u, 0u, 3u, 3u}; 111 COMPILE_ASSERT(arraysize(kCompactedSourceCode) == arraysize(kLineNumbers) + 1, 112 mismatched_test_data); 113 114 scoped_ptr<JtlParser> parser(CreateParserFromVerboseText(kSourceCode)); 115 EXPECT_EQ(kCompactedSourceCode, parser->compacted_source()); 116 for (size_t i = 0; i < arraysize(kLineNumbers); ++i) { 117 SCOPED_TRACE(testing::Message("Position ") << i); 118 EXPECT_EQ(kLineNumbers[i], parser->GetOriginalLineNumber(i)); 119 } 120 } 121 122 TEST(JtlParser, HandlingCommentsAndStringLiterals) { 123 struct TestCase { 124 const char* source_code; 125 const char* compacted_source_code; 126 } cases[] = { 127 {"//", ""}, 128 {"//comment", ""}, 129 {"foo // comment", "foo"}, 130 {"foo // // comment", "foo"}, 131 {"foo //", "foo"}, 132 {"\"literal\"", "\"literal\""}, 133 {"\"literal with space\"", "\"literal with space\""}, 134 {"\"\"", "\"\""}, 135 {"\"\"\"\"", "\"\"\"\""}, 136 {"\"\" // comment", "\"\""}, 137 {"\"literal\" // comment", "\"literal\""}, 138 {"\"literal\" \"literal\" // comment", "\"literal\"\"literal\""}, 139 {"foo // \"not a literal\"", "foo"}, 140 {"foo // \"not even matched", "foo"}, 141 {"foo // \"not a literal\" \"not even matched", "foo"}, 142 {"\"literal\" // \"not a literal\"", "\"literal\""}, 143 {"\"literal\" // \"not even matched", "\"literal\""}, 144 {"\"//not a comment//\"", "\"//not a comment//\""}, 145 {"\"//not a comment//\" // comment", "\"//not a comment//\""}, 146 {"// \"//not a literal//\" // comment", ""}, 147 {"\"literal\" // \"//not a literal//\" // comment", "\"literal\""}, 148 {"\"//not a comment//\" // \"//not a literal//\" // comment", 149 "\"//not a comment//\""}, 150 {"\"literal // \"not a literal nor a comment", 151 "\"literal // \"notaliteralnoracomment"}, 152 {"\"literal // \"not a literal nor a comment//\"", 153 "\"literal // \"notaliteralnoracomment"} 154 }; 155 156 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { 157 SCOPED_TRACE(cases[i].source_code); 158 scoped_ptr<JtlParser> parser( 159 CreateParserFromVerboseText(cases[i].source_code)); 160 EXPECT_EQ(cases[i].compacted_source_code, parser->compacted_source()); 161 } 162 } 163 164 TEST(JtlParser, MismatchedDoubleQuotesBeforeEndOfLine) { 165 struct TestCase { 166 const char* source_code; 167 size_t error_line_number; 168 } cases[] = { 169 {"\"", 0}, 170 {"\"mismatched literal", 0}, 171 {"\n\"", 1}, 172 {"\"\n\"", 0}, 173 {"\"\"\"", 0}, 174 {"\"\"\n\"", 1}, 175 {"\"\"\n\"\n\"", 1}, 176 {"\" // not a comment", 0}, 177 {"\" // not a comment\n\"", 0}, 178 {"\"\" // comment\n\"", 1}, 179 {"\"\"\" // not a comment\n\"", 0}, 180 {"\"\"\" // \" neither a literal nor a comment\"\n\"", 0}, 181 {"\"\" // comment\n\"// not a comment", 1}, 182 {"\" // not a comment\"\n\"// not a comment", 1}, 183 {"foo(\"bar\");\nfoo(\"mismatched);", 1}, 184 {"foo(\n\"bar\", \"mismatched);", 1}, 185 {"foo(\n\"bar\", \"mismatched); //comment", 1}, 186 {"foo(\n\"bar\", \"mismatched);\ngood(\"bar\")", 1} 187 }; 188 189 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { 190 SCOPED_TRACE(cases[i].source_code); 191 std::string compacted_source_code; 192 std::vector<size_t> newline_indices; 193 size_t error_line_number; 194 EXPECT_FALSE(JtlParser::RemoveCommentsAndAllWhitespace( 195 cases[i].source_code, 196 &compacted_source_code, 197 &newline_indices, 198 &error_line_number)); 199 EXPECT_EQ(cases[i].error_line_number, error_line_number); 200 } 201 } 202 203 TEST(JtlParser, ParsingEmpty) { 204 const char kSourceCode[] = ""; 205 206 scoped_ptr<JtlParser> parser(CreateParserFromVerboseText(kSourceCode)); 207 EXPECT_TRUE(parser->HasFinished()); 208 } 209 210 TEST(JtlParser, ParsingOneWellFormedOperation) { 211 struct TestCase { 212 const char* source_code; 213 const char* expected_name; 214 const char* expected_args; 215 const bool expected_ends_sentence; 216 } cases[] = { 217 {"foo1;", "foo1", "[]", true}, 218 {"foo2().", "foo2", "[]", false}, 219 {"foo3(true);", "foo3", "[true]", true}, 220 {"foo4(false).", "foo4", "[false]", false}, 221 {"foo5(\"bar\").", "foo5", "[\"bar\"]", false}, 222 {"foo6(\" b a r \").", "foo6", "[\" b a r \"]", false}, 223 {"foo7(true, \"bar\").", "foo7", "[true,\"bar\"]", false}, 224 {"foo8(\"bar\", false, true);", "foo8", "[\"bar\",false,true]", true}, 225 {"foo9(\"bar\", \" b a r \");", "foo9", "[\"bar\",\" b a r \"]", true} 226 }; 227 228 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { 229 SCOPED_TRACE(cases[i].expected_name); 230 scoped_ptr<JtlParser> parser( 231 CreateParserFromVerboseText(cases[i].source_code)); 232 ExpectNextOperation(parser.get(), 233 cases[i].expected_name, 234 cases[i].expected_args, 235 cases[i].expected_ends_sentence); 236 EXPECT_TRUE(parser->HasFinished()); 237 } 238 } 239 240 TEST(JtlParser, ParsingMultipleWellFormedOperations) { 241 const char kSourceCode[] = 242 "foo1(true).foo2.foo3(\"bar\");" 243 "foo4(\"bar\", false);"; 244 245 scoped_ptr<JtlParser> parser(CreateParserFromVerboseText(kSourceCode)); 246 ExpectNextOperation(parser.get(), "foo1", "[true]", false); 247 ExpectNextOperation(parser.get(), "foo2", "[]", false); 248 ExpectNextOperation(parser.get(), "foo3", "[\"bar\"]", true); 249 ExpectNextOperation(parser.get(), "foo4", "[\"bar\",false]", true); 250 EXPECT_TRUE(parser->HasFinished()); 251 } 252 253 TEST(JtlParser, ParsingTrickyStringLiterals) { 254 struct TestCase { 255 const char* source_code; 256 const char* expected_name; 257 const char* expected_args; 258 const bool expected_ends_sentence; 259 } cases[] = { 260 {"prev().foo1(\"\");next(true);", "foo1", "[\"\"]", true}, 261 {"prev().foo2(\" \");next(true);", "foo2", "[\" \"]", true}, 262 {"prev().foo3(\",\",true);next(true);", "foo3", "[\",\",true]", true}, 263 {"prev().foo4(\")\",true);next(true);", "foo4", "[\")\",true]", true}, 264 {"prev().foo5(\";\",true);next(true);", "foo5", "[\";\",true]", true}, 265 {"prev().foo6(\"/\",true).next(true);", "foo6", "[\"/\",true]", false}, 266 {"prev().foo7(\"//\",true).next(true);", "foo7", "[\"//\",true]", false}, 267 {"prev().foo8(\".\",true).next(true);", "foo8", "[\".\",true]", false}, 268 }; 269 270 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { 271 SCOPED_TRACE(cases[i].expected_name); 272 scoped_ptr<JtlParser> parser( 273 CreateParserFromVerboseText(cases[i].source_code)); 274 ExpectNextOperation(parser.get(), "prev", "[]", false); 275 ExpectNextOperation(parser.get(), 276 cases[i].expected_name, 277 cases[i].expected_args, 278 cases[i].expected_ends_sentence); 279 ExpectNextOperation(parser.get(), "next", "[true]", true); 280 EXPECT_TRUE(parser->HasFinished()); 281 } 282 } 283 284 TEST(JtlParser, FirstOperationIsIllFormed) { 285 struct TestCase { 286 const char* source_code; 287 const char* operation_name; 288 } cases[] = { 289 {";;", ";"}, 290 {"bad_args1(not a boolean value);", "bad_args1"}, 291 {"bad_args2(,);", "bad_args2"}, 292 {"bad_args3(...);", "bad_args3"}, 293 {"bad_args4(1);", "bad_args4"}, 294 {"bad_args5(1.2);", "bad_args5"}, 295 {"bad_args6([\"bar\"]);", "bad_args6"}, 296 {"bad_args7(False);", "bad_args7"}, 297 {"bad_args8(True);", "bad_args8"}, 298 {"bad_quotes1(missing both, true).good();", "bad_quotes1"}, 299 {"bad_quotes2(true, \"missing one).good(); //\"", "bad_quotes2"}, 300 {"bad_quotes3(\"too\" \"much\", true).good();", "bad_quotes3"}, 301 {"bad_missing_separator1", "bad_missing_separator1"}, 302 {"bad_missing_separator2()good();", "bad_missing_separator2"}, 303 {"bad_parenthesis1(true.good();", "bad_parenthesis1"}, 304 {"bad_parenthesis2(true.good());", "bad_parenthesis2"}, 305 {"bad_parenthesis3).good();", "bad_parenthesis3"} 306 }; 307 308 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { 309 SCOPED_TRACE(cases[i].operation_name); 310 scoped_ptr<JtlParser> parser( 311 CreateParserFromVerboseText(cases[i].source_code)); 312 ExpectNextOperationToFail(parser.get(), 0, cases[i].operation_name); 313 } 314 } 315 316 TEST(JtlParser, SecondOperationIsIllFormed) { 317 struct TestCase { 318 const char* source_code; 319 const char* bad_operation_name; 320 } cases[] = { 321 {"\ngood(true,false)\n.bad_args(,);", "bad_args"}, 322 {"\ngood(true,false)\n.bad_quotes1(missing both, true).good();", 323 "bad_quotes1"}, 324 {"\ngood(true,false)\n.bad_quotes2(\"missing one, true).good(); //\"", 325 "bad_quotes2"}, 326 {"\ngood(true,false)\n.bad_quotes3(\"too\" \"many\", true).good();", 327 "bad_quotes3"}, 328 {"\ngood(true,false)\n.bad_separator1()/good();", "bad_separator1"}, 329 {"\ngood(true,false)\n.missing_separator1", "missing_separator1"}, 330 {"\ngood(true,false)\n.missing_separator2()good();", 331 "missing_separator2"}, 332 {"\ngood(true,false)\n.bad_parens1(true.good();", "bad_parens1"}, 333 {"\ngood(true,false)\n.bad_parens2(true.good());", "bad_parens2"}, 334 {"\ngood(true,false)\n.bad_parens3).good();", "bad_parens3"} 335 }; 336 337 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { 338 SCOPED_TRACE(cases[i].bad_operation_name); 339 scoped_ptr<JtlParser> parser( 340 CreateParserFromVerboseText(cases[i].source_code)); 341 ExpectNextOperation(parser.get(), "good", "[true,false]", false); 342 ExpectNextOperationToFail(parser.get(), 2, cases[i].bad_operation_name); 343 } 344 } 345 346 } // namespace 347