Completed
Push — master ( d1e883...1f56b4 )
by
unknown
01:28
created

StatisticsTest.test_parse_request()   A

Complexity

Conditions 1

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
1
# coding: utf8
2
3
"""
4
This software is licensed under the Apache 2 license, quoted below.
5
6
Copyright 2014 Crystalnix Limited
7
8
Licensed under the Apache License, Version 2.0 (the "License"); you may not
9
use this file except in compliance with the License. You may obtain a copy of
10
the License at
11
12
    http://www.apache.org/licenses/LICENSE-2.0
13
14
Unless required by applicable law or agreed to in writing, software
15
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17
License for the specific language governing permissions and limitations under
18
the License.
19
"""
20
21
from builtins import range
22
23
from datetime import datetime
24
from uuid import UUID
25
26
from django.test import TestCase
27
from django.core.files.uploadedfile import SimpleUploadedFile
28
29
from mock import patch
30
from freezegun import freeze_time
31
from bitmapist import DayEvents, HourEvents, mark_event
32
33
from omaha.tests import fixtures
34
from omaha.parser import parse_request
35
from omaha.statistics import (
36
    userid_counting,
37
    add_app_statistics,
38
    is_user_active,
39
    get_kwargs_for_model,
40
    parse_os,
41
    parse_hw,
42
    parse_req,
43
    parse_apps,
44
    parse_events,
45
    collect_statistics,
46
    get_users_statistics_months,
47
    get_channel_statistics,
48
    get_users_versions,
49
)
50
51
from omaha.tests.utils import temporary_media_root, create_app_xml
52
from omaha.utils import redis, get_id
53
from omaha.settings import DEFAULT_CHANNEL
54
from omaha.models import (
55
    ACTIVE_USERS_DICT_CHOICES,
56
    Os,
57
    Hw,
58
    Request,
59
    AppRequest,
60
    Event,
61
    Application,
62
    Platform,
63
    Channel,
64
    Version,
65
)
66
from sparkle.models import SparkleVersion
67
from sparkle.statistics import userid_counting as mac_userid_counting
68
69
70
class StatisticsTest(TestCase):
71
    def setUp(self):
72
        redis.flushdb()
73
74
    def tearDown(self):
75
        redis.flushdb()
76
77
    @patch('omaha.statistics.add_app_statistics')
78
    def test_userid_counting(self, mock_add_app_statistics):
79
        now = datetime.utcnow()
80
81
        userid1 = '{F07B3878-CD6F-4B96-B52F-95C4D23077E0}'
82
        user1_id = get_id(userid1)
83
84
        userid2 = '{EC4C5647-F798-4BCA-83DA-926CD448A1D5}'
85
        user2_id = get_id(userid2)
86
87
        app_list = [dict(appid='{F97917B1-19AB-48C1-9802-CEF305B10804}'),
88
                    dict(appid='{555B8D18-076D-4576-9579-1FD7F0399EAE}')]
89
90
        request_events = DayEvents('request', now.year, now.month, now.day)
91
92
        self.assertFalse(user1_id in request_events)
93
        self.assertEqual(len(request_events), 0)
94
95
        userid_counting(userid1, app_list, 'win')
96
97
        for app in app_list:
98
            mock_add_app_statistics.assert_any_call(user1_id, 'win', app, now=None)
99
100
        self.assertEqual(len(request_events), 1)
101
        self.assertTrue(user1_id in request_events)
102
103
        userid_counting(userid1, app_list, 'win')
104
105
        for app in app_list:
106
            mock_add_app_statistics.assert_any_call(user1_id, 'win', app, now=None)
107
108
        self.assertEqual(len(request_events), 1)
109
110
        self.assertFalse(user2_id in request_events)
111
        userid_counting(userid2, app_list[:1], 'win')
112
        self.assertTrue(user2_id in request_events)
113
        for app in app_list[:1]:
114
            mock_add_app_statistics.assert_any_call(user2_id, 'win', app, now=None)
115
116
        self.assertEqual(len(request_events), 2)
117
118
    @freeze_time('2016-1-1')
119
    def test_add_app_statistics(self):
120
        now = datetime.utcnow()
121
        next_month = now.replace(month=now.month + 1)
