Merge lp:~charlesk/indicator-display/lp-1572545-prompt-in-lockscreen into lp:indicator-display/15.10

Proposed by Charles Kerr
Status: Superseded
Proposed branch: lp:~charlesk/indicator-display/lp-1572545-prompt-in-lockscreen
Merge into: lp:indicator-display/15.10
Diff against target: 867 lines (+446/-155)
12 files modified
src/greeter.cpp (+130/-81)
src/greeter.h (+8/-2)
src/usb-manager.cpp (+59/-46)
src/usb-snap.cpp (+1/-0)
tests/integration/usb-manager-test.cpp (+11/-5)
tests/unit/CMakeLists.txt (+5/-0)
tests/unit/greeter-test.cpp (+159/-0)
tests/unit/usb-snap-test.cpp (+3/-8)
tests/utils/adbd-server.h (+9/-11)
tests/utils/gtest-print-helpers.h (+18/-0)
tests/utils/mock-greeter.h (+2/-2)
tests/utils/mock-unity-greeter.py (+41/-0)
To merge this branch: bzr merge lp:~charlesk/indicator-display/lp-1572545-prompt-in-lockscreen
Reviewer Review Type Date Requested Status
Charles Kerr (community) Approve
Antti Kaijanmäki (community) Approve
PS Jenkins bot (community) continuous-integration Needs Fixing
Review via email: mp+292544@code.launchpad.net

This proposal has been superseded by a proposal from 2016-09-22.

Commit message

Fix timing issue when talking to com.canonical.UnityGreeter on the bus.

Description of the change

Fix timing issue when talking to com.canonical.UnityGreeter on the bus.

Old behavior: watch for propertychanged events and ask it the value of the IsActive property.

New behavior: watch for propertychanged events and ask it the value of the IsActive property whenever we see the greeter appear on the bus. This way we'll pick up the property value even if the greeter appears on the bus after indicator-display running.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Antti Kaijanmäki (kaijanmaki) wrote :

LGTM.

review: Approve
Revision history for this message
Antti Kaijanmäki (kaijanmaki) wrote :

LGTM.

Revision history for this message
Antti Kaijanmäki (kaijanmaki) wrote :

LGTM.

Revision history for this message
Antti Kaijanmäki (kaijanmaki) wrote :

LGTM.

review: Approve
38. By Charles Kerr

increase timeout interval on usb manager integration test

Revision history for this message
Charles Kerr (charlesk) :
review: Approve
39. By Charles Kerr

don't clear the snap decision when the greeter changes state

40. By Charles Kerr

always send a response to adb even in edge cases; e.g. send an auto-NO if the usb connection is broken while the prompt is up

41. By Charles Kerr

