Skip to content

Commit 05799af

Browse files
committed
Merge pull request #36 from dokterbob/unicode_regression
Unicode regression
2 parents 37928c4 + fc686da commit 05799af

4 files changed

Lines changed: 83 additions & 4 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
*.pyc
22
build
3-
dist
3+
dist
4+
.coverage

.travis.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,7 @@ python:
33
- "2.7"
44
install:
55
- pip install mock
6-
script: python gcm/test.py
6+
- pip install coveralls
7+
script: coverage run --source=gcm gcm/test.py
8+
after_success:
9+
coveralls

gcm/gcm.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,25 @@ def group_response(response, registration_ids, key):
4848
return grouping
4949

5050

51+
def urlencode_utf8(params):
52+
"""
53+
UTF-8 safe variant of urllib.urlencode.
54+
http://stackoverflow.com/a/8152242
55+
"""
56+
57+
if hasattr(params, 'items'):
58+
params = params.items()
59+
60+
params = (
61+
'='.join((
62+
urllib.quote_plus(k.encode('utf8'), safe='/'),
63+
urllib.quote_plus(v.encode('utf8'), safe='/')
64+
)) for k, v in params
65+
)
66+
67+
return '&'.join(params)
68+
69+
5170
class GCM(object):
5271

5372
# Timeunit is milliseconds.
@@ -133,7 +152,7 @@ def make_request(self, data, is_json=True):
133152
headers['Content-Type'] = 'application/json'
134153

135154
if not is_json:
136-
data = urllib.urlencode(data)
155+
data = urlencode_utf8(data)
137156
req = urllib2.Request(self.url, data, headers)
138157

139158
try:

gcm/test.py

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import unittest
22
from gcm import *
33
import json
4-
from mock import MagicMock
4+
from mock import MagicMock, patch
55
import time
66

77

@@ -15,6 +15,24 @@ def side_effect(*args, **kwargs):
1515
return side_effect
1616

1717

18+
class MockResponse(object):
19+
"""
20+
Mock urllib2.urlopen response.
21+
http://stackoverflow.com/a/2276727
22+
"""
23+
def __init__(self, resp_data, code=200, msg='OK'):
24+
self.resp_data = resp_data
25+
self.code = code
26+
self.msg = msg
27+
self.headers = {'content-type': 'text/xml; charset=utf-8'}
28+
29+
def read(self):
30+
return self.resp_data
31+
32+
def getcode(self):
33+
return self.code
34+
35+
1836
class GCMTest(unittest.TestCase):
1937

2038
def setUp(self):
@@ -166,6 +184,44 @@ def test_handle_plaintext_response(self):
166184
res = self.gcm.handle_plaintext_response(response)
167185
self.assertEqual(res, '3456')
168186

187+
@patch('urllib2.urlopen')
188+
def test_make_request_plaintext(self, urlopen_mock):
189+
""" Test plaintext make_request. """
190+
191+
# Set mock value for urlopen return value
192+
urlopen_mock.return_value = MockResponse('blah')
193+
194+
# Perform request
195+
response = self.gcm.make_request({'message': 'test'}, is_json=False)
196+
197+
# Get request (first positional argument to urlopen)
198+
# Ref: http://www.voidspace.org.uk/python/mock/mock.html#mock.Mock.call_args
199+
request = urlopen_mock.call_args[0][0]
200+
201+
# Test encoded data
202+
encoded_data = request.get_data()
203+
self.assertEquals(
204+
encoded_data, 'message=test'
205+
)
206+
207+
# Assert return value
208+
self.assertEquals(
209+
response,
210+
'blah'
211+
)
212+
213+
214+
@patch('urllib2.urlopen')
215+
def test_make_request_unicode(self, urlopen_mock):
216+
""" Regression: Test make_request with unicode payload. """
217+
218+
# Unicode character in data
219+
data = {
220+
'message': u'\x80abc'
221+
}
222+
223+
self.gcm.make_request(data, is_json=False)
224+
169225
def test_retry_plaintext_request_ok(self):
170226
returns = [GCMUnavailableException(), GCMUnavailableException(), 'id=123456789']
171227

0 commit comments

Comments
 (0)