122
        userid = 1
123
        channel = DEFAULT_CHANNEL
124
        platform = 'win'
125
        app_kwargs = dict(appid='{2882CF9B-D9C2-4edb-9AAF-8ED5FCF366F7}', nextversion='0.0.0.1')
126
        success_app = create_app_xml(events=fixtures.event_install_success, **app_kwargs)
127
        error_app = create_app_xml(events=fixtures.event_install_error, **app_kwargs)
128
        appid = app_kwargs.get('appid')
129
        version = app_kwargs.get('nextversion')
130
131
        events_request_appid = lambda date=now: DayEvents.from_date('request:%s' % appid, date)
132
        events_new_appid = lambda date=now: DayEvents.from_date('new_install:%s' % appid, date)
133
        events_request_appid_version = lambda date=now: DayEvents.from_date('request:{}:{}'.format(appid, version), date)
134
        events_request_appid_platform = lambda date=now: DayEvents.from_date('request:{}:{}'.format(appid, platform), date)
135
        events_new_appid_platform = lambda date=now: DayEvents.from_date('new_install:{}:{}'.format(appid, platform), date)
136
        events_request_appid_channel = lambda date=now: DayEvents.from_date('request:{}:{}'.format(appid, channel), date)
137
        events_request_appid_platform_version = lambda date=now: DayEvents.from_date('request:{}:{}:{}'.format(appid, platform, version), date)
138
139
        self.assertEqual(len(events_new_appid()), 0)
140
        self.assertEqual(len(events_request_appid()), 0)
141
        self.assertEqual(len(events_request_appid_version()), 0)
142
        self.assertEqual(len(events_request_appid_platform()), 0)
143
        self.assertEqual(len(events_new_appid_platform()), 0)
144
        self.assertEqual(len(events_request_appid_channel()), 0)
145
        self.assertEqual(len(events_request_appid_platform_version()), 0)
146
147
        add_app_statistics(userid, platform, error_app)
148
149
        self.assertEqual(len(events_new_appid()), 0)
150
        self.assertEqual(len(events_request_appid()), 0)
151
        self.assertEqual(len(events_request_appid_version()), 0)
152
        self.assertEqual(len(events_request_appid_platform()), 0)
153
        self.assertEqual(len(events_new_appid_platform()), 0)
154
        self.assertEqual(len(events_request_appid_channel()), 0)
155
        self.assertEqual(len(events_request_appid_platform_version()), 0)
156
157
        add_app_statistics(userid, platform, success_app)
158
        self.assertEqual(len(events_new_appid()), 1)
159
        self.assertEqual(len(events_request_appid()), 0)
160
        self.assertEqual(len(events_request_appid_version()), 1)
161
        self.assertEqual(len(events_new_appid_platform()), 1)
162
        self.assertEqual(len(events_request_appid_platform()), 0)
163
        self.assertEqual(len(events_request_appid_channel()), 1)
164
        self.assertEqual(len(events_request_appid_platform_version()), 1)
165
166
        self.assertIn(userid, events_new_appid())
167
        self.assertIn(userid, events_request_appid_version())
168
        self.assertIn(userid, events_new_appid_platform())
169
        self.assertIn(userid, events_request_appid_channel())
170
        self.assertIn(userid, events_request_appid_platform_version())
171
172
        add_app_statistics(userid, platform, success_app)
173
        self.assertEqual(len(events_new_appid()), 1)
174
        self.assertEqual(len(events_request_appid()), 0)
175
        self.assertEqual(len(events_request_appid_version()), 1)
176
        self.assertEqual(len(events_new_appid_platform()), 1)
177
        self.assertEqual(len(events_request_appid_platform()), 0)
178
        self.assertEqual(len(events_request_appid_channel()), 1)
179
        self.assertEqual(len(events_request_appid_platform_version()), 1)
180
181
        with freeze_time(next_month):
182
            add_app_statistics(userid, platform, error_app)
183
184
        self.assertEqual(len(events_request_appid(next_month)), 0)
185
        self.assertEqual(len(events_request_appid_platform(next_month)), 0)
186
187
        with freeze_time(next_month):
188
            add_app_statistics(userid, platform, success_app)
