1// Copyright (c) 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 "app/sql/connection.h"
6#include "app/sql/statement.h"
7#include "app/sql/transaction.h"
8#include "base/file_util.h"
9#include "base/memory/scoped_temp_dir.h"
10#include "testing/gtest/include/gtest/gtest.h"
11#include "third_party/sqlite/sqlite3.h"
12
13class SQLTransactionTest : public testing::Test {
14 public:
15  SQLTransactionTest() {}
16
17  void SetUp() {
18    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
19    ASSERT_TRUE(db_.Open(
20        temp_dir_.path().AppendASCII("SQLTransactionTest.db")));
21
22    ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)"));
23  }
24
25  void TearDown() {
26    db_.Close();
27  }
28
29  sql::Connection& db() { return db_; }
30
31  // Returns the number of rows in table "foo".
32  int CountFoo() {
33    sql::Statement count(db().GetUniqueStatement("SELECT count(*) FROM foo"));
34    count.Step();
35    return count.ColumnInt(0);
36  }
37
38 private:
39  ScopedTempDir temp_dir_;
40  sql::Connection db_;
41};
42
43TEST_F(SQLTransactionTest, Commit) {
44  {
45    sql::Transaction t(&db());
46    EXPECT_FALSE(t.is_open());
47    EXPECT_TRUE(t.Begin());
48    EXPECT_TRUE(t.is_open());
49
50    EXPECT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (1, 2)"));
51
52    t.Commit();
53    EXPECT_FALSE(t.is_open());
54  }
55
56  EXPECT_EQ(1, CountFoo());
57}
58
59TEST_F(SQLTransactionTest, Rollback) {
60  // Test some basic initialization, and that rollback runs when you exit the
61  // scope.
62  {
63    sql::Transaction t(&db());
64    EXPECT_FALSE(t.is_open());
65    EXPECT_TRUE(t.Begin());
66    EXPECT_TRUE(t.is_open());
67
68    EXPECT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (1, 2)"));
69  }
70
71  // Nothing should have been committed since it was implicitly rolled back.
72  EXPECT_EQ(0, CountFoo());
73
74  // Test explicit rollback.
75  sql::Transaction t2(&db());
76  EXPECT_FALSE(t2.is_open());
77  EXPECT_TRUE(t2.Begin());
78
79  EXPECT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (1, 2)"));
80  t2.Rollback();
81  EXPECT_FALSE(t2.is_open());
82
83  // Nothing should have been committed since it was explicitly rolled back.
84  EXPECT_EQ(0, CountFoo());
85}
86
87// Rolling back any part of a transaction should roll back all of them.
88TEST_F(SQLTransactionTest, NestedRollback) {
89  EXPECT_EQ(0, db().transaction_nesting());
90
91  // Outermost transaction.
92  {
93    sql::Transaction outer(&db());
94    EXPECT_TRUE(outer.Begin());
95    EXPECT_EQ(1, db().transaction_nesting());
96
97    // The first inner one gets committed.
98    {
99      sql::Transaction inner1(&db());
100      EXPECT_TRUE(inner1.Begin());
101      EXPECT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (1, 2)"));
102      EXPECT_EQ(2, db().transaction_nesting());
103
104      inner1.Commit();
105      EXPECT_EQ(1, db().transaction_nesting());
106    }
107
108    // One row should have gotten inserted.
109    EXPECT_EQ(1, CountFoo());
110
111    // The second inner one gets rolled back.
112    {
113      sql::Transaction inner2(&db());
114      EXPECT_TRUE(inner2.Begin());
115      EXPECT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (1, 2)"));
116      EXPECT_EQ(2, db().transaction_nesting());
117
118      inner2.Rollback();
119      EXPECT_EQ(1, db().transaction_nesting());
120    }
121
122    // A third inner one will fail in Begin since one has already been rolled
123    // back.
124    EXPECT_EQ(1, db().transaction_nesting());
125    {
126      sql::Transaction inner3(&db());
127      EXPECT_FALSE(inner3.Begin());
128      EXPECT_EQ(1, db().transaction_nesting());
129    }
130  }
131  EXPECT_EQ(0, db().transaction_nesting());
132  EXPECT_EQ(0, CountFoo());
133}
134