~azzar1/unity/add-show-desktop-key

« back to all changes in this revision

Viewing changes to ivle/webapp/publisher/test_publisher.py

  • Committer: David Coles
  • Date: 2010-07-28 10:52:48 UTC
  • mfrom: (1791.2.10 mediahandlers)
  • Revision ID: coles.david@gmail.com-20100728105248-zvbn9g72v1nsskvd
A series of HTML5 based media handlers using the <audio> and <video> tags.  
This replaces the previous page that just showed a download link (which is 
already available on the menu).

Also solves issue where media files were downloaded by the client twice (once 
in an AJAX request intended only for text).

Known issues:
    * Bug #588285: External BHO will not be able to play media due to not
      having IVLE cookie.
    * Bug #610745: Does not correctly preview revisions
    * Bug #610780: Ogg media does not work in Chromium

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
from nose.tools import assert_equal, raises
 
2
 
 
3
from ivle.webapp.publisher import (INF, InsufficientPathSegments, NoPath,
 
4
                                   NotFound, RouteConflict, Publisher, ROOT)
 
5
 
 
6
class Root(object):
 
7
    def __init__(self):
 
8
        self.subjects = {}
 
9
        self.users = {}
 
10
 
 
11
    def add_subject(self, subject):
 
12
        self.subjects[subject.name] = subject
 
13
 
 
14
    def add_user(self, user):
 
15
        self.users[user.login] = user
 
16
 
 
17
class User(object):
 
18
    def __init__(self, login):
 
19
        self.login = login
 
20
 
 
21
class Subject(object):
 
22
    def __init__(self, name, code):
 
23
        self.name = name
 
24
        self.code = code
 
25
        self.offerings = {}
 
26
 
 
27
    def add_offering(self, offering):
 
28
        assert self.name == offering.subject.name
 
29
        self.offerings[(offering.year, offering.semester)] = offering
 
30
 
 
31
class Offering(object):
 
32
    def __init__(self, subject, year, semester):
 
33
        self.subject = subject
 
34
        self.year = year
 
35
        self.semester = semester
 
36
        self.projects = {}
 
37
 
 
38
    def add_project(self, project):
 
39
        assert project.offering is self
 
40
        self.projects[project.name] = project
 
41
 
 
42
class OfferingFiles(object):
 
43
    def __init__(self, offering):
 
44
        self.offering = offering
 
45
 
 
46
class OfferingFile(object):
 
47
    def __init__(self, offeringfiles, path):
 
48
        self.offering = offeringfiles.offering
 
49
        self.path = path
 
50
 
 
51
class Project(object):
 
52
    def __init__(self, offering, name):
 
53
        self.offering = offering
 
54
        self.name = name
 
55
 
 
56
 
 
57
class View(object):
 
58
    def __init__(self, context):
 
59
        self.context = context
 
60
 
 
61
class RootIndex(View):
 
62
    pass
 
63
 
 
64
class UserServeView(View):
 
65
    pass
 
66
 
 
67
class SubjectIndex(View):
 
68
    pass
 
69
 
 
70
class SubjectEdit(View):
 
71
    pass
 
72
 
 
73
class OfferingIndex(View):
 
74
    pass
 
75
 
 
76
class OfferingEdit(View):
 
77
    pass
 
78
 
 
79
class OfferingAPIIndex(View):
 
80
    pass
 
81
 
 
82
class OfferingFilesIndex(View):
 
83
    pass
 
84
 
 
85
class OfferingFileIndex(View):
 
86
    pass
 
87
 
 
88
class ProjectIndex(View):
 
89
    pass
 
90
 
 
91
class OfferingProjects(View):
 
92
    pass
 
93
 
 
94
class OfferingAddProject(View):
 
95
    pass
 
96
 
 
97
class OfferingWorksheets(View):
 
98
    pass
 
99
 
 
100
class OfferingWorksheetMarks(View):
 
101
    pass
 
102
 
 
103
class OfferingWorksheetCSVMarks(View):
 
104
    pass
 
105
 
 
106
def root_to_subject_or_user(root, name):
 
107
    if name.startswith('~'):
 
108
        return root.users.get(name[1:])
 
109
    return root.subjects.get(name)
 
110
 
 
111
def subject_to_offering(subject, year, semester):
 
