def sync_batch(git_couchdb_url, design_couchdb_url, branch, app_subdir): if branch is None: branches = [ b["_id"] for b in get( posixpath.join(git_couchdb_url, "git-branches"))["branches"]] else: # TODO: Should really look for branches with this name using # an index branches = ["git-branch-" + branch] with mkdtemp() as temp_dir: for branch in branches: basename = urllib.quote(branch, safe="") local_dir = os.path.join(temp_dir, basename) commit = get(posixpath.join(git_couchdb_url, branch))["commit"]["_id"] tree = get(posixpath.join(git_couchdb_url, commit))["tree"]["_id"] if app_subdir != ".": # TODO: Support multi-level sub_dir assert "/" not in app_subdir, app_subdir for child in get(posixpath.join(git_couchdb_url, tree))["children"]: if child["basename"] == app_subdir: assert child["child"]["type"] == "git-tree", child tree = child["child"]["_id"] break else: raise Exception("Missing child %r" % (app_subdir,)) doc_url = posixpath.join(design_couchdb_url, "_design", url_quote(basename)) existing = get(doc_url) if existing.get("couchapp_git_tree_id") == tree: continue print "Updating %r..." % (branch,) tree_to_fs(git_couchdb_url, local_dir, tree) write_file(os.path.join(local_dir, "couchapp_git_tree_id"), tree) if os.path.exists(os.path.join(local_dir, "_id")): os.unlink(os.path.join(local_dir, "_id")) couchapp(doc_url, local_dir) print "...done %r" % (branch,)
def test(self): # Clean up any partially aborted test runs def mark_aborted(doc): doc.update({"status": "aborted"}) queue_url = posixpath.join(self.couchdb_url, "test-queue") queued_ids = set(i["_id"] for i in get(queue_url)["queue"]) pending_map_func = """\ function (doc) { var prefix = "test-run-"; if (doc._id.substr(0, prefix.length) == prefix) { if (doc.status == "pending") { emit(null, doc._id); } } } """ git_map_func = """\ function (doc) { var prefix = "test-"; if (doc._id.substr(0, prefix.length) != prefix) { emit(null, doc._id); } } """ design_doc = { "language": "javascript", "views": { "pending-tests": { "map": pending_map_func, }, "git-documents": { "map": git_map_func, }, }, } put_update(posixpath.join(self.couchdb_url, "_design/test"), lambda a=None: design_doc) result = get(posixpath.join(self.couchdb_url, "_design/test/_view/pending-tests")) pending_ids = set(i["id"] for i in result["rows"]) for missing_id in pending_ids - queued_ids: put_update(posixpath.join(self.couchdb_url, url_quote(missing_id)), mark_aborted) # Add this test run to the queue on the shared database def now(): return datetime.datetime.utcnow().isoformat() + "Z" document = { "request_time": now(), "start_time": "", "end_time": "", "status": "pending", } result = post_new(self.couchdb_url, document, id_template="test-run-%s") my_url = posixpath.join(self.couchdb_url, url_quote(result["id"])) def append_to_queue(queued): queued.setdefault("queue", []).append( {"_id": result["id"], "request_time": document["request_time"], }) def remove_from_queue(queued): queue = queued.get("queue", []) queue = [e for e in queue if e.get("_id") != result["id"]] queued["queue"] = queue put_update(queue_url, append_to_queue) try: while True: queue = get(queue_url) for i, entry in enumerate(queue.get("queue", [])): if entry["_id"] == result["id"]: my_index = i break else: raise Exception("I seem to have been un queued") # Is there a non-expired entry in the queue ahead of mine? if my_index == 0: def mark_running(job): job.update( {"status": "running", "start_time": now()}) status = "aborted" def mark_stopped(job): job.update( {"status": status, "stop_time": now()}) # Mark the test as running put_update(my_url, mark_running) try: try: self._run_test() except AssertionError: status = "failed" raise except Exception: status = "error" raise else: status = "passed" return finally: put_update(my_url, mark_stopped) else: # Set a timout for the next time to check, if necessary print "sleeping..." time.sleep(5) finally: # Mark the test as completed put_update(queue_url, remove_from_queue)
def _run_test(self): # Wipe out all git-related documents and the design document result = get(posixpath.join(self.couchdb_url, "_design/test/_view/git-documents")) for item in result["rows"]: delete(posixpath.join(self.couchdb_url, url_quote(item["id"]))) # Upload the gitbrowser to the couchdb design document gitbrowser_source_path = os.path.join(self.source_path, "gitbrowser") couchapp(posixpath.join(self.couchdb_url, "_design/gitbrowser"), gitbrowser_source_path) # Populate a test GIT repository with mkdtemp() as temp_dir: cwd_script = ["bash", "-c", 'cd "$1" && shift && exec "$@"', "-", temp_dir] subprocess.check_call(cwd_script + ["git", "init"]) ## write file README git = cwd_script + ["env", "HOME=%s" % (temp_dir,), "git"] subprocess.check_call(git + ["config", "user.name", "Tester"]) subprocess.check_call(git + ["config", "user.email", "*****@*****.**"]) path = os.path.join(temp_dir, "README") with open(path, "wb") as fh: fh.write("Initial version of text file\r\n") subprocess.check_call(git + ["add", path]) subprocess.check_call(git + ["commit", "-m", "Initial commit"]) ## write file README with open(path, "wb") as fh: fh.write("""\ # Purpose This is a minimal git repository for testing # Example Markdown Readme files like this are Markdown formatted: * Bullet points * Headings * Code blocks * _etc_ They can have hyperlinks: * to other hosts like [google](http://www.google.com/) * absolute paths, like the [hexdump file](/show/master/head/binary-file) * relative paths, like the [child folder](subfolder) """) subprocess.check_call(git + ["add", path]) ## write file binary path = os.path.join(temp_dir, "binary-file") with open(path, "wb") as fh: fh.write("".join(chr(i) for i in range(2**8))) subprocess.check_call(git + ["add", path]) ## write python file path = os.path.join(temp_dir, "example.py") with open(path, "wb") as fh: fh.write("""\ ## Example\r\n\ #This is an example python file\r\n\ #\r\n\ # - Example bullet list\r\n\ # - _etc_\r\n\ #\r\n\ #Because of the blank line, this documentation block should not\r\n\ #have any code on the right and the following code block should not\r\n\ #have any documentation to its left.\r\n\ \r\n\ from __future__ import division\r\n\ import os, sys, unittest\r\n\ \r\n\ class SomeTest(unittest.TestCase):\r\n\ \r\n\ def testSomething(self):\r\n\ '''Just an example test'''\r\n\ self.fail("Just an example")\r\n\ \r\n\ ## Main entry point\r\n\ #Some normal *Markdown* paragraph text, the example \r\n\ #heading should line up with the if statement.\r\n\ if __name__ == "__main__":\r\n\ # This comment, starting after column 0, is a normal comment shown\r\n\ # inline with the code instead of one that is shown in the left panel.\r\n\ unittest.main()\r\n\ """) subprocess.check_call(git + ["add", path]) ## write file .gitbrowser-project.json path = os.path.join(temp_dir, ".gitbrowser-project.json") with open(path, "wb") as fh: fh.write('{"title": "Example Project"}') subprocess.check_call(git + ["add", path]) ## write file subfolder/README path = os.path.join(temp_dir, "subfolder", "README") os.makedirs(os.path.dirname(path)) with open(path, "wb") as fh: fh.write("A file in a subfolder with a [parent link](..)") subprocess.check_call(git + ["add", path]) subprocess.check_call(git + ["commit", "-m", "Update"]) # Copy the git repository to the couchdb sync_script = os.path.join(self.source_path, "gitcouchdbsync.py") subprocess.check_call(cwd_script + ["python", sync_script, self.couchdb_url]) # Run selenium testing against the public URL print "Uploaded. Running tests..." self._go_to_the_selenium_stage()