sync UsbManagerFixture::USBDisconnectedDuringPrompt to r40, to expect a 'no' each time there's a usb disconnect

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/greeter.cpp'
--- src/greeter.cpp 2016-03-22 13:15:38 +0000
+++ src/greeter.cpp 2016-05-04 00:04:00 +0000
@@ -26,117 +26,166 @@
26{26{
27public:27public:
2828
29 Impl():29 Impl()
30 m_cancellable{g_cancellable_new()}30 {
31 {31 m_cancellable.reset(
32 g_bus_get(G_BUS_TYPE_SESSION, m_cancellable, on_bus_ready_static, this);32 g_cancellable_new(),
33 }33 [](GCancellable* o){
3434 g_cancellable_cancel(o);
35 ~Impl()35 g_clear_object(&o);
36 {36 }
37 g_cancellable_cancel(m_cancellable);37 );
38 g_clear_object(&m_cancellable);38
3939 g_bus_get(G_BUS_TYPE_SESSION, m_cancellable.get(), on_bus_ready, this);
40 if (m_subscription_id != 0)40 }
41 g_dbus_connection_signal_unsubscribe (m_bus, m_subscription_id);41
4242 ~Impl() =default;
43 g_clear_object(&m_bus);43
44 }44 core::Property<State>& state()
4545 {
46 core::Property<bool>& is_active()46 return m_state;
47 {
48 return m_is_active;
49 }47 }
5048
51private:49private:
5250
53 static void on_bus_ready_static(GObject* /*source*/, GAsyncResult* res, gpointer gself)51 void set_state(const State& state)
52 {
53 m_state.set(state);
54 }
55
56 static void on_bus_ready(
57 GObject* /*source*/,
58 GAsyncResult* res,
59 gpointer gself)
54 {60 {
55 GError* error {};61 GError* error {};
56 auto bus = g_bus_get_finish (res, &error);62 auto bus = g_bus_get_finish(res, &error);
57 if (error != nullptr) {63 if (error != nullptr)
64 {
58 if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))65 if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
59 g_warning("UsbSnap: Error getting session bus: %s", error->message);66 g_warning("Greeter: Error getting bus: %s", error->message);
60 g_clear_error(&error);67 g_clear_error(&error);
61 } else {68 }
62 static_cast<Impl*>(gself)->on_bus_ready(bus);69 else
63 }70 {
64 g_clear_object(&bus);71 auto self = static_cast<Impl*>(gself);
65 }72
6673 const auto watcher_id = g_bus_watch_name_on_connection(
67 void on_bus_ready(GDBusConnection* bus)74 bus,
68 {75 DBusNames::UnityGreeter::NAME,
69 m_bus = G_DBUS_CONNECTION(g_object_ref(G_OBJECT(bus)));76 G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
7077 on_greeter_appeared,
71 g_dbus_connection_call(m_bus,78 on_greeter_vanished,
72 DBusNames::UnityGreeter::NAME,79 gself,
73 DBusNames::UnityGreeter::PATH,80 nullptr);
74 DBusNames::Properties::INTERFACE,81
75 "Get",82 const auto subscription_id = g_dbus_connection_signal_subscribe(
76 g_variant_new("(ss)", DBusNames::UnityGreeter::INTERFACE, "IsActive"),83 bus,
77 G_VARIANT_TYPE("(v)"),84 DBusNames::UnityGreeter::NAME,
78 G_DBUS_CALL_FLAGS_NONE,85 DBusNames::Properties::INTERFACE,
79 -1,86 DBusNames::Properties::PropertiesChanged::NAME,
80 m_cancellable,87 DBusNames::UnityGreeter::PATH,
81 on_get_is_active_ready,88 DBusNames::UnityGreeter::INTERFACE,
82 this);89 G_DBUS_SIGNAL_FLAGS_NONE,
8390 on_properties_changed_signal,
84 m_subscription_id = g_dbus_connection_signal_subscribe(m_bus,91 gself,
85 DBusNames::UnityGreeter::NAME,92 nullptr);
86 DBusNames::Properties::INTERFACE,93
87 DBusNames::Properties::PropertiesChanged::NAME,94 self->m_bus.reset(
88 DBusNames::UnityGreeter::PATH,95 bus,
89 DBusNames::UnityGreeter::INTERFACE,96 [watcher_id, subscription_id](GDBusConnection* o){
90 G_DBUS_SIGNAL_FLAGS_NONE,97 g_bus_unwatch_name(watcher_id);
91 on_properties_changed_signal,98 g_dbus_connection_signal_unsubscribe(o, subscription_id);
92 this,99 g_clear_object(&o);
93 nullptr);100 }
94 }101 );
95102 }
96 static void on_get_is_active_ready(GObject* source, GAsyncResult* res, gpointer gself)103 }
104
105 static void on_greeter_appeared(
106 GDBusConnection* bus,
107 const char* /*name*/,
108 const char* name_owner,
109 gpointer gself)
110 {
111 auto self = static_cast<Impl*>(gself);
112
113 self->m_owner = name_owner;
114
115 g_dbus_connection_call(
116 bus,
117 DBusNames::UnityGreeter::NAME,
118 DBusNames::UnityGreeter::PATH,
119 DBusNames::Properties::INTERFACE,
120 "Get",
121 g_variant_new("(ss)", DBusNames::UnityGreeter::INTERFACE, "IsActive"),
122 G_VARIANT_TYPE("(v)"),
123 G_DBUS_CALL_FLAGS_NONE,
124 -1,
125 self->m_cancellable.get(),
126 on_get_is_active_ready,
127 gself);
128 }
129
130 static void on_greeter_vanished(
131 GDBusConnection* /*bus*/,
132 const char* /*name*/,
133 gpointer gself)
134 {
135 auto self = static_cast<Impl*>(gself);
136
137 self->m_owner.clear();
138 self->set_state(State::UNAVAILABLE);
139 }
140
141 static void on_get_is_active_ready(
142 GObject* source,
143 GAsyncResult* res,
144 gpointer gself)
97 {145 {
98 GError* error {};146 GError* error {};
99 auto v = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), res, &error);147 auto v = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), res, &error);
100 if (error != nullptr) {148 if (error != nullptr) {
101 if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))149 if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
102 g_warning("UsbSnap: Error getting session bus: %s", error->message);150 g_warning("Greeter: Error getting IsActive property: %s", error->message);
103 g_clear_error(&error);151 g_clear_error(&error);
104 } else {152 } else {
105 GVariant* is_active {};153 GVariant* is_active {};
106 g_variant_get_child(v, 0, "v", &is_active);154 g_variant_get_child(v, 0, "v", &is_active);
107 static_cast<Impl*>(gself)->m_is_active.set(g_variant_get_boolean(is_active));155 static_cast<Impl*>(gself)->set_state(g_variant_get_boolean(is_active) ? State::ACTIVE : State::INACTIVE);
108 g_clear_pointer(&is_active, g_variant_unref);156 g_clear_pointer(&is_active, g_variant_unref);
109 }157 }
110 g_clear_pointer(&v, g_variant_unref);158 g_clear_pointer(&v, g_variant_unref);
111 }159 }
112160
113 static void on_properties_changed_signal(GDBusConnection* /*connection*/,161 static void on_properties_changed_signal(
114 const gchar* /*sender_name*/,162 GDBusConnection* /*bus*/,
115 const gchar* object_path,163 const gchar* sender_name,
116 const gchar* interface_name,164 const gchar* object_path,
117 const gchar* signal_name,165 const gchar* interface_name,
118 GVariant* parameters,166 const gchar* signal_name,
119 gpointer gself)167 GVariant* parameters,
168 gpointer gself)
120 {169 {
170 auto self = static_cast<Impl*>(gself);
171
172 g_return_if_fail(!g_strcmp0(sender_name, self->m_owner.c_str()));
121 g_return_if_fail(!g_strcmp0(object_path, DBusNames::UnityGreeter::PATH));173 g_return_if_fail(!g_strcmp0(object_path, DBusNames::UnityGreeter::PATH));
122 g_return_if_fail(!g_strcmp0(interface_name, DBusNames::Properties::INTERFACE));174 g_return_if_fail(!g_strcmp0(interface_name, DBusNames::Properties::INTERFACE));
123 g_return_if_fail(!g_strcmp0(signal_name, DBusNames::Properties::PropertiesChanged::NAME));175 g_return_if_fail(!g_strcmp0(signal_name, DBusNames::Properties::PropertiesChanged::NAME));
124 g_return_if_fail(g_variant_is_of_type(parameters, G_VARIANT_TYPE(DBusNames::Properties::PropertiesChanged::ARGS_VARIANT_TYPE)));176 g_return_if_fail(g_variant_is_of_type(parameters, G_VARIANT_TYPE(DBusNames::Properties::PropertiesChanged::ARGS_VARIANT_TYPE)));
125177
126 auto v = g_variant_get_child_value (parameters, 1);178 auto v = g_variant_get_child_value(parameters, 1);
127 gboolean is_active {};179 gboolean is_active {};
128 if (g_variant_lookup(v, "IsActive", "b", &is_active))180 if (g_variant_lookup(v, "IsActive", "b", &is_active))
129 {181 self->set_state(is_active ? State::ACTIVE : State::INACTIVE);
130 g_debug("%s is_active changed to %d", G_STRLOC, int(is_active));
131 static_cast<Impl*>(gself)->m_is_active.set(is_active);
132 }
133 g_clear_pointer(&v, g_variant_unref);182 g_clear_pointer(&v, g_variant_unref);
134 }183 }
135184
136 core::Property<bool> m_is_active;185 core::Property<State> m_state {State::UNAVAILABLE};
137 GCancellable* m_cancellable {};186 std::shared_ptr<GCancellable> m_cancellable;
138 GDBusConnection* m_bus {};187 std::shared_ptr<GDBusConnection> m_bus;
139 unsigned int m_subscription_id {};188 std::string m_owner;
140};189};
141190
142/***191/***
@@ -154,8 +203,8 @@
154203
155UnityGreeter::~UnityGreeter() =default;204UnityGreeter::~UnityGreeter() =default;
156205
157core::Property<bool>&206core::Property<Greeter::State>&
158UnityGreeter::is_active()207UnityGreeter::state()
159{208{
160 return impl->is_active();209 return impl->state();
161}210}
162211
=== modified file 'src/greeter.h'
--- src/greeter.h 2016-03-21 23:15:05 +0000
+++ src/greeter.h 2016-05-04 00:04:00 +0000
@@ -29,7 +29,13 @@
29public:29public:
30 Greeter();30 Greeter();
31 virtual ~Greeter();31 virtual ~Greeter();
32 virtual core::Property<bool>& is_active() =0;32
33 enum class State { UNAVAILABLE, INACTIVE, ACTIVE };
34static inline const char* state_str(const State& state) {
35 static constexpr char const * state_str[] = { "Unavailable", "Inactive", "Active" };
36 return state_str[int(state)];
37}
38 virtual core::Property<State>& state() =0;
33};39};
3440
3541
@@ -38,7 +44,7 @@
38public:44public:
39 UnityGreeter();45 UnityGreeter();
40 virtual ~UnityGreeter();46 virtual ~UnityGreeter();
41 core::Property<bool>& is_active() override;47 core::Property<State>& state() override;
4248
43protected:49protected:
44 class Impl;50 class Impl;
4551
=== modified file 'src/usb-manager.cpp'
--- src/usb-manager.cpp 2016-03-23 19:44:23 +0000
+++ src/usb-manager.cpp 2016-05-04 00:04:00 +0000
@@ -46,72 +46,84 @@
46 m_greeter{greeter}46 m_greeter{greeter}
47 {47 {
48 m_usb_monitor->on_usb_disconnected().connect([this](const std::string& /*usb_name*/) {48 m_usb_monitor->on_usb_disconnected().connect([this](const std::string& /*usb_name*/) {
49 restart();49 m_req.reset();
50 });50 });
5151
52 m_greeter->is_active().changed().connect([this](bool /*is_active*/) {52 m_greeter->state().changed().connect([this](const Greeter::State& state) {
53 maybe_snap();53 if (state == Greeter::State::INACTIVE)
54 });54 maybe_snap();
5555 else
56 restart();56 stop_snap();
57 });
58
59 // create a new adbd client
60 m_adbd_client.reset(new GAdbdClient{m_socket_path});
61 m_adbd_client->on_pk_request().connect(
62 [this](const AdbdClient::PKRequest& req) {
63 g_debug("%s got pk request: %s, calling maybe_snap()", G_STRLOC, req.fingerprint.c_str());
64
65 m_response = AdbdClient::PKResponse::DENY; // set the fallback response
66 m_req.reset(
67 new AdbdClient::PKRequest(req),
68 [this](AdbdClient::PKRequest* r) {
69 stop_snap();
70 r->respond(m_response);
71 delete r;
72 }
73 );
74 maybe_snap();
75 }
76 );
57 }77 }
5878
59 ~Impl()79 ~Impl()
60 {80 {
61 if (m_restart_idle_tag)81 if (m_request_complete_idle_tag)
62 g_source_remove(m_restart_idle_tag);82 g_source_remove(m_request_complete_idle_tag);
63
64 clear();
65 }83 }
6684
67private:85private:
6886
69 void clear()87 void stop_snap()
70 {88 {
71 // clear out old state
72 m_snap_connections.clear();89 m_snap_connections.clear();
73 m_snap.reset();90 m_snap.reset();
74 m_req = decltype(m_req)();
75 m_adbd_client.reset();
76 }
77
78 void restart()
79 {
80 clear();
81
82 // set a new client
83 m_adbd_client.reset(new GAdbdClient{m_socket_path});
84 m_adbd_client->on_pk_request().connect(
85 [this](const AdbdClient::PKRequest& req) {
86 g_debug("%s got pk request: %s", G_STRLOC, req.fingerprint.c_str());
87 m_req = req;
88 maybe_snap();
89 }
90 );
91 }91 }
9292
93 void maybe_snap()93 void maybe_snap()
94 {94 {
95 // don't prompt in the greeter!95 // only prompt if there's something to prompt about
96 if (!m_req.public_key.empty() && !m_greeter->is_active().get())96 if (!m_req)
97 snap();97 return;
98
99 // only prompt in an unlocked session
100 if (m_greeter->state().get() != Greeter::State::INACTIVE)
101 return;
102
103 snap();
98 }104 }
99105
100 void snap()106 void snap()
101 {107 {
102 m_snap = std::make_shared<UsbSnap>(m_req.fingerprint);108 m_snap = std::make_shared<UsbSnap>(m_req->fingerprint);
103 m_snap_connections.insert((*m_snap).on_user_response().connect(109 m_snap_connections.insert((*m_snap).on_user_response().connect(
104 [this](AdbdClient::PKResponse response, bool remember_choice){110 [this](AdbdClient::PKResponse response, bool remember_choice){
105 g_debug("%s user responded! response %d, remember %d", G_STRLOC, int(response), int(remember_choice));111
106 m_req.respond(response);
107 if (remember_choice && (response == AdbdClient::PKResponse::ALLOW))112 if (remember_choice && (response == AdbdClient::PKResponse::ALLOW))
108 write_public_key(m_req.public_key);113 write_public_key(m_req->public_key);
109 m_restart_idle_tag = g_idle_add([](gpointer gself){114
110 auto self = static_cast<Impl*>(gself);115 m_response = response;
111 self->m_restart_idle_tag = 0;116
112 self->restart();117 // defer finishing the request into an idle func because
113 return G_SOURCE_REMOVE;118 // ScopedConnections can't be destroyed inside their callbacks
114 }, this);119 if (m_request_complete_idle_tag == 0) {
120 m_request_complete_idle_tag = g_idle_add([](gpointer gself){
121 auto self = static_cast<Impl*>(gself);
122 self->m_request_complete_idle_tag = 0;
123 self->m_req.reset();
124 return G_SOURCE_REMOVE;
125 }, this);
126 }
115 }127 }
116 ));128 ));
117 }129 }
@@ -152,12 +164,13 @@
152 const std::shared_ptr<UsbMonitor> m_usb_monitor;164 const std::shared_ptr<UsbMonitor> m_usb_monitor;
153 const std::shared_ptr<Greeter> m_greeter;165 const std::shared_ptr<Greeter> m_greeter;
154 166
155 unsigned int m_restart_idle_tag {};167 unsigned int m_request_complete_idle_tag {};
156168
157 std::shared_ptr<GAdbdClient> m_adbd_client;169 std::shared_ptr<GAdbdClient> m_adbd_client;
158 AdbdClient::PKRequest m_req;
159 std::shared_ptr<UsbSnap> m_snap;170 std::shared_ptr<UsbSnap> m_snap;
160 std::set<core::ScopedConnection> m_snap_connections;171 std::set<core::ScopedConnection> m_snap_connections;
172 AdbdClient::PKResponse m_response {AdbdClient::PKResponse::DENY};
173 std::shared_ptr<AdbdClient::PKRequest> m_req;
161};174};
162175
163/***176/***
164177
=== modified file 'src/usb-snap.cpp'
--- src/usb-snap.cpp 2016-03-23 17:16:06 +0000
+++ src/usb-snap.cpp 2016-05-04 00:04:00 +0000
@@ -208,6 +208,7 @@
208 const bool remember_this_choice = response == AdbdClient::PKResponse::ALLOW;208 const bool remember_this_choice = response == AdbdClient::PKResponse::ALLOW;
209209
210 m_on_user_response(response, remember_this_choice);210 m_on_user_response(response, remember_this_choice);
211 m_notification_id = 0;
211 }212 }
212213
213 void on_notification_closed(uint32_t close_reason)214 void on_notification_closed(uint32_t close_reason)
214215
=== modified file 'tests/integration/usb-manager-test.cpp'
--- tests/integration/usb-manager-test.cpp 2016-03-23 17:16:06 +0000
+++ tests/integration/usb-manager-test.cpp 2016-05-04 00:04:00 +0000
@@ -143,7 +143,7 @@
143 );143 );
144144
145 // confirm that the AdbdServer got the right response145 // confirm that the AdbdServer got the right response
146 wait_for([adbd_server](){return !adbd_server->m_responses.empty();}, 2000);146 wait_for([adbd_server](){return !adbd_server->m_responses.empty();}, 5000);
147 ASSERT_EQ(1, adbd_server->m_responses.size());147 ASSERT_EQ(1, adbd_server->m_responses.size());
148 EXPECT_EQ("OK", adbd_server->m_responses.front());148 EXPECT_EQ("OK", adbd_server->m_responses.front());
149149
@@ -163,13 +163,16 @@
163 const std::shared_ptr<std::string> public_keys_path {new std::string{*m_tmpdir+"/adb_keys"}, file_deleter};163 const std::shared_ptr<std::string> public_keys_path {new std::string{*m_tmpdir+"/adb_keys"}, file_deleter};
164164
165 // start a mock AdbdServer ready to submit a request165 // start a mock AdbdServer ready to submit a request
166 const size_t N_TESTS {3};
166 const std::string public_key {"public_key"};167 const std::string public_key {"public_key"};
167 auto adbd_server = std::make_shared<GAdbdServer>(*socket_path, std::vector<std::string>{"PK"+public_key});168 const std::vector<std::string> requests(N_TESTS, "PK"+public_key);
169 const std::vector<std::string> expected_responses(N_TESTS, "NO");
170 auto adbd_server = std::make_shared<GAdbdServer>(*socket_path, requests);
168171
169 // set up a UsbManager to process the request172 // set up a UsbManager to process the request
170 auto usb_manager = std::make_shared<UsbManager>(*socket_path, *public_keys_path, m_usb_monitor, m_greeter);173 auto usb_manager = std::make_shared<UsbManager>(*socket_path, *public_keys_path, m_usb_monitor, m_greeter);
171174
172 for (int i=0; i<3; i++)175 for (std::remove_const<decltype(N_TESTS)>::type i=0; i<N_TESTS; ++i)
173 {176 {
174 // add a signal spy to listen to the notification daemon177 // add a signal spy to listen to the notification daemon
175 QSignalSpy notificationsSpy(178 QSignalSpy notificationsSpy(
@@ -194,6 +197,9 @@
194 EXPECT_EQ("CloseNotification", notificationsSpy.at(0).at(0));197 EXPECT_EQ("CloseNotification", notificationsSpy.at(0).at(0));
195 notificationsSpy.clear();198 notificationsSpy.clear();
196 }199 }
200
201 EXPECT_TRUE(wait_for([adbd_server, N_TESTS](){return adbd_server->m_responses.size() == N_TESTS;}, 5000));
202 EXPECT_EQ(expected_responses, adbd_server->m_responses);
197}203}
198204
199TEST_F(UsbManagerFixture, Greeter)205TEST_F(UsbManagerFixture, Greeter)
@@ -206,7 +212,7 @@
206 auto adbd_server = std::make_shared<GAdbdServer>(*socket_path, std::vector<std::string>{"PK"+public_key});212 auto adbd_server = std::make_shared<GAdbdServer>(*socket_path, std::vector<std::string>{"PK"+public_key});
207213
208 // set up a UsbManager to process the request214 // set up a UsbManager to process the request
209 m_greeter->m_is_active.set(true);215 m_greeter->m_state.set(Greeter::State::ACTIVE);
210 auto usb_manager = std::make_shared<UsbManager>(*socket_path, *public_keys_path, m_usb_monitor, m_greeter);216 auto usb_manager = std::make_shared<UsbManager>(*socket_path, *public_keys_path, m_usb_monitor, m_greeter);
211217
212 // add a signal spy to listen to the notification daemon218 // add a signal spy to listen to the notification daemon
@@ -219,7 +225,7 @@
219 EXPECT_FALSE(notificationsSpy.wait(2000));225 EXPECT_FALSE(notificationsSpy.wait(2000));
220226
221 // disable the greeter, the notification should appear227 // disable the greeter, the notification should appear
222 m_greeter->m_is_active.set(false);228 m_greeter->m_state.set(Greeter::State::INACTIVE);
223 wait_for_signals(notificationsSpy, 1);229 wait_for_signals(notificationsSpy, 1);
224 EXPECT_EQ("Notify", notificationsSpy.at(0).at(0));230 EXPECT_EQ("Notify", notificationsSpy.at(0).at(0));
225 notificationsSpy.clear();231 notificationsSpy.clear();
226232
=== modified file 'tests/unit/CMakeLists.txt'
--- tests/unit/CMakeLists.txt 2016-03-16 00:25:33 +0000
+++ tests/unit/CMakeLists.txt 2016-05-04 00:04:00 +0000
@@ -14,6 +14,10 @@
14 ${GMOCK_LIBRARIES}14 ${GMOCK_LIBRARIES}
15)15)
1616
17add_definitions(
18 -DGREETER_TEMPLATE="${CMAKE_SOURCE_DIR}/tests/utils/mock-unity-greeter.py"
19)
20
17function(add_test_by_name name)21function(add_test_by_name name)
18 set(TEST_NAME ${name})22 set(TEST_NAME ${name})
19 add_executable (${TEST_NAME} ${TEST_NAME}.cpp)23 add_executable (${TEST_NAME} ${TEST_NAME}.cpp)
@@ -31,4 +35,5 @@
31 set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT ${CTEST_ENVIRONMENT})35 set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT ${CTEST_ENVIRONMENT})
32 target_link_libraries(${TEST_NAME} ${SERVICE_LINK_LIBRARIES} ${QT_LINK_LIBRARIES} ${TEST_LINK_LIBRARIES} ${THREAD_LINK_LIBRARIES})36 target_link_libraries(${TEST_NAME} ${SERVICE_LINK_LIBRARIES} ${QT_LINK_LIBRARIES} ${TEST_LINK_LIBRARIES} ${THREAD_LINK_LIBRARIES})
33endfunction()37endfunction()
38add_qt_test_by_name(greeter-test)
34add_qt_test_by_name(usb-snap-test)39add_qt_test_by_name(usb-snap-test)
3540
=== added file 'tests/unit/greeter-test.cpp'
--- tests/unit/greeter-test.cpp 1970-01-01 00:00:00 +0000
+++ tests/unit/greeter-test.cpp 2016-05-04 00:04:00 +0000
@@ -0,0 +1,159 @@
1/*
2 * Copyright 2016 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Charles Kerr <charles.kerr@canonical.com>
18 */
19
20#include <tests/utils/qt-fixture.h>
21#include <tests/utils/gtest-print-helpers.h>
22
23#include <src/dbus-names.h>
24#include <src/greeter.h>
25
26#include <libqtdbustest/DBusTestRunner.h>
27#include <libqtdbustest/QProcessDBusService.h>
28#include <libqtdbusmock/DBusMock.h>
29
30class GreeterFixture: public QtFixture
31{
32private:
33
34 using super = QtFixture;
35
36public:
37
38 GreeterFixture() =default;
39 ~GreeterFixture() =default;
40
41protected:
42
43 std::shared_ptr<QtDBusTest::DBusTestRunner> m_dbus_runner;
44 std::shared_ptr<QtDBusMock::DBusMock> m_dbus_mock;
45 GDBusConnection* m_bus {};
46
47 void SetUp() override
48 {
49 super::SetUp();
50
51 // use a fresh bus for each test run
52 m_dbus_runner.reset(new QtDBusTest::DBusTestRunner());
53 m_dbus_mock.reset(new QtDBusMock::DBusMock(*m_dbus_runner.get()));
54
55 GError* error {};
56 m_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, nullptr, &error);
57 g_assert_no_error(error);
58 g_dbus_connection_set_exit_on_close(m_bus, FALSE);
59 }
60
61 void TearDown() override
62 {
63 g_clear_object(&m_bus);
64
65 super::TearDown();
66 }
67
68 void start_greeter_service(bool is_active)
69 {
70 // set a watcher to look for our mock greeter to appear
71 bool owned {};
72 QDBusServiceWatcher watcher(
73 DBusNames::UnityGreeter::NAME,
74 m_dbus_runner->sessionConnection()
75 );
76 QObject::connect(
77 &watcher,
78 &QDBusServiceWatcher::serviceRegistered,
79 [&owned](const QString&){owned = true;}
80 );
81
82 // start the mock greeter
83 QVariantMap parameters;
84 parameters["IsActive"] = QVariant(is_active);
85 m_dbus_mock->registerTemplate(
86 DBusNames::UnityGreeter::NAME,
87 GREETER_TEMPLATE,
88 parameters,
89 QDBusConnection::SessionBus
90 );
91 m_dbus_runner->startServices();
92
93 // wait for the watcher
94 ASSERT_TRUE(wait_for([&owned]{return owned;}));
95 }
96};
97
98#define ASSERT_PROPERTY_EQ_EVENTUALLY(expected_in, property_in) \
99 do { \
100 const auto& e = expected_in; \
101 const auto& p = property_in; \
102 ASSERT_TRUE(wait_for([e, &p](){return e == p.get();})) \
103 << "expected " << e << " but got " << p.get(); \
104 } while(0)
105
106/**
107 * Test startup timing by looking at four different cases:
108 * [unity greeter shows up on bus (before, after) we start listening]
109 * x [unity greeter is (active, inactive)]
110 */
111
112TEST_F(GreeterFixture, ActiveServiceStartsBeforeWatcher)
113{
114 constexpr bool is_active {true};
115 constexpr Greeter::State expected {Greeter::State::ACTIVE};
116
117 start_greeter_service(is_active);
118
119 UnityGreeter greeter;
120
121 ASSERT_PROPERTY_EQ_EVENTUALLY(expected, greeter.state());
122}
123
124TEST_F(GreeterFixture, WatcherStartsBeforeActiveService)
125{
126 constexpr bool is_active {true};
127 constexpr Greeter::State expected {Greeter::State::ACTIVE};
128
129 UnityGreeter greeter;
130
131 start_greeter_service(is_active);
132
133 ASSERT_PROPERTY_EQ_EVENTUALLY(expected, greeter.state());
134}
135
136TEST_F(GreeterFixture, InactiveServiceStartsBeforeWatcher)
137{
138 constexpr bool is_active {false};
139 constexpr Greeter::State expected {Greeter::State::INACTIVE};
140
141 start_greeter_service(is_active);
142
143 UnityGreeter greeter;
144
145 ASSERT_PROPERTY_EQ_EVENTUALLY(expected, greeter.state());
146}
147
148TEST_F(GreeterFixture, WatcherStartsBeforeInactiveService)
149{
150 constexpr bool is_active {false};
151 constexpr Greeter::State expected {Greeter::State::INACTIVE};
152
153 UnityGreeter greeter;
154
155 start_greeter_service(is_active);
156
157 ASSERT_PROPERTY_EQ_EVENTUALLY(expected, greeter.state());
158}
159
0160
=== modified file 'tests/unit/usb-snap-test.cpp'
--- tests/unit/usb-snap-test.cpp 2016-03-23 17:16:06 +0000
+++ tests/unit/usb-snap-test.cpp 2016-05-04 00:04:00 +0000
@@ -130,14 +130,9 @@
130 EXPECT_TRUE(user_response_set);130 EXPECT_TRUE(user_response_set);
131 ASSERT_EQ(test.expected_response, user_response);131 ASSERT_EQ(test.expected_response, user_response);
132132
133 // confirm that the snap dtor cleans up the notification133 // confirm that the snap dtor doesn't try to close
134 // the notification that's already been closed by user choice
134 snap.reset();135 snap.reset();
135 wait_for_signals(notificationsSpy, 1);136 EXPECT_FALSE(notificationsSpy.wait(1000));
136 {
137 QVariantList const& call(notificationsSpy.at(0));
138 EXPECT_EQ("CloseNotification", call.at(0));
139 QVariantList const& args(call.at(1).toList());
140 EXPECT_EQ(id, args.at(0));
141 }
142 }137 }
143}138}
144139
=== modified file 'tests/utils/adbd-server.h'
--- tests/utils/adbd-server.h 2016-03-24 16:01:16 +0000
+++ tests/utils/adbd-server.h 2016-05-04 00:04:00 +0000
@@ -38,7 +38,7 @@
38 GAdbdServer(const std::string& socket_path,38 GAdbdServer(const std::string& socket_path,
39 const std::vector<std::string>& requests):39 const std::vector<std::string>& requests):
40 m_requests{requests},40 m_requests{requests},
41 m_socket_path{socket_path},41 m_server_socket{create_server_socket(socket_path)},
42 m_cancellable{g_cancellable_new()},42 m_cancellable{g_cancellable_new()},
43 m_worker_thread{&GAdbdServer::worker_func, this}43 m_worker_thread{&GAdbdServer::worker_func, this}
44 {44 {
@@ -50,6 +50,7 @@
50 g_cancellable_cancel(m_cancellable);50 g_cancellable_cancel(m_cancellable);
51 m_worker_thread.join();51 m_worker_thread.join();
52 g_clear_object(&m_cancellable);52 g_clear_object(&m_cancellable);
53 g_clear_object(&m_server_socket);
53 }54 }
5455
55 const std::vector<std::string> m_requests;56 const std::vector<std::string> m_requests;
@@ -59,18 +60,14 @@
5960
60 void worker_func() // runs in worker thread61 void worker_func() // runs in worker thread
61 {62 {
62 auto server_socket = create_server_socket(m_socket_path);
63 auto requests = m_requests;63 auto requests = m_requests;
6464
65 GError* error {};
66 g_socket_listen (server_socket, &error);
67 g_assert_no_error (error);
68
69 while (!g_cancellable_is_cancelled(m_cancellable) && !requests.empty())65 while (!g_cancellable_is_cancelled(m_cancellable) && !requests.empty())
70 {66 {
71 // wait for a client connection67 // wait for a client connection
72 g_message("GAdbdServer::Impl::worker_func() calling g_socket_accept()");68 g_message("GAdbdServer::Impl::worker_func() calling g_socket_accept()");
73 auto client_socket = g_socket_accept(server_socket, m_cancellable, &error);69 GError* error {};
70 auto client_socket = g_socket_accept(m_server_socket, m_cancellable, &error);
74 if (error != nullptr) {71 if (error != nullptr) {
75 if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))72 if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
76 g_message("GAdbdServer: Error accepting socket connection: %s", error->message);73 g_message("GAdbdServer: Error accepting socket connection: %s", error->message);
@@ -121,8 +118,6 @@
121 // cleanup118 // cleanup
122 g_clear_object(&client_socket);119 g_clear_object(&client_socket);
123 }120 }
124
125 g_clear_object(&server_socket);
126 }121 }
127122
128 // bind to a local domain socket123 // bind to a local domain socket
@@ -139,11 +134,14 @@
139 g_assert_no_error (error);134 g_assert_no_error (error);
140 g_clear_object (&address);135 g_clear_object (&address);
141136
137 g_socket_listen (socket, &error);
138 g_assert_no_error (error);
139
142 return socket;140 return socket;
143 }141 }
144142
145 const std::string m_socket_path;143 GSocket* m_server_socket {};
146 GCancellable* m_cancellable = nullptr;144 GCancellable* m_cancellable {};
147 std::thread m_worker_thread;145 std::thread m_worker_thread;
148};146};
149147
150148
=== added file 'tests/utils/gtest-print-helpers.h'
--- tests/utils/gtest-print-helpers.h 1970-01-01 00:00:00 +0000
+++ tests/utils/gtest-print-helpers.h 2016-05-04 00:04:00 +0000
@@ -0,0 +1,18 @@
1
2#pragma once
3
4#include <src/greeter.h>
5
6inline void PrintTo(const Greeter::State& state, std::ostream* os) {
7 switch(state) {
8 case Greeter::State::ACTIVE: *os << "Active"; break;
9 case Greeter::State::INACTIVE: *os << "Inactive"; break;
10 case Greeter::State::UNAVAILABLE: *os << "Unavailable"; break;
11 }
12}
13
14inline std::ostream& operator<<(std::ostream& os, const Greeter::State& state) {
15 PrintTo(state, &os);
16 return os;
17}
18
019
=== modified file 'tests/utils/mock-greeter.h'
--- tests/utils/mock-greeter.h 2016-03-22 20:39:36 +0000
+++ tests/utils/mock-greeter.h 2016-05-04 00:04:00 +0000
@@ -26,7 +26,7 @@
26public:26public:
27 MockGreeter() =default;27 MockGreeter() =default;
28 virtual ~MockGreeter() =default;28 virtual ~MockGreeter() =default;
29 core::Property<bool>& is_active() override {return m_is_active;}29 core::Property<Greeter::State>& state() override {return m_state;}
30 core::Property<bool> m_is_active {false};30 core::Property<Greeter::State> m_state {State::INACTIVE};
31};31};
3232
3333
=== added file 'tests/utils/mock-unity-greeter.py'
--- tests/utils/mock-unity-greeter.py 1970-01-01 00:00:00 +0000
+++ tests/utils/mock-unity-greeter.py 2016-05-04 00:04:00 +0000
@@ -0,0 +1,41 @@
1'''unity greeter mock template
2
3Very basic template that just mocks the greeter is-active flag
4'''
5
6# This program is free software; you can redistribute it and/or modify it under
7# the terms of the GNU Lesser General Public License as published by the Free
8# Software Foundation; either version 3 of the License, or (at your option) any
9# later version. See http://www.gnu.org/copyleft/lgpl.html for the full text
10# of the license.
11
12__author__ = 'Charles Kerr'
13__email__ = 'charles.kerr@canonical.com'
14__copyright__ = '(c) 2016 Canonical Ltd.'
15__license__ = 'LGPL 3+'
16
17import dbus
18import os
19
20from dbusmock import MOCK_IFACE, mockobject
21
22BUS_NAME = 'com.canonical.UnityGreeter'
23MAIN_OBJ = '/'
24MAIN_IFACE = 'com.canonical.UnityGreeter'
25SYSTEM_BUS = False
26
27
28def load(mock, parameters):
29 mock.AddMethods(
30 MAIN_IFACE, [
31 ('HideGreeter', '', '', 'self.Set("com.canonical.UnityGreeter", "IsActive", False)'),
32 ('ShowGreeter', '', '', 'self.Set("com.canonical.UnityGreeter", "IsActive", True)')
33 ]
34 )
35 mock.AddProperties(
36 MAIN_IFACE,
37 dbus.Dictionary({
38 'IsActive': parameters.get('IsActive', False),
39 }, signature='sv')
40 )
41

Subscribers

People subscribed via source and target branches