112
    return subject.offerings.get((int(year), int(semester)))
 
113
 
 
114
def offering_to_files(offering):
 
115
    return OfferingFiles(offering)
 
116
 
 
117
def offering_files_to_file(offeringfiles, *path):
 
118
    return OfferingFile(offeringfiles, path)
 
119
 
 
120
def offering_to_project(offering, name):
 
121
    return offering.projects.get(name)
 
122
 
 
123
def subject_url(subject):
 
124
    return (ROOT, subject.name)
 
125
 
 
126
def offering_url(offering):
 
127
    return (offering.subject, (str(offering.year), str(offering.semester)))
 
128
 
 
129
def offering_files_url(offeringfiles):
 
130
    return (offeringfiles.offering, '+files')
 
131
 
 
132
def project_url(project):
 
133
    return (project.offering, ('+projects', project.name))
 
134
 
 
135
class BaseTest(object):
 
136
    def setUp(self):
 
137
        r = Root()
 
138
        self.r = r
 
139
 
 
140
        # A user would be nice.
 
141
        r.add_user(User('jsmith'))
 
142
 
 
143
        # Give us some subjects...
 
144
        r.add_subject(Subject('info1', '600151'))
 
145
        r.add_subject(Subject('info2', '600152'))
 
146
        r.add_subject(Subject('info3', '600251'))
 
147
 
 
148
        # ... and some offerings.
 
149
        r.subjects['info1'].add_offering(Offering(self.r.subjects['info1'],
 
150
                                         2008, 1))
 
151
        r.subjects['info1'].add_offering(Offering(self.r.subjects['info1'],
 
152
                                         2008, 2))
 
153
        r.subjects['info1'].add_offering(Offering(self.r.subjects['info1'],
 
154
                                         2009, 1))
 
155
        r.subjects['info2'].add_offering(Offering(self.r.subjects['info2'],
 
156
                                         2008, 2))
 
157
        r.subjects['info2'].add_offering(Offering(self.r.subjects['info2'],
 
158
                                         2009, 1))
 
159
        r.subjects['info3'].add_offering(Offering(self.r.subjects['info3'],
 
160
                                         2009, 1))
 
161
 
 
162
        # A normal project...
 
163
        r.subjects['info1'].offerings[(2009, 1)].add_project(
 
164
            Project(r.subjects['info1'].offerings[(2009, 1)], 'p1')
 
165
            )
 
166
 
 
167
        # And one conflicting with a deep view, just to be nasty.
 
168
        r.subjects['info1'].offerings[(2009, 1)].add_project(
 
169
            Project(r.subjects['info1'].offerings[(2009, 1)], '+new')
 
170
            )
 
171
 
 
172
class TestResolution(BaseTest):
 
173
    def setUp(self):
 
174
        super(TestResolution, self).setUp()
 
175
        self.rtr = Publisher(root=self.r, viewset='browser')
 
176
        self.rtr.add_set_switch('api', 'api')
 
177
        self.rtr.add_forward(Root, None, root_to_subject_or_user, 1)
 
178
        self.rtr.add_forward(Subject, None, subject_to_offering, 2)
 
179
        self.rtr.add_forward(Offering, '+files', offering_to_files, 0)
 
180
        self.rtr.add_forward(OfferingFiles, None, offering_files_to_file, INF)
 
181
        self.rtr.add_forward(Offering, '+projects', offering_to_project, 1)
 
182
        self.rtr.add_view(User, None, UserServeView, viewset='browser')
 
183
        self.rtr.add_view(Subject, '+index', SubjectIndex, viewset='browser')
 
184
        self.rtr.add_view(Subject, '+edit', SubjectEdit, viewset='browser')
 
185
        self.rtr.add_view(Offering, '+index', OfferingIndex, viewset='browser')
 
186
        self.rtr.add_view(Offering, '+index', OfferingAPIIndex, viewset='api')
 
187
        self.rtr.add_view(OfferingFiles, '+index', OfferingFilesIndex,
 
188
                          viewset='browser')
 
189
        self.rtr.add_view(OfferingFile, '+index', OfferingFileIndex,
 
190
                          viewset='browser')
 
191
        self.rtr.add_view(Project, '+index', ProjectIndex, viewset='browser')
 