189
190
        self.assertEqual(len(events_request_appid(next_month)), 1)
191
        self.assertEqual(len(events_request_appid_platform(next_month)), 1)
192
        self.assertEqual(len(events_new_appid(next_month)), 0)
193
        self.assertEqual(len(events_request_appid_version(next_month)), 1)
194
        self.assertEqual(len(events_new_appid_platform(next_month)), 0)
195
        self.assertEqual(len(events_request_appid_channel()), 1)
196
        self.assertEqual(len(events_request_appid_platform_version()), 1)
197
198
        self.assertIn(userid, events_request_appid(next_month))
199
        self.assertIn(userid, events_request_appid_platform(next_month))
200
201
    def test_is_user_active(self):
202
        userid = '{F07B3878-CD6F-4B96-B52F-95C4D23077E0}'
203
        id = get_id(userid)
204
205
        self.assertTrue(is_user_active(ACTIVE_USERS_DICT_CHOICES['all'], userid))
206
        self.assertFalse(is_user_active(ACTIVE_USERS_DICT_CHOICES['week'], userid))
207
        self.assertFalse(is_user_active(ACTIVE_USERS_DICT_CHOICES['month'], userid))
208
209
        mark_event('request', id)
210
211
        self.assertTrue(is_user_active(ACTIVE_USERS_DICT_CHOICES['all'], userid))
212
        self.assertTrue(is_user_active(ACTIVE_USERS_DICT_CHOICES['week'], userid))
213
        self.assertTrue(is_user_active(ACTIVE_USERS_DICT_CHOICES['month'], userid))
214
215
    def test_get_kwargs_for_model(self):
216
        os = dict(platform="win",
217
                  version="6.1",
218
                  sp="",
219
                  arch="x64")
220
        kwargs = get_kwargs_for_model(Os, os)
221
        self.assertDictEqual(kwargs, dict(platform="win",
222
                                          version="6.1",
223
                                          sp="",
224
                                          arch="x64",
225
                                          id=None))
226
227
    def test_parse_os(self):
228
        request = parse_request(fixtures.request_event)
229
        os = parse_os(request.os)
230
        self.assertIsInstance(os, Os)
231
        self.assertEqual(os.platform, 'win')
232
        self.assertEqual(os.version, '6.1')
233
        self.assertEqual(os.sp, '')
234
        self.assertEqual(os.arch, 'x64')
235
236
    def test_parse_hw(self):
237
        hw = dict(sse2="1")
238
        hw = parse_hw(hw)
239
        self.assertIsInstance(hw, Hw)
240
        self.assertEqual(hw.sse, None)
241
        self.assertEqual(hw.sse2, 1)
242
        self.assertEqual(hw.sse3, None)
243
        self.assertEqual(hw.ssse3, None)
244
        self.assertEqual(hw.sse41, None)
245
        self.assertEqual(hw.sse42, None)
246
        self.assertEqual(hw.avx, None)
247
        self.assertEqual(hw.physmemory, None)
248
249
    def test_parse_request(self):
250
        request = parse_request(fixtures.request_event)
251
        req = parse_req(request)
252
        self.assertIsInstance(req, Request)
253
        self.assertEqual(req.version, Request._meta.get_field_by_name('version')[0].to_python('1.3.23.0'))
254
        self.assertEqual(req.ismachine, 1)
255
        self.assertEqual(req.sessionid, '{2882CF9B-D9C2-4edb-9AAF-8ED5FCF366F7}')
256
        self.assertEqual(req.userid, '{D0BBD725-742D-44ae-8D46-0231E881D58E}')
257
        self.assertEqual(req.originurl, None)
258
        self.assertEqual(req.testsource, 'ossdev')
259
        self.assertEqual(req.updaterchannel, None)
260
261
    def test_parse_apps(self):
262
        request = parse_request(fixtures.request_event)
263
        req = parse_req(request)
264
        req.os = parse_os(request.os)
265
        req.hw = parse_hw(request.hw) if request.get('hw') else None
266
        req.save()
267
268
        apps = parse_apps(request.findall('app'), req)
269
        self.assertEqual(len(apps), 1)
270
        app = apps[0]
271
        self.assertIsInstance(app, AppRequest)
