Merge lp://qastaging/~cmiller/desktopcouch/add-running-context-as-parameter into lp://qastaging/desktopcouch

Proposed by Chad Miller
Status: Merged
Approved by: Eric Casteleijn
Approved revision: 106
Merged at revision: not available
Proposed branch: lp://qastaging/~cmiller/desktopcouch/add-running-context-as-parameter
Merge into: lp://qastaging/desktopcouch
Diff against target: 1259 lines (+337/-228)
18 files modified
desktopcouch/__init__.py (+3/-3)
desktopcouch/contacts/contactspicker.py (+3/-2)
desktopcouch/contacts/testing/create.py (+4/-3)
desktopcouch/contacts/tests/test_contactspicker.py (+4/-4)
desktopcouch/local_files.py (+92/-60)
desktopcouch/pair/couchdb_pairing/couchdb_io.py (+37/-26)
desktopcouch/pair/tests/test_couchdb_io.py (+27/-19)
desktopcouch/records/couchgrid.py (+6/-4)
desktopcouch/records/doc/records.txt (+1/-1)
desktopcouch/records/server.py (+9/-6)
desktopcouch/records/tests/test_couchgrid.py (+19/-14)
desktopcouch/records/tests/test_record.py (+5/-1)
desktopcouch/records/tests/test_server.py (+3/-3)
desktopcouch/start_local_couchdb.py (+41/-25)
desktopcouch/tests/__init__.py (+42/-41)
desktopcouch/tests/test_local_files.py (+9/-7)
desktopcouch/tests/test_replication.py (+21/-0)
desktopcouch/tests/test_start_local_couchdb.py (+11/-9)
To merge this branch: bzr merge lp://qastaging/~cmiller/desktopcouch/add-running-context-as-parameter
Reviewer Review Type Date Requested Status
Eric Casteleijn (community) Approve
Nicola Larosa (community) Abstain
Guillermo Gonzalez Approve
Review via email: mp+14824@code.qastaging.launchpad.net

Commit message

Use an explicit test context for testing. Add an implicit context for normal usage.

Add test context to pairing test functions.

Add a context to the update_design_documents() function. Make it peek at the context data dir location explicitly.

To post a comment you must log in.
Revision history for this message
Guillermo Gonzalez (verterok) wrote :

looks good, all tests [OK]

review: Approve
Revision history for this message
Nicola Larosa (teknico) wrote :

I cannot find a way to test this, please document it somewhere.

First I run "setup.py build".

Then I run "setup.py check", pylint cannot find a config file, so it uses defaults, and spews out a *huge* amount of notices.

Last I run "setup.py test", it says "running test" but does not seem to do anything.

review: Abstain
Revision history for this message
Nicola Larosa (teknico) wrote :

Ok, it's "trial desktopcouch", there was some fog this morning. Tests pass, but I'm not looking at the code, I'll let Eric do that.

review: Abstain
Revision history for this message
Eric Casteleijn (thisfred) wrote :

After updating trunk and merging this in I get a fairly trivial conflict in desktopcouch/__init__.py.

Also in that file a default argument is used:

local_files.DEFAULT_CONTEXT

but local_files is never imported. Is this a kind of magic that I forgot about, or are no errors triggered since the tests always pass an argument? In that case, we should add a test that does not pass a kw argument (but does not manipulate anything in the context, since that was what we set out to prevent)

Looks good, but when running;

PYTHONPATH=. trial desktopcouch

I get asked for access to the keyring 4 times, which I'm pretty sure is wrong, because the tests should not be using the tokens from there, unless I'm mistaken. (denying access makes the tests still pass)

Tests all pass.

review: Needs Information
106. By Chad Miller

Merge from trunk.

Revision history for this message
Chad Miller (cmiller) wrote :

> Also in __init__.py a default argument is used:
>
> local_files.DEFAULT_CONTEXT
>
> but local_files is never imported. Is this a kind of magic that I forgot
> about, or are no errors triggered since the tests always pass an argument? In
> that case, we should add a test that does not pass a kw argument (but does not
> manipulate anything in the context, since that was what we set out to prevent)

It's the __init__ for this module. Other parts of this module are available. We don't need to import ourselves, e.g., and access desktopcouch.local_files .

Try it. In __init__.py, add "print local_files" and start a python shell and "import desktopcouch" .

Revision history for this message
Eric Casteleijn (thisfred) wrote :

> Try it. In __init__.py, add "print local_files" and start a python shell and
> "import desktopcouch" .

Doh! I usually avoid using these kinds of imports and it has softened my brain into not recognizing them when I see them. NM

Revision history for this message
Eric Casteleijn (thisfred) wrote :

All issues resolved!