192
        self.rtr.add_view(Offering, ('+projects', '+new'), OfferingAddProject,
 
193
                          viewset='browser')
 
194
        self.rtr.add_view(Offering, ('+projects', '+index'), OfferingProjects,
 
195
                          viewset='browser')
 
196
        self.rtr.add_view(Offering, ('+worksheets', '+index'),
 
197
                          OfferingWorksheets, viewset='browser')
 
198
        self.rtr.add_view(Offering, ('+worksheets', '+marks', '+index'),
 
199
                          OfferingWorksheetMarks, viewset='browser')
 
200
        self.rtr.add_view(Offering, ('+worksheets', '+marks', 'marks.csv'),
 
201
                          OfferingWorksheetCSVMarks, viewset='browser')
 
202
 
 
203
    def testOneRoute(self):
 
204
        assert_equal(self.rtr.resolve('/info1'),
 
205
                     (self.r.subjects['info1'], SubjectIndex, ())
 
206
                     )
 
207
        assert_equal(self.rtr.resolve('/info3'),
 
208
                     (self.r.subjects['info3'], SubjectIndex, ())
 
209
                     )
 
210
 
 
211
    def testTwoRoutes(self):
 
212
        assert_equal(self.rtr.resolve('/info1/2009/1'),
 
213
             (self.r.subjects['info1'].offerings[(2009, 1)], OfferingIndex, ())
 
214
             )
 
215
        assert_equal(self.rtr.resolve('/info2/2008/2'),
 
216
             (self.r.subjects['info2'].offerings[(2008, 2)], OfferingIndex, ())
 
217
             )
 
218
 
 
219
    def testNamedRoute(self):
 
220
        assert_equal(type(self.rtr.resolve('/info1/2009/1/+files')[0]),
 
221
                     OfferingFiles
 
222
                    )
 
223
        assert_equal(self.rtr.resolve('/info1/2009/1/+files')[0].offering,
 
224
                     self.r.subjects['info1'].offerings[(2009, 1)]
 
225
                    )
 
226
 
 
227
    def testNonDefaultView(self):
 
228
        assert_equal(self.rtr.resolve('/info1/+edit'),
 
229
                     (self.r.subjects['info1'], SubjectEdit, ())
 
230
                     )
 
231
 
 
232
    def testDefaultView(self):
 
233
        assert_equal(self.rtr.resolve('/info1'),
 
234
                     (self.r.subjects['info1'], SubjectIndex, ())
 
235
                     )
 
236
 
 
237
    def testViewWithSubpath(self):
 
238
        assert_equal(self.rtr.resolve('/info1/+edit/foo/bar'),
 
239
                     (self.r.subjects['info1'], SubjectEdit, ('foo', 'bar'))
 
240
                     )
 
241
 
 
242
    def testNoDefaultView(self):
 
243
        try:
 
244
            self.rtr.default = 'not+index'
 
245
            self.rtr.resolve('/info1')
 
246
        except NotFound, e:
 
247
            assert_equal(e.args, (self.r.subjects['info1'], '+index', ()))
 
248
        except:
 
249
            raise
 
250
        else:
 
251
            raise AssertionError('did not raise NotFound')
 
252
        finally:
 
253
            self.rtr.default = '+index'
 
254
 
 
255
    def testMissingView(self):
 
256
        try:
 
257
            self.rtr.resolve('/info1/+foo')
 
258
        except NotFound, e:
 
259
            assert_equal(e.args, (self.r.subjects['info1'], '+foo', ()))
 
260
        except:
 
261
            raise
 
262
        else:
 
263
            raise AssertionError('did not raise NotFound')
 
264
 
 
265
    def testViewSetSeparation(self):
 
266
        try:
 
267
            self.rtr.resolve('/api/info1/+edit')
 
268
        except NotFound, e:
 
269
            assert_equal(e.args, (self.r.subjects['info1'], '+edit', ()))
 
270
        except:
 
271
            raise
 
272
        else:
 
273
            raise AssertionError('did not raise NotFound')
 
274
 
 
275
    def testRouteReturningNone(self):
 
276
        try:
 
277
            self.rtr.resolve('/info9/+index')
 
278
        except NotFound, e:
 
279
            assert_equal(e.args, (self.r, 'info9', ('+index',)))
 
280
        except:
 