272
        self.assertEqual(app.version, None)
273
        self.assertEqual(app.nextversion, Request._meta.get_field_by_name('version')[0].to_python('13.0.782.112'))
274
        self.assertEqual(app.lang, 'en')
275
        self.assertEqual(app.tag, 'stable')
276
        self.assertEqual(app.installage, 6)
277
        self.assertEqual(app.appid, '{D0AB2EBC-931B-4013-9FEB-C9C4C2225C8C}')
278
279
    def test_parse_events(self):
280
        request = parse_request(fixtures.request_event)
281
        events = request.findall('app')[0].findall('event')
282
        events = parse_events(events)
283
        self.assertEqual(len(events), 3)
284
        event = events[0]
285
        self.assertIsInstance(event, Event)
286
        self.assertEqual(event.eventtype, 9)
287
        self.assertEqual(event.eventresult, 1)
288
        self.assertEqual(event.errorcode, 0)
289
        self.assertEqual(event.extracode1, 0)
290
        self.assertEqual(event.download_time_ms, None)
291
        self.assertEqual(event.downloaded, None)
292
        self.assertEqual(event.total, None)
293
        self.assertEqual(event.update_check_time_ms, None)
294
        self.assertEqual(event.install_time_ms, None)
295
        self.assertEqual(event.source_url_index, None)
296
        self.assertEqual(event.state_cancelled, None)
297
        self.assertEqual(event.time_since_update_available_ms, None)
298
        self.assertEqual(event.time_since_download_start_ms, None)
299
        self.assertEqual(event.nextversion, None)
300
        self.assertEqual(event.previousversion, None)
301
302
    def test_collect_statistics(self):
303
        request = parse_request(fixtures.request_event)
304
305
        self.assertEqual(Os.objects.all().count(), 0)
306
        self.assertEqual(Hw.objects.all().count(), 0)
307
        self.assertEqual(Request.objects.all().count(), 0)
308
        self.assertEqual(AppRequest.objects.all().count(), 0)
309
        self.assertEqual(Event.objects.all().count(), 0)
310
311
        collect_statistics(request)
312
313
        self.assertEqual(Os.objects.all().count(), 1)
314
        self.assertEqual(Hw.objects.all().count(), 0)
315
        self.assertEqual(Request.objects.all().count(), 1)
316
        self.assertEqual(AppRequest.objects.all().count(), 1)
317
        self.assertEqual(Event.objects.all().count(), 3)
318
319
        os = Os.objects.get()
320
        req = Request.objects.get()
321
        app_req = AppRequest.objects.get()
322
        events = Event.objects.all()
323
324
        self.assertEqual(os.platform, 'win')
325
        self.assertEqual(os.version, '6.1')
326
        self.assertEqual(os.sp, '')
327
        self.assertEqual(os.arch, 'x64')
328
329
        self.assertEqual(req.version, Request._meta.get_field_by_name('version')[0].to_python('1.3.23.0'))
330
        self.assertEqual(req.ismachine, 1)
331
        self.assertEqual(req.sessionid, '{2882CF9B-D9C2-4edb-9AAF-8ED5FCF366F7}')
332
        self.assertEqual(req.userid, '{D0BBD725-742D-44ae-8D46-0231E881D58E}')
333
        self.assertEqual(req.originurl, None)
334
        self.assertEqual(req.testsource, 'ossdev')
335
        self.assertEqual(req.updaterchannel, None)
336
        self.assertEqual(req.os, os)
337
        self.assertEqual(req.hw, None)
338
339
        self.assertEqual(app_req.version, None)
340
        self.assertEqual(app_req.nextversion, Request._meta.get_field_by_name('version')[0].to_python('13.0.782.112'))
341
        self.assertEqual(app_req.lang, 'en')
342
        self.assertEqual(app_req.tag, 'stable')
343
        self.assertEqual(app_req.installage, 6)
344
        self.assertEqual(app_req.appid, '{D0AB2EBC-931B-4013-9FEB-C9C4C2225C8C}')
345
        self.assertEqual(app_req.request, req)
346
347
        event = events[0]
348
349
        self.assertEqual(event.eventtype, 9)
350
        self.assertEqual(event.eventresult, 1)
