Merge lp:~alecu/unity-scope-click/fix-empty-variant into lp:unity-scope-click

Proposed by Alejandro J. Cura on 2015-01-13
Status: Merged
Approved by: Alejandro J. Cura on 2015-01-14
Approved revision: 304
Merged at revision: 304
Proposed branch: lp:~alecu/unity-scope-click/fix-empty-variant
Merge into: lp:unity-scope-click
Diff against target: 198 lines (+76/-8)
5 files modified
libclickscope/click/index.h (+1/-0)
libclickscope/click/preview.cpp (+22/-6)
libclickscope/click/preview.h (+1/-0)
libclickscope/click/reviews.h (+2/-1)
libclickscope/tests/test_preview.cpp (+50/-1)
To merge this branch: bzr merge lp:~alecu/unity-scope-click/fix-empty-variant
Reviewer Review Type Date Requested Status
Michi Henning (community) Approve on 2015-01-14
PS Jenkins bot continuous-integration Approve on 2015-01-14
dobey (community) 2015-01-13 Approve on 2015-01-13
Review via email: mp+246365@code.launchpad.net

Commit Message

Check Variant for null value

To post a comment you must log in.
dobey (dobey) wrote :

Looks ok.

review: Approve
Michi Henning (michihenning) wrote :

Thanks for the fix, thanks!

I'm wondering about a more general issue here though. Basically, what happens is that, during destruction of an object, an exception is thrown, which brings down the process. I take it that this is simply the destruction of an object that is used by the scope internally?

As best as I know, any methods that are called by the scopes run time are exception-safe so, if scope throws an exception from anything that is called by the run time, things should be safe. If this is not the case, and the object is being destroyed within the context of a thread that is owned by the run time, I need to know so I can fix it :-)

In general, whenever I have a non-trivial destructor, I have a try-catch block around the code that, if an exception is thrown, logs the exception. If the program is no longer in a sane state after that, I call abort(). Something like:

