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 "base/files/scoped_temp_dir.h"
6#include "base/strings/stringprintf.h"
7#include "chrome/browser/extensions/activity_log/database_string_table.h"
8#include "sql/connection.h"
9#include "sql/statement.h"
10#include "sql/transaction.h"
11#include "testing/gtest/include/gtest/gtest.h"
12
13namespace extensions {
14
15class DatabaseStringTableTest : public testing::Test {
16 protected:
17  virtual void SetUp() OVERRIDE {
18    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
19    base::FilePath db_file = temp_dir_.path().AppendASCII("StringTable.db");
20
21    ASSERT_TRUE(db_.Open(db_file));
22  }
23
24  virtual void TearDown() OVERRIDE {
25    db_.Close();
26  }
27
28  base::ScopedTempDir temp_dir_;
29  sql::Connection db_;
30};
31
32// Check that initializing the database works.
33TEST_F(DatabaseStringTableTest, Init) {
34  DatabaseStringTable table("test");
35  table.Initialize(&db_);
36  ASSERT_TRUE(db_.DoesTableExist("test"));
37  ASSERT_TRUE(db_.DoesIndexExist("test_index"));
38}
39
40// Insert a new mapping into the table, then verify the table contents.
41TEST_F(DatabaseStringTableTest, Insert) {
42  DatabaseStringTable table("test");
43  table.Initialize(&db_);
44  int64 id;
45  ASSERT_TRUE(table.StringToInt(&db_, "abc", &id));
46
47  sql::Statement query(
48      db_.GetUniqueStatement("SELECT id FROM test WHERE value = 'abc'"));
49  ASSERT_TRUE(query.Step());
50  int64 raw_id = query.ColumnInt64(0);
51  ASSERT_EQ(id, raw_id);
52}
53
54// Check that different strings are mapped to different values, and the same
55// string is mapped to the same value repeatably.
56TEST_F(DatabaseStringTableTest, InsertMultiple) {
57  DatabaseStringTable table("test");
58  table.Initialize(&db_);
59
60  int64 id1;
61  int64 id2;
62  ASSERT_TRUE(table.StringToInt(&db_, "string1", &id1));
63  ASSERT_TRUE(table.StringToInt(&db_, "string2", &id2));
64  ASSERT_NE(id1, id2);
65
66  int64 id1a;
67  ASSERT_TRUE(table.StringToInt(&db_, "string1", &id1a));
68  ASSERT_EQ(id1, id1a);
69}
70
71// Check that values can be read back from the database even after the
72// in-memory cache is cleared.
73TEST_F(DatabaseStringTableTest, CacheCleared) {
74  DatabaseStringTable table("test");
75  table.Initialize(&db_);
76
77  int64 id1;
78  ASSERT_TRUE(table.StringToInt(&db_, "string1", &id1));
79
80  table.ClearCache();
81
82  int64 id2;
83  ASSERT_TRUE(table.StringToInt(&db_, "string1", &id2));
84  ASSERT_EQ(id1, id2);
85}
86
87// Check that direct database modifications are picked up after the cache is
88// cleared.
89TEST_F(DatabaseStringTableTest, DatabaseModified) {
90  DatabaseStringTable table("test");
91  table.Initialize(&db_);
92
93  int64 id1;
94  ASSERT_TRUE(table.StringToInt(&db_, "modified", &id1));
95
96  ASSERT_TRUE(
97      db_.Execute("UPDATE test SET id = id + 1 WHERE value = 'modified'"));
98
99  int64 id2;
100  ASSERT_TRUE(table.StringToInt(&db_, "modified", &id2));
101  ASSERT_EQ(id1, id2);
102
103  table.ClearCache();
104
105  int64 id3;
106  ASSERT_TRUE(table.StringToInt(&db_, "modified", &id3));
107  ASSERT_EQ(id1 + 1, id3);
108}
109
110// Check that looking up an unknown id returns an error.
111TEST_F(DatabaseStringTableTest, BadLookup) {
112  DatabaseStringTable table("test");
113  table.Initialize(&db_);
114  std::string value;
115  ASSERT_FALSE(table.IntToString(&db_, 1, &value));
116}
117
118// Check looking up an inserted value, both cached and not cached.
119TEST_F(DatabaseStringTableTest, Lookup) {
120  DatabaseStringTable table("test");
121  table.Initialize(&db_);
122  int64 id;
123  ASSERT_TRUE(table.StringToInt(&db_, "abc", &id));
124
125  std::string value;
126  ASSERT_TRUE(table.IntToString(&db_, id, &value));
127  ASSERT_EQ("abc", value);
128
129  table.ClearCache();
130  value = "";
131  ASSERT_TRUE(table.IntToString(&db_, id, &value));
132  ASSERT_EQ("abc", value);
133}
134
135// Check that the in-memory cache for the string table does not become too
136// large, even if many items are inserted.
137TEST_F(DatabaseStringTableTest, Prune) {
138  DatabaseStringTable table("size_test");
139  table.Initialize(&db_);
140
141  // Wrap the lookups in a transaction to improve performance.
142  sql::Transaction transaction(&db_);
143
144  transaction.Begin();
145  for (int i = 0; i < 2000; i++) {
146    int64 id;
147    ASSERT_TRUE(table.StringToInt(&db_, base::StringPrintf("value-%d", i),
148                                  &id));
149  }
150  transaction.Commit();
151
152  // The maximum size below should correspond to kMaximumCacheSize in
153  // database_string_table.cc, with a small amount of additional slop (an entry
154  // might be inserted after doing the pruning).
155  ASSERT_LE(table.id_to_value_.size(), 1005U);
156  ASSERT_LE(table.value_to_id_.size(), 1005U);
157}
158
159}  // namespace extensions
160