351
        self.assertEqual(event.errorcode, 0)
352
        self.assertEqual(event.extracode1, 0)
353
        self.assertEqual(event.download_time_ms, None)
354
        self.assertEqual(event.downloaded, None)
355
        self.assertEqual(event.total, None)
356
        self.assertEqual(event.update_check_time_ms, None)
357
        self.assertEqual(event.install_time_ms, None)
358
        self.assertEqual(event.source_url_index, None)
359
        self.assertEqual(event.state_cancelled, None)
360
        self.assertEqual(event.time_since_update_available_ms, None)
361
        self.assertEqual(event.time_since_download_start_ms, None)
362
        self.assertEqual(event.nextversion, None)
363
        self.assertEqual(event.previousversion, None)
364
365
        for e in events:
366
            self.assertIn(e, app_req.events.all())
367
368
    def test_live_statistics_install(self):
369
        request = parse_request(fixtures.request_event_install_success)
370
        apps = request.findall('app')
371
        app = apps[0]
372
373
        now = datetime.utcnow()
374
        userid = 1
375
        platform = 'win'
376
377
        appid = app.get('appid')
378
        version_1 = '0.0.0.1'
379
        version_2 = '0.0.0.2'
380
381
        events_appid_version = lambda version: HourEvents('request:{}:{}'.format(appid, version), now.year, now.month, now.day, now.hour)
382
        events_appid_platform_version = lambda version: HourEvents('request:{}:{}'.format(appid, version), now.year, now.month, now.day, now.hour)
383
384
        self.assertEqual(len(events_appid_version(version_1)), 0)
385
        self.assertEqual(len(events_appid_platform_version(version_1)), 0)
386
        userid_counting(userid, apps, platform)
387
388
        self.assertEqual(len(events_appid_version(version_1)), 1)
389
        self.assertEqual(len(events_appid_platform_version(version_1)), 1)
390
391
        request = parse_request(fixtures.request_event_update_success)
392
        apps = request.findall('app')
393
394
        userid_counting(userid, apps, platform)
395
396
        self.assertEqual(len(events_appid_version(version_1)), 0)
397
        self.assertEqual(len(events_appid_platform_version(version_1)), 0)
398
        self.assertEqual(len(events_appid_version(version_2)), 1)
399
        self.assertEqual(len(events_appid_platform_version(version_2)), 1)
400
401
        request = parse_request(fixtures.request_event_uninstall_success)
402
        apps = request.findall('app')
403
404
        userid_counting(userid, apps, platform)
405
406
        self.assertEqual(len(events_appid_version(version_2)), 1)
407
        self.assertEqual(len(events_appid_platform_version(version_2)), 1)
408
409
    def test_live_statistics_updatecheck(self):
410
        request = parse_request(fixtures.request_update_check)
411
        apps = request.findall('app')
412
        app = apps[0]
413
414
        now = datetime.utcnow()
415
        userid = 1
416
        platform = 'win'
417
418
        appid = app.get('appid')
419
        version = app.get('version')
420
421
        events_appid_version = HourEvents('request:{}:{}'.format(appid, version), now.year, now.month, now.day, now.hour)
422
        events_appid_platform_version = HourEvents('request:{}:{}:{}'.format(appid, platform, version), now.year, now.month, now.day, now.hour)
423
424
        self.assertEqual(len(events_appid_version), 0)
425
        self.assertEqual(len(events_appid_platform_version), 0)
426
427
        userid_counting(userid, apps, platform)
428
429
        self.assertEqual(len(events_appid_version), 1)
430
        self.assertEqual(len(events_appid_platform_version), 1)
431
432
433
class GetStatisticsTest(TestCase):
434
    maxDiff = None
435
436
    def _generate_fake_statistics(self):
437
        now = datetime.now()
438
        year = now.year
439
        n_users = 12
440
441
        for i in range(1, n_users+1):
442
            date = datetime(year=year, month=i, day=10)
443
            for id in range(1, i + 1):
444
                user_id = UUID(int=id)
445
                userid_counting(user_id, self.install_app_list, self.platform.name, now=date)
446
                user_id = UUID(int=n_users + id)
447
                mac_userid_counting(user_id, self.mac_app, 'mac', now=date)