281
            raise
 
282
        else:
 
283
            raise AssertionError('did not raise NotFound')
 
284
 
 
285
    def testRouteWithInfinitelyManyArguments(self):
 
286
        o, v, sp = self.rtr.resolve('/info1/2009/1/+files/foo/bar/baz')
 
287
 
 
288
        assert_equal(type(o), OfferingFile)
 
289
        assert_equal(o.path, ('foo', 'bar', 'baz'))
 
290
        assert_equal(o.offering, self.r.subjects['info1'].offerings[(2009, 1)])
 
291
        assert_equal(v, OfferingFileIndex)
 
292
        assert_equal(sp, ())
 
293
 
 
294
    def testMissingRoute(self):
 
295
        try:
 
296
            self.rtr.resolve('/info1/2009/1/+foo')
 
297
        except NotFound, e:
 
298
            assert_equal(e.args, (
 
299
                self.r.subjects['info1'].offerings[(2009, 1)],
 
300
                '+foo',
 
301
                ()
 
302
                ))
 
303
        except:
 
304
            raise
 
305
        else:
 
306
            raise AssertionError('did not raise NotFound')
 
307
 
 
308
    def testAlternateViewSetWithDefault(self):
 
309
        assert_equal(self.rtr.resolve('/info1/2009/1'),
 
310
             (self.r.subjects['info1'].offerings[(2009, 1)], OfferingIndex, ())
 
311
             )
 
312
 
 
313
        assert_equal(self.rtr.resolve('/api/info1/2009/1'),
 
314
          (self.r.subjects['info1'].offerings[(2009, 1)], OfferingAPIIndex, ())
 
315
          )
 
316
 
 
317
    def testDeepView(self):
 
318
        assert_equal(self.rtr.resolve('/info1/2009/1/+projects/+new'),
 
319
             (self.r.subjects['info1'].offerings[(2009, 1)],
 
320
              OfferingAddProject, ())
 
321
             )
 
322
 
 
323
    def testDefaultDeepView(self):
 
324
        assert_equal(self.rtr.resolve('/info1/2009/1/+projects'),
 
325
             (self.r.subjects['info1'].offerings[(2009, 1)],
 
326
              OfferingProjects, ())
 
327
             )
 
328
 
 
329
    def testAnotherDefaultDeepView(self):
 
330
        assert_equal(self.rtr.resolve('/info1/2009/1/+worksheets'),
 
331
             (self.r.subjects['info1'].offerings[(2009, 1)],
 
332
              OfferingWorksheets, ())
 
333
             )
 
334
 
 
335
    def testReallyDeepView(self):
 
336
        assert_equal(
 
337
             self.rtr.resolve('/info1/2009/1/+worksheets/+marks/marks.csv'),
 
338
             (self.r.subjects['info1'].offerings[(2009, 1)],
 
339
              OfferingWorksheetCSVMarks, ())
 
340
             )
 
341
 
 
342
    def testDefaultReallyDeepView(self):
 
343
        assert_equal(self.rtr.resolve('/info1/2009/1/+worksheets/+marks'),
 
344
             (self.r.subjects['info1'].offerings[(2009, 1)],
 
345
              OfferingWorksheetMarks, ())
 
346
             )
 
347
 
 
348
    def testNamedRouteWithDeepView(self):
 
349
        assert_equal(self.rtr.resolve('/info1/2009/1/+projects/p1'),
 
350
             (self.r.subjects['info1'].offerings[(2009, 1)].projects['p1'],
 
351
              ProjectIndex, ())
 
352
             )
 
353
 
 
354
    def testNullPathView(self):
 
355
        """Verify that views can be placed immediately under an object.
 
356
 
 
357
        There are some cases in which it is useful for a view with a
 
358
        subpath to exist immediately under an object, with no name.
 
359
        """
 
360
        assert_equal(self.rtr.resolve('/~jsmith/foo/bar'),
 
361
             (self.r.users['jsmith'], UserServeView, ('foo', 'bar')))
 
362
 
 
363
    def testTrailingSlashResolvesToDefaultView(self):
 
364
        assert_equal(
 
365
             self.rtr.resolve('/info1/2009/1/'),
 
366
             (self.r.subjects['info1'].offerings[(2009, 1)],
 
367
              OfferingIndex, ())
 
368
             )
 