Revision history for this message
Eric Casteleijn (thisfred) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'desktopcouch/__init__.py'
2--- desktopcouch/__init__.py 2009-10-29 21:45:19 +0000
3+++ desktopcouch/__init__.py 2009-11-18 18:51:14 +0000
4@@ -40,14 +40,14 @@
5 logging.getLogger('').setLevel(logging.DEBUG)
6 log = logging.getLogger('')
7
8-def find_pid(start_if_not_running=True):
9+def find_pid(start_if_not_running=True, ctx=local_files.DEFAULT_CONTEXT):
10 # Work out whether CouchDB is running by looking at its pid file
11- pid = read_pidfile()
12+ pid = read_pidfile(ctx=ctx)
13 if not process_is_couchdb(pid) and start_if_not_running:
14 # start CouchDB by running the startup script
15 log.info("Desktop CouchDB is not running; starting it.")
16 from desktopcouch import start_local_couchdb
17- pid = start_local_couchdb.start_couchdb()
18+ pid = start_local_couchdb.start_couchdb(ctx=ctx)
19 # now load the design documents, because it's started
20 start_local_couchdb.update_design_documents()
21
22
23=== modified file 'desktopcouch/contacts/contactspicker.py'
24--- desktopcouch/contacts/contactspicker.py 2009-08-24 20:35:25 +0000
25+++ desktopcouch/contacts/contactspicker.py 2009-11-18 18:51:14 +0000
26@@ -19,6 +19,7 @@
27 """A widget to allow users to pick contacts"""
28
29 import gtk
30+import desktopcouch
31 from desktopcouch.contacts.record import CONTACT_RECORD_TYPE
32 from desktopcouch.records.couchgrid import CouchGrid
33
34@@ -26,7 +27,7 @@
35 class ContactsPicker(gtk.VBox):
36 """A contacts picker"""
37
38- def __init__(self, uri=None):
39+ def __init__(self, uri=None, ctx=desktopcouch.local_files.DEFAULT_CONTEXT):
40 """Create a new ContactsPicker widget."""
41
42 gtk.VBox.__init__(self)
43@@ -43,7 +44,7 @@
44 hbox.pack_start(self.search_button, False, False, 3)
45
46 # Create CouchGrid to contain list of contacts
47- self.contacts_list = CouchGrid('contacts', uri=uri)
48+ self.contacts_list = CouchGrid('contacts', uri=uri, ctx=ctx)
49 self.contacts_list.editable = False
50 self.contacts_list.keys = [ "first_name", "last_name" ]
51 self.contacts_list.record_type = CONTACT_RECORD_TYPE
52
53=== modified file 'desktopcouch/contacts/testing/create.py'
54--- desktopcouch/contacts/testing/create.py 2009-09-14 23:42:59 +0000
55+++ desktopcouch/contacts/testing/create.py 2009-11-18 18:51:14 +0000
56@@ -21,6 +21,7 @@
57
58 import random, string, uuid
59
60+import desktopcouch.tests as test_environment
61 from desktopcouch.records.server import OAuthCapableServer
62 import desktopcouch
63
64@@ -163,10 +164,10 @@
65 app_annots=None, server_class=OAuthCapableServer):
66 """Make many contacts and create their records"""
67 if port is None:
68- desktopcouch.find_pid()
69- port = desktopcouch.find_port()
70+ pid = desktopcouch.find_pid(ctx=test_environment.test_context)
71+ port = desktopcouch.find_port(pid)
72 server_url = 'http://%s:%s/' % (host, port)
73- server = server_class(server_url)
74+ server = server_class(server_url, ctx=test_environment.test_context)
75 db = server[db_name] if db_name in server else server.create(db_name)
76 record_ids = []
77 for maincount in range(1, num_contacts + 1):
78
79=== modified file 'desktopcouch/contacts/tests/test_contactspicker.py'
80--- desktopcouch/contacts/tests/test_contactspicker.py 2009-09-01 22:29:01 +0000
81+++ desktopcouch/contacts/tests/test_contactspicker.py 2009-11-18 18:51:14 +0000
82@@ -21,7 +21,7 @@
83 import testtools
84 import gtk
85
86-from desktopcouch.tests import xdg_cache
87+import desktopcouch.tests as test_environment
88
89 from desktopcouch.contacts.contactspicker import ContactsPicker
90 from desktopcouch.records.server import CouchDatabase
91@@ -32,9 +32,9 @@
92 def setUp(self):
93 """setup each test"""
94 # Connect to CouchDB server
95- self.assert_(xdg_cache)
96 self.dbname = 'contacts'
97- self.database = CouchDatabase(self.dbname, create=True)
98+ self.database = CouchDatabase(self.dbname, create=True,
99+ ctx=test_environment.test_context)
100
101 def tearDown(self):
102 """tear down each test"""
103@@ -49,6 +49,6 @@
104 win.resize(300, 450)
105
106 # Create the contacts picker widget
107- picker = ContactsPicker()
108+ picker = ContactsPicker(ctx=test_environment.test_context)
109 win.get_content_area().pack_start(picker, True, True, 3)
110 self.assert_(picker.get_contacts_list())
111
112=== modified file 'desktopcouch/local_files.py'
113--- desktopcouch/local_files.py 2009-09-30 19:54:16 +0000
114+++ desktopcouch/local_files.py 2009-11-18 18:51:14 +0000
115@@ -24,7 +24,7 @@
116 """
117 from __future__ import with_statement
118 import os
119-import xdg.BaseDirectory
120+import xdg.BaseDirectory as xdg_base_dirs
121 import subprocess
122 import logging
123 try:
124@@ -32,23 +32,6 @@
125 except ImportError:
126 import configparser
127
128-def mkpath(rootdir, path):
129- "Remove .. from paths"
130- return os.path.realpath(os.path.join(rootdir, path))
131-
132-rootdir = os.path.join(xdg.BaseDirectory.xdg_cache_home, "desktop-couch")
133-if not os.path.isdir(rootdir):
134- os.mkdir(rootdir)
135-
136-config_dir = xdg.BaseDirectory.save_config_path("desktop-couch")
137-
138-FILE_INI = os.path.join(config_dir, "desktop-couchdb.ini")
139-DIR_DB = xdg.BaseDirectory.save_data_path("desktop-couch")
140-
141-FILE_PID = mkpath(rootdir, "desktop-couchdb.pid")
142-FILE_LOG = mkpath(rootdir, "desktop-couchdb.log")
143-FILE_STDOUT = mkpath(rootdir, "desktop-couchdb.stdout")
144-FILE_STDERR = mkpath(rootdir, "desktop-couchdb.stderr")
145
146 COUCH_EXE = os.environ.get('COUCHDB')
147 if not COUCH_EXE:
148@@ -58,30 +41,85 @@
149 if not COUCH_EXE:
150 raise ImportError("Could not find couchdb")
151
152-def couch_chain_ini_files():
153- process = subprocess.Popen([COUCH_EXE, '-V'], shell=False,
154- stdout=subprocess.PIPE)
155- line = process.stdout.read().split('\n')[0]
156- couchversion = line.split()[-1]
157-
158- # Explicitly add default ini file
159- ini_files = ["/etc/couchdb/default.ini"]
160-
161- # find all ini files in the desktopcouch XDG_CONFIG_DIRS and add them to
162- # the chain
163- xdg_config_dirs = xdg.BaseDirectory.load_config_paths("desktop-couch")
164- # Reverse the list because it's in most-important-first order
165- for folder in reversed(list(xdg_config_dirs)):
166- ini_files.extend([os.path.join(folder, x)
167- for x in sorted(os.listdir(folder))
168- if x.endswith(".ini")])
169-
170- if FILE_INI not in ini_files:
171- ini_files.append(FILE_INI)
172-
173- chain = "-n -a %s " % " -a ".join(ini_files)
174-
175- return chain
176+
177+class Context():
178+ """A mimic of xdg BaseDirectory, with overridable values that do not
179+ depend on environment variables."""
180+
181+ def __init__(self, run_dir, db_dir, config_dir): # (cache, data, config)
182+
183+ self.couchdb_log_level = 'info'
184+
185+ for d in (run_dir, db_dir, config_dir):
186+ if not os.path.isdir(d):
187+ os.makedirs(d, 0700)
188+ else:
189+ os.chmod(d, 0700)
190+
191+ self.run_dir = os.path.realpath(run_dir)
192+ self.config_dir = os.path.realpath(config_dir)
193+ self.db_dir = os.path.realpath(db_dir)
194+
195+ self.file_ini = os.path.join(config_dir, "desktop-couchdb.ini")
196+ self.file_pid = os.path.join(run_dir, "desktop-couchdb.pid")
197+ self.file_log = os.path.join(run_dir, "desktop-couchdb.log")
198+ self.file_stdout = os.path.join(run_dir, "desktop-couchdb.stdout")
199+ self.file_stderr = os.path.join(run_dir, "desktop-couchdb.stderr")
200+
201+ # You will need to add -b or -k on the end of this
202+ self.couch_exec_command = [COUCH_EXE, self.couch_chain_ini_files(),
203+ '-p', self.file_pid,
204+ '-o', self.file_stdout,
205+ '-e', self.file_stderr]
206+
207+
208+ def ensure_files_not_readable(self):
209+ for descr in ("ini", "pid", "log", "stdout", "stderr",):
210+ f = getattr(self, "file_" + descr)
211+ if os.path.isfile(f):
212+ os.chmod(f, 0600)
213+
214+ def load_config_paths(self):
215+ """This is xdg/BaseDirectory.py load_config_paths() with hard-code to
216+ use desktop-couch resource and read from this context."""
217+ yield self.config_dir
218+ for config_dir in \
219+ os.environ.get('XDG_CONFIG_DIRS', '/etc/xdg').split(':'):
220+ path = os.path.join(config_dir, "desktop-couch")
221+ if os.path.exists(path):
222+ yield path
223+
224+ def couch_chain_ini_files(self):
225+ process = subprocess.Popen([COUCH_EXE, '-V'], shell=False,
226+ stdout=subprocess.PIPE)
227+ line = process.stdout.read().split('\n')[0]
228+ couchversion = line.split()[-1]
229+
230+ # Explicitly add default ini file
231+ ini_files = ["/etc/couchdb/default.ini"]
232+
233+ # find all ini files in the desktopcouch XDG_CONFIG_DIRS and add them to
234+ # the chain
235+ config_dirs = self.load_config_paths()
236+ # Reverse the list because it's in most-important-first order
237+ for folder in reversed(list(config_dirs)):
238+ ini_files.extend([os.path.join(folder, x)
239+ for x in sorted(os.listdir(folder))
240+ if x.endswith(".ini")])
241+
242+ if self.file_ini not in ini_files:
243+ ini_files.append(self.file_ini)
244+
245+ chain = "-n -a %s " % " -a ".join(ini_files)
246+
247+ return chain
248+
249+
250+DEFAULT_CONTEXT = Context(
251+ os.path.join(xdg_base_dirs.xdg_cache_home, "desktop-couch"),
252+ xdg_base_dirs.save_data_path("desktop-couch"),
253+ xdg_base_dirs.save_config_path("desktop-couch"))
254+
255
256 class NoOAuthTokenException(Exception):
257 def __init__(self, file_name):
258@@ -91,7 +129,7 @@
259 return "OAuth details were not found in the ini file (%s)" % (
260 self.file_name)
261
262-def get_oauth_tokens(config_file_name=FILE_INI):
263+def get_oauth_tokens(config_file_name=None):
264 """Return the OAuth tokens from the desktop Couch ini file.
265 CouchDB OAuth is two-legged OAuth (not three-legged like most OAuth).
266 We have one "consumer", defined by a consumer_key and a secret,
267@@ -101,6 +139,9 @@
268 (More traditional 3-legged OAuth starts with a "request token" which is
269 then used to procure an "access token". We do not require this.)
270 """
271+ if config_file_name is None:
272+ config_file_name = DEFAULT_CONTEXT.file_ini
273+
274 c = configparser.ConfigParser()
275 # monkeypatch ConfigParser to stop it lower-casing option names
276 c.optionxform = lambda s: s
277@@ -123,9 +164,11 @@
278 raise NoOAuthTokenException(config_file_name)
279 return out
280
281-
282-def get_bind_address(config_file_name=FILE_INI):
283+def get_bind_address(config_file_name=None):
284 """Retreive a string if it exists, or None if it doesn't."""
285+ if config_file_name is None:
286+ config_file_name = DEFAULT_CONTEXT.file_ini
287+
288 c = configparser.ConfigParser()
289 try:
290 c.read(config_file_name)
291@@ -134,7 +177,10 @@
292 logging.warn("config file %r error. %s", config_file_name, e)
293 return None
294
295-def set_bind_address(address, config_file_name=FILE_INI):
296+def set_bind_address(address, config_file_name=None):
297+ if config_file_name is None:
298+ config_file_name = DEFAULT_CONTEXT.file_ini
299+
300 c = configparser.SafeConfigParser()
301 # monkeypatch ConfigParser to stop it lower-casing option names
302 c.optionxform = lambda s: s
303@@ -145,17 +191,3 @@
304 with open(config_file_name, 'wb') as configfile:
305 c.write(configfile)
306
307-
308-# You will need to add -b or -k on the end of this
309-COUCH_EXEC_COMMAND = [COUCH_EXE, couch_chain_ini_files(), '-p', FILE_PID,
310- '-o', FILE_STDOUT, '-e', FILE_STDERR]
311-
312-
313-# Set appropriate permissions on relevant files and folders
314-for fn in [FILE_PID, FILE_STDOUT, FILE_STDERR, FILE_INI, FILE_LOG]:
315- if os.path.exists(fn):
316- os.chmod(fn, 0600)
317-for dn in [rootdir, config_dir, DIR_DB]:
318- if os.path.isdir(dn):
319- os.chmod(dn, 0700)
320-
321
322=== modified file 'desktopcouch/pair/couchdb_pairing/couchdb_io.py'
323--- desktopcouch/pair/couchdb_pairing/couchdb_io.py 2009-10-27 19:00:33 +0000
324+++ desktopcouch/pair/couchdb_pairing/couchdb_io.py 2009-11-18 18:51:14 +0000
325@@ -24,7 +24,7 @@
326 import datetime
327 from itertools import cycle
328
329-from desktopcouch import find_pid, find_port as desktopcouch_find_port
330+from desktopcouch import find_pid, find_port as desktopcouch_find_port, local_files
331 from desktopcouch.records import server
332 from desktopcouch.records.record import Record
333
334@@ -56,11 +56,13 @@
335 port = str(port)
336 return "%s://%s%s:%s/%s" % (protocol, auth, hostname, port, path)
337
338-def _get_db(name, create=True, uri=None):
339+def _get_db(name, create=True, uri=None,
340+ ctx=local_files.DEFAULT_CONTEXT):
341 """Get (and create?) a database."""
342- return server.CouchDatabase(name, create=create, uri=uri)
343+ return server.CouchDatabase(name, create=create, uri=uri, ctx=ctx)
344
345-def put_paired_host(oauth_data, uri=None, **kwargs):
346+def put_paired_host(oauth_data, uri=None,
347+ ctx=local_files.DEFAULT_CONTEXT, **kwargs):
348 """Create a new paired-host record. OAuth information is required, and
349 after the uri, keyword parameters are added to the record."""
350 pairing_id = str(uuid.uuid4())
351@@ -77,25 +79,28 @@
352 "token_secret": str(oauth_data["token_secret"]),
353 }
354 data.update(kwargs)
355- d = _get_db("management", uri=uri)
356+ d = _get_db("management", uri=uri, ctx=ctx)
357 r = Record(data)
358 record_id = d.put_record(r)
359 return record_id
360
361-def put_static_paired_service(oauth_data, service_name, uri=None):
362+def put_static_paired_service(oauth_data, service_name, uri=None,
363+ ctx=local_files.DEFAULT_CONTEXT):
364 """Create a new service record."""
365 return put_paired_host(oauth_data, uri=uri, service_name=service_name,
366- pull_from_server=True, push_to_server=True)
367+ pull_from_server=True, push_to_server=True, ctx=ctx)
368
369-def put_dynamic_paired_host(hostname, remote_uuid, oauth_data, uri=None):
370+def put_dynamic_paired_host(hostname, remote_uuid, oauth_data, uri=None,
371+ ctx=local_files.DEFAULT_CONTEXT):
372 """Create a new dynamic-host record."""
373 return put_paired_host(oauth_data, uri=uri, pairing_identifier=remote_uuid,
374- push_to_server=True, server=hostname)
375+ push_to_server=True, server=hostname, ctx=ctx)
376
377-def get_static_paired_hosts(uri=None):
378+def get_static_paired_hosts(uri=None,
379+ ctx=local_files.DEFAULT_CONTEXT):
380 """Retreive a list of static hosts' information in the form of
381 (ID, service name, to_push, to_pull) ."""
382- db = _get_db("management", uri=uri)
383+ db = _get_db("management", uri=uri, ctx=ctx)
384 results = db.get_records(create_view=True)
385 found = dict()
386 for row in results[PAIRED_SERVER_RECORD_TYPE]:
387@@ -113,14 +118,16 @@
388 logging.debug("static pairings are %s", unique_hosts)
389 return unique_hosts
390
391-def get_database_names_replicatable(uri, oauth_tokens=None, service=False):
392+def get_database_names_replicatable(uri, oauth_tokens=None, service=False,
393+ ctx=local_files.DEFAULT_CONTEXT):
394 """Find a list of local databases, minus dbs that we do not want to
395 replicate (explicitly or implicitly)."""
396 if not uri:
397- find_pid()
398- port = desktopcouch_find_port()
399+ pid = find_pid(ctx=ctx)
400+ port = desktopcouch_find_port(pid=pid)
401 uri = "http://localhost:%s" % port
402- couchdb_server = server.OAuthCapableServer(uri, oauth_tokens=oauth_tokens)
403+ couchdb_server = server.OAuthCapableServer(uri, oauth_tokens=oauth_tokens,
404+ ctx=ctx)
405 try:
406 all_dbs = set([db_name for db_name in couchdb_server])
407 except socket.error, e:
408@@ -132,18 +139,19 @@
409 excluded.add("users")
410 if not service:
411 excluded_msets = _get_management_data(PAIRED_SERVER_RECORD_TYPE,
412- "excluded_names", uri=uri)
413+ "excluded_names", uri=uri, ctx=ctx)
414 for excluded_mset in excluded_msets:
415 excluded.update(excluded_mset)
416
417 return all_dbs - excluded
418
419-def get_my_host_unique_id(uri=None, create=True):
420+def get_my_host_unique_id(uri=None, create=True,
421+ ctx=local_files.DEFAULT_CONTEXT):
422 """Returns a list of ids we call ourselves. We complain in the log if it's
423 more than one, but it's really no error. If there are zero (id est, we've
424 never paired with anyone), then returns None."""
425
426- db = _get_db("management", uri=uri)
427+ db = _get_db("management", uri=uri, ctx=ctx)
428 ids = _get_management_data(MY_ID_RECORD_TYPE, "self_identity", uri=uri)
429 ids = list(set(ids)) # uniqify
430 if len(ids) > 1:
431@@ -165,11 +173,12 @@
432 logging.debug("set new self-identity value: %r", data["self_identity"])
433 return [data["self_identity"]]
434
435-def get_all_known_pairings(uri=None):
436+def get_all_known_pairings(uri=None,
437+ ctx=local_files.DEFAULT_CONTEXT):
438 """Info dicts about all pairings, even if marked "unpaired", keyed on
439 hostid with another dict as the value."""
440 d = {}
441- db = _get_db("management", uri=uri)
442+ db = _get_db("management", uri=uri, ctx=ctx)
443 for row in db.get_records(PAIRED_SERVER_RECORD_TYPE):
444 v = dict()
445 v["record_id"] = row.id
446@@ -182,8 +191,9 @@
447 d[hostid] = v
448 return d
449
450-def _get_management_data(record_type, key, uri=None):
451- db = _get_db("management", uri=uri)
452+def _get_management_data(record_type, key, uri=None,
453+ ctx=local_files.DEFAULT_CONTEXT):
454+ db = _get_db("management", uri=uri, ctx=ctx)
455 results = db.get_records(create_view=True)
456 values = list()
457 for record in results[record_type]:
458@@ -267,9 +277,9 @@
459 logging.exception("can't replicate %r %r <== %r", source_database,
460 url, obsfuscate(record))
461
462-def get_pairings(uri=None):
463+def get_pairings(uri=None, ctx=local_files.DEFAULT_CONTEXT):
464 """Get a list of paired servers."""
465- db = _get_db("management", create=True, uri=None)
466+ db = _get_db("management", create=True, uri=None, ctx=ctx)
467
468 design_doc = "paired_servers"
469 if not db.view_exists("paired_servers", design_doc):
470@@ -292,10 +302,11 @@
471
472 return db.execute_view("paired_servers")
473
474-def remove_pairing(record_id, is_reconciled, uri=None):
475+def remove_pairing(record_id, is_reconciled, uri=None,
476+ ctx=local_files.DEFAULT_CONTEXT):
477 """Remove a pairing record (or mark it as dead so it can be cleaned up
478 properly later)."""
479- db = _get_db("management", create=True, uri=None)
480+ db = _get_db("management", create=True, uri=None, ctx=ctx)
481 if is_reconciled:
482 db.delete_record(record_id)
483 else:
484
485=== modified file 'desktopcouch/pair/tests/test_couchdb_io.py'
486--- desktopcouch/pair/tests/test_couchdb_io.py 2009-10-27 19:00:33 +0000
487+++ desktopcouch/pair/tests/test_couchdb_io.py 2009-11-18 18:51:14 +0000
488@@ -18,23 +18,26 @@
489 import pygtk
490 pygtk.require('2.0')
491
492-import desktopcouch.tests as dctests
493+import desktopcouch.tests as test_environment
494
495 from desktopcouch.pair.couchdb_pairing import couchdb_io
496 from desktopcouch.records.server import CouchDatabase
497 from desktopcouch.records.record import Record
498-import unittest
499+from twisted.trial import unittest
500 import uuid
501 import os
502 import httplib2
503+import socket
504 URI = None # use autodiscovery that desktopcouch.tests permits.
505
506 class TestCouchdbIo(unittest.TestCase):
507
508 def setUp(self):
509 """setup each test"""
510- self.mgt_database = CouchDatabase('management', create=True, uri=URI)
511- self.foo_database = CouchDatabase('foo', create=True, uri=URI)
512+ self.mgt_database = CouchDatabase('management', create=True, uri=URI,
513+ ctx=test_environment.test_context)
514+ self.foo_database = CouchDatabase('foo', create=True, uri=URI,
515+ ctx=test_environment.test_context)
516 #create some records to pull out and test
517 self.foo_database.put_record(Record({
518 "key1_1": "val1_1", "key1_2": "val1_2", "key1_3": "val1_3",
519@@ -71,8 +74,8 @@
520 "token": str("opqrst"),
521 "token_secret": str("uvwxyz"),
522 }
523- couchdb_io.put_static_paired_service(oauth_data, service_name, uri=URI)
524- pairings = list(couchdb_io.get_pairings())
525+ couchdb_io.put_static_paired_service(oauth_data, service_name, uri=URI, ctx=test_environment.test_context)
526+ pairings = list(couchdb_io.get_pairings(ctx=test_environment.test_context))
527
528 def test_put_dynamic_paired_host(self):
529 hostname = "host%d" % (os.getpid(),)
530@@ -85,43 +88,48 @@
531 }
532
533 couchdb_io.put_dynamic_paired_host(hostname, remote_uuid, oauth_data,
534- uri=URI)
535- couchdb_io.put_dynamic_paired_host(hostname, remote_uuid, oauth_data,
536- uri=URI)
537- couchdb_io.put_dynamic_paired_host(hostname, remote_uuid, oauth_data,
538- uri=URI)
539+ uri=URI, ctx=test_environment.test_context)
540+ couchdb_io.put_dynamic_paired_host(hostname, remote_uuid, oauth_data,
541+ uri=URI, ctx=test_environment.test_context)
542+ couchdb_io.put_dynamic_paired_host(hostname, remote_uuid, oauth_data,
543+ uri=URI, ctx=test_environment.test_context)
544
545- pairings = list(couchdb_io.get_pairings())
546+ pairings = list(couchdb_io.get_pairings(ctx=test_environment.test_context))
547 self.assertEqual(3, len(pairings))
548 self.assertEqual(pairings[0].value["oauth"], oauth_data)
549 self.assertEqual(pairings[0].value["server"], hostname)
550 self.assertEqual(pairings[0].value["pairing_identifier"], remote_uuid)
551
552 for i, row in enumerate(pairings):
553- couchdb_io.remove_pairing(row.id, i == 1)
554+ couchdb_io.remove_pairing(row.id, i == 1, ctx=test_environment.test_context)
555
556- pairings = list(couchdb_io.get_pairings())
557+ pairings = list(couchdb_io.get_pairings(ctx=test_environment.test_context))
558 self.assertEqual(0, len(pairings))
559
560
561 def test_get_database_names_replicatable_bad_server(self):
562- # If this resolves, FIRE YOUR DNS PROVIDER.
563+ hostname = "test.desktopcouch.example.com"
564+ try:
565+ socket.gethostbyname(hostname)
566+ raise unittest.SkipTest("nxdomain hijacked")
567+ except socket.gaierror:
568+ pass
569
570 try:
571 names = couchdb_io.get_database_names_replicatable(
572- uri='http://test.desktopcouch.example.com:9/')
573+ uri='http://' + hostname + ':9/')
574 self.assertEqual(set(), names)
575 except httplib2.ServerNotFoundError:
576 pass
577
578 def test_get_database_names_replicatable(self):
579- names = couchdb_io.get_database_names_replicatable(uri=URI)
580+ names = couchdb_io.get_database_names_replicatable(uri=URI, ctx=test_environment.test_context)
581 self.assertFalse('management' in names)
582 self.assertTrue('foo' in names)
583
584 def test_get_my_host_unique_id(self):
585- got = couchdb_io.get_my_host_unique_id(uri=URI)
586- again = couchdb_io.get_my_host_unique_id(uri=URI)
587+ got = couchdb_io.get_my_host_unique_id(uri=URI, ctx=test_environment.test_context)
588+ again = couchdb_io.get_my_host_unique_id(uri=URI, ctx=test_environment.test_context)
589 self.assertEquals(len(got), 1)
590 self.assertEquals(got, again)
591
592
593=== modified file 'desktopcouch/records/couchgrid.py'
594--- desktopcouch/records/couchgrid.py 2009-10-10 23:56:45 +0000
595+++ desktopcouch/records/couchgrid.py 2009-11-18 18:51:14 +0000
596@@ -22,11 +22,12 @@
597 import gobject
598 from server import CouchDatabase
599 from record import Record
600+import desktopcouch
601
602 class CouchGrid(gtk.TreeView):
603
604- def __init__(
605- self, database_name, record_type=None, keys=None, uri=None):
606+ def __init__(self, database_name, record_type=None, keys=None, uri=None,
607+ ctx=desktopcouch.local_files.DEFAULT_CONTEXT):
608 """Create a new Couchwidget
609 arguments:
610 database_name - specify the name of the database in the desktop
611@@ -63,6 +64,7 @@
612 self.__keys = keys
613 self.__editable = False
614 self.uri = uri
615+ self.ctx = ctx
616
617 #set the datatabase
618 self.database = database_name
619@@ -122,9 +124,9 @@
620 @database.setter
621 def database(self, db_name):
622 if self.uri:
623- self.__db = CouchDatabase(db_name, create=True, uri=self.uri)
624+ self.__db = CouchDatabase(db_name, create=True, uri=self.uri, ctx=self.ctx)
625 else:
626- self.__db = CouchDatabase(db_name, create=True)
627+ self.__db = CouchDatabase(db_name, create=True, ctx=self.ctx)
628 if self.record_type != None:
629 self.__populate_treeview()(self.record_type)
630
631
632=== modified file 'desktopcouch/records/doc/records.txt'
633--- desktopcouch/records/doc/records.txt 2009-10-05 13:39:38 +0000
634+++ desktopcouch/records/doc/records.txt 2009-11-18 18:51:14 +0000
635@@ -6,7 +6,7 @@
636 Create a database object. Your database needs to exist. If it doesn't, you
637 can create it by passing create=True.
638
639->>> db = CouchDatabase('testing', create=True)
640+ >> db = CouchDatabase('testing', create=True)
641
642 Create a Record object. Records have a record type, which should be a
643 URL. The URL should point to a human-readable document which
644
645=== modified file 'desktopcouch/records/server.py'
646--- desktopcouch/records/server.py 2009-10-14 17:28:43 +0000
647+++ desktopcouch/records/server.py 2009-11-18 18:51:14 +0000
648@@ -28,13 +28,15 @@
649 import urlparse
650
651 class OAuthCapableServer(Server):
652- def __init__(self, uri, oauth_tokens=None):
653+ def __init__(self, uri, oauth_tokens=None, ctx=None):
654 """Subclass of couchdb.client.Server which creates a custom
655 httplib2.Http subclass which understands OAuth"""
656 http = server_base.OAuthCapableHttp(scheme=urlparse.urlparse(uri)[0])
657 http.force_exception_to_status_code = False
658+ if ctx is None:
659+ ctx = desktopcouch.local_files.DEFAULT_CONTEXT
660 if oauth_tokens is None:
661- oauth_tokens = desktopcouch.local_files.get_oauth_tokens()
662+ oauth_tokens = desktopcouch.local_files.get_oauth_tokens(ctx.file_ini)
663 (consumer_key, consumer_secret, token, token_secret) = (
664 oauth_tokens["consumer_key"], oauth_tokens["consumer_secret"],
665 oauth_tokens["token"], oauth_tokens["token_secret"])
666@@ -45,11 +47,12 @@
667 """An small records specific abstraction over a couch db database."""
668
669 def __init__(self, database, uri=None, record_factory=None, create=False,
670- server_class=OAuthCapableServer, oauth_tokens=None):
671+ server_class=OAuthCapableServer, oauth_tokens=None,
672+ ctx=desktopcouch.local_files.DEFAULT_CONTEXT):
673 if not uri:
674- desktopcouch.find_pid()
675- port = desktopcouch.find_port()
676+ pid = desktopcouch.find_pid(ctx=ctx)
677+ port = desktopcouch.find_port(pid)
678 uri = "http://localhost:%s" % port
679 super(CouchDatabase, self).__init__(
680 database, uri, record_factory=record_factory, create=create,
681- server_class=server_class, oauth_tokens=oauth_tokens)
682+ server_class=server_class, oauth_tokens=oauth_tokens, ctx=ctx)
683
684=== modified file 'desktopcouch/records/tests/test_couchgrid.py'
685--- desktopcouch/records/tests/test_couchgrid.py 2009-10-10 23:52:01 +0000
686+++ desktopcouch/records/tests/test_couchgrid.py 2009-11-18 18:51:14 +0000
687@@ -20,7 +20,7 @@
688
689 from testtools import TestCase
690
691-from desktopcouch.tests import xdg_cache
692+import desktopcouch.tests as test_environment
693
694 from desktopcouch.records.record import Record
695 from desktopcouch.records.server import CouchDatabase
696@@ -31,9 +31,9 @@
697 """Test the CouchGrid functionality"""
698
699 def setUp(self):
700- self.assert_(xdg_cache)
701 self.dbname = self._testMethodName
702- self.db = CouchDatabase(self.dbname, create=True)
703+ self.db = CouchDatabase(self.dbname, create=True,
704+ ctx=test_environment.test_context)
705 self.record_type = "test_record_type"
706
707 def tearDown(self):
708@@ -46,7 +46,7 @@
709 database name.
710 """
711 try:
712- cw = CouchGrid(None)
713+ cw = CouchGrid(None, ctx=test_environment.test_context)
714 except TypeError, inst:
715 self.assertEqual(
716 inst.args[0],"database_name is required and must be a string")
717@@ -55,7 +55,7 @@
718 """Test a simple creating a CouchGrid """
719
720 #create a test widget with test database values
721- cw = CouchGrid(self.dbname)
722+ cw = CouchGrid(self.dbname, ctx=test_environment.test_context)
723
724 #allow editing
725 cw.editable = True
726@@ -87,7 +87,7 @@
727
728 try:
729 #create a test widget with test database values
730- cw = CouchGrid(self.dbname)
731+ cw = CouchGrid(self.dbname, ctx=test_environment.test_context)
732
733 #set the record_type for the TreeView
734 #it will not populate without this value being set
735@@ -113,7 +113,8 @@
736
737 def test_all_from_database(self):
738 #create some records
739- db = CouchDatabase(self.dbname, create=True)
740+ db = CouchDatabase(self.dbname, create=True,
741+ ctx=test_environment.test_context)
742 db.put_record(Record({
743 "key1_1": "val1_1", "key1_2": "val1_2", "key1_3": "val1_3",
744 "record_type": self.record_type}))
745@@ -122,7 +123,7 @@
746 "record_type": self.record_type}))
747
748 #build the CouchGrid
749- cw = CouchGrid(self.dbname)
750+ cw = CouchGrid(self.dbname, ctx=test_environment.test_context)
751 cw.record_type = self.record_type
752 #make sure there are three columns and two rows
753 self.assertEqual(cw.get_model().get_n_columns(),4)
754@@ -130,7 +131,8 @@
755
756 def test_selected_id_property(self):
757 #create some records
758- db = CouchDatabase(self.dbname, create=True)
759+ db = CouchDatabase(self.dbname, create=True,
760+ ctx=test_environment.test_context)
761 id1 = db.put_record(Record({
762 "key1_1": "val1_1", "key1_2": "val1_2", "key1_3": "val1_3",
763 "record_type": self.record_type}))
764@@ -139,7 +141,7 @@
765 "record_type": self.record_type}))
766
767 #build the CouchGrid
768- cw = CouchGrid(self.dbname)
769+ cw = CouchGrid(self.dbname, ctx=test_environment.test_context)
770 cw.record_type = self.record_type
771
772 #make sure the record ids are selected properly
773@@ -158,7 +160,7 @@
774 "key1_1": "val2_1", "key1_2": "val2_2", "key1_3": "val2_3",
775 "record_type": self.record_type}))
776 #build the CouchGrid
777- cw = CouchGrid(self.dbname)
778+ cw = CouchGrid(self.dbname, ctx=test_environment.test_context)
779 cw.keys = ["key1_1"]
780 cw.record_type = self.record_type
781 #make sure there are three columns and two rows
782@@ -176,7 +178,8 @@
783 "record_type": self.record_type}))
784
785 #create a test widget with test database values
786- cw = CouchGrid(self.dbname, record_type=self.record_type)
787+ cw = CouchGrid(self.dbname, record_type=self.record_type,
788+ ctx=test_environment.test_context)
789
790 #make sure there are three columns and two rows
791 self.assertEqual(cw.get_model().get_n_columns(),4)
792@@ -188,7 +191,8 @@
793 #create a test widget with test database values
794 cw = CouchGrid(
795 self.dbname, record_type=self.record_type,
796- keys=["Key1", "Key2", "Key3", "Key4"])
797+ keys=["Key1", "Key2", "Key3", "Key4"],
798+ ctx=test_environment.test_context)
799
800 #create a row with all four columns set
801 cw.append_row(["val1", "val2", "val3", "val4"])
802@@ -214,7 +218,8 @@
803 "record_type": self.record_type}))
804
805 #create a test widget with test database values
806- cw = CouchGrid(self.dbname, record_type=self.record_type)
807+ cw = CouchGrid(self.dbname, record_type=self.record_type,
808+ ctx=test_environment.test_context)
809
810 #allow editing
811 cw.append_row(["boo", "ray"])
812
813=== modified file 'desktopcouch/records/tests/test_record.py'
814--- desktopcouch/records/tests/test_record.py 2009-11-17 22:03:11 +0000
815+++ desktopcouch/records/tests/test_record.py 2009-11-18 18:51:14 +0000
816@@ -23,8 +23,10 @@
817
818 # pylint does not like relative imports from containing packages
819 # pylint: disable-msg=F0401
820+from desktopcouch.records.server import CouchDatabase
821 from desktopcouch.records.record import (Record, RecordDict, MergeableList,
822 record_factory, IllegalKeyException, validate, NoRecordTypeSpecified)
823+import desktopcouch.tests as test_environment
824
825
826 class TestRecords(TestCase):
827@@ -202,7 +204,9 @@
828 self.record.record_type)
829
830 def test_run_doctests(self):
831- results = doctest.testfile('../doc/records.txt')
832+ ctx = test_environment.test_context
833+ globs = { "db": CouchDatabase('testing', create=True, ctx=ctx) }
834+ results = doctest.testfile('../doc/records.txt', globs=globs)
835 self.assertEqual(0, results.failed)
836
837
838
839=== modified file 'desktopcouch/records/tests/test_server.py'
840--- desktopcouch/records/tests/test_server.py 2009-11-18 18:29:04 +0000
841+++ desktopcouch/records/tests/test_server.py 2009-11-18 18:51:14 +0000
842@@ -19,7 +19,7 @@
843 """testing database/contact.py module"""
844 import testtools
845
846-from desktopcouch.tests import xdg_cache
847+import desktopcouch.tests as test_environment
848 from desktopcouch.records.server import CouchDatabase
849 from desktopcouch.records.server_base import row_is_deleted, NoSuchDatabase
850 from desktopcouch.records.record import Record
851@@ -41,9 +41,9 @@
852 def setUp(self):
853 """setup each test"""
854 # Connect to CouchDB server
855- self.assert_(xdg_cache)
856 self.dbname = self._testMethodName
857- self.database = CouchDatabase(self.dbname, create=True)
858+ self.database = CouchDatabase(self.dbname, create=True,
859+ ctx=test_environment.test_context)
860 #create some records to pull out and test
861 self.database.put_record(Record({
862 "key1_1": "val1_1", "key1_2": "val1_2", "key1_3": "val1_3",
863
864=== modified file 'desktopcouch/start_local_couchdb.py'
865--- desktopcouch/start_local_couchdb.py 2009-11-15 21:11:21 +0000
866+++ desktopcouch/start_local_couchdb.py 2009-11-18 18:51:14 +0000
867@@ -41,6 +41,8 @@
868 import errno
869 import time, gnomekeyring
870 from desktopcouch.records.server import CouchDatabase
871+import logging
872+import itertools
873
874 ACCEPTABLE_USERNAME_PASSWORD_CHARS = string.lowercase + string.uppercase
875
876@@ -58,9 +60,12 @@
877 fd.write("\n")
878 fd.close()
879
880-def create_ini_file(port="0"):
881+def create_ini_file(port="0", ctx=local_files.DEFAULT_CONTEXT):
882 """Write CouchDB ini file if not already present"""
883- if os.path.exists(local_files.FILE_INI):
884+
885+ print "ini file is at", ctx.file_ini
886+
887+ if os.path.exists(ctx.file_ini):
888 # load the username and password from the keyring
889 try:
890 data = gnomekeyring.find_items_sync(gnomekeyring.ITEM_GENERIC_SECRET,
891@@ -85,19 +90,18 @@
892 consumer_secret = make_random_string(10)
893 token = make_random_string(10)
894 token_secret = make_random_string(10)
895- db_dir = local_files.DIR_DB
896 local = {
897 'couchdb': {
898- 'database_dir': db_dir,
899- 'view_index_dir': db_dir,
900+ 'database_dir': ctx.db_dir,
901+ 'view_index_dir': ctx.db_dir,
902 },
903 'httpd': {
904 'bind_address': '127.0.0.1',
905 'port': port,
906 },
907 'log': {
908- 'file': local_files.FILE_LOG,
909- 'level': 'info',
910+ 'file': ctx.file_log,
911+ 'level': ctx.couchdb_log_level,
912 },
913 'admins': {
914 admin_account_username: admin_account_basic_auth_password
915@@ -116,7 +120,9 @@
916 }
917 }
918
919- dump_ini(local, local_files.FILE_INI)
920+ dump_ini(local, ctx.file_ini)
921+ ctx.ensure_files_not_readable()
922+
923 # save admin account details in keyring
924 item_id = gnomekeyring.item_create_sync(
925 None,
926@@ -169,25 +175,31 @@
927 except KeyError:
928 raise NotImplementedError("os %r is not yet supported" % (os_name,))
929
930-def read_pidfile():
931+def read_pidfile(ctx=local_files.DEFAULT_CONTEXT):
932 try:
933- pid_file = local_files.FILE_PID
934+ pid_file = ctx.file_pid
935+ if not os.path.exists(pid_file):
936+ return None
937 with open(pid_file) as fp:
938 try:
939 contents = fp.read()
940+ if contents == "\n":
941+ return None # not yet written to pid file
942 return int(contents)
943 except ValueError:
944+ logging.warn("Pid file does not contain int: %r", contents)
945 return None
946- except IOError:
947+ except IOError, e:
948+ logging.warn("Reading pid file caused error. %s", e)
949 return None
950
951-def run_couchdb():
952+def run_couchdb(ctx=local_files.DEFAULT_CONTEXT):
953 """Actually start the CouchDB process. Return its PID."""
954- pid = read_pidfile()
955+ pid = read_pidfile(ctx)
956 if pid is not None and not process_is_couchdb(pid):
957 print "Removing stale, deceptive pid file."
958- os.remove(local_files.FILE_PID)
959- local_exec = local_files.COUCH_EXEC_COMMAND + ['-b']
960+ os.remove(ctx.file_pid)
961+ local_exec = ctx.couch_exec_command + ['-b']
962 try:
963 # subprocess is buggy. Chad patched, but that takes time to propagate.
964 proc = subprocess.Popen(local_exec)
965@@ -209,14 +221,15 @@
966
967 # give the process a chance to start
968 for timeout in xrange(1000):
969- pid = read_pidfile()
970+ pid = read_pidfile(ctx=ctx)
971 time.sleep(0.3)
972 if pid is not None and process_is_couchdb(pid):
973 break
974 print "...waiting for couchdb to start..."
975+ ctx.ensure_files_not_readable()
976 return pid
977
978-def update_design_documents():
979+def update_design_documents(ctx=local_files.DEFAULT_CONTEXT):
980 """Check system design documents and update any that need updating
981
982 A database should be created if
983@@ -225,17 +238,20 @@
984 $XDG_DATA_DIRs/desktop-couch/databases/dbname/_design/designdocname/views/viewname/map.js
985 reduce.js may also exist in the same folder.
986 """
987- for base in xdg.BaseDirectory.xdg_data_dirs:
988+ ctx_data_dir = os.path.split(ctx.db_dir)[0]
989+ for base in itertools.chain([ctx_data_dir], xdg.BaseDirectory.xdg_data_dirs):
990+ # FIXME: base may have magic chars. assert not glob.has_magic(base) ?
991 db_spec = os.path.join(
992 base, "desktop-couch", "databases", "*", "database.cfg")
993 for database_path in glob.glob(db_spec):
994 database_root = os.path.split(database_path)[0]
995 database_name = os.path.split(database_root)[1]
996 # Just the presence of database.cfg is enough to create the database
997- db = CouchDatabase(database_name, create=True)
998+ db = CouchDatabase(database_name, create=True, ctx=ctx)
999 # look for design documents
1000 dd_spec = os.path.join(
1001 database_root, "_design", "*", "views", "*", "map.js")
1002+ # FIXME: dd_path may have magic chars.
1003 for dd_path in glob.glob(dd_spec):
1004 view_root = os.path.split(dd_path)[0]
1005 view_name = os.path.split(view_root)[1]
1006@@ -258,9 +274,9 @@
1007 # than inefficiently just overwriting it regardless
1008 db.add_view(view_name, mapjs, reducejs, dd_name)
1009
1010-def write_bookmark_file(username, password, pid):
1011+def write_bookmark_file(username, password, pid, ctx=local_files.DEFAULT_CONTEXT):
1012 """Write out an HTML document that the user can bookmark to find their DB"""
1013- bookmark_file = os.path.join(local_files.DIR_DB, "couchdb.html")
1014+ bookmark_file = os.path.join(ctx.db_dir, "couchdb.html")
1015
1016 if os.path.exists(
1017 os.path.join(os.path.split(__file__)[0], "../data/couchdb.tmpl")):
1018@@ -292,16 +308,16 @@
1019 finally:
1020 fp.close()
1021
1022-def start_couchdb():
1023+def start_couchdb(ctx=local_files.DEFAULT_CONTEXT):
1024 """Execute each step to start a desktop CouchDB."""
1025- username, password = create_ini_file()
1026- pid = run_couchdb()
1027+ username, password = create_ini_file(ctx=ctx)
1028+ pid = run_couchdb(ctx=ctx)
1029 # Note that we do not call update_design_documents here. This is because
1030 # Couch won't actually have started yet, so when update_design_documents
1031 # calls the Records API, that will call back into get_port and we end up
1032 # starting Couch again. Instead, get_port calls update_design_documents
1033 # *after* Couch startup has occurred.
1034- write_bookmark_file(username, password, pid)
1035+ write_bookmark_file(username, password, pid, ctx=ctx)
1036 return pid
1037
1038
1039
1040=== modified file 'desktopcouch/tests/__init__.py'
1041--- desktopcouch/tests/__init__.py 2009-09-14 15:56:42 +0000
1042+++ desktopcouch/tests/__init__.py 2009-11-18 18:51:14 +0000
1043@@ -1,47 +1,48 @@
1044 """Tests for Desktop CouchDB"""
1045
1046 import os, tempfile, atexit, shutil
1047+from desktopcouch.start_local_couchdb import start_couchdb, read_pidfile
1048 from desktopcouch.stop_local_couchdb import stop_couchdb
1049-
1050-def stop_test_couch():
1051- from desktopcouch.start_local_couchdb import read_pidfile
1052- pid = read_pidfile()
1053- stop_couchdb(pid=pid)
1054- shutil.rmtree(basedir)
1055-
1056-atexit.register(stop_test_couch)
1057-
1058-basedir = tempfile.mkdtemp()
1059-if not os.path.exists(basedir):
1060- os.mkdir(basedir)
1061-
1062-xdg_cache = os.path.join(basedir, 'xdg_cache')
1063-if not os.path.exists(xdg_cache):
1064- os.mkdir(xdg_cache)
1065-
1066-xdg_data = os.path.join(basedir, 'xdg_data')
1067-if not os.path.exists(xdg_data):
1068- os.mkdir(xdg_data)
1069-
1070-xdg_config = os.path.join(basedir, 'xdg_config')
1071-if not os.path.exists(xdg_config):
1072- os.mkdir(xdg_config)
1073-
1074-# Add etc folder to config
1075-SOURCE_TREE_ETC_FOLDER = os.path.realpath(
1076- os.path.join(os.path.split(__file__)[0], "..", "..", "config")
1077-)
1078-if os.path.isdir(SOURCE_TREE_ETC_FOLDER):
1079- os.environ["XDG_CONFIG_DIRS"] = SOURCE_TREE_ETC_FOLDER
1080-
1081-os.environ['XDG_CACHE_HOME'] = xdg_cache
1082-os.environ['XDG_DATA_HOME'] = xdg_data
1083-os.environ['XDG_CONFIG_HOME'] = xdg_config
1084+from desktopcouch import local_files
1085+
1086+import gobject
1087+gobject.set_application_name("desktopcouch testing")
1088+
1089+
1090+def create_new_test_environment():
1091+
1092+ basedir = tempfile.mkdtemp()
1093+ if not os.path.exists(basedir):
1094+ os.mkdir(basedir)
1095+
1096+ cache = os.path.join(basedir, 'cache')
1097+ data = os.path.join(basedir, 'data')
1098+ config = os.path.join(basedir, 'config')
1099+ new_context = local_files.Context(cache, data, config)
1100+ new_context.couchdb_log_level = 'debug'
1101+
1102+ # Add etc folder to config
1103+ SOURCE_TREE_ETC_FOLDER = os.path.realpath(
1104+ os.path.join(os.path.split(__file__)[0], "..", "..", "config")
1105+ )
1106+ if os.path.isdir(SOURCE_TREE_ETC_FOLDER):
1107+ os.environ["XDG_CONFIG_DIRS"] = SOURCE_TREE_ETC_FOLDER
1108+
1109+ def stop_test_couch(temp_dir, ctx):
1110+ pid = read_pidfile(ctx)
1111+ stop_couchdb(pid=pid)
1112+ shutil.rmtree(temp_dir)
1113+
1114+ start_couchdb(ctx=new_context)
1115+ atexit.register(stop_test_couch, basedir, new_context)
1116+
1117+ return new_context
1118+
1119+
1120+# TODO: Remove these after you're sure nothing we care about uses these env.
1121+os.environ['XDG_CACHE_HOME'] = "/cachehome"
1122+os.environ['XDG_DATA_HOME'] = "/datahome"
1123+os.environ['XDG_CONFIG_HOME'] = "/confighome"
1124 os.environ['XDG_DATA_DIRS'] = ''
1125
1126-# Force reload packages, so that the correct dekstopcouch will be
1127-# started.
1128-import xdg.BaseDirectory
1129-reload(xdg.BaseDirectory)
1130-from desktopcouch import local_files
1131-reload(local_files)
1132+test_context = create_new_test_environment()
1133
1134=== modified file 'desktopcouch/tests/test_local_files.py'
1135--- desktopcouch/tests/test_local_files.py 2009-11-17 22:03:11 +0000
1136+++ desktopcouch/tests/test_local_files.py 2009-11-18 18:51:14 +0000
1137@@ -1,7 +1,7 @@
1138 """testing desktopcouch/local_files.py module"""
1139
1140 import testtools
1141-import desktopcouch.tests
1142+import desktopcouch.tests as test_environment
1143 import desktopcouch
1144 import os
1145
1146@@ -11,19 +11,21 @@
1147 "Does local_files list all the files that it needs to?"
1148 import desktopcouch.local_files
1149 for required in [
1150- "FILE_LOG", "FILE_INI", "FILE_PID", "FILE_STDOUT",
1151- "FILE_STDERR", "DIR_DB", "COUCH_EXE", "COUCH_EXEC_COMMAND"]:
1152- self.assertTrue(required in dir(desktopcouch.local_files))
1153+ "file_log", "file_ini", "file_pid", "file_stdout",
1154+ "file_stderr", "db_dir"]:
1155+ #"couch_exe", "couch_exec_command"
1156+ self.assertTrue(required in dir(test_environment.test_context))
1157
1158 def test_xdg_overwrite_works(self):
1159 # this should really check that it's in os.environ["TMP"]
1160- self.assertTrue(desktopcouch.local_files.FILE_INI.startswith("/tmp"))
1161+ self.assertTrue(test_environment.test_context.file_ini.startswith("/tmp"))
1162
1163 def test_couch_chain_ini_files(self):
1164 "Is compulsory-auth.ini picked up by the ini file finder?"
1165 import desktopcouch.local_files
1166- ok = [x for x in desktopcouch.local_files.couch_chain_ini_files().split()
1167- if x.endswith("compulsory-auth.ini")]
1168+ ok = [x for x
1169+ in test_environment.test_context.couch_chain_ini_files().split()
1170+ if x.endswith("compulsory-auth.ini")]
1171 self.assertTrue(len(ok) > 0)
1172
1173 def test_bind_address(self):
1174
1175=== added file 'desktopcouch/tests/test_replication.py'
1176--- desktopcouch/tests/test_replication.py 1970-01-01 00:00:00 +0000
1177+++ desktopcouch/tests/test_replication.py 2009-11-18 18:51:14 +0000
1178@@ -0,0 +1,21 @@
1179+"""testing desktopcouch/start_local_couchdb.py module"""
1180+
1181+import testtools
1182+import os, sys
1183+import desktopcouch.tests as test_environment
1184+import desktopcouch
1185+sys.path.append(
1186+ os.path.join(os.path.split(desktopcouch.__file__)[0], "..", "contrib"))
1187+
1188+class TestReplication(testtools.TestCase):
1189+ """Testing that the database/designdoc filesystem loader works"""
1190+
1191+ def setUp(self):
1192+ self.db_apple = desktopcouch.records.server.CouchDatabase("apple",
1193+ create=True, ctx=test_environment.test_context)
1194+ banana = test_environment.create_new_test_environment()
1195+ self.db_banana = desktopcouch.records.server.CouchDatabase("banana",
1196+ create=True, ctx=banana)
1197+
1198+ def test_creation(self):
1199+ pass
1200
1201=== modified file 'desktopcouch/tests/test_start_local_couchdb.py'
1202--- desktopcouch/tests/test_start_local_couchdb.py 2009-09-14 15:56:42 +0000
1203+++ desktopcouch/tests/test_start_local_couchdb.py 2009-11-18 18:51:14 +0000
1204@@ -2,7 +2,7 @@
1205
1206 import testtools
1207 import os, sys
1208-from desktopcouch.tests import xdg_data
1209+import desktopcouch.tests as test_environment
1210 import desktopcouch
1211 sys.path.append(
1212 os.path.join(os.path.split(desktopcouch.__file__)[0], "..", "contrib"))
1213@@ -71,6 +71,7 @@
1214
1215 def setUp(self):
1216 # create temp folder with databases and design documents in
1217+ xdg_data = os.path.split(test_environment.test_context.db_dir)[0]
1218 try:
1219 os.mkdir(os.path.join(xdg_data, "desktop-couch"))
1220 except OSError:
1221@@ -90,21 +91,22 @@
1222 couchdb = mocker.replace("desktopcouch.records.server.CouchDatabase")
1223
1224 # databases that should be created
1225- couchdb("cfg", create=True)
1226- couchdb("cfg_and_empty_design", create=True)
1227- couchdb("cfg_and_design_no_views", create=True)
1228- couchdb("cfg_and_design_one_view_no_map", create=True)
1229- couchdb("cfg_and_design_one_view_map_no_reduce", create=True)
1230+ couchdb("cfg", create=True, ctx=test_environment.test_context)
1231+ couchdb("cfg_and_empty_design", create=True, ctx=test_environment.test_context)
1232+ couchdb("cfg_and_design_no_views", create=True, ctx=test_environment.test_context)
1233+ couchdb("cfg_and_design_one_view_no_map", create=True, ctx=test_environment.test_context)
1234+ couchdb("cfg_and_design_one_view_map_no_reduce", create=True, ctx=test_environment.test_context)
1235+
1236 dbmock1 = mocker.mock()
1237 mocker.result(dbmock1)
1238 dbmock1.add_view("view1", "cfg_and_design_one_view_map_no_reduce:map",
1239 None, "doc1")
1240- couchdb("cfg_and_design_one_view_map_reduce", create=True)
1241+ couchdb("cfg_and_design_one_view_map_reduce", create=True, ctx=test_environment.test_context)
1242 dbmock2 = mocker.mock()
1243 mocker.result(dbmock2)
1244 dbmock2.add_view("view1", "cfg_and_design_one_view_map_reduce:map",
1245 "cfg_and_design_one_view_map_reduce:reduce", "doc1")
1246- couchdb("cfg_and_design_two_views_map_reduce", create=True)
1247+ couchdb("cfg_and_design_two_views_map_reduce", create=True, ctx=test_environment.test_context)
1248 dbmock3 = mocker.mock()
1249 mocker.result(dbmock3)
1250 dbmock3.add_view("view1", "cfg_and_design_two_views_map_reduce:map1",
1251@@ -116,7 +118,7 @@
1252 # all the right things
1253 mocker.replay()
1254 from desktopcouch.start_local_couchdb import update_design_documents
1255- update_design_documents()
1256+ update_design_documents(ctx=test_environment.test_context)
1257
1258 mocker.restore()
1259 mocker.verify()

Subscribers

People subscribed via source and target branches