aboutsummaryrefslogtreecommitdiffstats
path: root/tests/unit/unittest/sqlitesessions-test.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/unit/unittest/sqlitesessions-test.cpp')
-rw-r--r--tests/unit/unittest/sqlitesessions-test.cpp443
1 files changed, 443 insertions, 0 deletions
diff --git a/tests/unit/unittest/sqlitesessions-test.cpp b/tests/unit/unittest/sqlitesessions-test.cpp
new file mode 100644
index 0000000000..f10dde1bc8
--- /dev/null
+++ b/tests/unit/unittest/sqlitesessions-test.cpp
@@ -0,0 +1,443 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "googletest.h"
+
+#include <sqlitedatabase.h>
+#include <sqlitereadstatement.h>
+#include <sqlitesessionchangeset.h>
+#include <sqlitesessions.h>
+#include <sqlitetransaction.h>
+#include <sqlitewritestatement.h>
+
+#include <ostream>
+
+namespace {
+
+using Sqlite::SessionChangeSet;
+using Sqlite::SessionChangeSets;
+
+class DatabaseExecute
+{
+public:
+ DatabaseExecute(Utils::SmallStringView sqlStatement, Sqlite::Database &database)
+ {
+ database.execute(sqlStatement);
+ }
+};
+
+class Data
+{
+public:
+ Data(Sqlite::ValueView name, Sqlite::ValueView number, Sqlite::ValueView value)
+ : name(name)
+ , number(number)
+ , value(value)
+ {}
+
+ Sqlite::Value name;
+ Sqlite::Value number;
+ Sqlite::Value value;
+};
+
+std::ostream &operator<<(std::ostream &out, const Data &data)
+{
+ return out << "(" << data.name << ", " << data.number << " " << data.value << ")";
+}
+
+MATCHER_P3(HasData,
+ name,
+ number,
+ value,
+ std::string(negation ? "hasn't " : "has ") + PrintToString(name) + ", "
+ + PrintToString(number) + ", " + PrintToString(value))
+{
+ const Data &data = arg;
+
+ return data.name == name && data.number == number && data.value == value;
+}
+
+class Tag
+{
+public:
+ Tag(Sqlite::ValueView name, Sqlite::ValueView tag)
+ : name(name)
+ , tag(tag)
+ {}
+
+ Sqlite::Value name;
+ Sqlite::Value tag;
+};
+
+std::ostream &operator<<(std::ostream &out, const Tag &tag)
+{
+ return out << "(" << tag.name << ", " << tag.tag << ")";
+}
+
+MATCHER_P2(HasTag,
+ name,
+ tag,
+ std::string(negation ? "hasn't " : "has ") + PrintToString(name) + ", " + PrintToString(tag))
+{
+ const Tag &t = arg;
+
+ return t.name == name && t.tag == tag;
+}
+
+class Sessions : public testing::Test
+{
+protected:
+ Sessions() { sessions.setAttachedTables({"data", "tags"}); }
+
+ std::vector<Data> fetchData() { return selectData.values<Data, 3>(8); }
+ std::vector<Tag> fetchTags() { return selectTags.values<Tag, 2>(8); }
+ SessionChangeSets fetchChangeSets() { return selectChangeSets.values<SessionChangeSet>(8); }
+
+protected:
+ Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory};
+ DatabaseExecute createTable{"CREATE TABLE data(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT "
+ "UNIQUE, number NUMERIC, value NUMERIC)",
+ database};
+ DatabaseExecute createTable2{"CREATE TABLE tags(id INTEGER PRIMARY KEY AUTOINCREMENT, dataId "
+ "INTEGER NOT NULL REFERENCES data ON DELETE CASCADE DEFERRABLE "
+ "INITIALLY DEFERRED, tag NUMERIC)",
+ database};
+ Sqlite::Sessions sessions{database, "main", "testsessions"};
+ Sqlite::WriteStatement insertData{"INSERT INTO data(name, number, value) VALUES (?1, ?2, ?3) "
+ "ON CONFLICT (name) DO UPDATE SET (number, value) = (?2, ?3)",
+ database};
+ Sqlite::WriteStatement updateNumber{"UPDATE data SET number = ?002 WHERE name=?001", database};
+ Sqlite::WriteStatement updateValue{"UPDATE data SET value = ?002 WHERE name=?001", database};
+ Sqlite::WriteStatement deleteData{"DELETE FROM data WHERE name=?", database};
+ Sqlite::WriteStatement deleteTag{
+ "DELETE FROM tags WHERE dataId=(SELECT id FROM data WHERE name=?)", database};
+ Sqlite::WriteStatement insertTag{
+ "INSERT INTO tags(dataId, tag) VALUES ((SELECT id FROM data WHERE name=?1), ?2) ", database};
+ Sqlite::ReadStatement selectData{"SELECT name, number, value FROM data", database};
+ Sqlite::ReadStatement selectTags{"SELECT name, tag FROM tags JOIN data ON data.id=tags.dataId",
+ database};
+ Sqlite::ReadStatement selectChangeSets{"SELECT changeset FROM testsessions", database};
+};
+
+TEST_F(Sessions, DontThrowForCommittingWithoutSessionStart)
+{
+ ASSERT_NO_THROW(sessions.commit());
+}
+
+TEST_F(Sessions, CreateEmptySession)
+{
+ sessions.create();
+ sessions.commit();
+
+ ASSERT_THAT(fetchChangeSets(), IsEmpty());
+}
+
+TEST_F(Sessions, CreateSessionWithInsert)
+{
+ sessions.create();
+ insertData.write("foo", 22, 3.14);
+ sessions.commit();
+
+ ASSERT_THAT(fetchChangeSets(), SizeIs(1));
+}
+
+TEST_F(Sessions, CreateSessionWithUpdate)
+{
+ insertData.write("foo", 22, 3.14);
+
+ sessions.create();
+ updateNumber.write("foo", "bar");
+ sessions.commit();
+
+ ASSERT_THAT(fetchChangeSets(), SizeIs(1));
+}
+
+TEST_F(Sessions, CreateSessionWithDelete)
+{
+ insertData.write("foo", 22, 3.14);
+
+ sessions.create();
+ deleteData.write("foo");
+ sessions.commit();
+
+ ASSERT_THAT(fetchChangeSets(), SizeIs(1));
+}
+
+TEST_F(Sessions, CreateSessionWithInsertAndUpdate)
+{
+ sessions.create();
+ insertData.write("foo", 22, 3.14);
+ sessions.commit();
+
+ sessions.create();
+ updateNumber.write("foo", "bar");
+ sessions.commit();
+
+ ASSERT_THAT(fetchChangeSets(), SizeIs(2));
+}
+
+TEST_F(Sessions, CreateSession)
+{
+ sessions.create();
+ insertData.write("foo", 22, 3.14);
+
+ sessions.commit();
+
+ ASSERT_THAT(fetchChangeSets(), SizeIs(1));
+}
+
+TEST_F(Sessions, RevertSession)
+{
+ sessions.create();
+ insertData.write("foo", 22, 3.14);
+ sessions.commit();
+
+ sessions.revert();
+
+ ASSERT_THAT(fetchData(), IsEmpty());
+}
+
+TEST_F(Sessions, RevertSessionToBase)
+{
+ insertData.write("bar", "foo", 99);
+ sessions.create();
+ insertData.write("foo", 22, 3.14);
+ sessions.commit();
+
+ sessions.revert();
+
+ ASSERT_THAT(fetchData(), ElementsAre(HasData("bar", "foo", 99)));
+}
+
+TEST_F(Sessions, RevertMultipleSession)
+{
+ sessions.create();
+ insertData.write("foo", 22, 3.14);
+ sessions.commit();
+ sessions.create();
+ updateNumber.write("foo", "bar");
+ sessions.commit();
+
+ sessions.revert();
+
+ ASSERT_THAT(fetchData(), IsEmpty());
+}
+
+TEST_F(Sessions, ApplySession)
+{
+ sessions.create();
+ insertData.write("foo", 22, 3.14);
+ sessions.commit();
+
+ sessions.apply();
+
+ ASSERT_THAT(fetchData(), ElementsAre(HasData("foo", 22, 3.14)));
+}
+
+TEST_F(Sessions, ApplySessionAfterAddingNewEntries)
+{
+ sessions.create();
+ insertData.write("foo", 22, 3.14);
+ sessions.commit();
+ insertData.write("bar", "foo", 99);
+
+ sessions.apply();
+
+ ASSERT_THAT(fetchData(),
+ UnorderedElementsAre(HasData("foo", 22, 3.14), HasData("bar", "foo", 99)));
+}
+
+TEST_F(Sessions, ApplyOverridesEntriesWithUniqueConstraint)
+{
+ sessions.create();
+ insertData.write("foo", 22, 3.14);
+ sessions.commit();
+ insertData.write("foo", "bar", 3.14);
+
+ sessions.apply();
+
+ ASSERT_THAT(fetchData(), ElementsAre(HasData("foo", 22, 3.14)));
+}
+
+TEST_F(Sessions, ApplyDoesNotOverrideDeletedEntries)
+{
+ insertData.write("foo", "bar", 3.14);
+ sessions.create();
+ insertData.write("foo", 22, 3.14);
+ sessions.commit();
+ deleteData.write("foo");
+
+ sessions.apply();
+
+ ASSERT_THAT(fetchData(), IsEmpty());
+}
+
+TEST_F(Sessions, ApplyDoesOnlyOverwriteUpdatedValues)
+{
+ insertData.write("foo", "bar", 3.14);
+ sessions.create();
+ updateValue.write("foo", 1234);
+ sessions.commit();
+ insertData.write("foo", "poo", 891);
+
+ sessions.apply();
+
+ ASSERT_THAT(fetchData(), ElementsAre(HasData("foo", "poo", 1234)));
+}
+
+TEST_F(Sessions, ApplyDoesDoesNotOverrideForeignKeyIfReferenceIsDeleted)
+{
+ insertData.write("foo2", "bar", 3.14);
+ insertData.write("foo", "bar", 3.14);
+ sessions.create();
+ insertTag.write("foo2", 4321);
+ insertTag.write("foo", 1234);
+ sessions.commit();
+ deleteData.write("foo");
+
+ sessions.apply();
+
+ ASSERT_THAT(fetchTags(), ElementsAre(HasTag("foo2", 4321)));
+}
+
+TEST_F(Sessions, ApplyDoesDoesNotOverrideIfConstraintsIsApplied)
+{
+ insertData.write("foo", "bar", 3.14);
+ sessions.create();
+ deleteData.write("foo");
+ sessions.commit();
+ sessions.revert();
+ insertTag.write("foo", 1234);
+
+ sessions.apply();
+
+ ASSERT_THAT(fetchTags(), IsEmpty());
+}
+
+TEST_F(Sessions, ApplyDoesDoesNotOverrideForeignKeyIfReferenceIsDeletedDeferred)
+{
+ Sqlite::DeferredTransaction transaction{database};
+ insertData.write("foo2", "bar", 3.14);
+ insertData.write("foo", "bar", 3.14);
+ sessions.create();
+ insertTag.write("foo2", 4321);
+ insertTag.write("foo", 1234);
+ sessions.commit();
+ deleteData.write("foo");
+
+ sessions.apply();
+
+ transaction.commit();
+ ASSERT_THAT(fetchTags(), ElementsAre(HasTag("foo2", 4321)));
+}
+
+TEST_F(Sessions, EndSessionOnRollback)
+{
+ insertData.write("foo", "bar", 3.14);
+ sessions.create();
+ updateValue.write("foo", 99);
+ sessions.rollback();
+ sessions.commit();
+ sessions.create();
+ updateNumber.write("foo", 333);
+ sessions.commit();
+ updateValue.write("foo", 666);
+
+ sessions.apply();
+
+ ASSERT_THAT(fetchData(), ElementsAre(HasData("foo", 333, 666)));
+}
+
+TEST_F(Sessions, EndSessionOnCommit)
+{
+ insertData.write("foo", "bar", 3.14);
+ sessions.create();
+ updateValue.write("foo", 99);
+ sessions.commit();
+ updateValue.write("foo", 666);
+ sessions.commit();
+
+ sessions.apply();
+
+ ASSERT_THAT(fetchData(), ElementsAre(HasData("foo", "bar", 99)));
+}
+
+TEST_F(Sessions, DeleteSessions)
+{
+ insertData.write("foo", "bar", 3.14);
+ sessions.create();
+ updateValue.write("foo", 99);
+ sessions.commit();
+ sessions.revert();
+
+ sessions.deleteAll();
+
+ sessions.apply();
+ ASSERT_THAT(fetchData(), ElementsAre(HasData("foo", "bar", 3.14)));
+}
+
+TEST_F(Sessions, DeleteAllSessions)
+{
+ insertData.write("foo", "bar", 3.14);
+ sessions.create();
+ updateValue.write("foo", 99);
+ sessions.commit();
+ sessions.revert();
+
+ sessions.deleteAll();
+
+ sessions.apply();
+ ASSERT_THAT(fetchData(), ElementsAre(HasData("foo", "bar", 3.14)));
+}
+
+TEST_F(Sessions, ApplyAndUpdateSessions)
+{
+ insertData.write("foo", "bar", 3.14);
+ sessions.create();
+ updateValue.write("foo", 99);
+ sessions.commit();
+ updateValue.write("foo", 99);
+
+ sessions.applyAndUpdateSessions();
+
+ updateValue.write("foo", 22);
+ sessions.apply();
+ ASSERT_THAT(fetchData(), ElementsAre(HasData("foo", "bar", 22)));
+}
+
+TEST_F(Sessions, ApplyAndUpdateSessionsHasOnlyOneChangeSet)
+{
+ insertData.write("foo", "bar", 3.14);
+ sessions.create();
+ updateValue.write("foo", 99);
+ sessions.commit();
+ updateValue.write("foo", 99);
+
+ sessions.applyAndUpdateSessions();
+
+ ASSERT_THAT(fetchChangeSets(), SizeIs(1));
+}
+
+} // namespace