448
            userid_counting(UUID(int=i), self.uninstall_app_list, self.platform.name, now=date)
449
450
451
    @temporary_media_root()
452
    def setUp(self):
453
        redis.flushdb()
454
        self.app = Application.objects.create(id='app', name='app')
455
        self.channel = Channel.objects.create(name='stable')
456
        self.platform = Platform.objects.create(name='win')
457
        self.version1 = Version.objects.create(
458
            app=self.app,
459
            platform=self.platform,
460
            channel=self.channel,
461
            version='1.0.0.0',
462
            file=SimpleUploadedFile('./chrome_installer.exe', False))
463
        self.version2 = Version.objects.create(
464
            app=self.app,
465
            platform=self.platform,
466
            channel=self.channel,
467
            version='2.0.0.0',
468
            file=SimpleUploadedFile('./chrome_installer.exe', False))
469
        self.mac_version = SparkleVersion.objects.create(
470
            app=self.app,
471
            channel=self.channel,
472
            version='782.112',
473
            short_version='13.0.782.112',
474
            dsa_signature='MCwCFCdoW13VBGJWIfIklKxQVyetgxE7AhQTVuY9uQT0KOV1UEk21epBsGZMPg==',
475
            file=SimpleUploadedFile('./chrome.dmg', b'_' * 23963192),
476
            file_size=23963192)
477
478
        app_kwargs = dict(appid=self.app.id, version=str(self.version1.version))
479
        install_app = create_app_xml(events=[fixtures.event_install_success], **app_kwargs)
480
        uninstall_app = create_app_xml(events=[fixtures.event_uninstall_success], **app_kwargs)
481
        self.install_app_list = [install_app]
482
        self.uninstall_app_list = [uninstall_app]
483
        self.mac_app = dict(appid=self.app.id, version=str(self.mac_version.short_version))
484
485
        self._generate_fake_statistics()
486
        now = datetime.now()
487
        win_updates = [(datetime(now.year, x, 1).strftime("%Y-%m"), x - 1) for x in range(1, 13)]
488
        win_installs = [(datetime(now.year, x, 1).strftime("%Y-%m"), 1) for x in range(1, 13)]
489
        uninstalls = [(datetime(now.year, x, 1).strftime("%Y-%m"), 1) for x in range(1, 13)]
490
        mac_updates = [(datetime(now.year, x, 1).strftime("%Y-%m"), x - 1) for x in range(1, 13)]
491
        mac_installs = [(datetime(now.year, x, 1).strftime("%Y-%m"), 1) for x in range(1, 13)]
492
        total_installs = map(lambda x, y: (x[0], x[1] + y[1]), win_installs, mac_installs)
493
        total_updates = map(lambda x, y: (x[0], x[1] + y[1]), win_updates, mac_updates)
494
        self.users_statistics = dict(new=total_installs, updates=total_updates, uninstalls=uninstalls)
495
        self.win_users_statistics = dict(new=win_installs, updates=win_updates, uninstalls=uninstalls)
496
        self.mac_users_statistics = dict(new=mac_installs, updates=mac_updates)
497
498
499
    def tearDown(self):
500
        redis.flushdb()
501
502
    def test_get_users_statistics_months(self):
503
        self.assertDictEqual(get_users_statistics_months(app_id=self.app.id), self.users_statistics)
504
        self.assertDictEqual(get_users_statistics_months(app_id=self.app.id, platform='win'), self.win_users_statistics)
505
        self.assertDictEqual(get_users_statistics_months(app_id=self.app.id, platform='mac'), self.mac_users_statistics)
506
507
508
    def test_get_chanels_statistics(self):
509
        now = datetime.now()
510
        with freeze_time(datetime(year=now.year, month=now.month, day=10)):
511
            self.assertListEqual(get_channel_statistics(self.app.id), [('stable', now.month*2)])
512
513
    def test_get_users_versions(self):
514
        now = datetime.now()
515
        expected = dict(win={'1.0.0.0': now.month}, mac={'13.0.782.112': now.month})
516
        with freeze_time(datetime(year=now.year, month=now.month, day=10)):
517
            self.assertDictEqual(get_users_versions(self.app.id), expected)
518