Merge lp:~kalikiana/u1db-qt/upstream-tests into lp:u1db-qt

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
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

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

Subscribers

People subscribed via source and target branches

to all changes: