Merge lp:~kalikiana/u1db-qt/upstream-tests into lp:u1db-qt
- upstream-tests
- Merge into trunk
Proposed by
Cris Dywan
Status: | Work in progress |
---|---|
Proposed branch: | lp:~kalikiana/u1db-qt/upstream-tests |
Merge into: | lp:u1db-qt |
Diff against target: |
693 lines (+678/-0) 3 files modified
tests/bridged.py (+486/-0) tests/setup.py (+6/-0) tests/test-bridged.py (+186/-0) |
To merge this branch: | bzr merge lp:~kalikiana/u1db-qt/upstream-tests |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
U1DB Qt developers | Pending | ||
Review via email: mp+182339@code.launchpad.net |
Commit message
First attempt at implementing Python U1Db class for unit tests
Description of the change
To post a comment you must log in.
Unmerged revisions
- 68. By Cris Dywan
-
First attempt at implementing Python U1Db class for unit tests
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added file 'tests/__init__.py' |
2 | === added file 'tests/bridged.py' |
3 | --- tests/bridged.py 1970-01-01 00:00:00 +0000 |
4 | +++ tests/bridged.py 2013-08-27 10:28:13 +0000 |
5 | @@ -0,0 +1,486 @@ |
6 | +import atexit |
7 | +import functools |
8 | +import inspect |
9 | +import json |
10 | +import os |
11 | + |
12 | +from u1db import ( |
13 | + Database as DbInterface, |
14 | + SyncTarget as SyncTargetInterface, |
15 | + Document, |
16 | + errors |
17 | +) |
18 | +from tools import js_bridge |
19 | + |
20 | + |
21 | +_js_bridge = None |
22 | + |
23 | +XHR_IMPL = [ |
24 | + 'platform/core.js', |
25 | + 'platform/rhino.js', |
26 | + 'console.js', |
27 | + 'dom.js', |
28 | + 'event.js', |
29 | + 'html.js', |
30 | + 'timer.js', |
31 | + 'xhr.js' |
32 | + ] |
33 | + |
34 | + |
35 | +def get_u1db_js_bridge(): |
36 | + global _js_bridge |
37 | + if _js_bridge is None: |
38 | + _js_bridge = js_bridge.JsBridge() |
39 | + if _js_bridge.rhino: |
40 | + for xhr_impl_bit in XHR_IMPL: |
41 | + _js_bridge.load(os.path.join('tests', |
42 | + 'dist-env-js', xhr_impl_bit)) |
43 | + _js_bridge.load('oauth/sha1.js') |
44 | + _js_bridge.load('oauth/oauth.js') |
45 | + _js_bridge.load('u1db.js') |
46 | + _js_bridge._orig_globals = _js_bridge.global_names() |
47 | + _js_bridge._check_escaped = os.getenv('CHECK_ESCAPED') is not None |
48 | + atexit.register(_js_bridge.quit) |
49 | + return _js_bridge |
50 | + |
51 | + |
52 | +to_js = json.dumps |
53 | + |
54 | + |
55 | +def apply_content(doc, content): |
56 | + if content is None: |
57 | + doc.set_json(None) |
58 | + else: |
59 | + doc.content = content |
60 | + |
61 | + |
62 | +def doc_from_dic(val): |
63 | + doc = Document(doc_id=val['doc_id'], rev=val['rev'], |
64 | + has_conflicts=val['has_conflicts']) |
65 | + apply_content(doc, val['content']) |
66 | + return doc |
67 | + |
68 | + |
69 | +class BridgedJsObject(object): |
70 | + |
71 | + # medium level interface |
72 | + |
73 | + def _check_for_escaped(self): |
74 | + if self._js_bridge._check_escaped: |
75 | + orig_globals = self._js_bridge._orig_globals |
76 | + cur_globals = self._js_bridge.global_names() |
77 | + if cur_globals != orig_globals: |
78 | + raise Exception("escaped vars: %r" % |
79 | + cur_globals.symmetric_difference(orig_globals)) |
80 | + |
81 | + _count = 1 |
82 | + |
83 | + def _fresh_count(self): |
84 | + c = self._count |
85 | + BridgedJsObject._count += 1 |
86 | + return c |
87 | + |
88 | + def _make_db(self, dbname): |
89 | + dbvar = "db%d" % self._fresh_count() |
90 | + self._js_bridge.retain(dbvar, "new U1DB(%s, true)" % to_js(dbname)) |
91 | + self._check_for_escaped() |
92 | + return dbvar |
93 | + |
94 | + def _forget_ref(self, var): |
95 | + self._js_bridge.forget([var]) |
96 | + |
97 | + _forget_db = _forget_ref |
98 | + |
99 | + def _make_http_sync_target(self, url): |
100 | + tgtvar = "tgt%d" % self._fresh_count() |
101 | + self._js_bridge.retain(tgtvar, "new U1DBHTTPSyncTarget(%s)" % to_js(url)) |
102 | + self._check_for_escaped() |
103 | + return tgtvar |
104 | + |
105 | + _forget_http_sync_target = _forget_ref |
106 | + |
107 | + def _make_synchronizer(self, db_ref, tgt_ref): |
108 | + synchronizervar = "synchronizer%d" % self._fresh_count() |
109 | + self._js_bridge.retain(synchronizervar, "new U1DBSynchronizer(%s, %s)" % |
110 | + (db_ref, tgt_ref)) |
111 | + self._check_for_escaped() |
112 | + return synchronizervar |
113 | + |
114 | + _forget_synchronizer = _forget_ref |
115 | + |
116 | + def _temp_doc_expr(self, doc): |
117 | + return "new U1DBDocument(%s,%s,%s,%s)" % ( |
118 | + to_js(doc.doc_id), to_js(doc.rev), |
119 | + to_js(doc.content), to_js(doc.has_conflicts)) |
120 | + |
121 | + def _make_doc(self, doc): |
122 | + docvar = "doc%d" % self._fresh_count() |
123 | + self._js_bridge.retain(docvar, self._temp_doc_expr(doc)) |
124 | + self._check_for_escaped() |
125 | + return docvar |
126 | + |
127 | + _forget_doc = _forget_ref |
128 | + |
129 | + _nil_cb = "null" |
130 | + |
131 | + def _make_cb(self, func): |
132 | + cbvar = "cb%d" % self._fresh_count() |
133 | + self._js_bridge.make_cb(cbvar, func) |
134 | + return cbvar |
135 | + |
136 | + def _make_cb_doc(self, func): |
137 | + @functools.wraps(func) |
138 | + def wrap(doc_dic, *args): |
139 | + return func(doc_from_dic(doc_dic), *args) |
140 | + return self._make_cb(wrap) |
141 | + |
142 | + def _forget_cb(self, cb_ref): |
143 | + self._js_bridge.forget_cb(cb_ref) |
144 | + |
145 | + def _do_invoke(self, ref, meth_name, opts, js_args): |
146 | + if opts: |
147 | + js_args = js_args + [to_js(opts[0])] |
148 | + try: |
149 | + res = self._js_bridge.eval("%s.%s(%s)" % (ref, meth_name, |
150 | + ', '.join(js_args))) |
151 | + except js_bridge.JSError, js_err: |
152 | + if hasattr(errors, js_err.name): |
153 | + if js_err.name == "HTTPError": |
154 | + raise errors.HTTPError(500, js_err.message) # xxx != 500 |
155 | + raise getattr(errors, js_err.name)(js_err.message) |
156 | + raise |
157 | + self._check_for_escaped() |
158 | + return res |
159 | + |
160 | + natural_calling = True |
161 | + |
162 | + def _invoke(self, ref, meth_name, opts, args): |
163 | + return self._do_invoke(ref, meth_name, opts, map(to_js, args)) |
164 | + |
165 | + def _invoke_varargs(self, ref, meth_name, opts, args, rest): |
166 | + return self._do_invoke(ref, meth_name, opts, |
167 | + map(to_js, args+list(rest))) |
168 | + |
169 | + def _invoke_ret_tuple(self, ref, meth_name, opts, args): |
170 | + return tuple(self._do_invoke(ref, meth_name, opts, map(to_js, args))) |
171 | + |
172 | + def _invoke_ret_doc(self, ref, meth_name, opts, args): |
173 | + dic = self._do_invoke(ref, meth_name, opts, map(to_js, args)) |
174 | + if dic is None: |
175 | + return None |
176 | + return doc_from_dic(dic) |
177 | + |
178 | + def _invoke_ret_whats_changed(self, ref, meth_name, opts, args): |
179 | + # one single optional arg, like whats_changed |
180 | + if self.natural_calling and not args and opts: |
181 | + if opts[0]: |
182 | + args = [opts[0].values()[0]] |
183 | + opts = None |
184 | + gen, trans_id, changes = self._do_invoke(ref, meth_name, opts, |
185 | + map(to_js, args)) |
186 | + return gen, trans_id, map(tuple, changes) |
187 | + |
188 | + def _invoke_ret_docs(self, ref, meth_name, opts, args): |
189 | + dics = self._do_invoke(ref, meth_name, opts, map(to_js, args)) |
190 | + return map(doc_from_dic, dics) |
191 | + |
192 | + def _invoke_varargs_ret_docs(self, ref, meth_name, opts, args, rest): |
193 | + dics = self._do_invoke(ref, meth_name, opts, |
194 | + map(to_js, args+list(rest))) |
195 | + return map(doc_from_dic, dics) |
196 | + |
197 | + def _invoke_ret_tuples(self, ref, meth_name, opts, args): |
198 | + lst = self._do_invoke(ref, meth_name, opts, map(to_js, args)) |
199 | + return map(tuple, lst) |
200 | + |
201 | + def _invoke_ret_gen_docs(self, ref, meth_name, opts, args): |
202 | + gen, dics = self._do_invoke(ref, meth_name, opts, map(to_js, args)) |
203 | + return gen, map(doc_from_dic, dics) |
204 | + |
205 | + def _invoke_doc(self, ref, meth_name, opts, doc_ref, args): |
206 | + return self._do_invoke(ref, meth_name, opts, [doc_ref] + map(to_js, |
207 | + args)) |
208 | + |
209 | + def _invoke_doc_ret_tuple(self, ref, meth_name, opts, doc_ref, args): |
210 | + return tuple(self._do_invoke(ref, meth_name, opts, |
211 | + [doc_ref] + map(to_js, args))) |
212 | + |
213 | + def _read_into_doc(self, doc_ref, doc): |
214 | + doc.rev, doc.has_conflicts, content = self._js_bridge.eval( |
215 | + "[%s.rev, %s.has_conflicts, %s.content]" % ((doc_ref,)*3)) |
216 | + apply_content(doc, content) |
217 | + |
218 | + def _invoke_sync_exchange(self, ref, docs_by_generations, |
219 | + source_replica_uid, |
220 | + last_known_generation, last_known_trans_id, |
221 | + return_doc_cb_ref, ensure_cb_ref): |
222 | + docs_by_gen_exprs = [] |
223 | + for doc, gen, trans_id in docs_by_generations: |
224 | + docs_by_gen_exprs.append("[%s, %s, %s]" % ( |
225 | + self._temp_doc_expr(doc), to_js(gen), to_js(trans_id))) |
226 | + docs_by_gen_expr = "[%s]" % ', '.join(docs_by_gen_exprs) |
227 | + return tuple(self._do_invoke(ref, 'sync_exchange', None, |
228 | + [docs_by_gen_expr, |
229 | + to_js(source_replica_uid), |
230 | + to_js(last_known_generation), |
231 | + to_js(last_known_trans_id), |
232 | + return_doc_cb_ref, |
233 | + ensure_cb_ref])) |
234 | + |
235 | + def _invoke_cb(self, ref, meth_name, cb_ref): |
236 | + return self._do_invoke(ref, meth_name, None, [cb_ref]) |
237 | + |
238 | + # end of medium level interface |
239 | + |
240 | + def _parse_opts(self, opt_spec, *args): |
241 | + if opt_spec is None: |
242 | + return args, None |
243 | + opt_args, defl_vals = opt_spec |
244 | + opt = len(defl_vals) |
245 | + opts, defl = {}, {} |
246 | + for argname, argval, argdefl in zip(opt_args, args[-opt:], defl_vals): |
247 | + if argval != argdefl: |
248 | + opts[argname] = argval |
249 | + else: |
250 | + defl[argname] = argdefl |
251 | + return args[:-opt], (opts, defl) |
252 | + |
253 | + def _bridge_meth(self, name, opts, args): |
254 | + return self._invoke(self._ref, name, opts, args) |
255 | + |
256 | + def _bridge_meth_doc(self, name, opts, args, ret_kind=None): |
257 | + invoke = self._invoke_doc |
258 | + if ret_kind != None: |
259 | + invoke = getattr(self, '_invoke_doc_ret_%s' % ret_kind) |
260 | + doc = args[0] |
261 | + doc_ref = self._make_doc(doc) |
262 | + try: |
263 | + res = invoke(self._ref, name, opts, doc_ref, args[1:]) |
264 | + self._read_into_doc(doc_ref, doc) |
265 | + return res |
266 | + finally: |
267 | + self._forget_doc(doc_ref) |
268 | + |
269 | + _bridge_meth_doc_conv = _bridge_meth_doc |
270 | + |
271 | + def _bridge_meth_conv(self, name, opts, args, ret_kind): |
272 | + invoke = getattr(self, '_invoke_ret_%s' % ret_kind) |
273 | + return invoke(self._ref, name, opts, args) |
274 | + |
275 | + def _bridge_meth_varargs(self, name, opts, args, rest, ret_kind=None): |
276 | + invoke = self._invoke_varargs |
277 | + if ret_kind != None: |
278 | + invoke = getattr(self, '_invoke_varargs_ret_%s' % ret_kind) |
279 | + return invoke(self._ref, name, opts, args, rest) |
280 | + |
281 | + _bridge_meth_varargs_conv = _bridge_meth_varargs |
282 | + |
283 | + def _bridge_meth_sync_exchange(self, docs_by_generations, |
284 | + source_replica_uid, |
285 | + last_known_generation, last_known_trans_id, |
286 | + return_doc_cb, ensure_callback): |
287 | + return_doc_cb_ref = self._make_cb_doc(return_doc_cb) |
288 | + if ensure_callback is not None: |
289 | + ensure_cb_ref = self._make_cb(ensure_callback) |
290 | + else: |
291 | + ensure_cb_ref = self._nil_cb |
292 | + try: |
293 | + return self._invoke_sync_exchange(self._ref, docs_by_generations, |
294 | + source_replica_uid, |
295 | + last_known_generation, |
296 | + last_known_trans_id, |
297 | + return_doc_cb_ref, |
298 | + ensure_cb_ref) |
299 | + finally: |
300 | + if ensure_callback is not None: |
301 | + self._forget_cb(ensure_cb_ref) |
302 | + self._forget_cb(return_doc_cb_ref) |
303 | + |
304 | + |
305 | +_BRIDGE_METH_TEMPL = """ |
306 | +def bridge_meth(self, %(args)s): |
307 | + args, opts = self._parse_opts(%(opt_spec)r, %(args)s) |
308 | + return self._bridge_meth%(sign)s(%(name)r, opts, args%(conv)s) |
309 | +""" |
310 | + |
311 | + |
312 | +def _complete_interface(interface_cls, bridge_cls, prot_exposed, skip, ret): |
313 | + for name, val in interface_cls.__dict__.items(): |
314 | + if name.startswith('_') and name not in prot_exposed: |
315 | + continue |
316 | + if name in skip: |
317 | + continue |
318 | + if name in bridge_cls.__dict__: |
319 | + continue |
320 | + spec = inspect.getargspec(getattr(interface_cls, name)) |
321 | + assert spec.args[0] == 'self' |
322 | + args = spec.args[1:] |
323 | + if spec.varargs: |
324 | + assert spec.keywords is None |
325 | + assert False, "*args, not supported yet" |
326 | + else: |
327 | + opt_spec = None |
328 | + if spec.defaults: |
329 | + opt_spec = (args[-len(spec.defaults):], spec.defaults) |
330 | + sign = '' |
331 | + if args and args[0] == 'doc': |
332 | + sign += '_doc' |
333 | + ret_kind = ret.get(name) |
334 | + if ret_kind: |
335 | + sign += '_conv' |
336 | + conv = ", %r" % ret_kind |
337 | + else: |
338 | + conv = '' |
339 | + def_code = _BRIDGE_METH_TEMPL % dict(name=name, opt_spec=opt_spec, |
340 | + sign=sign, args=', '.join(args), |
341 | + conv=conv) |
342 | + ns = {} |
343 | + exec def_code in ns |
344 | + bridge_meth = ns['bridge_meth'] |
345 | + bridge_meth.__name__ = name |
346 | + bridge_meth.func_defaults = spec.defaults |
347 | + setattr(bridge_cls, name, bridge_meth) |
348 | + |
349 | + |
350 | +class BridgedJsDatabase(BridgedJsObject): |
351 | + |
352 | + def __init__(self, replica_uid): |
353 | + self._js_bridge = get_u1db_js_bridge() |
354 | + self._ref = self._make_db(replica_uid) |
355 | + |
356 | + def _forget(self): |
357 | + self._forget_db(self._ref) |
358 | + |
359 | + def delete_db(self): |
360 | + return self._bridge_meth('delete_db', None, []) |
361 | + |
362 | + # testing/internal api |
363 | + |
364 | + def _get_transaction_log(self): |
365 | + return self._bridge_meth('_get_transaction_log', None, []) |
366 | + |
367 | + def _validate_source(self, other_replica_uid, other_generation, |
368 | + other_transaction_id): |
369 | + return self._bridge_meth('_validate_source', None, |
370 | + [other_replica_uid, other_generation, |
371 | + other_transaction_id]) |
372 | + |
373 | + def validate_gen_and_trans_id(self, generation, trans_id): |
374 | + return self._bridge_meth('validate_gen_and_trans_id', None, |
375 | + [generation, trans_id]) |
376 | + |
377 | + def _get_generation_info(self): |
378 | + return self._bridge_meth_conv('_get_generation_info', |
379 | + None, [], ret_kind='tuple') |
380 | + |
381 | + def create_index(self, name, *index_expressions): |
382 | + return self._bridge_meth_varargs('create_index', None, |
383 | + [name], index_expressions) |
384 | + |
385 | + def get_from_index(self, name, *keys): |
386 | + return self._bridge_meth_varargs_conv('get_from_index', |
387 | + None, [name], keys, |
388 | + ret_kind='docs') |
389 | + |
390 | + def get_range_from_index(self, name, start_keys, end_keys=None): |
391 | + return self._bridge_meth_conv('get_range_from_index', |
392 | + None, [name, start_keys, end_keys], |
393 | + ret_kind='docs') |
394 | + |
395 | + |
396 | +_complete_interface(DbInterface, BridgedJsDatabase, |
397 | + prot_exposed=set([ |
398 | + '_put_doc_if_newer', |
399 | + '_get_replica_gen_and_trans_id', |
400 | + '_set_replica_gen_and_trans_id', |
401 | + ]), |
402 | + skip=set(['set_document_factory',]), |
403 | + ret={ |
404 | + 'create_doc_from_json': 'doc', |
405 | + 'create_doc': 'doc', |
406 | + 'get_doc': 'doc', |
407 | + 'get_doc_conflicts': 'docs', |
408 | + 'get_docs': 'docs', |
409 | + 'get_all_docs': 'gen_docs', |
410 | + 'whats_changed': 'whats_changed', |
411 | + '_get_replica_gen_and_trans_id': 'tuple', |
412 | + '_put_doc_if_newer': 'tuple', |
413 | + 'list_indexes': 'tuples', |
414 | + 'get_index_keys': 'tuples' |
415 | + }) |
416 | + |
417 | + |
418 | +class BridgedJsHTTPSyncTarget(BridgedJsObject): |
419 | + |
420 | + def __init__(self, url): |
421 | + self._js_bridge = get_u1db_js_bridge() |
422 | + self._ref = self._make_http_sync_target(url) |
423 | + self._cb_ref = None |
424 | + |
425 | + def _forget(self): |
426 | + if self._cb_ref: |
427 | + self._forget_cb(self._cb_ref) |
428 | + self._forget_http_sync_target(self._ref) |
429 | + |
430 | + def set_oauth_credentials(self, consumer_key, consumer_secret, |
431 | + token_key, token_secret): |
432 | + self._bridge_meth('set_oauth_credentials', None, |
433 | + [consumer_key, consumer_secret, |
434 | + token_key, token_secret]) |
435 | + |
436 | + def sync_exchange(self, docs_by_generations, source_replica_uid, |
437 | + last_known_generation, last_known_trans_id, |
438 | + return_doc_cb, ensure_callback=None): |
439 | + return self._bridge_meth_sync_exchange(docs_by_generations, |
440 | + source_replica_uid, |
441 | + last_known_generation, |
442 | + last_known_trans_id, |
443 | + return_doc_cb, |
444 | + ensure_callback) |
445 | + |
446 | + # testing/internal api |
447 | + |
448 | + def _set_trace_hook_shallow(self, cb): |
449 | + self._cb_ref = self._make_cb(cb) |
450 | + self._invoke_cb(self._ref, '_set_trace_hook_shallow', self._cb_ref) |
451 | + |
452 | + |
453 | +_complete_interface(SyncTargetInterface, BridgedJsHTTPSyncTarget, |
454 | + prot_exposed=set(), |
455 | + skip=set(), |
456 | + ret = { |
457 | + 'get_sync_info': 'tuple', |
458 | + }) |
459 | + |
460 | + |
461 | +class BridgedJsSynchronizer(BridgedJsObject): |
462 | + |
463 | + def __init__(self, bridged_source_db, bridged_target): |
464 | + self._js_bridge = get_u1db_js_bridge() |
465 | + self._ref = self._make_synchronizer(bridged_source_db._ref, |
466 | + bridged_target._ref) |
467 | + |
468 | + def _forget(self): |
469 | + self._forget_synchronizer(self._ref) |
470 | + |
471 | + def sync(self): |
472 | + return self._bridge_meth('sync', None, []) |
473 | + |
474 | + |
475 | +def make_database(test, replica_uid): |
476 | + db = BridgedJsDatabase(replica_uid) |
477 | + def cleanup(): |
478 | + db.delete_db() |
479 | + db._forget() |
480 | + test.addCleanup(cleanup) |
481 | + return db |
482 | + |
483 | +def make_http_sync_target(test, path): |
484 | + target = BridgedJsHTTPSyncTarget(test.getURL(path)) |
485 | + test.addCleanup(target._forget) |
486 | + return target |
487 | + |
488 | +def make_synchronizer(test, source, target): |
489 | + synchronizer = BridgedJsSynchronizer(source, target) |
490 | + test.addCleanup(synchronizer._forget) |
491 | + return synchronizer |
492 | |
493 | === added file 'tests/setup.py' |
494 | --- tests/setup.py 1970-01-01 00:00:00 +0000 |
495 | +++ tests/setup.py 2013-08-27 10:28:13 +0000 |
496 | @@ -0,0 +1,6 @@ |
497 | +from distutils.core import setup, Extension |
498 | + |
499 | +modul = Extension("u1dbqt", sources=["tests/qt-backend-wrapper.cpp"]) |
500 | +setup(name = "U1DbQt", version = "1.0", |
501 | + description = "", ext_modules = [modul]) |
502 | + |
503 | |
504 | === added file 'tests/test-bridged.py' |
505 | --- tests/test-bridged.py 1970-01-01 00:00:00 +0000 |
506 | +++ tests/test-bridged.py 2013-08-27 10:28:13 +0000 |
507 | @@ -0,0 +1,186 @@ |
508 | +import os |
509 | + |
510 | +from u1db import ( |
511 | + errors, |
512 | + sync, |
513 | + tests, |
514 | + ) |
515 | +from u1db.tests import ( |
516 | + test_backends, |
517 | + test_remote_sync_target, |
518 | + test_sync, |
519 | + ) |
520 | +from u1db.remote.cors_middleware import CORSMiddleware |
521 | + |
522 | +from tools import serving |
523 | + |
524 | +from bridged import ( |
525 | + get_u1db_js_bridge, |
526 | + BridgedJsObject, |
527 | + make_database, |
528 | + make_http_sync_target, |
529 | + make_synchronizer, |
530 | + ) |
531 | + |
532 | +_test_count = 0 |
533 | + |
534 | +def setUp(self): |
535 | + if FLAG: |
536 | + global _test_count |
537 | + _test_count += 1 |
538 | + get_u1db_js_bridge().flag("#%d %s" % (_test_count, self.id().split('.', 2)[2])) |
539 | + super(self.__class__, self).setUp() |
540 | + |
541 | +def startServer(self): |
542 | + app = self.make_app() |
543 | + bridge = get_u1db_js_bridge() |
544 | + if not SAME_ORIGIN or bridge.rhino: |
545 | + (self.server_thread, self.server, |
546 | + self.base_url) = serving.serve(app, 0, log_server=LOG_SERVER) |
547 | + self.addCleanup(self.server_thread.join) |
548 | + self.addCleanup(self.server.shutdown) |
549 | + else: |
550 | + self.server = bridge.get_server() |
551 | + self.base_url = bridge.get_base_url() |
552 | + bridge.set_fallback_app(app) |
553 | + if getattr(app, 'base_url', 1) is None: |
554 | + app.base_url = self.base_url |
555 | + |
556 | +SCENARIOS = [("jsbridged", { |
557 | + "make_database_for_test": make_database, |
558 | + })] |
559 | + |
560 | +def skip_test(test): |
561 | + test.skipTest("skipped: not applicable/relevant") |
562 | + |
563 | +class BridgedAllDatabaseTests(test_backends.AllDatabaseTests): |
564 | + scenarios = SCENARIOS |
565 | + setUp = setUp |
566 | + |
567 | +class BridgedLocalDatabaseTests(test_backends.LocalDatabaseTests): |
568 | + accept_fixed_trans_id = True |
569 | + scenarios = SCENARIOS |
570 | + setUp = setUp |
571 | + |
572 | +class BridgedLocalDatabaseValidateSourceGenTests(test_backends.LocalDatabaseValidateSourceGenTests): |
573 | + scenarios = SCENARIOS |
574 | + setUp = setUp |
575 | + |
576 | +class BridgedLocalDatabaseValidateGenNTransIdTests(test_backends.LocalDatabaseValidateGenNTransIdTests): |
577 | + scenarios = SCENARIOS |
578 | + setUp = setUp |
579 | + test_validate_gen_and_trans_id_invalid_gen = skip_test |
580 | + |
581 | +class BridgedLocalDatabaseWithConflictsTests(test_backends.LocalDatabaseWithConflictsTests): |
582 | + accept_fixed_trans_id = True |
583 | + scenarios = SCENARIOS |
584 | + setUp = setUp |
585 | + |
586 | +class BridgedDatabaseIndexTests(test_backends.DatabaseIndexTests): |
587 | + scenarios = SCENARIOS |
588 | + setUp = setUp |
589 | + |
590 | +class MyCORSMiddleware(CORSMiddleware): |
591 | + _base_url = None |
592 | + |
593 | + @property |
594 | + def base_url(self): |
595 | + return self._base_url |
596 | + |
597 | + @base_url.setter |
598 | + def base_url(self, value): |
599 | + self.app.base_url = self._base_url = value |
600 | + |
601 | +def make_http_app(state): |
602 | + app = test_remote_sync_target.make_http_app(state) |
603 | + bridge = get_u1db_js_bridge() |
604 | + if not SAME_ORIGIN: |
605 | + return MyCORSMiddleware(app, ['*']) |
606 | + return app |
607 | + |
608 | +def make_oauth_http_app(state): |
609 | + app = test_remote_sync_target.make_oauth_http_app(state) |
610 | + bridge = get_u1db_js_bridge() |
611 | + if not SAME_ORIGIN: |
612 | + return MyCORSMiddleware(app, ['*']) |
613 | + return app |
614 | + |
615 | +def oauth_http_sync_target(test, path): |
616 | + st = make_http_sync_target(test, '~/' + path) |
617 | + st.set_oauth_credentials(tests.consumer1.key, tests.consumer1.secret, |
618 | + tests.token1.key, tests.token1.secret) |
619 | + return st |
620 | + |
621 | +class BridgedTestHttpSyncTarget(test_remote_sync_target.TestRemoteSyncTargets): |
622 | + startServer = startServer |
623 | + setUp = setUp |
624 | + |
625 | + scenarios = [ |
626 | + ('http', {'make_app_with_state': make_http_app, |
627 | + 'make_document_for_test': tests.make_document_for_test, |
628 | + 'sync_target': make_http_sync_target}), |
629 | + ('oauth_http', {'make_app_with_state': |
630 | + make_oauth_http_app, |
631 | + 'make_document_for_test': tests.make_document_for_test, |
632 | + 'sync_target': oauth_http_sync_target}), |
633 | + ] |
634 | + |
635 | + if not SAME_ORIGIN: |
636 | + failure_scenario_exceptions = (errors.UserQuotaExceeded, |
637 | + errors.UserQuotaExceeded) |
638 | + |
639 | +def copy_database_for_test(test, db): |
640 | + if isinstance(db, BridgedJsObject): |
641 | + test.skipTest("js dbs cannot be copied") |
642 | + return test_sync.copy_database_for_http_test(test, db) |
643 | + |
644 | +def sync_for_test(test, db_source, db_target, trace_hook=None, trace_hook_shallow=None): |
645 | + if isinstance(db_source, BridgedJsObject): |
646 | + if trace_hook: |
647 | + test.skipTest("full trace hook not supported by http target") |
648 | + path = test._http_at[db_target] |
649 | + target = make_http_sync_target(test, path) |
650 | + if trace_hook_shallow: |
651 | + target._set_trace_hook_shallow(trace_hook_shallow) |
652 | + return make_synchronizer(test, db_source, target).sync() |
653 | + target = db_target.get_sync_target() |
654 | + target._set_trace_hook_shallow(trace_hook or trace_hook_shallow) |
655 | + return sync.Synchronizer(db_source, target).sync() |
656 | + |
657 | +class BridgedDatabaseSyncTests(test_sync.DatabaseSyncTests): |
658 | + accept_fixed_trans_id = True |
659 | + startServer = startServer |
660 | + |
661 | + scenarios = [('jshttp', { |
662 | + 'make_database_for_test': None, # see create_database_for_role |
663 | + 'copy_database_for_test': copy_database_for_test, |
664 | + 'make_document_for_test': tests.make_document_for_test, |
665 | + 'make_app_with_state': make_http_app, |
666 | + 'do_sync': sync_for_test, |
667 | + })] |
668 | + |
669 | + def create_database_for_role(self, replica_uid, sync_role): |
670 | + if sync_role == 'source': |
671 | + js_db = make_database(self, replica_uid) |
672 | + js_db._replica_uid = replica_uid |
673 | + return js_db |
674 | + return test_sync.make_database_for_http_test(self, replica_uid) |
675 | + |
676 | + setUp = setUp |
677 | + test_optional_sync_preserve_json = skip_test |
678 | + |
679 | +class BridgedTestDbSync(test_sync.TestDbSync): |
680 | + startServer = startServer |
681 | + scenarios = [ |
682 | + ('jshttp', { |
683 | + 'make_app_with_state': make_http_app, |
684 | + 'make_database_for_test': make_database, |
685 | + }), |
686 | + ('jsoauthhttp', { |
687 | + 'make_app_with_state': make_oauth_http_app, |
688 | + 'make_database_for_test': make_database, |
689 | + 'oauth': True |
690 | + })] |
691 | + setUp = setUp |
692 | + |
693 | +load_tests = tests.load_with_scenarios |