1// Copyright 2011 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/browser/profile_resetter/jtl_interpreter.h"
6
7#include <numeric>
8
9#include "base/strings/string_util.h"
10#include "base/test/values_test_util.h"
11#include "chrome/browser/profile_resetter/jtl_foundation.h"
12#include "chrome/browser/profile_resetter/jtl_instructions.h"
13#include "crypto/hmac.h"
14#include "testing/gtest/include/gtest/gtest.h"
15
16namespace {
17
18const char seed[] = "foobar";
19
20#define KEY_HASH_1 GetHash("KEY_HASH_1")
21#define KEY_HASH_2 GetHash("KEY_HASH_2")
22#define KEY_HASH_3 GetHash("KEY_HASH_3")
23#define KEY_HASH_4 GetHash("KEY_HASH_4")
24
25#define VALUE_HASH_1 GetHash("VALUE_HASH_1")
26#define VALUE_HASH_2 GetHash("VALUE_HASH_2")
27
28#define VAR_HASH_1 "01234567890123456789012345678901"
29#define VAR_HASH_2 "12345678901234567890123456789012"
30
31std::string GetHash(const std::string& input) {
32  return jtl_foundation::Hasher(seed).GetHash(input);
33}
34
35std::string EncodeUint32(uint32 value) {
36  std::string bytecode;
37  for (int i = 0; i < 4; ++i) {
38    bytecode.push_back(static_cast<char>(value & 0xFFu));
39    value >>= 8;
40  }
41  return bytecode;
42}
43
44// escaped_json_param may contain ' characters that are replaced with ". This
45// makes the code more readable because we need less escaping.
46#define INIT_INTERPRETER(program_param, escaped_json_param) \
47    const char* escaped_json = escaped_json_param; \
48    std::string json; \
49    base::ReplaceChars(escaped_json, "'", "\"", &json); \
50    scoped_ptr<base::Value> json_value(ParseJson(json)); \
51    JtlInterpreter interpreter( \
52        seed, \
53        program_param, \
54        static_cast<const base::DictionaryValue*>(json_value.get())); \
55    interpreter.Execute()
56
57using base::test::ParseJson;
58
59TEST(JtlInterpreter, Store) {
60  INIT_INTERPRETER(
61      OP_STORE_BOOL(VAR_HASH_1, VALUE_TRUE),
62      "{ 'KEY_HASH_1': 'VALUE_HASH_1' }");
63  EXPECT_EQ(JtlInterpreter::OK, interpreter.result());
64  base::ExpectDictBooleanValue(true, *interpreter.working_memory(), VAR_HASH_1);
65}
66
67TEST(JtlInterpreter, NavigateAndStore) {
68  INIT_INTERPRETER(
69      OP_NAVIGATE(KEY_HASH_1) +
70      OP_STORE_BOOL(VAR_HASH_1, VALUE_TRUE),
71      "{ 'KEY_HASH_1': 'VALUE_HASH_1' }");
72  EXPECT_EQ(JtlInterpreter::OK, interpreter.result());
73  base::ExpectDictBooleanValue(true, *interpreter.working_memory(), VAR_HASH_1);
74}
75
76TEST(JtlInterpreter, FailNavigate) {
77  INIT_INTERPRETER(
78      OP_NAVIGATE(KEY_HASH_2) +
79      OP_STORE_BOOL(VAR_HASH_1, VALUE_TRUE),
80      "{ 'KEY_HASH_1': 'VALUE_HASH_1' }");
81  EXPECT_EQ(JtlInterpreter::OK, interpreter.result());
82  EXPECT_FALSE(interpreter.working_memory()->HasKey(VAR_HASH_1));
83}
84
85TEST(JtlInterpreter, ConsecutiveNavigate) {
86  INIT_INTERPRETER(
87      OP_NAVIGATE(KEY_HASH_1) +
88      OP_NAVIGATE(KEY_HASH_2) +
89      OP_STORE_BOOL(VAR_HASH_1, VALUE_TRUE),
90      "{ 'KEY_HASH_1': { 'KEY_HASH_2': 'VALUE_HASH_1' } }");
91  EXPECT_EQ(JtlInterpreter::OK, interpreter.result());
92  base::ExpectDictBooleanValue(true, *interpreter.working_memory(), VAR_HASH_1);
93}
94
95TEST(JtlInterpreter, FailConsecutiveNavigate) {
96  INIT_INTERPRETER(
97      OP_NAVIGATE(KEY_HASH_1) +
98      OP_NAVIGATE(KEY_HASH_2) +
99      OP_STORE_BOOL(VAR_HASH_1, VALUE_TRUE),
100      "{ 'KEY_HASH_1': 'foo' }");
101  EXPECT_EQ(JtlInterpreter::OK, interpreter.result());
102  EXPECT_FALSE(interpreter.working_memory()->HasKey(VAR_HASH_1));
103}
104
105TEST(JtlInterpreter, NavigateAnyInDictionary) {
106  INIT_INTERPRETER(
107      OP_NAVIGATE(KEY_HASH_1) +
108      OP_NAVIGATE_ANY +
109      OP_NAVIGATE(KEY_HASH_4) +
110      OP_STORE_BOOL(VAR_HASH_1, VALUE_TRUE),
111          "{ 'KEY_HASH_1':"
112          "  { 'KEY_HASH_2': {'KEY_HASH_3': 'VALUE_HASH_1' },"
113          "    'KEY_HASH_3': {'KEY_HASH_4': 'VALUE_HASH_1' }"
114          "  } }");
115  EXPECT_EQ(JtlInterpreter::OK, interpreter.result());
116  base::ExpectDictBooleanValue(true, *interpreter.working_memory(), VAR_HASH_1);
117}
118
119TEST(JtlInterpreter, NavigateAnyInList) {
120  INIT_INTERPRETER(
121      OP_NAVIGATE(KEY_HASH_1) +
122      OP_NAVIGATE_ANY +
123      OP_NAVIGATE(KEY_HASH_4) +
124      OP_STORE_BOOL(VAR_HASH_1, VALUE_TRUE),
125          "{ 'KEY_HASH_1':"
126          "  [ {'KEY_HASH_3': 'VALUE_HASH_1' },"
127          "    {'KEY_HASH_4': 'VALUE_HASH_1' }"
128          "  ] }");
129  EXPECT_EQ(JtlInterpreter::OK, interpreter.result());
130  base::ExpectDictBooleanValue(true, *interpreter.working_memory(), VAR_HASH_1);
131}
132
133TEST(JtlInterpreter, NavigateBack) {
134  INIT_INTERPRETER(
135      OP_NAVIGATE(KEY_HASH_1) +
136      OP_NAVIGATE(KEY_HASH_2) +
137      OP_NAVIGATE_BACK +
138      OP_NAVIGATE(KEY_HASH_3) +
139      OP_NAVIGATE(KEY_HASH_4) +
140      OP_STORE_BOOL(VAR_HASH_1, VALUE_TRUE),
141          "{ 'KEY_HASH_1':"
142          "  { 'KEY_HASH_2': {'KEY_HASH_3': 'VALUE_HASH_1' },"
143          "    'KEY_HASH_3': {'KEY_HASH_4': 'VALUE_HASH_1' }"
144          "  } }");
145  EXPECT_EQ(JtlInterpreter::OK, interpreter.result());
146  base::ExpectDictBooleanValue(true, *interpreter.working_memory(), VAR_HASH_1);
147}
148
149TEST(JtlInterpreter, StoreTwoValues) {
150  INIT_INTERPRETER(
151      OP_NAVIGATE(KEY_HASH_1) +
152      OP_NAVIGATE(KEY_HASH_2) +
153      OP_STORE_BOOL(VAR_HASH_1, VALUE_TRUE) +
154      OP_STORE_HASH(VAR_HASH_2, VALUE_HASH_1),
155      "{ 'KEY_HASH_1': { 'KEY_HASH_2': 'VALUE_HASH_1' } }");
156  EXPECT_EQ(JtlInterpreter::OK, interpreter.result());
157  base::ExpectDictBooleanValue(true, *interpreter.working_memory(), VAR_HASH_1);
158  base::ExpectDictStringValue(VALUE_HASH_1, *interpreter.working_memory(),
159                              VAR_HASH_2);
160}
161
162TEST(JtlInterpreter, CompareStoredMatch) {
163  INIT_INTERPRETER(
164      OP_NAVIGATE(KEY_HASH_1) +
165      OP_STORE_BOOL(VAR_HASH_1, VALUE_TRUE) +
166      OP_NAVIGATE(KEY_HASH_2) +
167      OP_COMPARE_STORED_BOOL(VAR_HASH_1, VALUE_TRUE, VALUE_FALSE) +
168      OP_STORE_BOOL(VAR_HASH_2, VALUE_TRUE),
169      "{ 'KEY_HASH_1': { 'KEY_HASH_2': 'VALUE_HASH_1' } }");
170  EXPECT_EQ(JtlInterpreter::OK, interpreter.result());
171  base::ExpectDictBooleanValue(true, *interpreter.working_memory(), VAR_HASH_1);
172  base::ExpectDictBooleanValue(true, *interpreter.working_memory(), VAR_HASH_2);
173}
174
175TEST(JtlInterpreter, CompareStoredMismatch) {
176  INIT_INTERPRETER(
177      OP_NAVIGATE(KEY_HASH_1) +
178      OP_STORE_BOOL(VAR_HASH_1, VALUE_TRUE) +
179      OP_NAVIGATE(KEY_HASH_2) +
180      OP_COMPARE_STORED_BOOL(VAR_HASH_1, VALUE_FALSE, VALUE_TRUE) +
181      OP_STORE_BOOL(VAR_HASH_2, VALUE_TRUE),
182      "{ 'KEY_HASH_1': { 'KEY_HASH_2': 'VALUE_HASH_1' } }");
183  EXPECT_EQ(JtlInterpreter::OK, interpreter.result());
184  base::ExpectDictBooleanValue(true, *interpreter.working_memory(), VAR_HASH_1);
185  EXPECT_FALSE(interpreter.working_memory()->HasKey(VAR_HASH_2));
186}
187
188TEST(JtlInterpreter, CompareStoredNoValueMatchingDefault) {
189  INIT_INTERPRETER(
190      OP_NAVIGATE(KEY_HASH_1) +
191      OP_NAVIGATE(KEY_HASH_2) +
192      OP_COMPARE_STORED_BOOL(VAR_HASH_1, VALUE_TRUE, VALUE_TRUE) +
193      OP_STORE_BOOL(VAR_HASH_2, VALUE_TRUE),
194      "{ 'KEY_HASH_1': { 'KEY_HASH_2': 'VALUE_HASH_1' } }");
195  EXPECT_EQ(JtlInterpreter::OK, interpreter.result());
196  base::ExpectDictBooleanValue(true, *interpreter.working_memory(), VAR_HASH_2);
197}
198
199TEST(JtlInterpreter, CompareStoredNoValueMismatchingDefault) {
200  INIT_INTERPRETER(
201      OP_NAVIGATE(KEY_HASH_1) +
202      OP_NAVIGATE(KEY_HASH_2) +
203      OP_COMPARE_STORED_BOOL(VAR_HASH_1, VALUE_TRUE, VALUE_FALSE) +
204      OP_STORE_BOOL(VAR_HASH_2, VALUE_TRUE),
205      "{ 'KEY_HASH_1': { 'KEY_HASH_2': 'VALUE_HASH_1' } }");
206  EXPECT_EQ(JtlInterpreter::OK, interpreter.result());
207  EXPECT_FALSE(interpreter.working_memory()->HasKey(VAR_HASH_2));
208}
209
210TEST(JtlInterpreter, CompareBool) {
211  struct TestCase {
212    std::string expected_value;
213    const char* json;
214    bool expected_success;
215  } cases[] = {
216    { VALUE_TRUE, "{ 'KEY_HASH_1': true }", true },
217    { VALUE_FALSE, "{ 'KEY_HASH_1': false }", true },
218    { VALUE_TRUE, "{ 'KEY_HASH_1': false }", false },
219    { VALUE_TRUE, "{ 'KEY_HASH_1': 'abc' }", false },
220    { VALUE_TRUE, "{ 'KEY_HASH_1': 1 }", false },
221    { VALUE_TRUE, "{ 'KEY_HASH_1': 1.2 }", false },
222    { VALUE_TRUE, "{ 'KEY_HASH_1': [1] }", false },
223    { VALUE_TRUE, "{ 'KEY_HASH_1': {'a': 'b'} }", false },
224  };
225
226  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
227    SCOPED_TRACE(testing::Message() << "Iteration " << i);
228    INIT_INTERPRETER(
229        OP_NAVIGATE(KEY_HASH_1) +
230        OP_COMPARE_NODE_BOOL(cases[i].expected_value) +
231        OP_STORE_BOOL(VAR_HASH_1, VALUE_TRUE),
232        cases[i].json);
233    EXPECT_EQ(JtlInterpreter::OK, interpreter.result());
234    if (cases[i].expected_success) {
235      base::ExpectDictBooleanValue(
236          true, *interpreter.working_memory(), VAR_HASH_1);
237    } else {
238      EXPECT_FALSE(interpreter.working_memory()->HasKey(VAR_HASH_1));
239    }
240  }
241}
242
243TEST(JtlInterpreter, CompareHashString) {
244  struct TestCase {
245    std::string expected_value;
246    const char* json;
247    bool expected_success;
248  } cases[] = {
249    { VALUE_HASH_1, "{ 'KEY_HASH_1': 'VALUE_HASH_1' }", true },
250    { VALUE_HASH_1, "{ 'KEY_HASH_1': 'VALUE_HASH_2' }", false },
251    { VALUE_HASH_1, "{ 'KEY_HASH_1': true }", false },
252    { VALUE_HASH_1, "{ 'KEY_HASH_1': 1 }", false },
253    { VALUE_HASH_1, "{ 'KEY_HASH_1': 1.1 }", false },
254    { VALUE_HASH_1, "{ 'KEY_HASH_1': [1] }", false },
255    { VALUE_HASH_1, "{ 'KEY_HASH_1': {'a': 'b'} }", false },
256
257    { GetHash("1.2"), "{ 'KEY_HASH_1': 1.2 }", true },
258    { GetHash("1.2"), "{ 'KEY_HASH_1': 'VALUE_HASH_1' }", false },
259    { GetHash("1.2"), "{ 'KEY_HASH_1': true }", false },
260    { GetHash("1.2"), "{ 'KEY_HASH_1': 1 }", false },
261    { GetHash("1.2"), "{ 'KEY_HASH_1': 1.3 }", false },
262    { GetHash("1.2"), "{ 'KEY_HASH_1': [1] }", false },
263    { GetHash("1.2"), "{ 'KEY_HASH_1': {'a': 'b'} }", false },
264
265    { GetHash("1"), "{ 'KEY_HASH_1': 1 }", true },
266    { GetHash("1"), "{ 'KEY_HASH_1': 'VALUE_HASH_1' }", false },
267    { GetHash("1"), "{ 'KEY_HASH_1': true }", false },
268    { GetHash("1"), "{ 'KEY_HASH_1': 2 }", false },
269    { GetHash("1"), "{ 'KEY_HASH_1': 1.1 }", false },
270    { GetHash("1"), "{ 'KEY_HASH_1': [1] }", false },
271    { GetHash("1"), "{ 'KEY_HASH_1': {'a': 'b'} }", false },
272  };
273
274  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
275    SCOPED_TRACE(testing::Message() << "Iteration " << i);
276    INIT_INTERPRETER(
277        OP_NAVIGATE(KEY_HASH_1) +
278        OP_COMPARE_NODE_HASH(cases[i].expected_value) +
279        OP_STORE_BOOL(VAR_HASH_1, VALUE_TRUE),
280        cases[i].json);
281    EXPECT_EQ(JtlInterpreter::OK, interpreter.result());
282    if (cases[i].expected_success) {
283      base::ExpectDictBooleanValue(
284          true, *interpreter.working_memory(), VAR_HASH_1);
285    } else {
286      EXPECT_FALSE(interpreter.working_memory()->HasKey(VAR_HASH_1));
287    }
288  }
289
290  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
291    SCOPED_TRACE(testing::Message() << "Negated, Iteration " << i);
292    INIT_INTERPRETER(
293        OP_NAVIGATE(KEY_HASH_1) +
294        OP_COMPARE_NODE_HASH_NOT(cases[i].expected_value) +
295        OP_STORE_BOOL(VAR_HASH_1, VALUE_TRUE),
296        cases[i].json);
297    EXPECT_EQ(JtlInterpreter::OK, interpreter.result());
298    if (!cases[i].expected_success) {
299      base::ExpectDictBooleanValue(
300          true, *interpreter.working_memory(), VAR_HASH_1);
301    } else {
302      EXPECT_FALSE(interpreter.working_memory()->HasKey(VAR_HASH_1));
303    }
304  }
305}
306
307TEST(JtlInterpreter, StoreNodeBool) {
308  struct TestCase {
309    bool expected_value;
310    const char* json;
311    bool expected_success;
312  } cases[] = {
313    { true, "{ 'KEY_HASH_1': true }", true },
314    { false, "{ 'KEY_HASH_1': false }", true },
315    { false, "{ 'KEY_HASH_1': 'abc' }", false },
316    { false, "{ 'KEY_HASH_1': 1 }", false },
317    { false, "{ 'KEY_HASH_1': 1.2 }", false },
318    { false, "{ 'KEY_HASH_1': [1] }", false },
319    { false, "{ 'KEY_HASH_1': {'a': 'b'} }", false },
320  };
321
322  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
323    SCOPED_TRACE(testing::Message() << "Iteration " << i);
324    INIT_INTERPRETER(
325        OP_NAVIGATE(KEY_HASH_1) +
326        OP_STORE_NODE_BOOL(VAR_HASH_1) +
327        OP_STORE_BOOL(VAR_HASH_2, VALUE_TRUE),
328        cases[i].json);
329    EXPECT_EQ(JtlInterpreter::OK, interpreter.result());
330    if (cases[i].expected_success) {
331      base::ExpectDictBooleanValue(
332          cases[i].expected_value, *interpreter.working_memory(), VAR_HASH_1);
333      base::ExpectDictBooleanValue(
334          true, *interpreter.working_memory(), VAR_HASH_2);
335    } else {
336      EXPECT_FALSE(interpreter.working_memory()->HasKey(VAR_HASH_1));
337      EXPECT_FALSE(interpreter.working_memory()->HasKey(VAR_HASH_2));
338    }
339  }
340}
341
342TEST(JtlInterpreter, CompareNodeToStoredBool) {
343  struct TestCase {
344    std::string stored_value;
345    const char* json;
346    bool expected_success;
347  } cases[] = {
348    { VALUE_TRUE, "{ 'KEY_HASH_1': true }", true },
349    { VALUE_FALSE, "{ 'KEY_HASH_1': false }", true },
350    { VALUE_FALSE, "{ 'KEY_HASH_1': true }", false },
351    { std::string(), "{ 'KEY_HASH_1': true }", false },
352
353    { VALUE_HASH_1, "{ 'KEY_HASH_1': 'VALUE_HASH_1' }", false },
354    { GetHash("1"), "{ 'KEY_HASH_1': 1 }", false },
355    { GetHash("1.2"), "{ 'KEY_HASH_1': 1.2 }", false },
356
357    { VALUE_HASH_1, "{ 'KEY_HASH_1': true }", false },
358    { GetHash("1"), "{ 'KEY_HASH_1': true }", false },
359    { GetHash("1.2"), "{ 'KEY_HASH_1': true }", false },
360
361    { VALUE_TRUE, "{ 'KEY_HASH_1': 'VALUE_HASH_1' }", false },
362    { VALUE_TRUE, "{ 'KEY_HASH_1': 1 }", false },
363    { VALUE_TRUE, "{ 'KEY_HASH_1': 1.2 }", false },
364    { VALUE_TRUE, "{ 'KEY_HASH_1': [1] }", false },
365    { VALUE_TRUE, "{ 'KEY_HASH_1': {'a': 'b'} }", false },
366  };
367
368  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
369    SCOPED_TRACE(testing::Message() << "Iteration " << i);
370    std::string store_op;
371    if (cases[i].stored_value == VALUE_TRUE ||
372        cases[i].stored_value == VALUE_FALSE)
373      store_op = OP_STORE_BOOL(VAR_HASH_1, cases[i].stored_value);
374    else if (!cases[i].stored_value.empty())
375      store_op = OP_STORE_HASH(VAR_HASH_1, cases[i].stored_value);
376    INIT_INTERPRETER(
377        store_op +
378        OP_NAVIGATE(KEY_HASH_1) +
379        OP_COMPARE_NODE_TO_STORED_BOOL(VAR_HASH_1) +
380        OP_STORE_BOOL(VAR_HASH_2, VALUE_TRUE),
381        cases[i].json);
382    EXPECT_EQ(JtlInterpreter::OK, interpreter.result());
383    if (cases[i].expected_success) {
384      base::ExpectDictBooleanValue(
385          true, *interpreter.working_memory(), VAR_HASH_2);
386    } else {
387      EXPECT_FALSE(interpreter.working_memory()->HasKey(VAR_HASH_2));
388    }
389  }
390}
391
392TEST(JtlInterpreter, StoreNodeHash) {
393  struct TestCase {
394    std::string expected_value;
395    const char* json;
396    bool expected_success;
397  } cases[] = {
398    { VALUE_HASH_1, "{ 'KEY_HASH_1': 'VALUE_HASH_1' }", true },
399    { VALUE_HASH_2, "{ 'KEY_HASH_1': 'VALUE_HASH_2' }", true },
400    { GetHash("1"), "{ 'KEY_HASH_1': 1 }", true },
401    { GetHash("1.2"), "{ 'KEY_HASH_1': 1.2 }", true },
402    { std::string(), "{ 'KEY_HASH_1': true }", false },
403    { std::string(), "{ 'KEY_HASH_1': [1] }", false },
404    { std::string(), "{ 'KEY_HASH_1': {'a': 'b'} }", false },
405  };
406
407  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
408    SCOPED_TRACE(testing::Message() << "Iteration " << i);
409    INIT_INTERPRETER(
410        OP_NAVIGATE(KEY_HASH_1) +
411        OP_STORE_NODE_HASH(VAR_HASH_1) +
412        OP_STORE_BOOL(VAR_HASH_2, VALUE_TRUE),
413        cases[i].json);
414    EXPECT_EQ(JtlInterpreter::OK, interpreter.result());
415    if (cases[i].expected_success) {
416      base::ExpectDictStringValue(
417          cases[i].expected_value, *interpreter.working_memory(), VAR_HASH_1);
418      base::ExpectDictBooleanValue(
419          true, *interpreter.working_memory(), VAR_HASH_2);
420    } else {
421      EXPECT_FALSE(interpreter.working_memory()->HasKey(VAR_HASH_1));
422      EXPECT_FALSE(interpreter.working_memory()->HasKey(VAR_HASH_2));
423    }
424  }
425}
426
427TEST(JtlInterpreter, CompareNodeToStoredHash) {
428  struct TestCase {
429    std::string stored_value;
430    const char* json;
431    bool expected_success;
432  } cases[] = {
433    { VALUE_HASH_1, "{ 'KEY_HASH_1': 'VALUE_HASH_1' }", true },
434    { VALUE_HASH_2, "{ 'KEY_HASH_1': 'VALUE_HASH_2' }", true },
435    { std::string(), "{ 'KEY_HASH_1': 'VALUE_HASH_2' }", false },
436    { VALUE_HASH_1, "{ 'KEY_HASH_1': 'VALUE_HASH_2' }", false },
437    { VALUE_HASH_1, "{ 'KEY_HASH_1': true }", false },
438    { VALUE_HASH_1, "{ 'KEY_HASH_1': 1 }", false },
439    { VALUE_HASH_1, "{ 'KEY_HASH_1': 1.1 }", false },
440    { VALUE_HASH_1, "{ 'KEY_HASH_1': [1] }", false },
441    { VALUE_HASH_1, "{ 'KEY_HASH_1': {'a': 'b'} }", false },
442
443    { GetHash("1.2"), "{ 'KEY_HASH_1': 1.2 }", true },
444    { GetHash("1.3"), "{ 'KEY_HASH_1': 1.3 }", true },
445    { std::string(), "{ 'KEY_HASH_1': 1.2 }", false },
446    { GetHash("1.2"), "{ 'KEY_HASH_1': 'VALUE_HASH_1' }", false },
447    { GetHash("1.2"), "{ 'KEY_HASH_1': true }", false },
448    { GetHash("1.2"), "{ 'KEY_HASH_1': 1 }", false },
449    { GetHash("1.2"), "{ 'KEY_HASH_1': 1.3 }", false },
450    { GetHash("1.2"), "{ 'KEY_HASH_1': [1] }", false },
451    { GetHash("1.2"), "{ 'KEY_HASH_1': {'a': 'b'} }", false },
452
453    { GetHash("1"), "{ 'KEY_HASH_1': 1 }", true },
454    { GetHash("2"), "{ 'KEY_HASH_1': 2 }", true },
455    { std::string(), "{ 'KEY_HASH_1': 2 }", false },
456    { GetHash("1"), "{ 'KEY_HASH_1': 'VALUE_HASH_1' }", false },
457    { GetHash("1"), "{ 'KEY_HASH_1': true }", false },
458    { GetHash("1"), "{ 'KEY_HASH_1': 2 }", false },
459    { GetHash("1"), "{ 'KEY_HASH_1': 1.1 }", false },
460    { GetHash("1"), "{ 'KEY_HASH_1': [1] }", false },
461    { GetHash("1"), "{ 'KEY_HASH_1': {'a': 'b'} }", false },
462
463    { VALUE_TRUE, "{ 'KEY_HASH_1': 'VALUE_HASH_1' }", false },
464    { VALUE_TRUE, "{ 'KEY_HASH_1': 1 }", false },
465    { VALUE_TRUE, "{ 'KEY_HASH_1': 1.3 }", false },
466    { VALUE_TRUE, "{ 'KEY_HASH_1': [1] }", false },
467    { VALUE_TRUE, "{ 'KEY_HASH_1': {'a': 'b'} }", false },
468
469    { VALUE_TRUE, "{ 'KEY_HASH_1': true }", false },
470    { VALUE_FALSE, "{ 'KEY_HASH_1': false }", false },
471  };
472
473  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
474    SCOPED_TRACE(testing::Message() << "Iteration " << i);
475    std::string store_op;
476    if (cases[i].stored_value == VALUE_TRUE ||
477        cases[i].stored_value == VALUE_FALSE)
478      store_op = OP_STORE_BOOL(VAR_HASH_1, cases[i].stored_value);
479    else if (!cases[i].stored_value.empty())
480      store_op = OP_STORE_HASH(VAR_HASH_1, cases[i].stored_value);
481    INIT_INTERPRETER(
482        store_op +
483        OP_NAVIGATE(KEY_HASH_1) +
484        OP_COMPARE_NODE_TO_STORED_HASH(VAR_HASH_1) +
485        OP_STORE_BOOL(VAR_HASH_2, VALUE_TRUE),
486        cases[i].json);
487    EXPECT_EQ(JtlInterpreter::OK, interpreter.result());
488    if (cases[i].expected_success) {
489      base::ExpectDictBooleanValue(
490          true, *interpreter.working_memory(), VAR_HASH_2);
491    } else {
492      EXPECT_FALSE(interpreter.working_memory()->HasKey(VAR_HASH_2));
493    }
494  }
495}
496
497TEST(JtlInterpreter, CompareSubstring) {
498  struct TestCase {
499    std::string pattern;
500    const char* json;
501    bool expected_success;
502  } cases[] = {
503    { "abc", "{ 'KEY_HASH_1': 'abcdefghijklmnopqrstuvwxyz' }", true },
504    { "xyz", "{ 'KEY_HASH_1': 'abcdefghijklmnopqrstuvwxyz' }", true },
505    { "m", "{ 'KEY_HASH_1': 'abcdefghijklmnopqrstuvwxyz' }", true },
506    { "abc", "{ 'KEY_HASH_1': 'abc' }", true },
507    { "cba", "{ 'KEY_HASH_1': 'abcdefghijklmnopqrstuvwxyz' }", false },
508    { "acd", "{ 'KEY_HASH_1': 'abcdefghijklmnopqrstuvwxyz' }", false },
509    { "waaaaaaay_too_long", "{ 'KEY_HASH_1': 'abc' }", false },
510
511    { VALUE_HASH_1, "{ 'KEY_HASH_1': true }", false },
512    { VALUE_HASH_1, "{ 'KEY_HASH_1': 1 }", false },
513    { VALUE_HASH_1, "{ 'KEY_HASH_1': 1.1 }", false },
514    { VALUE_HASH_1, "{ 'KEY_HASH_1': [1] }", false },
515    { VALUE_HASH_1, "{ 'KEY_HASH_1': {'a': 'b'} }", false },
516  };
517
518  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
519    SCOPED_TRACE(testing::Message() << "Iteration " << i);
520    std::string pattern = cases[i].pattern;
521    uint32 pattern_sum = std::accumulate(
522        pattern.begin(), pattern.end(), static_cast<uint32>(0u));
523    INIT_INTERPRETER(
524        OP_NAVIGATE(KEY_HASH_1) +
525        OP_COMPARE_NODE_SUBSTRING(GetHash(pattern),
526                                  EncodeUint32(pattern.size()),
527                                  EncodeUint32(pattern_sum)) +
528        OP_STORE_BOOL(VAR_HASH_1, VALUE_TRUE),
529        cases[i].json);
530    EXPECT_EQ(JtlInterpreter::OK, interpreter.result());
531    if (cases[i].expected_success) {
532      base::ExpectDictBooleanValue(
533          true, *interpreter.working_memory(), VAR_HASH_1);
534    } else {
535      EXPECT_FALSE(interpreter.working_memory()->HasKey(VAR_HASH_1));
536    }
537  }
538}
539
540TEST(JtlInterpreter, StoreNodeRegisterableDomainHash) {
541  struct TestCase {
542    std::string expected_value;
543    const char* json;
544    bool expected_success;
545  } cases[] = {
546    { GetHash("google"), "{ 'KEY_HASH_1': 'http://google.com/path' }", true },
547    { GetHash("google"), "{ 'KEY_HASH_1': 'http://mail.google.com/' }", true },
548    { GetHash("google"), "{ 'KEY_HASH_1': 'http://google.co.uk/' }", true },
549    { GetHash("google"), "{ 'KEY_HASH_1': 'http://google.com./' }", true },
550    { GetHash("google"), "{ 'KEY_HASH_1': 'http://..google.com/' }", true },
551
552    { GetHash("foo"), "{ 'KEY_HASH_1': 'http://foo.bar/path' }", true },
553    { GetHash("foo"), "{ 'KEY_HASH_1': 'http://sub.foo.bar' }", true },
554    { GetHash("foo"), "{ 'KEY_HASH_1': 'http://foo.appspot.com/' }", true },
555    { GetHash("foo"), "{ 'KEY_HASH_1': 'http://sub.foo.appspot.com' }", true },
556
557    { std::string(), "{ 'KEY_HASH_1': 'http://google.com../' }", false },
558
559    { std::string(), "{ 'KEY_HASH_1': 'http://bar/path' }", false },
560    { std::string(), "{ 'KEY_HASH_1': 'http://co.uk/path' }", false },
561    { std::string(), "{ 'KEY_HASH_1': 'http://appspot.com/path' }", false },
562    { std::string(), "{ 'KEY_HASH_1': 'http://127.0.0.1/path' }", false },
563    { std::string(), "{ 'KEY_HASH_1': 'file:///C:/bar.html' }", false },
564
565    { std::string(), "{ 'KEY_HASH_1': 1 }", false },
566    { std::string(), "{ 'KEY_HASH_1': 1.2 }", false },
567    { std::string(), "{ 'KEY_HASH_1': true }", false },
568    { std::string(), "{ 'KEY_HASH_1': [1] }", false },
569    { std::string(), "{ 'KEY_HASH_1': {'a': 'b'} }", false },
570  };
571
572  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
573    SCOPED_TRACE(testing::Message() << "Iteration " << i);
574    INIT_INTERPRETER(
575        OP_NAVIGATE(KEY_HASH_1) +
576        OP_STORE_NODE_REGISTERABLE_DOMAIN_HASH(VAR_HASH_1) +
577        OP_STORE_BOOL(VAR_HASH_2, VALUE_TRUE),
578        cases[i].json);
579    EXPECT_EQ(JtlInterpreter::OK, interpreter.result());
580    if (cases[i].expected_success) {
581      base::ExpectDictStringValue(
582          cases[i].expected_value, *interpreter.working_memory(), VAR_HASH_1);
583      base::ExpectDictBooleanValue(
584          true, *interpreter.working_memory(), VAR_HASH_2);
585    } else {
586      EXPECT_FALSE(interpreter.working_memory()->HasKey(VAR_HASH_1));
587      EXPECT_FALSE(interpreter.working_memory()->HasKey(VAR_HASH_2));
588    }
589  }
590}
591
592TEST(JtlInterpreter, Stop) {
593  INIT_INTERPRETER(
594      OP_NAVIGATE(KEY_HASH_1) +
595      OP_NAVIGATE(KEY_HASH_2) +
596      OP_STORE_BOOL(VAR_HASH_1, VALUE_TRUE) +
597      OP_STOP_EXECUTING_SENTENCE +
598      OP_STORE_BOOL(VAR_HASH_2, VALUE_TRUE),
599      "{ 'KEY_HASH_1': { 'KEY_HASH_2': 'VALUE_HASH_1' } }");
600  EXPECT_EQ(JtlInterpreter::OK, interpreter.result());
601  base::ExpectDictBooleanValue(true, *interpreter.working_memory(), VAR_HASH_1);
602  EXPECT_FALSE(interpreter.working_memory()->HasKey(VAR_HASH_2));
603}
604
605TEST(JtlInterpreter, EndOfSentence) {
606  INIT_INTERPRETER(
607      OP_NAVIGATE(KEY_HASH_1) +
608      OP_NAVIGATE(KEY_HASH_2) +
609      OP_STORE_BOOL(VAR_HASH_1, VALUE_TRUE) +
610      OP_END_OF_SENTENCE +
611      OP_STORE_BOOL(VAR_HASH_2, VALUE_TRUE),
612      "{ 'KEY_HASH_1': { 'KEY_HASH_2': 'VALUE_HASH_1' } }");
613  EXPECT_EQ(JtlInterpreter::OK, interpreter.result());
614  base::ExpectDictBooleanValue(true, *interpreter.working_memory(), VAR_HASH_1);
615  base::ExpectDictBooleanValue(true, *interpreter.working_memory(), VAR_HASH_2);
616}
617
618TEST(JtlInterpreter, InvalidBack) {
619  INIT_INTERPRETER(
620      OP_NAVIGATE(KEY_HASH_1) +
621      OP_NAVIGATE_BACK +
622      OP_NAVIGATE_BACK,
623      "{ 'KEY_HASH_1': { 'KEY_HASH_2': 'VALUE_HASH_1' } }");
624  EXPECT_EQ(JtlInterpreter::RUNTIME_ERROR, interpreter.result());
625}
626
627TEST(JtlInterpreter, IncorrectPrograms) {
628  std::string missing_hash;
629  std::string missing_bool;
630  std::string invalid_hash("123");
631  std::string invalid_bool("\x02", 1);
632  std::string invalid_operation("\x99", 1);
633  std::string programs[] = {
634    OP_NAVIGATE(missing_hash),
635    OP_NAVIGATE(invalid_hash),
636    OP_STORE_BOOL(VAR_HASH_1, invalid_bool),
637    OP_STORE_BOOL(missing_hash, VALUE_TRUE),
638    OP_STORE_BOOL(invalid_hash, VALUE_TRUE),
639    OP_COMPARE_STORED_BOOL(invalid_hash, VALUE_TRUE, VALUE_TRUE),
640    OP_COMPARE_STORED_BOOL(VAR_HASH_1, invalid_bool, VALUE_TRUE),
641    OP_COMPARE_STORED_BOOL(VAR_HASH_1, VALUE_TRUE, invalid_bool),
642    OP_COMPARE_STORED_BOOL(VAR_HASH_1, VALUE_TRUE, missing_bool),
643    OP_STORE_NODE_BOOL(missing_hash),
644    OP_STORE_NODE_BOOL(invalid_hash),
645    OP_STORE_NODE_HASH(missing_hash),
646    OP_STORE_NODE_HASH(invalid_hash),
647    OP_COMPARE_NODE_BOOL(missing_bool),
648    OP_COMPARE_NODE_BOOL(invalid_bool),
649    OP_COMPARE_NODE_HASH(missing_hash),
650    OP_COMPARE_NODE_HASH(invalid_hash),
651    OP_COMPARE_NODE_TO_STORED_BOOL(missing_hash),
652    OP_COMPARE_NODE_TO_STORED_BOOL(invalid_hash),
653    OP_COMPARE_NODE_TO_STORED_HASH(missing_hash),
654    OP_COMPARE_NODE_TO_STORED_HASH(invalid_hash),
655    invalid_operation,
656  };
657  for (size_t i = 0; i < arraysize(programs); ++i) {
658    SCOPED_TRACE(testing::Message() << "Iteration " << i);
659    INIT_INTERPRETER(programs[i],
660                     "{ 'KEY_HASH_1': { 'KEY_HASH_2': 'VALUE_HASH_1' } }");
661    EXPECT_EQ(JtlInterpreter::PARSE_ERROR, interpreter.result());
662  }
663}
664
665TEST(JtlInterpreter, GetOutput) {
666  INIT_INTERPRETER(
667      OP_STORE_BOOL(GetHash("output1"), VALUE_TRUE) +
668      OP_STORE_HASH(GetHash("output2"), VALUE_HASH_1),
669      "{}");
670  bool output1 = false;
671  std::string output2;
672  EXPECT_TRUE(interpreter.GetOutputBoolean("output1", &output1));
673  EXPECT_EQ(true, output1);
674  EXPECT_TRUE(interpreter.GetOutputString("output2", &output2));
675  EXPECT_EQ(VALUE_HASH_1, output2);
676  EXPECT_FALSE(interpreter.GetOutputBoolean("outputxx", &output1));
677  EXPECT_FALSE(interpreter.GetOutputString("outputxx", &output2));
678}
679
680TEST(JtlInterpreter, CalculateProgramChecksum) {
681  const char kTestSeed[] = "Irrelevant seed value.";
682  const char kTestProgram[] = "The quick brown fox jumps over the lazy dog.";
683  // This program is invalid, but we are not actually executing it.
684  base::DictionaryValue input;
685  JtlInterpreter interpreter(kTestSeed, kTestProgram, &input);
686  EXPECT_EQ(0xef537f, interpreter.CalculateProgramChecksum());
687}
688
689}  // namespace
690