369
 
 
370
    def testTrailingSlashResolvesToDeepDefaultView(self):
 
371
        assert_equal(
 
372
             self.rtr.resolve('/info1/2009/1/+worksheets/+marks/'),
 
373
             (self.r.subjects['info1'].offerings[(2009, 1)],
 
374
              OfferingWorksheetMarks, ())
 
375
             )
 
376
 
 
377
    def testSubpathIndicatesTrailingSlash(self):
 
378
        assert_equal(
 
379
             self.rtr.resolve('/info1/2009/1/+index/'),
 
380
             (self.r.subjects['info1'].offerings[(2009, 1)],
 
381
              OfferingIndex, ('',))
 
382
             )
 
383
 
 
384
class TestGeneration(BaseTest):
 
385
    def setUp(self):
 
386
        super(TestGeneration, self).setUp()
 
387
        self.rtr = Publisher(root=self.r, viewset='browser')
 
388
        self.rtr.add_set_switch('api', 'api')
 
389
        self.rtr.add_reverse(Subject, subject_url)
 
390
        self.rtr.add_reverse(Offering, offering_url)
 
391
        self.rtr.add_reverse(OfferingFiles, offering_files_url)
 
392
        self.rtr.add_reverse(Project, project_url)
 
393
        self.rtr.add_view(Subject, '+index', SubjectIndex, viewset='browser')
 
394
        self.rtr.add_view(Subject, '+edit', SubjectEdit, viewset='browser')
 
395
        self.rtr.add_view(Offering, '+index', OfferingIndex, viewset='browser')
 
396
        self.rtr.add_view(Offering, '+index', OfferingAPIIndex, viewset='api')
 
397
        self.rtr.add_view(Project, '+index', ProjectIndex, viewset='browser')
 
398
        self.rtr.add_view(Offering, ('+projects', '+new'), OfferingAddProject,
 
399
                          viewset='browser')
 
400
        self.rtr.add_view(Offering, ('+projects', '+index'), OfferingProjects,
 
401
                          viewset='browser')
 
402
 
 
403
    def testOneLevel(self):
 
404
        assert_equal(self.rtr.generate(self.r.subjects['info1']), '/info1')
 
405
 
 
406
    def testTwoLevel(self):
 
407
        assert_equal(
 
408
            self.rtr.generate(self.r.subjects['info1'].offerings[(2009, 1)]),
 
409
            '/info1/2009/1'
 
410
            )
 
411
        assert_equal(
 
412
            self.rtr.generate(self.r.subjects['info2'].offerings[(2008, 2)]),
 
413
            '/info2/2008/2'
 
414
            )
 
415
 
 
416
    def testNamedRoute(self):
 
417
        assert_equal(self.rtr.generate(
 
418
                OfferingFiles(self.r.subjects['info1'].offerings[(2009, 1)])),
 
419
                '/info1/2009/1/+files'
 
420
            )
 
421
 
 
422
    def testView(self):
 
423
        assert_equal(self.rtr.generate(self.r.subjects['info1'], SubjectEdit),
 
424
                     '/info1/+edit'
 
425
                     )
 
426
 
 
427
    def testDefaultView(self):
 
428
        assert_equal(
 
429
            self.rtr.generate(self.r.subjects['info1'].offerings[(2009, 1)],
 
430
                              OfferingIndex
 
431
                              ),
 
432
            '/info1/2009/1'
 
433
            )
 
434
 
 
435
    def testViewWithSubpath(self):
 
436
        assert_equal(self.rtr.generate(self.r.subjects['info1'], SubjectEdit,
 
437
                                       ('foo', 'bar')),
 
438
                     '/info1/+edit/foo/bar'
 
439
                     )
 
440
 
 
441
    def testViewWithStringSubpath(self):
 
442
        assert_equal(self.rtr.generate(self.r.subjects['info1'], SubjectEdit,
 
443
                                       'foo/bar'),
 
444
                     '/info1/+edit/foo/bar'
 
445
                     )
 
446
 
 
447
    def testAlternateViewSetWithDefault(self):
 
448
        assert_equal(
 
449
            self.rtr.generate(self.r.subjects['info1'].offerings[(2009, 1)],
 
450
                              OfferingAPIIndex
 
451
                              ),
 
452
            '/api/info1/2009/1'
 
453
            )
 