SomeObject::~SomeObject()
{
    try
    {
        something_->func();
    }
    catch (std::exception const& e)
    {
        LOG(string("~SomeObject, func() threw ") + e.what();
        // call abort() here if it's not possible to recover.
    }
    catch (...)
    {
        LOG("~SomeObject, func() unknown exception");
        // call abort() here if it's not possible to recover.
    }
}

review: Needs Information
Michi Henning (michihenning) wrote :

Looks right.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'libclickscope/click/index.h'
2--- libclickscope/click/index.h 2014-10-01 03:37:34 +0000
3+++ libclickscope/click/index.h 2015-01-13 22:19:09 +0000
4@@ -74,6 +74,7 @@
5
6 public:
7 enum class Error {NoError, CredentialsError, NetworkError};
8+ Index() {}
9 Index(const QSharedPointer<click::web::Client>& client,
10 const QSharedPointer<Configuration> configuration=QSharedPointer<Configuration>(new Configuration()));
11 virtual std::pair<Packages, Packages> package_lists_from_json(const std::string& json);
12
13=== modified file 'libclickscope/click/preview.cpp'
14--- libclickscope/click/preview.cpp 2014-10-22 19:32:37 +0000
15+++ libclickscope/click/preview.cpp 2015-01-13 22:19:09 +0000
16@@ -254,6 +254,22 @@
17 return b.str();
18 }
19
20+void PreviewStrategy::run_under_qt(const std::function<void ()> &task)
21+{
22+ qt::core::world::enter_with_task([task]() {
23+ task();
24+ });
25+}
26+
27+std::string get_string_maybe_null(scopes::Variant variant)
28+{
29+ if (variant.is_null()) {
30+ return "";
31+ } else {
32+ return variant.get_string();
33+ }
34+}
35+
36 // TODO: error handling - once get_details provides errors, we can
37 // return them from populateDetails and check them in the calling code
38 // to decide whether to show error widgets. see bug LP: #1289541
39@@ -262,15 +278,15 @@
40 click::Reviews::Error)> reviews_callback)
41 {
42
43- std::string app_name = result["name"].get_string();
44+ std::string app_name = get_string_maybe_null(result["name"]);
45
46 if (app_name.empty()) {
47 click::PackageDetails details;
48 qDebug() << "in populateDetails(), app_name is empty";
49 details.package.title = result.title();
50 details.package.icon_url = result.art();
51- details.description = result["description"].get_string();
52- details.main_screenshot_url = result["main_screenshot"].get_string();
53+ details.description = get_string_maybe_null(result["description"]);
54+ details.main_screenshot_url = get_string_maybe_null(result["main_screenshot"]);
55 details_callback(details);
56 reviews_callback(click::ReviewList(), click::Reviews::Error::NoError);
57 } else {
58@@ -278,7 +294,7 @@
59 // I think this should not be required when we switch the click::Index over
60 // to using the Qt bridge. With that, the qt dependency becomes an implementation detail
61 // and code using it does not need to worry about threading/event loop topics.
62- qt::core::world::enter_with_task([this, details_callback, reviews_callback, app_name]()
63+ run_under_qt([this, details_callback, reviews_callback, app_name]()
64 {
65 index_operation = index->get_details(app_name, [this, app_name, details_callback, reviews_callback](PackageDetails details, click::Index::Error error){
66 if(error == click::Index::Error::NoError) {
67@@ -289,8 +305,8 @@
68 click::PackageDetails details;
69 details.package.title = result.title();
70 details.package.icon_url = result.art();
71- details.description = result["description"].get_string();
72- details.main_screenshot_url = result["main_screenshot"].get_string();
73+ details.description = get_string_maybe_null(result["description"]);
74+ details.main_screenshot_url = get_string_maybe_null(result["main_screenshot"]);
75 details_callback(details);
76 }
77 reviews_operation = reviews->fetch_reviews(app_name,
78
79=== modified file 'libclickscope/click/preview.h'
80--- libclickscope/click/preview.h 2014-10-16 21:25:16 +0000
81+++ libclickscope/click/preview.h 2015-01-13 22:19:09 +0000
82@@ -149,6 +149,7 @@
83 virtual std::string build_other_metadata(const PackageDetails& details);
84 virtual std::string build_updates_table(const PackageDetails& details);
85 virtual std::string build_whats_new(const PackageDetails& details);
86+ virtual void run_under_qt(const std::function<void ()> &task);
87
88 scopes::Result result;
89 QSharedPointer<click::web::Client> client;
90
91=== modified file 'libclickscope/click/reviews.h'
92--- libclickscope/click/reviews.h 2014-05-26 14:02:45 +0000
93+++ libclickscope/click/reviews.h 2015-01-13 22:19:09 +0000
94@@ -72,10 +72,11 @@
95 QSharedPointer<web::Client> client;
96 public:
97 enum class Error {NoError, CredentialsError, NetworkError};
98+ Reviews() {}
99 Reviews(const QSharedPointer<click::web::Client>& client);
100 virtual ~Reviews();
101
102- click::web::Cancellable fetch_reviews (const std::string& package_name,
103+ virtual click::web::Cancellable fetch_reviews (const std::string& package_name,
104 std::function<void(ReviewList, Error)> callback);
105 click::web::Cancellable submit_review (const Review& review,
106 std::function<void(Error)> callback);
107
108=== modified file 'libclickscope/tests/test_preview.cpp'
109--- libclickscope/tests/test_preview.cpp 2014-10-17 19:55:56 +0000
110+++ libclickscope/tests/test_preview.cpp 2015-01-13 22:19:09 +0000
111@@ -33,6 +33,8 @@
112 #include <gtest/gtest.h>
113 #include <click/preview.h>
114 #include <fake_json.h>
115+#include <click/index.h>
116+#include <click/reviews.h>
117
118 using namespace ::testing;
119 using namespace unity::scopes;
120@@ -46,23 +48,57 @@
121
122 };
123
124+class FakeIndex : public click::Index
125+{
126+public:
127+ FakeIndex() {
128+
129+ }
130+ click::web::Cancellable get_details(const std::string& /*package_name*/, std::function<void(click::PackageDetails, Error)> callback) override {
131+ callback(click::PackageDetails(), Error::NetworkError);
132+ return click::web::Cancellable();
133+ }
134+
135+};
136+
137+class FakeReviews : public click::Reviews
138+{
139+public:
140+ FakeReviews() {
141+
142+ }
143+
144+ click::web::Cancellable fetch_reviews(const std::string &/*package_name*/, std::function<void (click::ReviewList, Error)> callback) override {
145+ callback(click::ReviewList(), Error::NoError);
146+ return click::web::Cancellable();
147+ }
148+};
149+
150 class FakePreview : public click::PreviewStrategy
151 {
152 public:
153 FakePreview(Result& result) : click::PreviewStrategy::PreviewStrategy(result)
154 {
155-
156+ index.reset(new FakeIndex());
157+ reviews.reset(new FakeReviews());
158 }
159
160 void run(const PreviewReplyProxy &/*reply*/)
161 {
162
163 }
164+
165+ void run_under_qt(const std::function<void()> &task) {
166+ // when testing, do not actually run under qt
167+ task();
168+ }
169+
170 using click::PreviewStrategy::screenshotsWidgets;
171 using click::PreviewStrategy::descriptionWidgets;
172 using click::PreviewStrategy::build_other_metadata;
173 using click::PreviewStrategy::build_updates_table;
174 using click::PreviewStrategy::build_whats_new;
175+ using click::PreviewStrategy::populateDetails;
176 };
177
178 class PreviewsBaseTest : public Test
179@@ -103,6 +139,19 @@
180 ASSERT_EQ(sources[2].get_string(), "sshot3");
181 }
182
183+TEST_F(PreviewStrategyTest, testEmptyResults)
184+{
185+ FakeResult result{vm};
186+ result["name"] = "fake name";
187+ FakePreview preview{result};
188+ preview.populateDetails(
189+ [](const click::PackageDetails &){
190+ },
191+ [](const click::ReviewList&, click::Reviews::Error){
192+ });
193+
194+}
195+
196 class PreviewStrategyDescriptionTest : public PreviewStrategyTest
197 {
198 public:

Subscribers

People subscribed via source and target branches