# HG changeset patch
# User Philippe Pepiot <philippe.pepiot@logilab.fr>
# Date 1522945588 -7200
#      Thu Apr 05 18:26:28 2018 +0200
# Node ID 5f4d909fe90b5d91c0ff72102364ebed93fd34c2
# Parent  8ffc0035a0e7383332ddc3372dbc1a4760dc46dc
Implement http exchange benchmarks

By spawning a hg serve in setup() that serve repositories in repo/

It use a dynamic port allocation, used port is then read from "hg serve" stdout.

diff --git a/benchmarks/basic_commands.py b/benchmarks/basic_commands.py
--- a/benchmarks/basic_commands.py
+++ b/benchmarks/basic_commands.py
@@ -4,9 +4,18 @@
 import os.path
 import time
 import timeit
+import threading
+import re
 import shutil
 import subprocess
 import stat
+import sys
+
+if sys.version_info[0] == 2:
+    import Queue as queue
+else:
+    import queue
+
 
 from .utils import (BaseNChangesetsTestSuite, BaseTimeTestSuite,
                     BaseTrackTestSuite, median, REPOS_DIR)
@@ -121,11 +130,55 @@
         self._execute("bundle", "--base", ":(-%d)" % (n+1), "/tmp/bundle.bundle")
 
 
+class HgWeb(object):
+
+    def __init__(self):
+        super(HgWeb, self).__init__()
+        self.queue = queue.Queue()
+        self.proc = None
+        self.thread = None
+
+    def start(self, hgpath, environ):
+        config = os.path.abspath(os.path.join(
+            os.path.dirname(__file__), os.pardir, 'hgweb.config'))
+        self.proc = subprocess.Popen([
+            hgpath, 'serve', '--cwd', REPOS_DIR,
+            '-a', 'localhost', '-p', '0',
+            '--webdir-conf', config],
+            env=environ,
+            stdout=subprocess.PIPE)
+        # we have to read output in a thread to avoid deadlocks
+        self.thread = threading.Thread(
+            target=self._enqueue, args=(self.queue, self.proc.stdout))
+        self.thread.daemon = True
+        self.thread.start()
+        # wait the server to be started
+        statusline = self.queue.get()
+        if not statusline:
+            self.stop()
+            raise RuntimeError('hg serve has crashed')
+        return re.search(':(\d+)', statusline).groups()[0]
+
+    @staticmethod
+    def _enqueue(queue, fd):
+        while True:
+            data = fd.readline()
+            if not data:
+                break
+            queue.put(data)
+        queue.put(None)
+
+    def stop(self):
+        self.proc.kill()
+        self.proc.wait()
+        self.thread.join()
+
+
 class BaseExchangeTimeSuite(BaseTimeTestSuite):
     param_names = BaseTimeTestSuite.param_names + [
         'repo_type', 'strip', 'revset']
     params = BaseTimeTestSuite.params + [
-        ['local', 'ssh'],
+        ['local', 'ssh', 'http'],
         ['same', 'last(all(), 10)', 'last(all(), 100)', 'last(all(), 1000)'],
         [None, 'default']]
     timer = timeit.default_timer
@@ -144,6 +197,12 @@
             return [
                 '--remotecmd', os.path.abspath('hg_wrapper'),
                 'ssh://localhost/{}'.format(os.path.abspath(path))]
+        elif self.repo_type == 'http':
+            path = os.path.abspath(path)
+            repo_dir = os.path.abspath(REPOS_DIR)
+            assert path.startswith(REPOS_DIR), path
+            return ['http://localhost:{}/{}'.format(
+                self.hgport, path[len(repo_dir) + 1:])]
         else:
             raise NotImplementedError
 
@@ -170,6 +229,15 @@
             self.rev = None
         self.strip = strip
         self.repo_type = repo_type
+        if repo_type == 'http':
+            self._hgserve = HgWeb()
+            self.hgport = self._hgserve.start(self.hgpath, self.environ)
+        else:
+            self._hgserve = None
+
+    def teardown(self, *args, **kwargs):
+        if self._hgserve is not None:
+            self._hgserve.stop()
 
 
 class ExchangeTimeSuite(BaseExchangeTimeSuite):
@@ -184,7 +252,7 @@
 class DiscoveryTimeSuite(BaseExchangeTimeSuite):
     # debugdiscovery does not support revset argument
     params = BaseTimeTestSuite.params + [
-        ['local', 'ssh'],
+        ['local', 'ssh', 'http'],
         ['same', 'last(all(), 10)', 'last(all(), 100)', 'last(all(), 1000)'],
         [None]]
 
@@ -228,7 +296,7 @@
 
 class CloneTimeSuite(BaseExchangeTimeSuite):
     # skip other repos since they are too big
-    params = [['mercurial-2017'], ['local', 'ssh'], ['same'], [None, 'default']]
+    params = [['mercurial-2017'], ['local', 'ssh', 'http'], ['same'], [None, 'default']]
 
     timeout = 300
 
@@ -245,6 +313,7 @@
         shutil.rmtree(self.tmp_clone_path, ignore_errors=True)
 
     def teardown(self, *args, **kwargs):
+        super(CloneTimeSuite, self).teardown(*args, **kwargs)
         shutil.rmtree(self.tmp_clone_path, ignore_errors=True)
 
     def time_clone(self, *args, **kwargs):
diff --git a/hgweb.config b/hgweb.config
new file mode 100644
--- /dev/null
+++ b/hgweb.config
@@ -0,0 +1,2 @@
+[paths]
+/ = *