Merge lp:pay-service/devel into lp:pay-service/14.10

Proposed by Ted Gould
Status: Merged
Merged at revision: 9
Proposed branch: lp:pay-service/devel
Merge into: lp:pay-service/14.10
Diff against target: 1627 lines (+946/-91)
35 files modified
.bzrignore (+3/-1)
CMakeLists.txt (+7/-4)
MERGE-REVIEW (+14/-0)
data/pay-service.conf.in (+3/-1)
debian/changelog (+11/-0)
debian/control (+4/-0)
debian/pay-service.click-hook (+5/-0)
debian/rules (+1/-1)
libpay/pay-package.cpp (+35/-3)
service/CMakeLists.txt (+7/-3)
service/dbus-interface.cpp (+7/-1)
service/dbus-interface.h (+2/-0)
service/item-memory.cpp (+24/-26)
service/main.cpp (+20/-19)
service/purchase-ual.cpp (+20/-5)
service/qtbridge.cpp (+189/-0)
service/qtbridge.h (+88/-0)
service/token-grabber-u1.cpp (+113/-0)
service/token-grabber-u1.h (+41/-0)
service/token-grabber.h (+39/-0)
service/verification-curl.cpp (+90/-11)
service/verification-curl.h (+7/-1)
tests/CMakeLists.txt (+20/-4)
tests/click-data/com.canonical.payui/0.1/.click/info/com.canonical.payui.manifest (+9/-0)
tests/click-data/com.canonical.payui/0.1/test.desktop (+3/-0)
tests/click-db/test.conf.in (+2/-0)
tests/dbus-interface-tests.cpp (+24/-2)
tests/libpay-tests.cpp (+5/-5)
tests/manual (+30/-0)
tests/purchase-ual-tests.cpp (+9/-2)
tests/setup-staging.sh (+19/-0)
tests/token-grabber-null.h (+36/-0)
tests/verification-curl-endpoints/good/device-id.in (+5/-0)
tests/verification-curl-endpoints/package-name.in (+3/-0)
tests/verification-curl-tests.cpp (+51/-2)
To merge this branch: bzr merge lp:pay-service/devel
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Needs Fixing
Indicator Applet Developers Pending
Review via email: mp+223106@code.launchpad.net

Commit message

Fixes from integration work to make the pay demo come together.

Description of the change

Release prep.

To post a comment you must log in.
Revision history for this message
Ted Gould (ted) wrote :

Marking for review to get Jenkin's opinion. Needs a acceptance test branch before fully ready.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
lp:pay-service/devel updated
14. By Ted Gould

Fix scoping of root objects

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
lp:pay-service/devel updated
15. By Ted Gould

Check to ensure that the package is available to start

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
lp:pay-service/devel updated
16. By Ted Gould

Bringing in tests and merge commit rules

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
lp:pay-service/devel updated
17. By Ted Gould

Ensure that the debian changelog matches the trunk

18. By Ted Gould