454
 
 
455
    def testDeepView(self):
 
456
        assert_equal(
 
457
            self.rtr.generate(
 
458
                self.r.subjects['info1'].offerings[(2009, 1)],
 
459
                OfferingAddProject
 
460
                ),
 
461
        '/info1/2009/1/+projects/+new'
 
462
        )
 
463
 
 
464
    def testDefaultDeepView(self):
 
465
        assert_equal(
 
466
            self.rtr.generate(
 
467
                self.r.subjects['info1'].offerings[(2009, 1)],
 
468
                OfferingProjects
 
469
                ),
 
470
        '/info1/2009/1/+projects'
 
471
        )
 
472
 
 
473
    def testDefaultDeepViewWithSubpath(self):
 
474
        assert_equal(
 
475
            self.rtr.generate(
 
476
                self.r.subjects['info1'].offerings[(2009, 1)],
 
477
                OfferingProjects, ('foo', 'bar')
 
478
                ),
 
479
        '/info1/2009/1/+projects/+index/foo/bar'
 
480
        )
 
481
 
 
482
    def testNamedRouteWithDeepView(self):
 
483
        assert_equal(
 
484
            self.rtr.generate(
 
485
                self.r.subjects['info1'].offerings[(2009, 1)].projects['p1'],
 
486
                ProjectIndex
 
487
                ),
 
488
        '/info1/2009/1/+projects/p1'
 
489
        )
 
490
 
 
491
    def testRoot(self):
 
492
        assert_equal(self.rtr.generate(self.r), '/')
 
493
 
 
494
 
 
495
class TestErrors(BaseTest):
 
496
    def setUp(self):
 
497
        super(TestErrors, self).setUp()
 
498
        self.rtr = Publisher(root=self.r)
 
499
        self.rtr.add_forward(Root, None, root_to_subject_or_user, 1)
 
500
        self.rtr.add_forward(Subject, '+foo', lambda s: s.name + 'foo', 0)
 
501
        self.rtr.add_forward(Subject, None, subject_to_offering, 2)
 
502
        self.rtr.add_reverse(Subject, subject_url)
 
503
        self.rtr.add_reverse(Offering, offering_url)
 
504
        self.rtr.add_view(Offering, '+index', OfferingIndex)
 
505
        self.rtr.add_view(Offering, '+index', OfferingAPIIndex, viewset='api')
 
506
        self.rtr.add_set_switch('rest', 'rest')
 
507
 
 
508
    @raises(RouteConflict)
 
509
    def testForwardConflict(self):
 
510
        self.rtr.add_forward(Subject, '+foo', object(), 2)
 
511
 
 
512
    @raises(RouteConflict)
 
513
    def testReverseConflict(self):
 
514
        self.rtr.add_reverse(Subject, object())
 
515
 
 
516
    @raises(RouteConflict)
 
517
    def testViewConflict(self):
 
518
        self.rtr.add_view(Offering, '+index', object())
 
519
 
 
520
    @raises(RouteConflict)
 
521
    def testSetSwitchForwardConflict(self):
 
522
        self.rtr.add_set_switch('rest', 'foo')
 
523
 
 
524
    @raises(RouteConflict)
 
525
    def testSetSwitchReverseConflict(self):
 
526
        self.rtr.add_set_switch('bar', 'rest')
 
527
 
 
528
    @raises(NoPath)
 
529
    def testNoPath(self):
 
530
        self.rtr.generate(object())
 
531
 
 
532
    @raises(NoPath)
 
533
    def testNoSetSwitch(self):
 
534
        self.rtr.generate(self.r.subjects['info1'].offerings[(2009, 1)],
 
535
                          OfferingAPIIndex)
 
536
 
 
537
    @raises(NoPath)
 
538
    def testUnregisteredView(self):
 
539
        self.rtr.generate(self.r.subjects['info1'], SubjectIndex)
 
540
 
 
541
    @raises(NotFound)
 
542
    def testNotFound(self):
 
543
        self.rtr.resolve('/bar')
 
544
 
 
545
    @raises(InsufficientPathSegments)
 
546
    def testInsufficientPathSegments(self):
 
547
        self.rtr.resolve('/info1/foo')