Home | History | Annotate | Download | only in profile_reset
      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