Doing the changelog manually for CI Train

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2014-05-20 19:06:37 +0000
3+++ .bzrignore 2014-06-18 18:46:48 +0000
4@@ -10,7 +10,9 @@
5 item-memory-tests
6 pay-service.conf
7 verification-curl-tests
8-tests/verification-curl-endpoints/good-simple
9+tests/verification-curl-endpoints/good/simple
10 purchase-ual-tests
11+click-db/test.conf
12 libpay.so.1.0.0
13 libpay-tests
14+verification-curl-endpoints/package-name
15
16=== modified file 'CMakeLists.txt'
17--- CMakeLists.txt 2014-05-26 14:01:00 +0000
18+++ CMakeLists.txt 2014-06-18 18:46:48 +0000
19@@ -29,19 +29,22 @@
20 ## Check for prerequisites
21 ##
22
23+set(CMAKE_AUTOMOC ON)
24+
25 find_package (PkgConfig REQUIRED)
26+find_package (Qt5Core REQUIRED)
27 include (FindPkgConfig)
28
29-pkg_check_modules (PROCESS_CPP REQUIRED process-cpp)
30-
31 pkg_check_modules (SERVICE_DEPS REQUIRED
32 libcurl
33 ubuntu-app-launch-2
34 dbustest-1
35 gio-2.0
36 gio-unix-2.0
37- process-cpp)
38-include_directories (SYSTEM ${PROCESS_CPP_INCLUDE_DIRS} ${SERVICE_DEPS_INCLUDE_DIRS})
39+ process-cpp
40+ properties-cpp
41+ ubuntuoneauth-2.0)
42+include_directories (SYSTEM ${SERVICE_DEPS_INCLUDE_DIRS})
43
44 ##
45 ## Gdbus
46
47=== added file 'MERGE-REVIEW'
48--- MERGE-REVIEW 1970-01-01 00:00:00 +0000
49+++ MERGE-REVIEW 2014-06-18 18:46:48 +0000
50@@ -0,0 +1,14 @@
51+
52+This documents the expections that the project has on what both submitters
53+and reviewers should ensure that they've done for a merge into the project.
54+
55+== Submitter Responsibilities ==
56+
57+ * Ensure the project compiles and the test suite executes without error
58+ * Ensure that non-obvious code has comments explaining it
59+
60+== Reviewer Responsibilities ==
61+
62+ * Did the Jenkins build compile? Pass? Run unit tests successfully?
63+ * Are there appropriate tests to cover any new functionality?
64+
65
66=== modified file 'data/pay-service.conf.in'
67--- data/pay-service.conf.in 2014-05-26 10:40:36 +0000
68+++ data/pay-service.conf.in 2014-06-18 18:46:48 +0000
69@@ -1,6 +1,8 @@
70 description "Service to process payments"
71
72-start on desktop-start
73+start on desktop-start or started unity8
74 stop on desktop-end
75
76+respawn
77+
78 exec @CMAKE_INSTALL_FULL_LIBEXECDIR@/pay-service/pay-service
79
80=== modified file 'debian/changelog'
81--- debian/changelog 2014-06-01 21:16:48 +0000
82+++ debian/changelog 2014-06-18 18:46:48 +0000
83@@ -1,3 +1,14 @@
84+pay-service (0.1+14.10.20140601-0ubuntu2) UNRELEASED; urgency=medium
85+
86+ * Add acceptance tests and merge commit rules
87+ * Fix scoping of root object
88+ * Allow purchasing items without verifications
89+ * Use proper URLs for verification
90+ * Launch Pay UI for finishing payment
91+ * Add a dummy click hook to allow for usage in Pay UI
92+
93+ -- Ted Gould <ted@ubuntu.com> Wed, 18 Jun 2014 13:44:26 -0500
94+
95 pay-service (0.1+14.10.20140601-0ubuntu1) utopic; urgency=low
96
97 [ Ted Gould ]
98
99=== modified file 'debian/control'
100--- debian/control 2014-05-28 09:20:54 +0000
101+++ debian/control 2014-06-18 18:46:48 +0000
102@@ -3,6 +3,7 @@
103 Priority: optional
104 Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
105 Build-Depends: astyle,
106+ click-dev,
107 cmake,
108 dbus,
109 dbus-test-runner,
110@@ -10,10 +11,13 @@
111 google-mock,
112 libcurl4-gnutls-dev,
113 libdbustest1-dev,
114+ libglib2.0-dev,
115 libgtest-dev,
116 libprocess-cpp-dev,
117 libubuntu-app-launch2-dev,
118+ libubuntuoneauth-2.0-dev,
119 python3-dbusmock,
120+ qtbase5-dev,
121 Standards-Version: 3.9.5
122 Homepage: http://launchpad.net/pay-service
123 # If you aren't a member of ~indicator-applet-developers but need to upload
124
125=== added file 'debian/pay-service.click-hook'
126--- debian/pay-service.click-hook 1970-01-01 00:00:00 +0000
127+++ debian/pay-service.click-hook 2014-06-18 18:46:48 +0000
128@@ -0,0 +1,5 @@
129+# NOTE: Temporary until trusted sessions land
130+Pattern: ${home}/.local/share/applications/${id}.desktop
131+Exec: /bin/true
132+User-Level: yes
133+Hook-Name: pay-ui
134
135=== modified file 'debian/rules'
136--- debian/rules 2014-05-21 03:44:48 +0000
137+++ debian/rules 2014-06-18 18:46:48 +0000
138@@ -8,4 +8,4 @@
139 dh_auto_configure -- -DCMAKE_INSTALL_LIBEXECDIR=/usr/lib/$(DEB_HOST_MULTIARCH)/pay-service -DCMAKE_INSTALL_INCLUDEDIR=/usr/include/$(DEB_HOST_MULTIARCH)/
140
141 %:
142- dh $@ --fail-missing
143+ dh $@ --fail-missing --with click
144
145=== modified file 'libpay/pay-package.cpp'
146--- libpay/pay-package.cpp 2014-05-20 22:12:48 +0000
147+++ libpay/pay-package.cpp 2014-06-18 18:46:48 +0000
148@@ -31,6 +31,7 @@
149 class Package
150 {
151 std::string id;
152+ std::string path;
153 /* NOTE: Using the shared_ptr here because gcc 4.7 map doesn't have emplace */
154 std::map <std::pair<PayPackageItemObserver, void*>, std::shared_ptr<core::ScopedConnection>> observers;
155 core::Signal<std::string, PayPackageItemStatus> itemChanged;
156@@ -46,6 +47,9 @@
157 public:
158 Package (const char* packageid) : id(packageid), cancellable(g_cancellable_new()), loop(nullptr)
159 {
160+ path = std::string("/com/canonical/pay/");
161+ path += encodePath(id);
162+
163 /* Keeps item cache up-to-data as we get signals about it */
164 itemChanged.connect([this](std::string itemid, PayPackageItemStatus status)
165 {
166@@ -62,13 +66,11 @@
167 g_main_context_push_thread_default(context);
168 context_mutex.unlock();
169
170- std::string path("/com/canonical/pay/");
171- path += id; /* TODO: encode */
172 proxy = proxy_pay_package_proxy_new_for_bus_sync(G_BUS_TYPE_SESSION,
173 G_DBUS_PROXY_FLAGS_NONE,
174 "com.canonical.pay",
175 path.c_str(),
176- cancellable,
177+ NULL,
178 &error);
179
180 if (error != nullptr)
181@@ -248,6 +250,36 @@
182 {
183 return functionCall(proxy_pay_package_call_purchase_item, proxy_pay_package_call_purchase_item_finish, itemid);
184 }
185+
186+ std::string
187+ encodePath (const std::string& input)
188+ {
189+ std::string output = "";
190+ bool first = true;
191+
192+ for (unsigned char c : input)
193+ {
194+ std::string retval;
195+
196+ if ((c >= 'a' && c <= 'z') ||
197+ (c >= 'A' && c <= 'Z') ||
198+ (c >= '0' && c <= '9' && !first))
199+ {
200+ retval = std::string((char*)&c, 1);
201+ }
202+ else
203+ {
204+ char buffer[5] = {0};
205+ std::snprintf(buffer, 4, "_%2X", c);
206+ retval = std::string(buffer);
207+ }
208+
209+ output += retval;
210+ first = false;
211+ }
212+
213+ return output;
214+ }
215 };
216
217
218
219=== modified file 'service/CMakeLists.txt'
220--- service/CMakeLists.txt 2014-05-15 20:11:25 +0000
221+++ service/CMakeLists.txt 2014-06-18 18:46:48 +0000
222@@ -1,5 +1,5 @@
223
224-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g ${GCOV_FLAGS}")
225+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g -fPIE ${GCOV_FLAGS}")
226 include_directories(${CMAKE_CURRENT_BINARY_DIR})
227
228
229@@ -14,11 +14,16 @@
230 item-null.h
231 item-memory.h
232 item-memory.cpp
233+ qtbridge.h
234+ qtbridge.cpp
235 purchase-factory.h
236 purchase-null.h
237 purchase-null.cpp
238 purchase-ual.h
239 purchase-ual.cpp
240+ token-grabber.h
241+ token-grabber-u1.h
242+ token-grabber-u1.cpp
243 verification-curl.cpp
244 verification-null.cpp)
245
246@@ -34,8 +39,7 @@
247
248 add_executable(pay-service main.cpp)
249 target_link_libraries(pay-service
250- pay-service-lib
251- ${PROCESS_CPP_LIBRARIES})
252+ pay-service-lib)
253
254 install(TARGETS pay-service RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR})
255
256
257=== modified file 'service/dbus-interface.cpp'
258--- service/dbus-interface.cpp 2014-05-19 22:03:01 +0000
259+++ service/dbus-interface.cpp 2014-06-18 18:46:48 +0000
260@@ -51,7 +51,11 @@
261 bus(nullptr),
262 serviceProxy(nullptr),
263 packageProxy(nullptr),
264- errorQuark(g_quark_from_static_string("dbus-interface-impl"))
265+ errorQuark(g_quark_from_static_string("dbus-interface-impl")),
266+ subtree_registration(0)
267+ { }
268+
269+ void run ()
270 {
271 t = std::thread([this]()
272 {
273@@ -398,6 +402,8 @@
274 {
275 connectionReady();
276 });
277+
278+ impl->run();
279 }
280
281 bool
282
283=== modified file 'service/dbus-interface.h'
284--- service/dbus-interface.h 2014-05-19 21:03:42 +0000
285+++ service/dbus-interface.h 2014-06-18 18:46:48 +0000
286@@ -39,6 +39,8 @@
287
288 core::Signal<> connectionReady;
289
290+ typedef std::shared_ptr<DBusInterface> Ptr;
291+
292 private:
293 std::shared_ptr<DBusInterfaceImpl> impl;
294 };
295
296=== modified file 'service/item-memory.cpp'
297--- service/item-memory.cpp 2014-05-08 20:48:54 +0000
298+++ service/item-memory.cpp 2014-06-18 18:46:48 +0000
299@@ -105,43 +105,41 @@
300 bool purchase (void)
301 {
302 /* First check to see if a purchase makes sense */
303- if (status != NOT_PURCHASED)
304- {
305- return false;
306- }
307-
308- if (pitem != nullptr)
309+ if (status == PURCHASED)
310 {
311 return true;
312 }
313
314- pitem = pfactory->purchaseItem(app, id);
315 if (pitem == nullptr)
316 {
317- /* Uhg, failed */
318- return false;
319+ pitem = pfactory->purchaseItem(app, id);
320+ if (pitem == nullptr)
321+ {
322+ /* Uhg, failed */
323+ return false;
324+ }
325+
326+ pitem->purchaseComplete.connect([this](Purchase::Item::Status status)
327+ {
328+ switch (status)
329+ {
330+ case Purchase::Item::PURCHASED:
331+ setStatus(Item::Status::PURCHASED);
332+ break;
333+ case Purchase::Item::ERROR:
334+ case Purchase::Item::NOT_PURCHASED:
335+ default: /* Fall through, an error is same as status we don't know */
336+ /* We know we were not purchased before, so let's stay that way */
337+ setStatus(Item::Status::NOT_PURCHASED);
338+ break;
339+ }
340+ return;
341+ });
342 }
343
344 /* New purchase instance, tell the world! */
345 setStatus(Item::Status::PURCHASING);
346
347- pitem->purchaseComplete.connect([this](Purchase::Item::Status status)
348- {
349- switch (status)
350- {
351- case Purchase::Item::PURCHASED:
352- setStatus(Item::Status::PURCHASED);
353- break;
354- case Purchase::Item::ERROR:
355- case Purchase::Item::NOT_PURCHASED:
356- default: /* Fall through, an error is same as status we don't know */
357- /* We know we were not purchased before, so let's stay that way */
358- setStatus(Item::Status::NOT_PURCHASED);
359- break;
360- }
361- return;
362- });
363-
364 return pitem->run();
365 }
366
367
368=== modified file 'service/main.cpp'
369--- service/main.cpp 2014-05-14 03:35:25 +0000
370+++ service/main.cpp 2014-06-18 18:46:48 +0000
371@@ -17,33 +17,34 @@
372 * Ted Gould <ted.gould@canonical.com>
373 */
374
375-#include <core/posix/signal.h>
376-
377 #include "dbus-interface.h"
378 #include "item-memory.h"
379 #include "verification-curl.h"
380 #include "purchase-ual.h"
381+#include "qtbridge.h"
382+#include "token-grabber-u1.h"
383
384 int
385 main (int argv, char* argc[])
386 {
387- auto trap = core::posix::trap_signals_for_all_subsequent_threads(
388- {
389- core::posix::Signal::sig_int,
390- core::posix::Signal::sig_term
391- });
392-
393- trap->signal_raised().connect([trap](core::posix::Signal)
394- {
395- trap->stop();
396- });
397-
398- auto vfactory = std::make_shared<Verification::CurlFactory>();
399- auto pfactory = std::make_shared<Purchase::UalFactory>();
400- auto items = std::make_shared<Item::MemoryStore>(vfactory, pfactory);
401- auto dbus = std::make_shared<DBusInterface>(items);
402-
403- trap->run();
404+ TokenGrabber::Ptr token;
405+ Verification::Factory::Ptr vfactory;
406+ Purchase::Factory::Ptr pfactory;
407+ Item::Store::Ptr items;
408+ DBusInterface::Ptr dbus;
409+
410+ qt::core::world::build_and_run(argv, argc, [&token, &vfactory, &pfactory, &items, &dbus]()
411+ {
412+ /* Initialize the other object after Qt is built */
413+ token = std::make_shared<TokenGrabberU1>();
414+ vfactory = std::make_shared<Verification::CurlFactory>(token);
415+ pfactory = std::make_shared<Purchase::UalFactory>();
416+ items = std::make_shared<Item::MemoryStore>(vfactory, pfactory);
417+ dbus = std::make_shared<DBusInterface>(items);
418+ });
419+
420+ qt::core::world::destroy();
421
422 return EXIT_SUCCESS;
423 }
424+
425
426=== modified file 'service/purchase-ual.cpp'
427--- service/purchase-ual.cpp 2014-05-26 14:01:00 +0000
428+++ service/purchase-ual.cpp 2014-06-18 18:46:48 +0000
429@@ -31,10 +31,20 @@
430 typedef std::shared_ptr<Item> Ptr;
431
432 UalItem (std::string& in_appid, std::string& in_itemid) :
433- appid(in_appid), itemid(in_itemid), loop(nullptr), status(Item::ERROR)
434+ appid(in_appid),
435+ itemid(in_itemid),
436+ loop(nullptr),
437+ status(Item::ERROR)
438 {
439 /* TODO: ui_appid needs to be grabbed from the click hook */
440- ui_appid = "gedit";
441+ gchar* appidc = ubuntu_app_launch_triplet_to_app_id("com.canonical.payui",
442+ nullptr,
443+ nullptr);
444+ if (appidc != nullptr)
445+ {
446+ ui_appid = appidc;
447+ g_free(appidc);
448+ }
449 }
450
451 ~UalItem ()
452@@ -79,9 +89,14 @@
453 /* Building a URL so that we can pass this information today without
454 using trusted helpers and setting environment vars */
455 /* TODO: Use trusted helpers */
456- std::string purchase_url = "purchase:///";
457- purchase_url += appid;
458- purchase_url += "/";
459+ std::string purchase_url = "purchase://";
460+
461+ if (appid != "click-scope")
462+ {
463+ purchase_url += appid;
464+ purchase_url += "/";
465+ }
466+
467 purchase_url += itemid;
468 const gchar* urls[2] = {0};
469 urls[0] = purchase_url.c_str();
470
471=== added file 'service/qtbridge.cpp'
472--- service/qtbridge.cpp 1970-01-01 00:00:00 +0000
473+++ service/qtbridge.cpp 2014-06-18 18:46:48 +0000
474@@ -0,0 +1,189 @@
475+/*
476+ * Copyright © 2014 Canonical Ltd.
477+ *
478+ * This program is free software: you can redistribute it and/or modify it
479+ * under the terms of the GNU Lesser General Public License version 3,
480+ * as published by the Free Software Foundation.
481+ *
482+ * This program is distributed in the hope that it will be useful,
483+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
484+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
485+ * GNU Lesser General Public License for more details.
486+ *
487+ * You should have received a copy of the GNU Lesser General Public License
488+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
489+ *
490+ * Authored by: Jussi Pakkanen <jussi.pakkanen@canonical.com>
491+ * Thomas Voß <thomas.voss@canonical.com>
492+ */
493+
494+#include "qtbridge.h"
495+
496+#include<QCoreApplication>
497+#include<QNetworkAccessManager>
498+#include<QNetworkRequest>
499+#include<QNetworkReply>
500+#include<QThread>
501+#include<QDebug>
502+
503+#include <iostream>
504+
505+namespace
506+{
507+QCoreApplication* app = nullptr;
508+}
509+
510+namespace qt
511+{
512+namespace core
513+{
514+namespace world
515+{
516+namespace detail
517+{
518+QEvent::Type qt_core_world_task_event_type()
519+{
520+ static QEvent::Type event_type = static_cast<QEvent::Type>(QEvent::registerEventType());
521+ return event_type;
522+}
523+
524+class TaskEvent : public QEvent
525+{
526+public:
527+ TaskEvent(const std::function<void()>& task)
528+ : QEvent(qt_core_world_task_event_type()),
529+ task(task)
530+ {
531+ }
532+
533+ void run()
534+ {
535+ try
536+ {
537+ task();
538+ promise.set_value();
539+ }
540+ catch (...)
541+ {
542+ promise.set_exception(std::current_exception());
543+ }
544+ }
545+
546+ std::future<void> get_future()
547+ {
548+ return promise.get_future();
549+ }
550+
551+private:
552+ std::function<void()> task;
553+ std::promise<void> promise;
554+};
555+
556+class TaskHandler : public QObject
557+{
558+ Q_OBJECT
559+
560+public:
561+ TaskHandler(QObject* parent) : QObject(parent)
562+ {
563+ }
564+
565+ bool event(QEvent* e);
566+};
567+
568+
569+
570+void createCoreApplicationInstanceWithArgs(int argc, char** argv)
571+{
572+ app = new QCoreApplication(argc, argv);
573+}
574+
575+void destroyCoreApplicationInstace()
576+{
577+ delete app;
578+}
579+
580+QCoreApplication* coreApplicationInstance()
581+{
582+ return app;
583+}
584+
585+TaskHandler* task_handler()
586+{
587+ static TaskHandler* instance = new TaskHandler(coreApplicationInstance());
588+ return instance;
589+}
590+
591+bool TaskHandler::event(QEvent* e)
592+{
593+ if (e->type() != qt_core_world_task_event_type())
594+ {
595+ return QObject::event(e);
596+ }
597+
598+ auto te = dynamic_cast<TaskEvent*>(e);
599+ if (te)
600+ {
601+ te->run();
602+ return true;
603+ }
604+
605+ return false;
606+}
607+}
608+
609+void build_and_run(int argc, char** argv, const std::function<void()>& ready)
610+{
611+ QThread::currentThread();
612+ if (QCoreApplication::instance() != nullptr)
613+ throw std::runtime_error(
614+ "qt::core::world::build_and_run: "
615+ "There is already a QCoreApplication running.");
616+
617+ detail::createCoreApplicationInstanceWithArgs(argc, argv);
618+
619+ detail::task_handler()->moveToThread(
620+ detail::coreApplicationInstance()->thread());
621+
622+ // Signal to other worlds that we are good to go.
623+ ready();
624+
625+ detail::coreApplicationInstance()->exec();
626+
627+ // Someone has called quit and we clean up on the correct thread here.
628+ detail::destroyCoreApplicationInstace();
629+}
630+
631+void destroy()
632+{
633+ enter_with_task([]()
634+ {
635+ // We make sure that all tasks have completed before quitting the app.
636+ QEventLoopLocker locker;
637+ }).wait_for(std::chrono::seconds {1});
638+}
639+
640+std::future<void> enter_with_task(const std::function<void()>& task)
641+{
642+ QCoreApplication* instance = QCoreApplication::instance();
643+
644+ if (!instance)
645+ {
646+ throw std::runtime_error("Qt world has not been built before calling this function.");
647+ }
648+
649+ detail::TaskEvent* te = new detail::TaskEvent(task);
650+ auto future = te->get_future();
651+
652+ // We hand over ownership of te here. The event is deleted later after it has
653+ // been processed by the event loop.
654+ instance->postEvent(detail::task_handler(), te);
655+
656+ return future;
657+}
658+
659+}
660+}
661+}
662+
663+#include "qtbridge.moc"
664
665=== added file 'service/qtbridge.h'
666--- service/qtbridge.h 1970-01-01 00:00:00 +0000
667+++ service/qtbridge.h 2014-06-18 18:46:48 +0000
668@@ -0,0 +1,88 @@
669+/*
670+ * Copyright © 2014 Canonical Ltd.
671+ *
672+ * This program is free software: you can redistribute it and/or modify it
673+ * under the terms of the GNU Lesser General Public License version 3,
674+ * as published by the Free Software Foundation.
675+ *
676+ * This program is distributed in the hope that it will be useful,
677+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
678+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
679+ * GNU Lesser General Public License for more details.
680+ *
681+ * You should have received a copy of the GNU Lesser General Public License
682+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
683+ *
684+ * Authored by: Jussi Pakkanen <jussi.pakkanen@canonical.com>
685+ * Thomas Voß <thomas.voss@canonical.com>
686+ */
687+
688+#ifndef QT_CORE_WORLD_BRIDGE_H_
689+#define QT_CORE_WORLD_BRIDGE_H_
690+
691+#include <QObject>
692+
693+#include <functional>
694+#include <future>
695+#include <iostream>
696+
697+namespace qt
698+{
699+namespace core
700+{
701+namespace world
702+{
703+/**
704+ * @brief Sets up the Qt core world and executes its event loop. Blocks until destroy() is called.
705+ * @param argc Number of arguments in argv.
706+ * @param argv Array of command-line arguments.
707+ * @param ready Functor be called when the world has been setup and is about to be executed.
708+ * @throw std::runtime_error in case of errors.
709+ */
710+void build_and_run(int argc, char** argv, const std::function<void()>& ready);
711+
712+/**
713+ * @brief Destroys the Qt core world and quits its event loop.
714+ */
715+void destroy();
716+
717+/**
718+ * @brief Enters the Qt core world and schedules the given task for execution.
719+ * @param task The task to be executed in the Qt core world.
720+ * @return A std::future that can be waited for to synchronize to the world's internal event loop.
721+ */
722+std::future<void> enter_with_task(const std::function<void()>& task);
723+
724+
725+/**
726+ * @brief Enters the Qt core world and schedules the given task for execution.
727+ * @param task The task to be executed in the Qt core world.
728+ * @return A std::future that can be waited for to get hold of the result of the task.
729+ */
730+template<typename T>
731+inline std::future<T> enter_with_task_and_expect_result(const std::function<T()>& task)
732+{
733+ std::shared_ptr<std::promise<T>> promise = std::make_shared<std::promise<T>>();
734+ std::future<T> future = promise->get_future();
735+
736+ auto t = [promise, task]()
737+ {
738+ try
739+ {
740+ promise->set_value(task());
741+ }
742+ catch (...)
743+ {
744+ promise->set_exception(std::current_exception());
745+ }
746+ };
747+
748+ enter_with_task(t);
749+
750+ return future;
751+}
752+}
753+}
754+}
755+
756+#endif // QT_CORE_WORLD_BRIDGE_H_
757
758=== added file 'service/token-grabber-u1.cpp'
759--- service/token-grabber-u1.cpp 1970-01-01 00:00:00 +0000
760+++ service/token-grabber-u1.cpp 2014-06-18 18:46:48 +0000
761@@ -0,0 +1,113 @@
762+/*
763+ * Copyright © 2014 Canonical Ltd.
764+ *
765+ * This program is free software: you can redistribute it and/or modify it
766+ * under the terms of the GNU General Public License version 3, as published
767+ * by the Free Software Foundation.
768+ *
769+ * This program is distributed in the hope that it will be useful, but
770+ * WITHOUT ANY WARRANTY; without even the implied warranties of
771+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
772+ * PURPOSE. See the GNU General Public License for more details.
773+ *
774+ * You should have received a copy of the GNU General Public License along
775+ * with this program. If not, see <http://www.gnu.org/licenses/>.
776+ *
777+ * Authors:
778+ * Ted Gould <ted.gould@canonical.com>
779+ */
780+
781+#include "token-grabber-u1.h"
782+#include "qtbridge.h"
783+
784+#include <ssoservice.h>
785+#include <token.h>
786+
787+class TokenGrabberU1Qt: public QObject
788+{
789+ Q_OBJECT
790+
791+public:
792+ explicit TokenGrabberU1Qt (QObject* parent = 0);
793+ void run (void);
794+ std::string signUrl(std::string url, std::string type);
795+
796+private Q_SLOTS:
797+ void handleCredentialsFound(const UbuntuOne::Token& token);
798+ void handleCredentialsNotFound();
799+
800+private:
801+ UbuntuOne::Token token;
802+ UbuntuOne::SSOService service;
803+};
804+
805+TokenGrabberU1Qt::TokenGrabberU1Qt (QObject* parent) :
806+ QObject(parent)
807+{
808+ std::cout << "Token grabber built" << std::endl;
809+}
810+
811+void TokenGrabberU1Qt::run (void)
812+{
813+ std::cout << "Token grabber running" << std::endl;
814+
815+ QObject::connect(&service,
816+ &UbuntuOne::SSOService::credentialsFound,
817+ this,
818+ &TokenGrabberU1Qt::handleCredentialsFound);
819+ QObject::connect(&service,
820+ &UbuntuOne::SSOService::credentialsNotFound,
821+ this,
822+ &TokenGrabberU1Qt::handleCredentialsNotFound);
823+
824+ service.getCredentials();
825+}
826+
827+void TokenGrabberU1Qt::handleCredentialsFound(const UbuntuOne::Token& in_token)
828+{
829+ token = in_token;
830+ std::cout << "Got a Token" << std::endl;
831+}
832+
833+void TokenGrabberU1Qt::handleCredentialsNotFound()
834+{
835+ std::cout << "No Token :-(" << std::endl;
836+}
837+
838+std::string TokenGrabberU1Qt::signUrl (std::string url, std::string type)
839+{
840+ std::string retval;
841+
842+ auto qretval = token.signUrl(url.c_str(), type.c_str());
843+ retval = std::string(qretval.toUtf8());
844+
845+ return retval;
846+}
847+
848+TokenGrabberU1::TokenGrabberU1 (void)
849+{
850+ //qt = std::make_shared<TokenGrabberU1Qt>();
851+ qtfuture = qt::core::world::enter_with_task_and_expect_result<std::shared_ptr<TokenGrabberU1Qt>>([]()
852+ {
853+ auto qtgrabber = std::make_shared<TokenGrabberU1Qt>();
854+ qtgrabber->run();
855+ return qtgrabber;
856+ });
857+}
858+
859+TokenGrabberU1::~TokenGrabberU1 (void)
860+{
861+}
862+
863+std::string TokenGrabberU1::signUrl (std::string url, std::string type)
864+{
865+ if (qtfuture.valid())
866+ {
867+ return qtfuture.get()->signUrl(url, type);
868+ }
869+
870+ std::string retval;
871+ return retval;
872+}
873+
874+#include "token-grabber-u1.moc"
875
876=== added file 'service/token-grabber-u1.h'
877--- service/token-grabber-u1.h 1970-01-01 00:00:00 +0000
878+++ service/token-grabber-u1.h 2014-06-18 18:46:48 +0000
879@@ -0,0 +1,41 @@
880+/*
881+ * Copyright © 2014 Canonical Ltd.
882+ *
883+ * This program is free software: you can redistribute it and/or modify it
884+ * under the terms of the GNU General Public License version 3, as published
885+ * by the Free Software Foundation.
886+ *
887+ * This program is distributed in the hope that it will be useful, but
888+ * WITHOUT ANY WARRANTY; without even the implied warranties of
889+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
890+ * PURPOSE. See the GNU General Public License for more details.
891+ *
892+ * You should have received a copy of the GNU General Public License along
893+ * with this program. If not, see <http://www.gnu.org/licenses/>.
894+ *
895+ * Authors:
896+ * Ted Gould <ted.gould@canonical.com>
897+ */
898+
899+#ifndef TOKEN_GRABBER_U1_HPP__
900+#define TOKEN_GRABBER_U1_HPP__ 1
901+
902+#include <future>
903+#include "token-grabber.h"
904+
905+class TokenGrabberU1Qt;
906+
907+class TokenGrabberU1 : public TokenGrabber
908+{
909+public:
910+ TokenGrabberU1 (void);
911+ virtual ~TokenGrabberU1 (void);
912+
913+ virtual std::string signUrl(std::string url, std::string type);
914+
915+private:
916+ std::future<std::shared_ptr<TokenGrabberU1Qt>> qtfuture;
917+};
918+
919+
920+#endif /* TOKEN_GRABBER_U1_HPP__ */
921
922=== added file 'service/token-grabber.h'
923--- service/token-grabber.h 1970-01-01 00:00:00 +0000
924+++ service/token-grabber.h 2014-06-18 18:46:48 +0000
925@@ -0,0 +1,39 @@
926+/*
927+ * Copyright © 2014 Canonical Ltd.
928+ *
929+ * This program is free software: you can redistribute it and/or modify it
930+ * under the terms of the GNU General Public License version 3, as published
931+ * by the Free Software Foundation.
932+ *
933+ * This program is distributed in the hope that it will be useful, but
934+ * WITHOUT ANY WARRANTY; without even the implied warranties of
935+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
936+ * PURPOSE. See the GNU General Public License for more details.
937+ *
938+ * You should have received a copy of the GNU General Public License along
939+ * with this program. If not, see <http://www.gnu.org/licenses/>.
940+ *
941+ * Authors:
942+ * Ted Gould <ted.gould@canonical.com>
943+ */
944+
945+#ifndef TOKEN_GRABBER_HPP__
946+#define TOKEN_GRABBER_HPP__ 1
947+
948+#include <string>
949+#include <memory>
950+
951+#include <core/signal.h>
952+
953+class TokenGrabber
954+{
955+public:
956+ virtual std::string signUrl(std::string url, std::string type) = 0;
957+
958+ typedef std::shared_ptr<TokenGrabber> Ptr;
959+
960+ /* Signals */
961+ core::Signal<bool> tokenUpdated;
962+};
963+
964+#endif /* TOKEN_GRABBER_HPP__ */
965
966=== modified file 'service/verification-curl.cpp'
967--- service/verification-curl.cpp 2014-05-14 03:35:34 +0000
968+++ service/verification-curl.cpp 2014-06-18 18:46:48 +0000
969@@ -30,9 +30,39 @@
970 class CurlItem : public Item
971 {
972 public:
973- CurlItem (std::string& app, std::string& item, std::string& endpoint) : exec(nullptr)
974+ CurlItem (std::string& app,
975+ std::string& item,
976+ std::string& endpoint,
977+ std::string& device,
978+ TokenGrabber::Ptr token) :
979+ stop(false),
980+ handle(nullptr),
981+ curlHeaders(nullptr)
982 {
983- url = (endpoint + "/" + app + "-" + item);
984+ url = endpoint;
985+
986+ if (app != "click-scope")
987+ {
988+ url += "/" + app;
989+ }
990+ url += "/" + item;
991+ if (!device.empty())
992+ {
993+ url += "?device=" + device;
994+ }
995+
996+ /* Sign the request */
997+ auto auth = token->signUrl(url, "GET");
998+ if (!auth.empty())
999+ {
1000+ std::string header("Authorization: ");
1001+ header += auth;
1002+ curlHeaders = curl_slist_append(curlHeaders, auth.c_str());
1003+ }
1004+
1005+ /* Ensure we get JSON back */
1006+ curlHeaders = curl_slist_append(curlHeaders, "Accept: application/json");
1007+
1008 handle = curl_easy_init();
1009
1010 /* Helps with threads */
1011@@ -40,16 +70,28 @@
1012 curl_easy_setopt(handle, CURLOPT_URL, url.c_str());
1013 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, curlWrite);
1014 curl_easy_setopt(handle, CURLOPT_WRITEDATA, this);
1015+ if (curlHeaders != nullptr)
1016+ {
1017+ curl_easy_setopt(handle, CURLOPT_HTTPHEADER, curlHeaders);
1018+ }
1019 }
1020
1021 ~CurlItem (void)
1022 {
1023- if (exec->joinable())
1024+ stop = true;
1025+
1026+ if (exec.joinable())
1027 {
1028- exec->join();
1029+ exec.join();
1030 }
1031
1032 curl_easy_cleanup(handle);
1033+
1034+ if (curlHeaders != nullptr)
1035+ {
1036+ curl_slist_free_all (curlHeaders) ;
1037+ curlHeaders = nullptr;
1038+ }
1039 }
1040
1041 virtual bool run (void)
1042@@ -58,14 +100,23 @@
1043
1044 /* Do the execution in another thread so we can wait on the
1045 network socket. */
1046- exec = std::make_shared<std::thread>([this]()
1047+ exec = std::thread([this]()
1048 {
1049 auto status = curl_easy_perform(handle);
1050
1051 if (status == CURLE_OK)
1052 {
1053- /* TODO: Clearly we need to be a bit more sophisticated here */
1054- verificationComplete(Status::NOT_PURCHASED);
1055+ long responsecode = 0;
1056+ curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &responsecode);
1057+
1058+ if (responsecode == 200)
1059+ {
1060+ verificationComplete(Status::PURCHASED);
1061+ }
1062+ else
1063+ {
1064+ verificationComplete(Status::NOT_PURCHASED);
1065+ }
1066 }
1067 else
1068 {
1069@@ -79,8 +130,10 @@
1070 private:
1071 CURL* handle;
1072 std::string transferBuffer;
1073- std::shared_ptr<std::thread> exec;
1074+ std::thread exec;
1075 std::string url;
1076+ bool stop;
1077+ struct curl_slist* curlHeaders;
1078
1079 /* This is the callback from cURL as it does the transfer. We're
1080 pretty simple in that we're just putting it into a string. */
1081@@ -89,14 +142,33 @@
1082 auto datasize = size * nmemb;
1083 //std::cout << "Got data: " << datasize << std::endl;
1084 CurlItem* item = static_cast<CurlItem*>(user_data);
1085+ if (item->stop)
1086+ {
1087+ std::cout << "cURL transaction stopped prematurely" << std::endl;
1088+ return 0;
1089+ }
1090 item->transferBuffer.append(static_cast<char*>(buffer), datasize);
1091 return datasize;
1092 }
1093 };
1094
1095-CurlFactory::CurlFactory () :
1096- endpoint("https://launchpad.net")
1097+/*********************
1098+ * CurlFactory
1099+ *********************/
1100+
1101+CurlFactory::CurlFactory (TokenGrabber::Ptr token) :
1102+ endpoint("https://sc.ubuntu.com/api/2.0/click/purchases"),
1103+ tokenGrabber(token)
1104 {
1105+ /* This is how we enable staging */
1106+ const char* envendpoint(getenv("PAY_BASE_URL"));
1107+ if (envendpoint != nullptr)
1108+ {
1109+ endpoint = envendpoint;
1110+ /* Our endpoint is slightly more specific */
1111+ endpoint += "/purchases";
1112+ }
1113+
1114 /* TODO: We should check to see if we have networking someday */
1115 curl_global_init(CURL_GLOBAL_SSL);
1116 }
1117@@ -116,7 +188,7 @@
1118 Item::Ptr
1119 CurlFactory::verifyItem (std::string& appid, std::string& itemid)
1120 {
1121- return std::make_shared<CurlItem>(appid, itemid, endpoint);
1122+ return std::make_shared<CurlItem>(appid, itemid, endpoint, device, tokenGrabber);
1123 }
1124
1125 void
1126@@ -125,4 +197,11 @@
1127 endpoint = in_endpoint;
1128 }
1129
1130+void
1131+CurlFactory::setDevice (std::string& in_device)
1132+{
1133+ device = in_device;
1134+}
1135+
1136 } // ns Verification
1137+
1138
1139=== modified file 'service/verification-curl.h'
1140--- service/verification-curl.h 2014-05-07 21:01:52 +0000
1141+++ service/verification-curl.h 2014-06-18 18:46:48 +0000
1142@@ -18,6 +18,9 @@
1143 */
1144
1145 #include "verification-factory.h"
1146+#include "token-grabber.h"
1147+
1148+#include <memory>
1149
1150 #ifndef VERIFICATION_CURL_HPP__
1151 #define VERIFICATION_CURL_HPP__ 1
1152@@ -26,7 +29,7 @@
1153
1154 class CurlFactory : public Factory {
1155 public:
1156- CurlFactory ();
1157+ CurlFactory (TokenGrabber::Ptr token);
1158 CurlFactory (const std::string& endpoint);
1159 ~CurlFactory ();
1160
1161@@ -34,9 +37,12 @@
1162 virtual Item::Ptr verifyItem (std::string& appid, std::string& itemid);
1163
1164 void setEndpoint (std::string& endpoint);
1165+ void setDevice (std::string& device);
1166
1167 private:
1168 std::string endpoint;
1169+ std::string device;
1170+ TokenGrabber::Ptr tokenGrabber;
1171 };
1172
1173 } // ns Verification
1174
1175=== modified file 'tests/CMakeLists.txt'
1176--- tests/CMakeLists.txt 2014-05-20 19:06:37 +0000
1177+++ tests/CMakeLists.txt 2014-06-18 18:46:48 +0000
1178@@ -18,7 +18,6 @@
1179
1180 ${GMOCK_INCLUDE_DIR}
1181 ${GTEST_INCLUDE_DIR}
1182- ${PROCESS_CPP_INCLUDE_DIRS}
1183 )
1184
1185 #############################
1186@@ -36,7 +35,6 @@
1187 pay-service-lib
1188
1189 ${SERVICE_DEPS_LIBRARIES}
1190- ${PROCESS_CPP_LIBRARIES}
1191 ${CMAKE_THREAD_LIBS_INIT}
1192 ${GMOCK_BOTH_LIBRARIES}
1193 )
1194@@ -84,8 +82,14 @@
1195 )
1196
1197 configure_file(
1198- ${CMAKE_CURRENT_SOURCE_DIR}/verification-curl-endpoints/good-simple.in
1199- ${CMAKE_CURRENT_BINARY_DIR}/verification-curl-endpoints/good-simple @ONLY)
1200+ ${CMAKE_CURRENT_SOURCE_DIR}/verification-curl-endpoints/good/simple.in
1201+ ${CMAKE_CURRENT_BINARY_DIR}/verification-curl-endpoints/good/simple @ONLY)
1202+configure_file(
1203+ ${CMAKE_CURRENT_SOURCE_DIR}/verification-curl-endpoints/good/device-id.in
1204+ ${CMAKE_CURRENT_BINARY_DIR}/verification-curl-endpoints/good/device-id @ONLY)
1205+configure_file(
1206+ ${CMAKE_CURRENT_SOURCE_DIR}/verification-curl-endpoints/package-name.in
1207+ ${CMAKE_CURRENT_BINARY_DIR}/verification-curl-endpoints/package-name @ONLY)
1208
1209 add_test(verification-curl-tests ${CMAKE_CURRENT_BINARY_DIR}/verification-curl-tests)
1210
1211@@ -93,6 +97,13 @@
1212 # Purchase UAL
1213 #############################
1214
1215+configure_file(
1216+ ${CMAKE_CURRENT_SOURCE_DIR}/click-db/test.conf.in
1217+ ${CMAKE_CURRENT_BINARY_DIR}/click-db/test.conf @ONLY)
1218+
1219+add_definitions(-DCMAKE_BINARY_DIR="${CMAKE_CURRENT_BINARY_DIR}")
1220+add_definitions(-DCMAKE_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
1221+
1222 add_executable(
1223 purchase-ual-tests
1224 purchase-ual-tests.cpp
1225@@ -126,3 +137,8 @@
1226
1227 add_test(libpay-tests ${CMAKE_CURRENT_BINARY_DIR}/libpay-tests)
1228
1229+#############################
1230+# staging script
1231+#############################
1232+
1233+install(PROGRAMS setup-staging.sh DESTINATION ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR})
1234
1235=== added directory 'tests/click-data'
1236=== added directory 'tests/click-data/.click'
1237=== added directory 'tests/click-data/.click/users'
1238=== added directory 'tests/click-data/.click/users/test-user'
1239=== added symlink 'tests/click-data/.click/users/test-user/com.canonical.payui'
1240=== target is u'../../../com.canonical.payui/0.1/'
1241=== added directory 'tests/click-data/com.canonical.payui'
1242=== added directory 'tests/click-data/com.canonical.payui/0.1'
1243=== added directory 'tests/click-data/com.canonical.payui/0.1/.click'
1244=== added directory 'tests/click-data/com.canonical.payui/0.1/.click/info'
1245=== added file 'tests/click-data/com.canonical.payui/0.1/.click/info/com.canonical.payui.manifest'
1246--- tests/click-data/com.canonical.payui/0.1/.click/info/com.canonical.payui.manifest 1970-01-01 00:00:00 +0000
1247+++ tests/click-data/com.canonical.payui/0.1/.click/info/com.canonical.payui.manifest 2014-06-18 18:46:48 +0000
1248@@ -0,0 +1,9 @@
1249+{
1250+ "version": "0.1",
1251+ "name": "com.canonical.payui",
1252+ "hooks": {
1253+ "app1": {
1254+ "desktop": "test.desktop"
1255+ }
1256+ }
1257+}
1258
1259=== added file 'tests/click-data/com.canonical.payui/0.1/test.desktop'
1260--- tests/click-data/com.canonical.payui/0.1/test.desktop 1970-01-01 00:00:00 +0000
1261+++ tests/click-data/com.canonical.payui/0.1/test.desktop 2014-06-18 18:46:48 +0000
1262@@ -0,0 +1,3 @@
1263+[Desktop Entry]
1264+Name=Test
1265+Exec=test
1266
1267=== added directory 'tests/click-db'
1268=== added file 'tests/click-db/test.conf.in'
1269--- tests/click-db/test.conf.in 1970-01-01 00:00:00 +0000
1270+++ tests/click-db/test.conf.in 2014-06-18 18:46:48 +0000
1271@@ -0,0 +1,2 @@
1272+[Click Database]
1273+root = @CMAKE_CURRENT_SOURCE_DIR@/click-data
1274
1275=== modified file 'tests/dbus-interface-tests.cpp'
1276--- tests/dbus-interface-tests.cpp 2014-05-28 14:57:59 +0000
1277+++ tests/dbus-interface-tests.cpp 2014-06-18 18:46:48 +0000
1278@@ -143,10 +143,18 @@
1279
1280 void signalAppend (GObject* obj, const gchar* itemid, const gchar* status, std::vector<std::string>& list)
1281 {
1282+ std::cout << "Signal append: " << itemid << ", " << status << std::endl;
1283 ASSERT_STREQ("fooitem", itemid);
1284 list.push_back(status);
1285 }
1286
1287+int loop_quit (void* data)
1288+{
1289+ auto loop = reinterpret_cast<GMainLoop*>(data);
1290+ g_main_loop_quit(loop);
1291+ return G_SOURCE_REMOVE;
1292+}
1293+
1294 TEST_F(DbusInterfaceTests, ItemSignalTests)
1295 {
1296 core::testing::CrossProcessSync cps1;
1297@@ -178,6 +186,9 @@
1298 titem->test_setStatus(Item::Item::NOT_PURCHASED, true);
1299 titem->test_setStatus(Item::Item::PURCHASED, true);
1300
1301+ /* Let the signals escape before we shut things down */
1302+ usleep(100000);
1303+
1304 /* Force deallocation so we can catch stuff from it */
1305 test_store.reset();
1306 pay_service.reset();
1307@@ -185,7 +196,7 @@
1308 return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success;
1309 };
1310
1311- auto client = [this, &cps1, &cps2]()
1312+ auto client = [this, &cps1, &cps2]() -> core::posix::exit::Status
1313 {
1314 GMainContext* context = g_main_context_new();
1315 g_main_context_push_thread_default(context);
1316@@ -220,8 +231,19 @@
1317 trap->run();
1318
1319 /* Pull the events through */
1320- while (g_main_context_iteration(context, FALSE)) {}
1321+ auto loop = g_main_loop_new(context, FALSE);
1322+ auto timeout = g_timeout_source_new(200);
1323+ g_source_set_callback(timeout, loop_quit, loop, nullptr);
1324+ g_source_attach(timeout, context);
1325+ g_main_loop_run(loop);
1326+ g_main_loop_unref(loop);
1327
1328+ /* Can't use assert in lambdas */
1329+ if (4 != itemsignals.size())
1330+ {
1331+ std::cerr << "ERROR: Item signals isn't correct size (4): " << itemsignals.size() << std::endl;
1332+ throw;
1333+ }
1334 EXPECT_EQ("verifying", itemsignals[0]);
1335 EXPECT_EQ("purchasing", itemsignals[1]);
1336 EXPECT_EQ("not purchased", itemsignals[2]);
1337
1338=== modified file 'tests/libpay-tests.cpp'
1339--- tests/libpay-tests.cpp 2014-05-29 06:51:32 +0000
1340+++ tests/libpay-tests.cpp 2014-06-18 18:46:48 +0000
1341@@ -44,10 +44,10 @@
1342 "ListPackages",
1343 nullptr,
1344 G_VARIANT_TYPE("ao"), /* out */
1345- "ret = [ dbus.ObjectPath('/com/canonical/pay/package') ]", /* python */
1346+ "ret = [ dbus.ObjectPath('/com/canonical/pay/package_2Dname') ]", /* python */
1347 nullptr); /* error */
1348
1349- pkgobj = dbus_test_dbus_mock_get_object(mock, "/com/canonical/pay/package", "com.canonical.pay.package", nullptr);
1350+ pkgobj = dbus_test_dbus_mock_get_object(mock, "/com/canonical/pay/package_2Dname", "com.canonical.pay.package", nullptr);
1351
1352 dbus_test_dbus_mock_object_add_method(mock, pkgobj,
1353 "VerifyItem",
1354@@ -94,13 +94,13 @@
1355
1356 TEST_F(LibPayTests, InitTest)
1357 {
1358- auto package = pay_package_new("package");
1359+ auto package = pay_package_new("package-name");
1360 pay_package_delete(package);
1361 }
1362
1363 TEST_F(LibPayTests, ItemLifecycle)
1364 {
1365- auto package = pay_package_new("package");
1366+ auto package = pay_package_new("package-name");
1367
1368 EXPECT_EQ(PAY_PACKAGE_ITEM_STATUS_UNKNOWN, pay_package_item_status(package, "item"));
1369
1370@@ -187,7 +187,7 @@
1371 {
1372 GError* error = nullptr;
1373 guint callcount = 0;
1374- auto package = pay_package_new("package");
1375+ auto package = pay_package_new("package-name");
1376
1377 EXPECT_TRUE(pay_package_item_start_verification(package, "item"));
1378
1379
1380=== added file 'tests/manual'
1381--- tests/manual 1970-01-01 00:00:00 +0000
1382+++ tests/manual 2014-06-18 18:46:48 +0000
1383@@ -0,0 +1,30 @@
1384+Test-case pay-service/setup-staging-account
1385+<dl>
1386+ <dt>Ensure that no U1 accounts are configured on the device</dt>
1387+ <dd>Going to System Settings → Accounts shouldn't have any "Ubuntu One" entries shown</dd>
1388+ <dd>Delete the account if there is one shown and exit system settings</dd>
1389+ <dt>Set up the device to use the staging servers for Ubuntu One</dt>
1390+ <dd>Execute the script: /usr/lib/*/pay-service/setup-staging.sh</dd>
1391+ <dd>It should run to completion without error</dd>
1392+ <dt>Create an Ubuntu One account: ${your name}+${unique number}@canonical.com</dt>
1393+ <dd>NOTE: This is only currently available to Canonical employees. There is work underway to make it more widely available but it is not complete yet.</dd>
1394+ <dd>The System Settings app should allow creating the account and the account show show up in the list of accounts on the device</dd>
1395+ <dt>Add a testing credit card to your account</dt>
1396+ <dd>The System Settings pane for Payment Options should allow for setting up the testing credit cards (numbers not listed here)</dd>
1397+</dl>
1398+
1399+Test-case pay-service/purchase-application
1400+<dl>
1401+ <dt>Create a new account with no previously purchased items</dt>
1402+ <dd>The instructions in <tt>pay-service/setup-staging-account</tt> pass without error</dd>
1403+ <dt>Find the "QR-Code" app in the application scope</dt>
1404+ <dd>By searching for "QR-Code" an app should be listed and clicking on it show its preview</dd>
1405+ <dt>Ensure that the QR-Code app has not been previously purchased</dt>
1406+ <dd>The button in the preview should show the price of the app and not "Open" or "Install"</dd>
1407+ <dt>Wait 10 minutes for the token to expire</dt>
1408+ <dd>Take a nice walk on a sunny day</dd>
1409+ <dt>Select the "Purchase" button for the "QR-Code" application</dt>
1410+ <dd>The button should depress and a window should appear asking for your Ubuntu One password</dd>
1411+ <dt>Enter your password and confirm the purchase</dt>
1412+ <dd>The window should close and the application should begin downloading and install</dd>
1413+</dl>
1414
1415=== modified file 'tests/purchase-ual-tests.cpp'
1416--- tests/purchase-ual-tests.cpp 2014-05-26 14:01:00 +0000
1417+++ tests/purchase-ual-tests.cpp 2014-06-18 18:46:48 +0000
1418@@ -33,6 +33,13 @@
1419 GDBusConnection * bus = NULL;
1420
1421 virtual void SetUp() {
1422+ g_setenv("TEST_CLICK_DB", "click-db", TRUE);
1423+ g_setenv("TEST_CLICK_USER", "test-user", TRUE);
1424+
1425+ gchar * linkfarm = g_build_filename(CMAKE_SOURCE_DIR, "ual-link-farm", NULL);
1426+ g_setenv("UPSTART_APP_LAUNCH_LINK_FARM", linkfarm, TRUE);
1427+ g_free(linkfarm);
1428+
1429 service = dbus_test_service_new(NULL);
1430
1431 mock = dbus_test_dbus_mock_new("com.ubuntu.Upstart");
1432@@ -107,7 +114,7 @@
1433 EXPECT_TRUE(item->run());
1434 usleep(20 * 1000);
1435
1436- dbus_test_dbus_mock_object_emit_signal(mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"), g_variant_new_parsed("('stopped', ['JOB=application-legacy', 'INSTANCE=gedit-'])"), NULL);
1437+ dbus_test_dbus_mock_object_emit_signal(mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"), g_variant_new_parsed("('stopped', ['JOB=application-click', 'INSTANCE=com.canonical.payui_app1_0.1'])"), NULL);
1438 usleep(20 * 1000);
1439
1440 EXPECT_EQ(Purchase::Item::Status::PURCHASED, status);
1441@@ -128,7 +135,7 @@
1442 usleep(20 * 1000);
1443
1444 GError * error = NULL;
1445- g_spawn_command_line_async("gdbus emit --session --object-path / --signal com.canonical.UbuntuAppLaunch.ApplicationFailed gedit crash", &error);
1446+ g_spawn_command_line_async("gdbus emit --session --object-path / --signal com.canonical.UbuntuAppLaunch.ApplicationFailed com.canonical.payui_app1_0.1 crash", &error);
1447 ASSERT_EQ(nullptr, error);
1448
1449 usleep(100 * 1000);
1450
1451=== added file 'tests/setup-staging.sh'
1452--- tests/setup-staging.sh 1970-01-01 00:00:00 +0000
1453+++ tests/setup-staging.sh 2014-06-18 18:46:48 +0000
1454@@ -0,0 +1,19 @@
1455+#!/bin/bash -e
1456+# This small script sets up the variables needed for acceptance
1457+# testing of the pay-service and pay related things.
1458+
1459+echo "Setting up staging environment variables"
1460+
1461+initctl set-env --global SSO_AUTH_BASE_URL=https://login.staging.ubuntu.com
1462+initctl set-env --global SSO_UONE_BASE_URL=https://staging.one.ubuntu.com
1463+initctl set-env --global PAY_BASE_URL=https://sc.staging.ubuntu.com/api/2.0/click/
1464+initctl set-env --global URL_PACKAGE_INFO=https://search.apps.staging.ubuntu.com/api/v1/package/
1465+initctl set-env --global ACCOUNT_CREDS_URL=https://login.staging.ubuntu.com/api/v2/tokens/oauth
1466+initctl set-env --global ADD_PAYMENT_URL=https://sc.staging.ubuntu.com/api/2.0/click/paymentmethods/add/
1467+initctl set-env --global U1_SEARCH_BASE_URL=https://search.apps.staging.ubuntu.com/
1468+
1469+echo "Restarting scope registry"
1470+restart scope-registry
1471+
1472+echo "Restarting pay service"
1473+restart pay-service
1474
1475=== added file 'tests/token-grabber-null.h'
1476--- tests/token-grabber-null.h 1970-01-01 00:00:00 +0000
1477+++ tests/token-grabber-null.h 2014-06-18 18:46:48 +0000
1478@@ -0,0 +1,36 @@
1479+/*
1480+ * Copyright © 2014 Canonical Ltd.
1481+ *
1482+ * This program is free software: you can redistribute it and/or modify it
1483+ * under the terms of the GNU General Public License version 3, as published
1484+ * by the Free Software Foundation.
1485+ *
1486+ * This program is distributed in the hope that it will be useful, but
1487+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1488+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1489+ * PURPOSE. See the GNU General Public License for more details.
1490+ *
1491+ * You should have received a copy of the GNU General Public License along
1492+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1493+ *
1494+ * Authors:
1495+ * Ted Gould <ted.gould@canonical.com>
1496+ */
1497+
1498+#ifndef TOKEN_GRABBER_NULL_HPP__
1499+#define TOKEN_GRABBER_NULL_HPP__ 1
1500+
1501+#include "service/token-grabber.h"
1502+
1503+class TokenGrabberNull : public TokenGrabber
1504+{
1505+public:
1506+ virtual std::string signUrl (std::string url, std::string type)
1507+ {
1508+ std::string retval;
1509+ return retval;
1510+ }
1511+};
1512+
1513+#endif /* TOKEN_GRABBER_NULL_HPP__ */
1514+
1515
1516=== added directory 'tests/ual-link-farm'
1517=== added file 'tests/ual-link-farm/com.canonical.payui_app1_0.1.desktop'
1518=== added directory 'tests/verification-curl-endpoints/good'
1519=== added file 'tests/verification-curl-endpoints/good/device-id.in'
1520--- tests/verification-curl-endpoints/good/device-id.in 1970-01-01 00:00:00 +0000
1521+++ tests/verification-curl-endpoints/good/device-id.in 2014-06-18 18:46:48 +0000
1522@@ -0,0 +1,5 @@
1523+{
1524+ /* Not sure of all the detail here, but making it simple to get started */
1525+ good: "simple",
1526+ device: "1234"
1527+}
1528
1529=== renamed file 'tests/verification-curl-endpoints/good-simple.in' => 'tests/verification-curl-endpoints/good/simple.in'
1530=== added file 'tests/verification-curl-endpoints/package-name.in'
1531--- tests/verification-curl-endpoints/package-name.in 1970-01-01 00:00:00 +0000
1532+++ tests/verification-curl-endpoints/package-name.in 2014-06-18 18:46:48 +0000
1533@@ -0,0 +1,3 @@
1534+{
1535+ "item": "package-name"
1536+}
1537
1538=== modified file 'tests/verification-curl-tests.cpp'
1539--- tests/verification-curl-tests.cpp 2014-05-19 14:47:16 +0000
1540+++ tests/verification-curl-tests.cpp 2014-06-18 18:46:48 +0000
1541@@ -19,6 +19,7 @@
1542
1543 #include <gtest/gtest.h>
1544 #include "service/verification-curl.h"
1545+#include "token-grabber-null.h"
1546
1547 struct VerificationCurlTests : public ::testing::Test
1548 {
1549@@ -26,16 +27,20 @@
1550 virtual void SetUp() {
1551 endpoint = "file://";
1552 endpoint += VERIFICATION_CURL_ENDPOINTS_DIR;
1553+
1554+ device = "1234";
1555 }
1556
1557 virtual void TearDown() {
1558 }
1559
1560 std::string endpoint;
1561+ std::string device;
1562 };
1563
1564 TEST_F(VerificationCurlTests, InitTest) {
1565- auto verify = std::make_shared<Verification::CurlFactory>();
1566+ auto token = std::make_shared<TokenGrabberNull>();
1567+ auto verify = std::make_shared<Verification::CurlFactory>(token);
1568 EXPECT_NE(nullptr, verify);
1569 verify->setEndpoint(endpoint);
1570 verify.reset();
1571@@ -43,7 +48,8 @@
1572 }
1573
1574 TEST_F(VerificationCurlTests, PurchaseItem) {
1575- auto verify = std::make_shared<Verification::CurlFactory>();
1576+ auto token = std::make_shared<TokenGrabberNull>();
1577+ auto verify = std::make_shared<Verification::CurlFactory>(token);
1578 ASSERT_NE(nullptr, verify);
1579 verify->setEndpoint(endpoint);
1580
1581@@ -73,3 +79,46 @@
1582
1583 EXPECT_EQ(Verification::Item::Status::ERROR, badstatus);
1584 }
1585+
1586+TEST_F(VerificationCurlTests, ClickScope) {
1587+ auto token = std::make_shared<TokenGrabberNull>();
1588+ auto verify = std::make_shared<Verification::CurlFactory>(token);
1589+ ASSERT_NE(nullptr, verify);
1590+ verify->setEndpoint(endpoint);
1591+
1592+ std::string appid("click-scope");
1593+ std::string itemid("package-name");
1594+
1595+ auto item = verify->verifyItem(appid, itemid);
1596+ ASSERT_NE(nullptr, item);
1597+
1598+ Verification::Item::Status status = Verification::Item::Status::ERROR;
1599+ item->verificationComplete.connect([&status] (Verification::Item::Status in_status) { status = in_status; });
1600+
1601+ ASSERT_TRUE(item->run());
1602+ usleep(20 * 1000);
1603+
1604+ EXPECT_EQ(Verification::Item::Status::NOT_PURCHASED, status);
1605+}
1606+
1607+TEST_F(VerificationCurlTests, DeviceId) {
1608+ auto token = std::make_shared<TokenGrabberNull>();
1609+ auto verify = std::make_shared<Verification::CurlFactory>(token);
1610+ ASSERT_NE(nullptr, verify);
1611+ verify->setEndpoint(endpoint);
1612+ verify->setDevice(device);
1613+
1614+ std::string appid("good");
1615+ std::string itemid("device-id");
1616+
1617+ auto item = verify->verifyItem(appid, itemid);
1618+ ASSERT_NE(nullptr, item);
1619+
1620+ Verification::Item::Status status = Verification::Item::Status::ERROR;
1621+ item->verificationComplete.connect([&status] (Verification::Item::Status in_status) { status = in_status; });
1622+
1623+ ASSERT_TRUE(item->run());
1624+ usleep(20 * 1000);
1625+
1626+ EXPECT_EQ(Verification::Item::Status::NOT_PURCHASED, status);
1627+}

Subscribers

People subscribed via source and target branches

to all changes: