Merge lp:~stolowski/unity-scope-click/departments-db into lp:unity-scope-click/devel

Proposed by Paweł Stołowski
Status: Merged
Approved by: Alejandro J. Cura
Approved revision: 316
Merged at revision: 328
Proposed branch: lp:~stolowski/unity-scope-click/departments-db
Merge into: lp:unity-scope-click/devel
Diff against target: 745 lines (+651/-1)
7 files modified
debian/changelog (+6/-0)
debian/control (+1/-0)
libclickscope/click/CMakeLists.txt (+4/-0)
libclickscope/click/departments-db.cpp (+311/-0)
libclickscope/click/departments-db.h (+99/-0)
libclickscope/tests/CMakeLists.txt (+4/-1)
libclickscope/tests/test_departments-db.cpp (+226/-0)
To merge this branch: bzr merge lp:~stolowski/unity-scope-click/departments-db
Reviewer Review Type Date Requested Status
Alejandro J. Cura (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+225322@code.launchpad.net

Commit message

Provide DepartmentsDb class used for storing and retrieving package - department mapping.

Description of the change

Provide DepartmentsDb class used for storing and retrieving package - department mapping, as well as department hierarchy for installed apps. There will be separate MPs that make use of it.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
314. By Paweł Stołowski

Updated changelog.

315. By Paweł Stołowski

Merged devel

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
316. By Paweł Stołowski

Merged devel

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alejandro J. Cura (alecu) wrote :

Small comment inline

review: Needs Fixing
Revision history for this message
Paweł Stołowski (stolowski) wrote :

Replied inline.

Revision history for this message
Alejandro J. Cura (alecu) wrote :

Happy now. +1

review: Approve
Revision history for this message
Alejandro J. Cura (alecu) wrote :

btw, I like the testing done in this branch, +2!

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/changelog'
2--- debian/changelog 2014-07-07 15:15:08 +0000
3+++ debian/changelog 2014-07-10 07:20:52 +0000
4@@ -28,6 +28,12 @@
5
6 -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 25 Jun 2014 13:25:34 +0000
7
8+unity-scope-click (0.1+14.10.20140618.1-0ubuntu2) UNRELEASED; urgency=medium
9+
10+ * Introduced explicit sqlite3 >= 3.8.5 runtime dependency to ensure support for recursive SQL.
11+
12+ -- Pawel Stolowski <pawel.stolowski@ubuntu.com> Wed, 02 Jul 2014 15:20:16 +0200
13+
14 unity-scope-click (0.1+14.10.20140618.1-0ubuntu1) utopic; urgency=low
15
16 [ Pawel Stolowski ]
17
18=== modified file 'debian/control'
19--- debian/control 2014-07-08 20:32:51 +0000
20+++ debian/control 2014-07-10 07:20:52 +0000
21@@ -30,6 +30,7 @@
22 ubuntu-app-launch-tools,
23 ubuntu-download-manager,
24 ubuntu-sdk-libs,
25+ sqlite3 (>= 3.8.5),
26 ${misc:Depends},
27 ${shlibs:Depends},
28 Breaks: unity (<< 7.0),
29
30=== modified file 'libclickscope/click/CMakeLists.txt'
31--- libclickscope/click/CMakeLists.txt 2014-07-03 13:12:08 +0000
32+++ libclickscope/click/CMakeLists.txt 2014-07-10 07:20:52 +0000
33@@ -1,6 +1,7 @@
34 SET (CMAKE_INCLUDE_CURRENT_DIR ON)
35 SET (CMAKE_AUTOMOC ON)
36 find_package (Qt5Core REQUIRED)
37+find_package (Qt5Sql REQUIRED)
38 pkg_check_modules(JSON_CPP REQUIRED jsoncpp)
39 pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
40
41@@ -16,6 +17,7 @@
42 download-manager.cpp
43 department-lookup.cpp
44 departments.cpp
45+ departments-db.cpp
46 highlights.cpp
47 index.cpp
48 interface.cpp
49@@ -32,6 +34,8 @@
50 webclient.cpp
51 )
52
53+qt5_use_modules(${SCOPE_LIB_NAME} Sql)
54+
55 include_directories(
56 ${JSON_CPP_INCLUDE_DIRS}
57 ${GSETTINGS_QT_INCLUDE_DIRS}
58
59=== added file 'libclickscope/click/departments-db.cpp'
60--- libclickscope/click/departments-db.cpp 1970-01-01 00:00:00 +0000
61+++ libclickscope/click/departments-db.cpp 2014-07-10 07:20:52 +0000
62@@ -0,0 +1,311 @@
63+/*
64+ * Copyright (C) 2014 Canonical Ltd.
65+ *
66+ * This program is free software: you can redistribute it and/or modify it
67+ * under the terms of the GNU General Public License version 3, as published
68+ * by the Free Software Foundation.
69+ *
70+ * This program is distributed in the hope that it will be useful, but
71+ * WITHOUT ANY WARRANTY; without even the implied warranties of
72+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
73+ * PURPOSE. See the GNU General Public License for more details.
74+ *
75+ * You should have received a copy of the GNU General Public License along
76+ * with this program. If not, see <http://www.gnu.org/licenses/>.
77+ *
78+ * In addition, as a special exception, the copyright holders give
79+ * permission to link the code of portions of this program with the
80+ * OpenSSL library under certain conditions as described in each
81+ * individual source file, and distribute linked combinations
82+ * including the two.
83+ * You must obey the GNU General Public License in all respects
84+ * for all of the code used other than OpenSSL. If you modify
85+ * file(s) with this exception, you may extend this exception to your
86+ * version of the file(s), but you are not obligated to do so. If you
87+ * do not wish to do so, delete this exception statement from your
88+ * version. If you delete this exception statement from all source
89+ * files in the program, then also delete it here.
90+ */
91+
92+#include "departments-db.h"
93+#include <stdexcept>
94+#include <iostream>
95+#include <QSqlError>
96+#include <QSqlRecord>
97+#include <QVariant>
98+#include <QStandardPaths>
99+#include <QDir>
100+
101+namespace click
102+{
103+
104+std::unique_ptr<click::DepartmentsDb> DepartmentsDb::create_db()
105+{
106+ auto const path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
107+ if (!path.isEmpty())
108+ {
109+ QDir("/").mkpath(path);
110+ const std::string dbpath = path.toStdString() + "/clickscope/click-departments.db";
111+ return std::unique_ptr<DepartmentsDb>(new DepartmentsDb(dbpath));
112+ }
113+ throw std::runtime_error("Cannot determine cache directory");
114+}
115+
116+DepartmentsDb::DepartmentsDb(const std::string& name)
117+{
118+ init_db(name);
119+
120+ delete_pkgmap_query_.reset(new QSqlQuery(db_));
121+ insert_pkgmap_query_.reset(new QSqlQuery(db_));
122+ insert_dept_id_query_.reset(new QSqlQuery(db_));
123+ insert_dept_name_query_.reset(new QSqlQuery(db_));
124+ select_pkgs_by_dept_.reset(new QSqlQuery(db_));
125+ select_pkgs_by_dept_recursive_.reset(new QSqlQuery(db_));
126+ select_parent_dept_.reset(new QSqlQuery(db_));
127+ select_children_depts_.reset(new QSqlQuery(db_));
128+ select_dept_name_.reset(new QSqlQuery(db_));
129+
130+ delete_pkgmap_query_->prepare("DELETE FROM pkgmap WHERE pkgid=:pkgid");
131+ insert_pkgmap_query_->prepare("INSERT OR REPLACE INTO pkgmap (pkgid, deptid) VALUES (:pkgid, :deptid)");
132+ insert_dept_id_query_->prepare("INSERT OR REPLACE INTO depts (deptid, parentid) VALUES (:deptid, :parentid)");
133+ insert_dept_name_query_->prepare("INSERT OR REPLACE INTO deptnames (deptid, locale, name) VALUES (:deptid, :locale, :name)");
134+ select_pkgs_by_dept_->prepare("SELECT pkgid FROM pkgmap WHERE deptid=:deptid");
135+ select_pkgs_by_dept_recursive_->prepare("WITH RECURSIVE recdepts(deptid) AS (SELECT deptid FROM depts_v WHERE deptid=:deptid UNION SELECT depts_v.deptid FROM recdepts,depts_v WHERE recdepts.deptid=depts_v.parentid) SELECT pkgid FROM pkgmap NATURAL JOIN recdepts");
136+ select_children_depts_->prepare("SELECT deptid,(SELECT COUNT(1) from DEPTS_V AS inner WHERE inner.parentid=outer.deptid) FROM DEPTS_V AS outer WHERE parentid=:parentid");
137+ select_parent_dept_->prepare("SELECT parentid FROM depts_v WHERE deptid=:deptid");
138+ select_dept_name_->prepare("SELECT name FROM deptnames WHERE deptid=:deptid AND locale=:locale");
139+}
140+
141+void DepartmentsDb::init_db(const std::string& name)
142+{
143+ db_ = QSqlDatabase::addDatabase("QSQLITE");
144+ db_.setDatabaseName(QString::fromStdString(name));
145+ if (!db_.open())
146+ {
147+ throw std::runtime_error("Cannot open departments database");
148+ }
149+
150+ QSqlQuery query;
151+
152+ // FIXME: for some reason enabling foreign keys gives errors about number of arguments of prepared queries when doing query.exec(); do not enable
153+ // them for now.
154+ // query.exec("PRAGMA foreign_keys = ON");
155+
156+ // package id -> department id mapping table
157+ if (!query.exec("CREATE TABLE IF NOT EXISTS pkgmap (pkgid TEXT, deptid TEXT, CONSTRAINT pkey PRIMARY KEY (pkgid, deptid))"))
158+ {
159+ report_db_error(query.lastError(), "Failed to create pkgmap table");
160+ }
161+
162+ // department id -> parent department id mapping table
163+ if (!query.exec("CREATE TABLE IF NOT EXISTS depts (deptid TEXT, parentid TEXT, CONSTRAINT pkey PRIMARY KEY (deptid, parentid), CONSTRAINT fkey FOREIGN KEY (deptid) REFERENCES deptnames(deptid))"))
164+ {
165+ report_db_error(query.lastError(), "Failed to create depts table");
166+ }
167+
168+ // department id, locale -> deparment name mapping table
169+ if (!query.exec("CREATE TABLE IF NOT EXISTS deptnames (deptid TEXT, locale TEXT, name TEXT, CONSTRAINT deptuniq PRIMARY KEY (deptid, locale))"))
170+ {
171+ report_db_error(query.lastError(), "Failed to create depts table");
172+ }
173+
174+ // name -> value table for storing arbitrary values such as schema version
175+ if (!query.exec("CREATE TABLE IF NOT EXISTS meta (name TEXT PRIMARY KEY, value TEXT)"))
176+ {
177+ report_db_error(query.lastError(), "Failed to create meta table");
178+ }
179+ query.exec("INSERT INTO meta (name, value) VALUES ('version', 1)");
180+
181+ // view of the depts table that automatically adds fake "" department for root departments
182+ if (!query.exec("CREATE VIEW IF NOT EXISTS depts_v AS SELECT deptid, parentid FROM depts UNION SELECT deptid,'' AS parentid FROM deptnames WHERE NOT EXISTS "
183+ "(SELECT * FROM depts WHERE depts.deptid=deptnames.deptid)"))
184+ {
185+ report_db_error(query.lastError(), "Failed to create depts_v view");
186+ }
187+}
188+
189+void DepartmentsDb::report_db_error(const QSqlError& error, const std::string& message)
190+{
191+ throw std::runtime_error(message + ": " + error.text().toStdString());
192+}
193+
194+std::string DepartmentsDb::get_department_name(const std::string& department_id, const std::list<std::string>& locales)
195+{
196+ for (auto const& locale: locales)
197+ {
198+ select_dept_name_->bindValue(":deptid", QVariant(QString::fromStdString(department_id)));
199+ select_dept_name_->bindValue(":locale", QVariant(QString::fromStdString(locale)));
200+
201+ if (!select_dept_name_->exec())
202+ {
203+ report_db_error(select_dept_name_->lastError(), "Failed to query for department name of " + department_id + ", locale " + locale);
204+ }
205+
206+ if (select_dept_name_->next())
207+ {
208+ return select_dept_name_->value(0).toString().toStdString();
209+ }
210+ }
211+ throw std::logic_error("No name for department " + department_id);
212+}
213+
214+std::string DepartmentsDb::get_parent_department_id(const std::string& department_id)
215+{
216+ select_parent_dept_->bindValue(":deptid", QVariant(QString::fromStdString(department_id)));
217+ if (!select_parent_dept_->exec())
218+ {
219+ report_db_error(select_parent_dept_->lastError(), "Failed to query for parent department " + department_id);
220+ }
221+ if (!select_parent_dept_->next())
222+ {
223+ throw std::logic_error("Unknown department '" + department_id + "'");
224+ }
225+ return select_parent_dept_->value(0).toString().toStdString();
226+}
227+
228+std::list<DepartmentsDb::DepartmentInfo> DepartmentsDb::get_children_departments(const std::string& department_id)
229+{
230+ // FIXME: this should only return departments that have results, and set 'has_children' flag on the same basis.
231+ select_children_depts_->bindValue(":parentid", QVariant(QString::fromStdString(department_id)));
232+ if (!select_children_depts_->exec())
233+ {
234+ report_db_error(select_children_depts_->lastError(), "Failed to query for children departments of " + department_id);
235+ }
236+
237+ std::list<DepartmentInfo> depts;
238+ while (select_children_depts_->next())
239+ {
240+ const DepartmentInfo inf(select_children_depts_->value(0).toString().toStdString(), select_children_depts_->value(1).toBool());
241+ depts.push_back(inf);
242+ }
243+
244+ return depts;
245+}
246+
247+std::unordered_set<std::string> DepartmentsDb::get_packages_for_department(const std::string& department_id, bool recursive)
248+{
249+ std::unordered_set<std::string> pkgs;
250+ QSqlQuery *query = recursive ? select_pkgs_by_dept_recursive_.get() : select_pkgs_by_dept_.get();
251+ query->bindValue(":deptid", QVariant(QString::fromStdString(department_id)));
252+ if (!query->exec())
253+ {
254+ report_db_error(query->lastError(), "Failed to query for packages of department " + department_id);
255+ }
256+ while (query->next())
257+ {
258+ pkgs.insert(query->value(0).toString().toStdString());
259+ }
260+ return pkgs;
261+}
262+
263+void DepartmentsDb::store_package_mapping(const std::string& package_id, const std::string& department_id)
264+{
265+ if (package_id.empty())
266+ {
267+ throw std::logic_error("Invalid empty package_id");
268+ }
269+
270+ if (department_id.empty())
271+ {
272+ throw std::logic_error("Invalid empty department id");
273+ }
274+
275+ if (!db_.transaction())
276+ {
277+ std::cerr << "Failed to start transaction" << std::endl;
278+ }
279+
280+ // delete package mapping first from any departments
281+ delete_pkgmap_query_->bindValue(":pkgid", QVariant(QString::fromStdString(package_id)));
282+ delete_pkgmap_query_->exec();
283+
284+ insert_pkgmap_query_->bindValue(":pkgid", QVariant(QString::fromStdString(package_id)));
285+ insert_pkgmap_query_->bindValue(":deptid", QVariant(QString::fromStdString(department_id)));
286+ if (!insert_pkgmap_query_->exec())
287+ {
288+ if (!db_.rollback())
289+ {
290+ std::cerr << "Failed to rollback transaction" << std::endl;
291+ }
292+ report_db_error(insert_pkgmap_query_->lastError(), "Failed to insert into pkgmap");
293+ }
294+
295+ if (!db_.commit())
296+ {
297+ std::cerr << "Failed to commit transaction" << std::endl;
298+ }
299+}
300+
301+void DepartmentsDb::store_department_mapping(const std::string& department_id, const std::string& parent_department_id)
302+{
303+ if (department_id.empty())
304+ {
305+ throw std::logic_error("Invalid empty department id");
306+ }
307+
308+ if (parent_department_id.empty())
309+ {
310+ throw std::logic_error("Invalid empty parent department id");
311+ }
312+
313+ insert_dept_id_query_->bindValue(":deptid", QVariant(QString::fromStdString(department_id)));
314+ insert_dept_id_query_->bindValue(":parentid", QVariant(QString::fromStdString(parent_department_id)));
315+ if (!insert_dept_id_query_->exec())
316+ {
317+ report_db_error(insert_dept_id_query_->lastError(), "Failed to insert into depts");
318+ }
319+}
320+
321+void DepartmentsDb::store_department_name(const std::string& department_id, const std::string& locale, const std::string& name)
322+{
323+ if (department_id.empty())
324+ {
325+ throw std::logic_error("Invalid empty department id");
326+ }
327+
328+ if (name.empty())
329+ {
330+ throw std::logic_error("Invalid empty department name");
331+ }
332+
333+ insert_dept_name_query_->bindValue(":deptid", QVariant(QString::fromStdString(department_id)));
334+ insert_dept_name_query_->bindValue(":locale", QVariant(QString::fromStdString(locale)));
335+ insert_dept_name_query_->bindValue(":name", QVariant(QString::fromStdString(name)));
336+
337+ if (!insert_dept_name_query_->exec())
338+ {
339+ report_db_error(insert_dept_name_query_->lastError(), "Failed to insert into deptnames");
340+ }
341+}
342+
343+int DepartmentsDb::department_mapping_count() const
344+{
345+ QSqlQuery q(db_);
346+ if (!q.exec("SELECT COUNT(*) FROM depts") || !q.next())
347+ {
348+ report_db_error(q.lastError(), "Failed to query depts table");
349+ }
350+ return q.value(0).toInt();
351+}
352+
353+int DepartmentsDb::package_count() const
354+{
355+ QSqlQuery q(db_);
356+ if (!q.exec("SELECT COUNT(*) FROM pkgmap") || !q.next())
357+ {
358+ report_db_error(q.lastError(), "Failed to query pkgmap table");
359+ }
360+ return q.value(0).toInt();
361+}
362+
363+int DepartmentsDb::department_name_count() const
364+{
365+ QSqlQuery q(db_);
366+ if (!q.exec("SELECT COUNT(*) FROM deptnames") || !q.next())
367+ {
368+ report_db_error(q.lastError(), "Failed to query deptnames table");
369+ }
370+ return q.value(0).toInt();
371+}
372+
373+}
374
375=== added file 'libclickscope/click/departments-db.h'
376--- libclickscope/click/departments-db.h 1970-01-01 00:00:00 +0000
377+++ libclickscope/click/departments-db.h 2014-07-10 07:20:52 +0000
378@@ -0,0 +1,99 @@
379+/*
380+ * Copyright (C) 2014 Canonical Ltd.
381+ *
382+ * This program is free software: you can redistribute it and/or modify it
383+ * under the terms of the GNU General Public License version 3, as published
384+ * by the Free Software Foundation.
385+ *
386+ * This program is distributed in the hope that it will be useful, but
387+ * WITHOUT ANY WARRANTY; without even the implied warranties of
388+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
389+ * PURPOSE. See the GNU General Public License for more details.
390+ *
391+ * You should have received a copy of the GNU General Public License along
392+ * with this program. If not, see <http://www.gnu.org/licenses/>.
393+ *
394+ * In addition, as a special exception, the copyright holders give
395+ * permission to link the code of portions of this program with the
396+ * OpenSSL library under certain conditions as described in each
397+ * individual source file, and distribute linked combinations
398+ * including the two.
399+ * You must obey the GNU General Public License in all respects
400+ * for all of the code used other than OpenSSL. If you modify
401+ * file(s) with this exception, you may extend this exception to your
402+ * version of the file(s), but you are not obligated to do so. If you
403+ * do not wish to do so, delete this exception statement from your
404+ * version. If you delete this exception statement from all source
405+ * files in the program, then also delete it here.
406+ */
407+
408+#ifndef CLICK_DEPARTMENTS_DB_H
409+#define CLICK_DEPARTMENTS_DB_H
410+
411+#include <string>
412+#include <set>
413+#include <unordered_set>
414+#include <list>
415+#include <QSqlDatabase>
416+#include <QSqlQuery>
417+#include <memory>
418+
419+class QSqlError;
420+
421+namespace click
422+{
423+
424+class DepartmentsDb final
425+{
426+public:
427+ struct DepartmentInfo
428+ {
429+ DepartmentInfo(const std::string &id, bool children): id(id), has_children(children) {}
430+ std::string id;
431+ bool has_children;
432+
433+ bool operator==(const DepartmentInfo& other) const
434+ {
435+ return id == other.id && has_children == other.has_children;
436+ }
437+ };
438+
439+ DepartmentsDb(const std::string& name);
440+ DepartmentsDb(const DepartmentsDb& other) = delete;
441+ DepartmentsDb& operator=(const DepartmentsDb&) = delete;
442+
443+ std::string get_department_name(const std::string& department_id, const std::list<std::string>& locales);
444+ std::unordered_set<std::string> get_packages_for_department(const std::string& department_id, bool recursive = true);
445+ std::string get_parent_department_id(const std::string& department_id);
446+ std::list<DepartmentInfo> get_children_departments(const std::string& department_id);
447+
448+ void store_package_mapping(const std::string& package_id, const std::string& department_id);
449+ void store_department_mapping(const std::string& department_id, const std::string& parent_department_id);
450+ void store_department_name(const std::string& department_id, const std::string& locale, const std::string& name);
451+
452+ // these methods are mostly for tests
453+ int department_mapping_count() const;
454+ int package_count() const;
455+ int department_name_count() const;
456+
457+ static std::unique_ptr<DepartmentsDb> create_db();
458+
459+private:
460+ void init_db(const std::string& name);
461+ static void report_db_error(const QSqlError& error, const std::string& message);
462+
463+ QSqlDatabase db_;
464+ std::unique_ptr<QSqlQuery> delete_pkgmap_query_;
465+ std::unique_ptr<QSqlQuery> insert_pkgmap_query_;
466+ std::unique_ptr<QSqlQuery> insert_dept_id_query_;
467+ std::unique_ptr<QSqlQuery> insert_dept_name_query_;
468+ std::unique_ptr<QSqlQuery> select_pkgs_by_dept_;
469+ std::unique_ptr<QSqlQuery> select_pkgs_by_dept_recursive_;
470+ std::unique_ptr<QSqlQuery> select_parent_dept_;
471+ std::unique_ptr<QSqlQuery> select_children_depts_;
472+ std::unique_ptr<QSqlQuery> select_dept_name_;
473+};
474+
475+}
476+
477+#endif
478
479=== modified file 'libclickscope/tests/CMakeLists.txt'
480--- libclickscope/tests/CMakeLists.txt 2014-06-26 17:52:31 +0000
481+++ libclickscope/tests/CMakeLists.txt 2014-07-10 07:20:52 +0000
482@@ -6,6 +6,7 @@
483 SET (CMAKE_AUTOMOC ON)
484
485 find_package(Qt5Core REQUIRED)
486+find_package(Qt5Sql REQUIRED)
487
488 include_directories (
489 ${CMAKE_SOURCE_DIR}/libclickscope
490@@ -15,6 +16,7 @@
491 )
492
493 configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test_data.cpp.in ${CMAKE_CURRENT_BINARY_DIR}/test_data.cpp)
494+add_definitions(-DTEST_DIR="${CMAKE_CURRENT_BINARY_DIR}")
495
496 add_executable (${LIBCLICKSCOPE_TESTS_TARGET}
497 mock_network_access_manager.h
498@@ -24,6 +26,7 @@
499 test_bootstrap.cpp
500 test_configuration.cpp
501 test_departments.cpp
502+ test_departments-db.cpp
503 test_download_manager.cpp
504 test_index.cpp
505 test_interface.cpp
506@@ -35,7 +38,7 @@
507 ${CMAKE_CURRENT_BINARY_DIR}/test_data.cpp
508 )
509
510-qt5_use_modules(${LIBCLICKSCOPE_TESTS_TARGET} Core)
511+qt5_use_modules(${LIBCLICKSCOPE_TESTS_TARGET} Core Sql)
512
513 target_link_libraries(${LIBCLICKSCOPE_TESTS_TARGET}
514 ${SCOPE_LIB_NAME}
515
516=== added file 'libclickscope/tests/test_departments-db.cpp'
517--- libclickscope/tests/test_departments-db.cpp 1970-01-01 00:00:00 +0000
518+++ libclickscope/tests/test_departments-db.cpp 2014-07-10 07:20:52 +0000
519@@ -0,0 +1,226 @@
520+/*
521+ * Copyright (C) 2014 Canonical Ltd.
522+ *
523+ * This program is free software: you can redistribute it and/or modify it
524+ * under the terms of the GNU General Public License version 3, as published
525+ * by the Free Software Foundation.
526+ *
527+ * This program is distributed in the hope that it will be useful, but
528+ * WITHOUT ANY WARRANTY; without even the implied warranties of
529+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
530+ * PURPOSE. See the GNU General Public License for more details.
531+ *
532+ * You should have received a copy of the GNU General Public License along
533+ * with this program. If not, see <http://www.gnu.org/licenses/>.
534+ *
535+ * In addition, as a special exception, the copyright holders give
536+ * permission to link the code of portions of this program with the
537+ * OpenSSL library under certain conditions as described in each
538+ * individual source file, and distribute linked combinations
539+ * including the two.
540+ * You must obey the GNU General Public License in all respects
541+ * for all of the code used other than OpenSSL. If you modify
542+ * file(s) with this exception, you may extend this exception to your
543+ * version of the file(s), but you are not obligated to do so. If you
544+ * do not wish to do so, delete this exception statement from your
545+ * version. If you delete this exception statement from all source
546+ * files in the program, then also delete it here.
547+ */
548+
549+#include <gtest/gtest.h>
550+#include "fake_json.h"
551+#include <click/departments-db.h>
552+#include <memory>
553+#include <algorithm>
554+#include <unistd.h>
555+
556+using namespace click;
557+
558+class DepartmentsDbTest: public ::testing::Test
559+{
560+public:
561+ const std::string db_path = TEST_DIR "/departments-db-test.sqlite";
562+
563+ void SetUp() override
564+ {
565+ db.reset(new DepartmentsDb(db_path));
566+ db->store_department_name("tools", "", "Tools");
567+ db->store_department_name("office", "", "Office");
568+
569+ db->store_department_mapping("office", "tools");
570+
571+ db->store_package_mapping("app1", "tools");
572+ db->store_package_mapping("app2", "office");
573+
574+ db->store_department_name("games", "", "Games");
575+ db->store_department_name("games", "pl_PL", "Gry");
576+ db->store_department_name("rpg", "", "RPG");
577+ db->store_department_name("card", "", "Card");
578+ db->store_department_name("fps", "", "First Person Shooter");
579+
580+ db->store_department_mapping("rpg", "games");
581+ db->store_department_mapping("card", "games");
582+ db->store_department_mapping("fps", "games");
583+
584+ db->store_package_mapping("game1", "rpg");
585+ db->store_package_mapping("game2", "fps");
586+ }
587+
588+ void TearDown() override
589+ {
590+ unlink(db_path.c_str());
591+ }
592+
593+protected:
594+ std::unique_ptr<DepartmentsDb> db;
595+};
596+
597+TEST_F(DepartmentsDbTest, testDepartmentNameLookup)
598+{
599+ {
600+ EXPECT_EQ("Games", db->get_department_name("games", {"en_EN", ""}));
601+ EXPECT_EQ("Gry", db->get_department_name("games", {"pl_PL", ""}));
602+ EXPECT_EQ("First Person Shooter", db->get_department_name("fps", {"en_EN", ""}));
603+ EXPECT_EQ("Office", db->get_department_name("office", {"en_EN", ""}));
604+ EXPECT_EQ("Tools", db->get_department_name("tools", {"en_EN", ""}));
605+
606+ EXPECT_THROW(db->get_department_name("xyz", {"en_EN", ""}), std::logic_error);
607+ }
608+}
609+
610+TEST_F(DepartmentsDbTest, testDepartmentNameUpdates)
611+{
612+ {
613+ EXPECT_EQ(7u, db->department_name_count());
614+ db->store_department_name("tools", "", "Tools!");
615+ EXPECT_EQ("Tools!", db->get_department_name("tools", {"en_EN", ""}));
616+ db->store_department_name("games", "pl_PL", "Gry!!!");
617+ EXPECT_EQ("Gry!!!", db->get_department_name("games", {"pl_PL"}));
618+ EXPECT_EQ(7u, db->department_name_count());
619+ }
620+}
621+
622+TEST_F(DepartmentsDbTest, testDepartmentParentLookup)
623+{
624+ {
625+ EXPECT_EQ("games", db->get_parent_department_id("rpg"));
626+ EXPECT_EQ("games", db->get_parent_department_id("card"));
627+ EXPECT_EQ("", db->get_parent_department_id("games"));
628+
629+ EXPECT_EQ("tools", db->get_parent_department_id("office"));
630+ EXPECT_EQ("", db->get_parent_department_id("tools"));
631+
632+ EXPECT_THROW(db->get_parent_department_id("xyz"), std::logic_error);
633+ }
634+}
635+
636+TEST_F(DepartmentsDbTest, testDepartmentChildrenLookup)
637+{
638+ {
639+ EXPECT_EQ(0, db->get_children_departments("xyz").size());
640+ }
641+ {
642+ auto depts = db->get_children_departments("");
643+ EXPECT_EQ(2u, depts.size());
644+ EXPECT_TRUE(std::find(depts.begin(), depts.end(), DepartmentsDb::DepartmentInfo("tools", true)) != depts.end());
645+ EXPECT_TRUE(std::find(depts.begin(), depts.end(), DepartmentsDb::DepartmentInfo("games", true)) != depts.end());
646+ }
647+ {
648+ auto depts = db->get_children_departments("tools");
649+ EXPECT_EQ(1u, depts.size());
650+ EXPECT_TRUE(std::find(depts.begin(), depts.end(), DepartmentsDb::DepartmentInfo("office", false)) != depts.end());
651+ }
652+ {
653+ auto depts = db->get_children_departments("games");
654+ EXPECT_EQ(3u, depts.size());
655+ EXPECT_TRUE(std::find(depts.begin(), depts.end(), DepartmentsDb::DepartmentInfo("rpg", false)) != depts.end());
656+ EXPECT_TRUE(std::find(depts.begin(), depts.end(), DepartmentsDb::DepartmentInfo("fps", false)) != depts.end());
657+ EXPECT_TRUE(std::find(depts.begin(), depts.end(), DepartmentsDb::DepartmentInfo("card", false)) != depts.end());
658+ }
659+}
660+
661+TEST_F(DepartmentsDbTest, testPackageLookup)
662+{
663+ {
664+ auto pkgs = db->get_packages_for_department("rpg", false);
665+ EXPECT_EQ(1u, pkgs.size());
666+ EXPECT_TRUE(pkgs.find("game1") != pkgs.end());
667+ }
668+ {
669+ auto pkgs = db->get_packages_for_department("fps", false);
670+ EXPECT_EQ(1u, pkgs.size());
671+ EXPECT_TRUE(pkgs.find("game2") != pkgs.end());
672+ }
673+ {
674+ auto pkgs = db->get_packages_for_department("card", false);
675+ EXPECT_EQ(0, pkgs.size());
676+ }
677+}
678+
679+TEST_F(DepartmentsDbTest, testRecursivePackageLookup)
680+{
681+ {
682+ // get packages from subdepartments of games department
683+ auto pkgs = db->get_packages_for_department("games", true);
684+ EXPECT_EQ(2u, pkgs.size());
685+ EXPECT_TRUE(pkgs.find("game1") != pkgs.end());
686+ EXPECT_TRUE(pkgs.find("game2") != pkgs.end());
687+ }
688+ {
689+ // rpg has no subdepartments, so we just get the packages of rpg department
690+ auto pkgs = db->get_packages_for_department("rpg", true);
691+ EXPECT_EQ(1u, pkgs.size());
692+ EXPECT_TRUE(pkgs.find("game1") != pkgs.end());
693+ }
694+ {
695+ auto pkgs = db->get_packages_for_department("card", true);
696+ EXPECT_EQ(0, pkgs.size());
697+ }
698+}
699+
700+TEST_F(DepartmentsDbTest, testPackageUpdates)
701+{
702+ auto pkgs = db->get_packages_for_department("fps");
703+ EXPECT_EQ(1, pkgs.size());
704+ EXPECT_TRUE(pkgs.find("game2") != pkgs.end());
705+
706+ // game2 gets moved to card and removed from fps
707+ db->store_package_mapping("game2", "card");
708+
709+ pkgs = db->get_packages_for_department("fps", false);
710+ EXPECT_EQ(0, pkgs.size());
711+ EXPECT_TRUE(pkgs.find("game2") == pkgs.end());
712+ pkgs = db->get_packages_for_department("card", false);
713+ EXPECT_EQ(1, pkgs.size());
714+ EXPECT_TRUE(pkgs.find("game2") != pkgs.end());
715+}
716+
717+TEST_F(DepartmentsDbTest, testInvalidDepartments)
718+{
719+ EXPECT_THROW(db->get_parent_department_id(""), std::logic_error);
720+ EXPECT_THROW(db->get_parent_department_id("foooo"), std::logic_error);
721+}
722+
723+TEST_F(DepartmentsDbTest, testEmptyArguments)
724+{
725+ EXPECT_THROW(db->store_department_name("", "", "Foo"), std::logic_error);
726+ EXPECT_THROW(db->store_department_name("foo", "", ""), std::logic_error);
727+ EXPECT_THROW(db->store_department_mapping("", "foo"), std::logic_error);
728+ EXPECT_THROW(db->store_department_mapping("foo", ""), std::logic_error);
729+ EXPECT_THROW(db->store_package_mapping("", "foo"), std::logic_error);
730+ EXPECT_THROW(db->store_package_mapping("foo", ""), std::logic_error);
731+}
732+
733+TEST_F(DepartmentsDbTest, testNoDuplicates)
734+{
735+ db->store_package_mapping("game2", "fps");
736+ db->store_package_mapping("game2", "fps");
737+ db->store_department_name("games", "pl_PL", "Gry");
738+ db->store_department_name("games", "pl_PL", "Gry");
739+ db->store_department_mapping("office", "tools");
740+ db->store_department_mapping("office", "tools");
741+
742+ EXPECT_EQ(7u, db->department_name_count());
743+ EXPECT_EQ(4u, db->package_count());
744+}
745+

Subscribers

People subscribed via source and target branches

to all changes: