1// Copyright (c) 2012 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/strings/string_number_conversions.h"
6#include "base/strings/utf_string_conversions.h"
7#include "content/public/browser/browser_context.h"
8#include "content/public/browser/download_manager.h"
9#include "content/public/browser/notification_service.h"
10#include "content/public/browser/notification_types.h"
11#include "content/public/browser/web_contents.h"
12#include "content/public/test/browser_test_utils.h"
13#include "content/public/test/content_browser_test.h"
14#include "content/public/test/content_browser_test_utils.h"
15#include "content/public/test/test_utils.h"
16#include "content/shell/browser/shell.h"
17#include "testing/gtest/include/gtest/gtest.h"
18
19namespace content {
20
21class DatabaseTest : public ContentBrowserTest {
22 public:
23  DatabaseTest() {}
24
25  void RunScriptAndCheckResult(Shell* shell,
26                               const std::string& script,
27                               const std::string& result) {
28    std::string data;
29    ASSERT_TRUE(ExecuteScriptAndExtractString(
30        shell->web_contents(),
31        script,
32        &data));
33    ASSERT_EQ(data, result);
34  }
35
36  void Navigate(Shell* shell) {
37    NavigateToURL(shell, GetTestUrl("", "simple_database.html"));
38  }
39
40  void CreateTable(Shell* shell) {
41    RunScriptAndCheckResult(shell, "createTable()", "done");
42  }
43
44  void InsertRecord(Shell* shell, const std::string& data) {
45    RunScriptAndCheckResult(shell, "insertRecord('" + data + "')", "done");
46  }
47
48  void UpdateRecord(Shell* shell, int index, const std::string& data) {
49    RunScriptAndCheckResult(
50        shell,
51        "updateRecord(" + base::IntToString(index) + ", '" + data + "')",
52        "done");
53  }
54
55  void DeleteRecord(Shell* shell, int index) {
56    RunScriptAndCheckResult(
57        shell, "deleteRecord(" + base::IntToString(index) + ")", "done");
58  }
59
60  void CompareRecords(Shell* shell, const std::string& expected) {
61    RunScriptAndCheckResult(shell, "getRecords()", expected);
62  }
63
64  bool HasTable(Shell* shell) {
65    std::string data;
66    CHECK(ExecuteScriptAndExtractString(
67        shell->web_contents(),
68        "getRecords()",
69        &data));
70    return data != "getRecords error: [object SQLError]";
71  }
72};
73
74// Insert records to the database.
75IN_PROC_BROWSER_TEST_F(DatabaseTest, InsertRecord) {
76  Navigate(shell());
77  CreateTable(shell());
78  InsertRecord(shell(), "text");
79  CompareRecords(shell(), "text");
80  InsertRecord(shell(), "text2");
81  CompareRecords(shell(), "text, text2");
82}
83
84// Update records in the database.
85IN_PROC_BROWSER_TEST_F(DatabaseTest, UpdateRecord) {
86  Navigate(shell());
87  CreateTable(shell());
88  InsertRecord(shell(), "text");
89  UpdateRecord(shell(), 0, "0");
90  CompareRecords(shell(), "0");
91
92  InsertRecord(shell(), "1");
93  InsertRecord(shell(), "2");
94  UpdateRecord(shell(), 1, "1000");
95  CompareRecords(shell(), "0, 1000, 2");
96}
97
98// Delete records in the database.
99IN_PROC_BROWSER_TEST_F(DatabaseTest, DeleteRecord) {
100  Navigate(shell());
101  CreateTable(shell());
102  InsertRecord(shell(), "text");
103  DeleteRecord(shell(), 0);
104  CompareRecords(shell(), std::string());
105
106  InsertRecord(shell(), "0");
107  InsertRecord(shell(), "1");
108  InsertRecord(shell(), "2");
109  DeleteRecord(shell(), 1);
110  CompareRecords(shell(), "0, 2");
111}
112
113// Attempts to delete a nonexistent row in the table.
114IN_PROC_BROWSER_TEST_F(DatabaseTest, DeleteNonexistentRow) {
115  Navigate(shell());
116  CreateTable(shell());
117  InsertRecord(shell(), "text");
118
119  RunScriptAndCheckResult(
120      shell(), "deleteRecord(1)", "could not find row with index: 1");
121
122  CompareRecords(shell(), "text");
123}
124
125// Insert, update, and delete records in the database.
126IN_PROC_BROWSER_TEST_F(DatabaseTest, DatabaseOperations) {
127  Navigate(shell());
128  CreateTable(shell());
129
130  std::string expected;
131  for (int i = 0; i < 10; ++i) {
132    std::string item = base::IntToString(i);
133    InsertRecord(shell(), item);
134    if (!expected.empty())
135      expected += ", ";
136    expected += item;
137  }
138  CompareRecords(shell(), expected);
139
140  expected.clear();
141  for (int i = 0; i < 10; ++i) {
142    std::string item = base::IntToString(i * i);
143    UpdateRecord(shell(), i, item);
144    if (!expected.empty())
145      expected += ", ";
146    expected += item;
147  }
148  CompareRecords(shell(), expected);
149
150  for (int i = 0; i < 10; ++i)
151    DeleteRecord(shell(), 0);
152
153  CompareRecords(shell(), std::string());
154
155  RunScriptAndCheckResult(
156      shell(), "deleteRecord(1)", "could not find row with index: 1");
157
158  CompareRecords(shell(), std::string());
159}
160
161// Create records in the database and verify they persist after reload.
162IN_PROC_BROWSER_TEST_F(DatabaseTest, ReloadPage) {
163  Navigate(shell());
164  CreateTable(shell());
165  InsertRecord(shell(), "text");
166
167  WindowedNotificationObserver load_stop_observer(
168      NOTIFICATION_LOAD_STOP,
169      NotificationService::AllSources());
170  shell()->Reload();
171  load_stop_observer.Wait();
172
173  CompareRecords(shell(), "text");
174}
175
176// Attempt to read a database created in a regular browser from an off the
177// record browser.
178IN_PROC_BROWSER_TEST_F(DatabaseTest, OffTheRecordCannotReadRegularDatabase) {
179  Navigate(shell());
180  CreateTable(shell());
181  InsertRecord(shell(), "text");
182
183  Shell* otr = CreateOffTheRecordBrowser();
184  Navigate(otr);
185  ASSERT_FALSE(HasTable(otr));
186
187  CreateTable(otr);
188  CompareRecords(otr, std::string());
189}
190
191// Attempt to read a database created in an off the record browser from a
192// regular browser.
193IN_PROC_BROWSER_TEST_F(DatabaseTest, RegularCannotReadOffTheRecordDatabase) {
194  Shell* otr = CreateOffTheRecordBrowser();
195  Navigate(otr);
196  CreateTable(otr);
197  InsertRecord(otr, "text");
198
199  Navigate(shell());
200  ASSERT_FALSE(HasTable(shell()));
201  CreateTable(shell());
202  CompareRecords(shell(), std::string());
203}
204
205// Verify DB changes within first window are present in the second window.
206IN_PROC_BROWSER_TEST_F(DatabaseTest, ModificationPersistInSecondTab) {
207  Navigate(shell());
208  CreateTable(shell());
209  InsertRecord(shell(), "text");
210
211  Shell* shell2 = CreateBrowser();
212  Navigate(shell2);
213  UpdateRecord(shell2, 0, "0");
214
215  CompareRecords(shell(), "0");
216  CompareRecords(shell2, "0");
217}
218
219// Verify database modifications persist after restarting browser.
220IN_PROC_BROWSER_TEST_F(DatabaseTest, PRE_DatabasePersistsAfterRelaunch) {
221  Navigate(shell());
222  CreateTable(shell());
223  InsertRecord(shell(), "text");
224}
225
226IN_PROC_BROWSER_TEST_F(DatabaseTest, DatabasePersistsAfterRelaunch) {
227  Navigate(shell());
228  CompareRecords(shell(), "text");
229}
230
231// Verify OTR database is removed after OTR window closes.
232IN_PROC_BROWSER_TEST_F(DatabaseTest, PRE_OffTheRecordDatabaseNotPersistent) {
233  Shell* otr = CreateOffTheRecordBrowser();
234  Navigate(otr);
235  CreateTable(otr);
236  InsertRecord(otr, "text");
237}
238
239IN_PROC_BROWSER_TEST_F(DatabaseTest, OffTheRecordDatabaseNotPersistent) {
240  Shell* otr = CreateOffTheRecordBrowser();
241  Navigate(otr);
242  ASSERT_FALSE(HasTable(otr));
243}
244
245// Verify database modifications persist after crashing window.
246IN_PROC_BROWSER_TEST_F(DatabaseTest, ModificationsPersistAfterRendererCrash) {
247  Navigate(shell());
248  CreateTable(shell());
249  InsertRecord(shell(), "1");
250
251  CrashTab(shell()->web_contents());
252  Navigate(shell());
253  CompareRecords(shell(), "1");
254}
255
256// Test to check if database modifications are persistent across windows in
257// off the record window.
258IN_PROC_BROWSER_TEST_F(DatabaseTest, OffTheRecordDBPersistentAcrossWindows) {
259  Shell* otr1 = CreateOffTheRecordBrowser();
260  Navigate(otr1);
261  CreateTable(otr1);
262  InsertRecord(otr1, "text");
263
264  Shell* otr2 = CreateOffTheRecordBrowser();
265  Navigate(otr2);
266  CompareRecords(otr2, "text");
267}
268
269}  // namespace content
270