Merge lp:~rhuddie/ubuntu-push/push-autopilot-tests into lp:ubuntu-push
- push-autopilot-tests
- Merge into trunk
Status: | Superseded |
---|---|
Proposed branch: | lp:~rhuddie/ubuntu-push/push-autopilot-tests |
Merge into: | lp:ubuntu-push |
Diff against target: |
545 lines (+506/-0) 7 files modified
tests/autopilot/push_notifications/README (+63/-0) tests/autopilot/push_notifications/__init__.py (+8/-0) tests/autopilot/push_notifications/config/__init__.py (+19/-0) tests/autopilot/push_notifications/config/push.conf (+23/-0) tests/autopilot/push_notifications/data.py (+81/-0) tests/autopilot/push_notifications/tests/__init__.py (+239/-0) tests/autopilot/push_notifications/tests/test_push_client.py (+73/-0) |
To merge this branch: | bzr merge lp:~rhuddie/ubuntu-push/push-autopilot-tests |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Leo Arias (community) | Needs Fixing | ||
VĂctor R. Ruiz | Pending | ||
Allan LeSage | Pending | ||
Review via email:
|
This proposal has been superseded by a proposal from 2014-04-22.
Commit message
Description of the change
A new autopilot test framework for testing push notifications.
The tests will configure the client to run against a specified push server and then send through a push notification message which is displayed on device's screen.
Currently the autopilot code for validating the notifications is not completed.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Leo Arias (elopio) wrote : | # |
53 +5) autopilot3 list push-notifications
54 +6) autopilot3 run push-notifications
You have a typo there ^. It should be push_notifications.
166 +class PushNotificatio
187 +class NotificationData:
I would put these classes in a push_notificati
210 + if dbus_info != None:
215 + elif copy_obj != None:
You should make checks like these with 'is not' instead of !=.
That's from pep8: "Comparisons to singletons like None should always be done with is or is not, never the equality operators."
203 + def __init__(self, dbus_info=None, copy_obj=None):
I find this to be a confusing constructor. I think it would be clearer to make two @classmethods, like
def from_dbus_
def copy(original_
Actually, I'm not sure you would need that copy. You could use the copy module.
- 110. By Richard Huddie
-
update for review comments
- 111. By Richard Huddie
-
fix pep8
- 112. By Richard Huddie
-
remove dbus dependency so it can run without root
- 113. By Richard Huddie
-
unity8 dialog validation
- 114. By Richard Huddie
-
added some new tests including locked greeter
- 115. By Richard Huddie
-
re-structure tests so that client is not re-started before each test
- 116. By Richard Huddie
-
restructure to use helper classes
- 117. By Richard Huddie
-
tidy and add test for device screen off
- 118. By Richard Huddie
-
fix flake8
- 119. By Richard Huddie
-
clear notifications before running test and move stuff into base class
- 120. By Richard Huddie
-
added cert_pem_file configuration to client
- 121. By Richard Huddie
-
certificate file path update
- 122. By Richard Huddie
-
rename tests for broadcast
- 123. By Richard Huddie
-
minor change
- 124. By Richard Huddie
-
tidy up
- 125. By Richard Huddie
-
update dialog assertion
- 126. By Richard Huddie
-
tidy up and add documentation to methods
- 127. By Richard Huddie
-
remove comment
- 128. By Richard Huddie
-
Readme updates
- 129. By Richard Huddie
-
disable mocking and minor updates
- 130. By Richard Huddie
-
move display message from base class
- 131. By Richard Huddie
-
fix review comments
- 132. By Richard Huddie
-
fix review comment
- 133. By Richard Huddie
-
Copyright header updates
- 134. By Richard Huddie
-
split greeter changes
- 135. By Richard Huddie
-
tidied up and added extra test
- 136. By Richard Huddie
-
merge with trunk
- 137. By Richard Huddie
-
update comment
- 138. By Richard Huddie
-
revert split greeter changes
- 139. By Richard Huddie
-
use /sbin/initctl
- 140. By Richard Huddie
-
update readme including emulator info
- 141. By Richard Huddie
-
wait for notification to dismiss automatically
Unmerged revisions
Preview Diff
1 | === added directory 'tests' | |||
2 | === added directory 'tests/autopilot' | |||
3 | === added directory 'tests/autopilot/push_notifications' | |||
4 | === added file 'tests/autopilot/push_notifications/README' | |||
5 | --- tests/autopilot/push_notifications/README 1970-01-01 00:00:00 +0000 | |||
6 | +++ tests/autopilot/push_notifications/README 2014-04-22 14:02:46 +0000 | |||
7 | @@ -0,0 +1,63 @@ | |||
8 | 1 | ================== | ||
9 | 2 | README | ||
10 | 3 | ================== | ||
11 | 4 | |||
12 | 5 | To run ubuntu-push autopilot tests you need to have a push server available. This can be running locally using loopback (127.0.0.1) or remotely on the same network. | ||
13 | 6 | |||
14 | 7 | ---------------------------------- | ||
15 | 8 | To configure and build the server: | ||
16 | 9 | ---------------------------------- | ||
17 | 10 | |||
18 | 11 | 1) export GOPATH=${PWD}/push | ||
19 | 12 | 2) mkdir -p push/src/launchpad.net | ||
20 | 13 | 3) cd push/src/launchpad.net | ||
21 | 14 | 4) bzr branch lp:ubuntu-push | ||
22 | 15 | 5) Edit ubuntu-push/sampleconfigs/dev.json: | ||
23 | 16 | "addr": "192.168.1.2:9090", | ||
24 | 17 | The ip should be changed to match your current environment | ||
25 | 18 | - use 127.0.0.1 if the client is also going to be running on the same machine as the server | ||
26 | 19 | - use real ip address if the client is going to be running on a different machine on the same network | ||
27 | 20 | 5) cd ubuntu-push | ||
28 | 21 | 6) make bootstrap | ||
29 | 22 | 7) make run-server-dev | ||
30 | 23 | Following output should be observed: | ||
31 | 24 | INFO listening for http on 192.168.1.2:8080 | ||
32 | 25 | INFO listening for devices on 192.168.1.2:9090 | ||
33 | 26 | |||
34 | 27 | ------------------------ | ||
35 | 28 | To configure the client: | ||
36 | 29 | ------------------------ | ||
37 | 30 | |||
38 | 31 | Note: Tests must be run as root user | ||
39 | 32 | |||
40 | 33 | 1) sudo apt-get install ubuntu-push-client | ||
41 | 34 | 2) bzr branch lp:ubuntu-push | ||
42 | 35 | 3) Edit ubuntu-push/tests/autopilot/push_notifications/config/push.conf: | ||
43 | 36 | [default] | ||
44 | 37 | environment = remote | ||
45 | 38 | [remote] | ||
46 | 39 | addr = 192.168.1.2 | ||
47 | 40 | listener_port = 8080 | ||
48 | 41 | device_port = 9090 | ||
49 | 42 | |||
50 | 43 | The environment can be toggled using the 'environment' setting under [default] section. The addr and port settings for the chosen environment shold match required. | ||
51 | 44 | |||
52 | 45 | 4) cd ubuntu-push/tests/autopilot | ||
53 | 46 | 5) autopilot3 list push_notifications | ||
54 | 47 | 6) autopilot3 run push_notifications | ||
55 | 48 | |||
56 | 49 | ---------------- | ||
57 | 50 | Troubleshooting: | ||
58 | 51 | ---------------- | ||
59 | 52 | |||
60 | 53 | 1) Ping from client to server to ensure connectivity is correct | ||
61 | 54 | 2) Delete .local/share/ubuntu-push-client/levels.db if no notifications are being displayed | ||
62 | 55 | 3) To send a notification manually: | ||
63 | 56 | echo '{"channel":"system", "data": {"ubuntu-touch/trusty-proposed/mako": [297, ""]}, "expire_on": "2015-12-19T16:39:57-08:00"}' | POST -c application/json http://192.168.1.2:8080/broadcast | ||
64 | 57 | Response should be: | ||
65 | 58 | {"ok":true} | ||
66 | 59 | Note that: | ||
67 | 60 | - The channel and device names must match the client. | ||
68 | 61 | - The build number must be greater than current installed version in order to trigger an update message. | ||
69 | 62 | - The expiration time must be in the future. | ||
70 | 63 | |||
71 | 0 | 64 | ||
72 | === added file 'tests/autopilot/push_notifications/__init__.py' | |||
73 | --- tests/autopilot/push_notifications/__init__.py 1970-01-01 00:00:00 +0000 | |||
74 | +++ tests/autopilot/push_notifications/__init__.py 2014-04-22 14:02:46 +0000 | |||
75 | @@ -0,0 +1,8 @@ | |||
76 | 1 | # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- | ||
77 | 2 | # Copyright 2014 Canonical | ||
78 | 3 | # | ||
79 | 4 | # This program is free software: you can redistribute it and/or modify it | ||
80 | 5 | # under the terms of the GNU General Public License version 3, as published | ||
81 | 6 | # by the Free Software Foundation. | ||
82 | 7 | |||
83 | 8 | """push-notifications autopilot tests.""" | ||
84 | 0 | 9 | ||
85 | === added directory 'tests/autopilot/push_notifications/config' | |||
86 | === added file 'tests/autopilot/push_notifications/config/__init__.py' | |||
87 | --- tests/autopilot/push_notifications/config/__init__.py 1970-01-01 00:00:00 +0000 | |||
88 | +++ tests/autopilot/push_notifications/config/__init__.py 2014-04-22 14:02:46 +0000 | |||
89 | @@ -0,0 +1,19 @@ | |||
90 | 1 | # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- | ||
91 | 2 | # Copyright 2014 Canonical | ||
92 | 3 | # | ||
93 | 4 | # This program is free software: you can redistribute it and/or modify it | ||
94 | 5 | # under the terms of the GNU General Public License version 3, as published | ||
95 | 6 | # by the Free Software Foundation. | ||
96 | 7 | |||
97 | 8 | |||
98 | 9 | import os | ||
99 | 10 | |||
100 | 11 | CONFIG_FILE = 'push.conf' | ||
101 | 12 | |||
102 | 13 | |||
103 | 14 | def get_config_file(): | ||
104 | 15 | """ | ||
105 | 16 | Return the path for the config file | ||
106 | 17 | """ | ||
107 | 18 | config_dir = os.path.dirname(__file__) | ||
108 | 19 | return os.path.join(config_dir, CONFIG_FILE) | ||
109 | 0 | 20 | ||
110 | === added file 'tests/autopilot/push_notifications/config/push.conf' | |||
111 | --- tests/autopilot/push_notifications/config/push.conf 1970-01-01 00:00:00 +0000 | |||
112 | +++ tests/autopilot/push_notifications/config/push.conf 2014-04-22 14:02:46 +0000 | |||
113 | @@ -0,0 +1,23 @@ | |||
114 | 1 | [default] | ||
115 | 2 | environment = remote | ||
116 | 3 | |||
117 | 4 | [local] | ||
118 | 5 | addr = 127.0.0.1 | ||
119 | 6 | listener_port = 8080 | ||
120 | 7 | device_port = 9090 | ||
121 | 8 | |||
122 | 9 | [remote] | ||
123 | 10 | addr = 192.168.1.2 | ||
124 | 11 | listener_port = 8080 | ||
125 | 12 | device_port = 9090 | ||
126 | 13 | |||
127 | 14 | [staging] | ||
128 | 15 | addr = staging-url.com | ||
129 | 16 | listener_port = 8080 | ||
130 | 17 | device_port = 9090 | ||
131 | 18 | |||
132 | 19 | [production] | ||
133 | 20 | addr = production-url.com | ||
134 | 21 | listener_port = 8080 | ||
135 | 22 | device_port = 9090 | ||
136 | 23 | |||
137 | 0 | 24 | ||
138 | === added file 'tests/autopilot/push_notifications/data.py' | |||
139 | --- tests/autopilot/push_notifications/data.py 1970-01-01 00:00:00 +0000 | |||
140 | +++ tests/autopilot/push_notifications/data.py 2014-04-22 14:02:46 +0000 | |||
141 | @@ -0,0 +1,81 @@ | |||
142 | 1 | # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- | ||
143 | 2 | # Copyright 2014 Canonical | ||
144 | 3 | # | ||
145 | 4 | # This program is free software: you can redistribute it and/or modify it | ||
146 | 5 | # under the terms of the GNU General Public License version 3, as published | ||
147 | 6 | # by the Free Software Foundation. | ||
148 | 7 | |||
149 | 8 | """Push-Notifications autopilot data structure classes""" | ||
150 | 9 | |||
151 | 10 | |||
152 | 11 | class PushNotificationMessage: | ||
153 | 12 | """ | ||
154 | 13 | Class to hold all the details required for a | ||
155 | 14 | push notification message | ||
156 | 15 | """ | ||
157 | 16 | channel = None | ||
158 | 17 | expire_after = None | ||
159 | 18 | data = None | ||
160 | 19 | |||
161 | 20 | def __init__(self, channel='system', data='', expire_after=''): | ||
162 | 21 | self.channel = channel | ||
163 | 22 | self.data = data | ||
164 | 23 | self.expire_after = expire_after | ||
165 | 24 | |||
166 | 25 | def json(self): | ||
167 | 26 | """ | ||
168 | 27 | Return json representation of message | ||
169 | 28 | """ | ||
170 | 29 | json_str = '{{"channel":"{0}", "data":{{{1}}}, "expire_on":"{2}"}}' | ||
171 | 30 | return json_str.format(self.channel, self.data, self.expire_after) | ||
172 | 31 | |||
173 | 32 | |||
174 | 33 | class NotificationData: | ||
175 | 34 | """ | ||
176 | 35 | Class to represent notification data including | ||
177 | 36 | Device software channel | ||
178 | 37 | Device build number | ||
179 | 38 | Device model | ||
180 | 39 | Device last update | ||
181 | 40 | Data for the notification | ||
182 | 41 | """ | ||
183 | 42 | channel = None | ||
184 | 43 | build_number = None | ||
185 | 44 | device = None | ||
186 | 45 | last_update = None | ||
187 | 46 | version = None | ||
188 | 47 | data = None | ||
189 | 48 | |||
190 | 49 | @classmethod | ||
191 | 50 | def from_dbus_info(cls, dbus_info=None): | ||
192 | 51 | """ | ||
193 | 52 | Create a new object based on dbus_info if provided | ||
194 | 53 | """ | ||
195 | 54 | nd = NotificationData() | ||
196 | 55 | if dbus_info is not None: | ||
197 | 56 | nd.device = dbus_info[1] | ||
198 | 57 | nd.channel = dbus_info[2] | ||
199 | 58 | nd.last_update = dbus_info[3] | ||
200 | 59 | nd.build_number = dbus_info[4]['version'] | ||
201 | 60 | return nd | ||
202 | 61 | |||
203 | 62 | def inc_build_number(self): | ||
204 | 63 | """ | ||
205 | 64 | Increment build number | ||
206 | 65 | """ | ||
207 | 66 | self.build_number = str(int(self.build_number) + 1) | ||
208 | 67 | |||
209 | 68 | def dec_build_number(self): | ||
210 | 69 | """ | ||
211 | 70 | Decrement build number | ||
212 | 71 | """ | ||
213 | 72 | self.build_number = str(int(self.build_number) - 1) | ||
214 | 73 | |||
215 | 74 | def json(self): | ||
216 | 75 | """ | ||
217 | 76 | Return json representation of info based: | ||
218 | 77 | "IMAGE-CHANNEL/DEVICE-MODEL": [BUILD-NUMBER, CHANNEL-ALIAS]" | ||
219 | 78 | """ | ||
220 | 79 | json_str = '"{0}/{1}": [{2}, "{3}"]' | ||
221 | 80 | return json_str.format(self.channel, self.device, self.build_number, | ||
222 | 81 | self.data) | ||
223 | 0 | 82 | ||
224 | === added directory 'tests/autopilot/push_notifications/tests' | |||
225 | === added file 'tests/autopilot/push_notifications/tests/__init__.py' | |||
226 | --- tests/autopilot/push_notifications/tests/__init__.py 1970-01-01 00:00:00 +0000 | |||
227 | +++ tests/autopilot/push_notifications/tests/__init__.py 2014-04-22 14:02:46 +0000 | |||
228 | @@ -0,0 +1,239 @@ | |||
229 | 1 | # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- | ||
230 | 2 | # Copyright 2014 Canonical | ||
231 | 3 | # | ||
232 | 4 | # This program is free software: you can redistribute it and/or modify it | ||
233 | 5 | # under the terms of the GNU General Public License version 3, as published | ||
234 | 6 | # by the Free Software Foundation. | ||
235 | 7 | |||
236 | 8 | """push-notifications autopilot tests.""" | ||
237 | 9 | |||
238 | 10 | |||
239 | 11 | import configparser | ||
240 | 12 | import http.client as http | ||
241 | 13 | import json | ||
242 | 14 | import os | ||
243 | 15 | import datetime | ||
244 | 16 | import subprocess | ||
245 | 17 | import dbus | ||
246 | 18 | import copy | ||
247 | 19 | |||
248 | 20 | from autopilot.testcase import AutopilotTestCase | ||
249 | 21 | from push_notifications.data import PushNotificationMessage | ||
250 | 22 | from push_notifications.data import NotificationData | ||
251 | 23 | from push_notifications import config | ||
252 | 24 | |||
253 | 25 | |||
254 | 26 | class PushNotificationTestBase(AutopilotTestCase): | ||
255 | 27 | """ | ||
256 | 28 | Base class for push notification test cases | ||
257 | 29 | """ | ||
258 | 30 | |||
259 | 31 | PUSH_CLIENT_DEFAULT_CONFIG_FILE = '/etc/xdg/ubuntu-push-client/config.json' | ||
260 | 32 | PUSH_CLIENT_CONFIG_FILE = '~/.config/ubuntu-push-client/config.json' | ||
261 | 33 | PUSH_SERVER_BROADCAST_URL = '/broadcast' | ||
262 | 34 | DEFAULT_DISPLAY_MESSAGE = 'There\'s an updated system image.' | ||
263 | 35 | PUSH_MIME_TYPE = 'application/json' | ||
264 | 36 | SECTION_DEFAULT = 'default' | ||
265 | 37 | KEY_ENVIRONMENT = 'environment' | ||
266 | 38 | KEY_ADDR = 'addr' | ||
267 | 39 | KEY_LISTENER_PORT = 'listener_port' | ||
268 | 40 | KEY_DEVICE_PORT = 'device_port' | ||
269 | 41 | |||
270 | 42 | def setUp(self): | ||
271 | 43 | """ | ||
272 | 44 | Start the client running with the correct server config | ||
273 | 45 | """ | ||
274 | 46 | # setup | ||
275 | 47 | super(PushNotificationTestBase, self).setUp() | ||
276 | 48 | # Read the config data | ||
277 | 49 | self.read_config_file() | ||
278 | 50 | # write server device address to the client config | ||
279 | 51 | self.write_client_test_config() | ||
280 | 52 | # restart the push client | ||
281 | 53 | self.restart_push_client() | ||
282 | 54 | # validate that the initialisation push message is displayed | ||
283 | 55 | self.validate_push_message(self.DEFAULT_DISPLAY_MESSAGE) | ||
284 | 56 | # dbus | ||
285 | 57 | self.get_device_info() | ||
286 | 58 | |||
287 | 59 | def create_notification_data_copy(self): | ||
288 | 60 | """ | ||
289 | 61 | Return a copy of the device's notification data | ||
290 | 62 | """ | ||
291 | 63 | return copy.deepcopy(self.notification_data) | ||
292 | 64 | |||
293 | 65 | def get_device_info(self): | ||
294 | 66 | """ | ||
295 | 67 | Discover the device's model and build info | ||
296 | 68 | """ | ||
297 | 69 | system_bus = dbus.SystemBus() | ||
298 | 70 | info_service = system_bus.get_object( | ||
299 | 71 | 'com.canonical.SystemImage', '/Service') | ||
300 | 72 | info = info_service.Info() | ||
301 | 73 | # Create a NotificationData object based on the dbus info | ||
302 | 74 | self.notification_data = NotificationData.from_dbus_info( | ||
303 | 75 | dbus_info=info) | ||
304 | 76 | |||
305 | 77 | def read_config_file(self): | ||
306 | 78 | """ | ||
307 | 79 | Read data from config file | ||
308 | 80 | """ | ||
309 | 81 | config_file = config.get_config_file() | ||
310 | 82 | self.config = configparser.ConfigParser() | ||
311 | 83 | self.config.read(config_file) | ||
312 | 84 | # read the name of the environment to use (local/remote) | ||
313 | 85 | self.env = self.config[self.SECTION_DEFAULT][self.KEY_ENVIRONMENT] | ||
314 | 86 | # format the server device and listener address | ||
315 | 87 | addr_fmt = '{0}:{1}' | ||
316 | 88 | self.server_listener_addr = addr_fmt.format( | ||
317 | 89 | self.get_server_addr(), self.get_listener_port()) | ||
318 | 90 | self.server_device_addr = addr_fmt.format( | ||
319 | 91 | self.get_server_addr(), self.get_device_port()) | ||
320 | 92 | |||
321 | 93 | def get_server_addr(self): | ||
322 | 94 | """ | ||
323 | 95 | Return the server address from config file | ||
324 | 96 | """ | ||
325 | 97 | return self.config[self.env][self.KEY_ADDR] | ||
326 | 98 | |||
327 | 99 | def get_listener_port(self): | ||
328 | 100 | """ | ||
329 | 101 | Return the server listener port from config file | ||
330 | 102 | """ | ||
331 | 103 | return self.config[self.env][self.KEY_LISTENER_PORT] | ||
332 | 104 | |||
333 | 105 | def get_device_port(self): | ||
334 | 106 | """ | ||
335 | 107 | Return the server listener port from config file | ||
336 | 108 | """ | ||
337 | 109 | return self.config[self.env][self.KEY_DEVICE_PORT] | ||
338 | 110 | |||
339 | 111 | def _control_client(self, command): | ||
340 | 112 | """ | ||
341 | 113 | start/stop/restart the ubuntu-push-client using initctl | ||
342 | 114 | """ | ||
343 | 115 | subprocess.call(['initctl', command, 'ubuntu-push-client']) | ||
344 | 116 | |||
345 | 117 | def stop_push_client(self): | ||
346 | 118 | """ | ||
347 | 119 | Stop the push client | ||
348 | 120 | """ | ||
349 | 121 | self._control_client('stop') | ||
350 | 122 | |||
351 | 123 | def start_push_client(self): | ||
352 | 124 | """ | ||
353 | 125 | Start the push client | ||
354 | 126 | """ | ||
355 | 127 | self._control_client('start') | ||
356 | 128 | |||
357 | 129 | def restart_push_client(self): | ||
358 | 130 | """ | ||
359 | 131 | Restart the push client | ||
360 | 132 | """ | ||
361 | 133 | self.stop_push_client() | ||
362 | 134 | self.start_push_client() | ||
363 | 135 | |||
364 | 136 | def write_client_test_config(self): | ||
365 | 137 | """ | ||
366 | 138 | Write the test server address to client config file | ||
367 | 139 | """ | ||
368 | 140 | # read the original config file | ||
369 | 141 | with open(self.PUSH_CLIENT_DEFAULT_CONFIG_FILE) as config_file: | ||
370 | 142 | config = json.load(config_file) | ||
371 | 143 | # change server address | ||
372 | 144 | config['addr'] = self.server_device_addr | ||
373 | 145 | # write the config json out to the ~.local address | ||
374 | 146 | abs_config_file = os.path.expanduser(self.PUSH_CLIENT_CONFIG_FILE) | ||
375 | 147 | config_dir = os.path.dirname(abs_config_file) | ||
376 | 148 | if not os.path.exists(config_dir): | ||
377 | 149 | os.makedirs(config_dir) | ||
378 | 150 | with open(abs_config_file, 'w+') as outfile: | ||
379 | 151 | json.dump(config, outfile, indent=4) | ||
380 | 152 | outfile.close() | ||
381 | 153 | |||
382 | 154 | def send_push_broadcast_notification(self, msg_json): | ||
383 | 155 | """ | ||
384 | 156 | Send the specified push message to the server broadcast url | ||
385 | 157 | using an HTTP POST command | ||
386 | 158 | """ | ||
387 | 159 | headers = {'Content-type': self.PUSH_MIME_TYPE} | ||
388 | 160 | conn = http.HTTPConnection(self.server_listener_addr) | ||
389 | 161 | conn.request( | ||
390 | 162 | 'POST', | ||
391 | 163 | self.PUSH_SERVER_BROADCAST_URL, | ||
392 | 164 | headers=headers, | ||
393 | 165 | body=msg_json) | ||
394 | 166 | return conn.getresponse() | ||
395 | 167 | |||
396 | 168 | def create_push_message(self, channel='system', data='', expire_after=''): | ||
397 | 169 | """ | ||
398 | 170 | Return a new push msg | ||
399 | 171 | If no expiry time is given, a future date will be assigned | ||
400 | 172 | """ | ||
401 | 173 | if expire_after == '': | ||
402 | 174 | expire_after = self.get_future_iso_time() | ||
403 | 175 | return PushNotificationMessage( | ||
404 | 176 | channel=channel, | ||
405 | 177 | data=data, | ||
406 | 178 | expire_after=expire_after) | ||
407 | 179 | |||
408 | 180 | def validate_push_message(self, display_message, timeout=10): | ||
409 | 181 | """ | ||
410 | 182 | Validate that a notification message is displayed on screen | ||
411 | 183 | """ | ||
412 | 184 | |||
413 | 185 | def get_past_iso_time(self): | ||
414 | 186 | """ | ||
415 | 187 | Return time 1 year in past in ISO format | ||
416 | 188 | """ | ||
417 | 189 | return self.get_iso_time(year_offset=-1) | ||
418 | 190 | |||
419 | 191 | def get_near_past_iso_time(self): | ||
420 | 192 | """ | ||
421 | 193 | Return time 1 minute in past in ISO format | ||
422 | 194 | """ | ||
423 | 195 | return self.get_iso_time(min_offset=-1) | ||
424 | 196 | |||
425 | 197 | def get_near_future_iso_time(self): | ||
426 | 198 | """ | ||
427 | 199 | Return time 1 minute in future in ISO format | ||
428 | 200 | """ | ||
429 | 201 | return self.get_iso_time(min_offset=1) | ||
430 | 202 | |||
431 | 203 | def get_future_iso_time(self): | ||
432 | 204 | """ | ||
433 | 205 | Return time 1 year in future in ISO format | ||
434 | 206 | """ | ||
435 | 207 | return self.get_iso_time(year_offset=1) | ||
436 | 208 | |||
437 | 209 | def get_current_iso_time(self): | ||
438 | 210 | """ | ||
439 | 211 | Return current time in ISO format | ||
440 | 212 | """ | ||
441 | 213 | return self.get_iso_time() | ||
442 | 214 | |||
443 | 215 | def get_iso_time(self, year_offset=0, month_offset=0, day_offset=0, | ||
444 | 216 | hour_offset=0, min_offset=0, sec_offset=0, | ||
445 | 217 | tz_hour_offset=0, tz_min_offset=0): | ||
446 | 218 | """ | ||
447 | 219 | Return an ISO8601 format date-time string, including time-zone | ||
448 | 220 | offset: YYYY-MM-DDTHH:MM:SS-HH:MM | ||
449 | 221 | """ | ||
450 | 222 | # calulate target time based on current time and format it | ||
451 | 223 | now = datetime.datetime.now() | ||
452 | 224 | target_time = datetime.datetime( | ||
453 | 225 | year=now.year + year_offset, | ||
454 | 226 | month=now.month + month_offset, | ||
455 | 227 | day=now.day + day_offset, | ||
456 | 228 | hour=now.hour + hour_offset, | ||
457 | 229 | minute=now.minute + min_offset, | ||
458 | 230 | second=now.second + sec_offset) | ||
459 | 231 | target_time_fmt = target_time.strftime('%Y-%m-%dT%H:%M:%S') | ||
460 | 232 | # format time zone offset | ||
461 | 233 | tz = datetime.time( | ||
462 | 234 | hour=tz_hour_offset, | ||
463 | 235 | minute=tz_min_offset) | ||
464 | 236 | tz_fmt = tz.strftime('%H:%M') | ||
465 | 237 | # combine target time and time zone offset | ||
466 | 238 | iso_time = '{0}-{1}'.format(target_time_fmt, tz_fmt) | ||
467 | 239 | return iso_time | ||
468 | 0 | 240 | ||
469 | === added file 'tests/autopilot/push_notifications/tests/test_push_client.py' | |||
470 | --- tests/autopilot/push_notifications/tests/test_push_client.py 1970-01-01 00:00:00 +0000 | |||
471 | +++ tests/autopilot/push_notifications/tests/test_push_client.py 2014-04-22 14:02:46 +0000 | |||
472 | @@ -0,0 +1,73 @@ | |||
473 | 1 | # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- | ||
474 | 2 | # Copyright 2014 Canonical | ||
475 | 3 | # | ||
476 | 4 | # This program is free software: you can redistribute it and/or modify it | ||
477 | 5 | # under the terms of the GNU General Public License version 3, as published | ||
478 | 6 | # by the Free Software Foundation. | ||
479 | 7 | |||
480 | 8 | """Tests for Push Notifications client""" | ||
481 | 9 | |||
482 | 10 | from __future__ import absolute_import | ||
483 | 11 | |||
484 | 12 | from testtools.matchers import Equals | ||
485 | 13 | from push_notifications.tests import PushNotificationTestBase | ||
486 | 14 | |||
487 | 15 | |||
488 | 16 | class TestPushClient(PushNotificationTestBase): | ||
489 | 17 | """ Tests a Push notification can be sent and received """ | ||
490 | 18 | |||
491 | 19 | def _validate_response(self, response, expected_status_code=200): | ||
492 | 20 | """ | ||
493 | 21 | Validate the received response status code against expected code | ||
494 | 22 | """ | ||
495 | 23 | self.assertThat(response.status, Equals(expected_status_code)) | ||
496 | 24 | |||
497 | 25 | def test_broadcast_push_notification(self): | ||
498 | 26 | """ | ||
499 | 27 | Positive test case to send a valid broadcast push notification | ||
500 | 28 | to the client and validate that a notification message is displayed | ||
501 | 29 | """ | ||
502 | 30 | # create a copy of the device's build info | ||
503 | 31 | msg_data = self.create_notification_data_copy() | ||
504 | 32 | # increment the build number to trigger an update | ||
505 | 33 | msg_data.inc_build_number() | ||
506 | 34 | # create message based on the data | ||
507 | 35 | msg = self.create_push_message(data=msg_data.json()) | ||
508 | 36 | # send the notification message to the server and check response | ||
509 | 37 | response = self.send_push_broadcast_notification(msg.json()) | ||
510 | 38 | self._validate_response(response) | ||
511 | 39 | |||
512 | 40 | # TODO validate that message is received on client | ||
513 | 41 | |||
514 | 42 | def test_expired_broadcast_push_notification(self): | ||
515 | 43 | """ | ||
516 | 44 | Send an expired broadcast notification message to server | ||
517 | 45 | """ | ||
518 | 46 | msg = self.create_push_message(expire_after=self.get_past_iso_time()) | ||
519 | 47 | response = self.send_push_broadcast_notification(msg.json()) | ||
520 | 48 | # 400 status is received for an expired message | ||
521 | 49 | self._validate_response(response, expected_status_code='400') | ||
522 | 50 | |||
523 | 51 | # TODO validate that message is not received on client | ||
524 | 52 | |||
525 | 53 | def test_near_expiry_broadcast_push_notification(self): | ||
526 | 54 | """ | ||
527 | 55 | Send a broadcast message with a short validity time | ||
528 | 56 | """ | ||
529 | 57 | msg = self.create_push_message( | ||
530 | 58 | expire_after=self.get_near_future_iso_time()) | ||
531 | 59 | response = self.send_push_broadcast_notification(msg.json()) | ||
532 | 60 | self._validate_response(response) | ||
533 | 61 | |||
534 | 62 | # TODO validate that message is received on client | ||
535 | 63 | |||
536 | 64 | def test_just_expired_broadcast_push_notification(self): | ||
537 | 65 | """ | ||
538 | 66 | Send a broadcast message which has just expired | ||
539 | 67 | """ | ||
540 | 68 | msg = self.create_push_message( | ||
541 | 69 | expire_after=self.get_near_past_iso_time()) | ||
542 | 70 | response = self.send_push_broadcast_notification(msg.json()) | ||
543 | 71 | self._validate_response(response) | ||
544 | 72 | |||
545 | 73 | # TODO validate that message is not received on client |
Hey Richard. You need to resubmit your MP against lp:ubuntu-push/automatic instead of trunk. I'll forward you the mail pedronis sent about this.