From 230aa7a89e914cc46c3f76282d9da1f8ed7fdbbe Mon Sep 17 00:00:00 2001
From: finn <finn.ball@codethink.com>
Date: Fri, 27 Jul 2018 09:24:48 +0100
Subject: [PATCH] Adding bot_interface unittests

---
 .gitlab-ci.yml                     |  2 +-
 app/commands/cmd_bot.py            | 10 ++++--
 buildgrid/bot/bot.py               | 35 +++++++++----------
 tests/integration/bot_interface.py | 54 ++++++++++++++++++++++++++++++
 4 files changed, 78 insertions(+), 23 deletions(-)
 create mode 100644 tests/integration/bot_interface.py

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 421b5be8d..e898aaa9f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -30,7 +30,7 @@ before_script:
   script:
     - ${BGD} server start &
     - sleep 1 # Allow server to boot
-    - ${BGD} bot --host=0.0.0.0 dummy &
+    - ${BGD} bot --host=0.0.0.0 --continuous dummy &
     - ${BGD} execute --host=0.0.0.0 request --wait-for-completion
 
 tests-debian-stretch:
diff --git a/app/commands/cmd_bot.py b/app/commands/cmd_bot.py
index c83d60ca2..1ac0f026f 100644
--- a/app/commands/cmd_bot.py
+++ b/app/commands/cmd_bot.py
@@ -43,15 +43,17 @@ from buildgrid._protos.build.bazel.remote.execution.v2 import remote_execution_p
 from google.protobuf import any_pb2
 
 @click.group(short_help = 'Create a bot client')
+@click.option('--continuous', is_flag=True)
 @click.option('--parent', default='bgd_test')
 @click.option('--number-of-leases', default=1)
 @click.option('--port', default='50051')
 @click.option('--host', default='localhost')
 @pass_context
-def cli(context, host, port, number_of_leases, parent):
+def cli(context, host, port, number_of_leases, parent, continuous):
     context.logger = logging.getLogger(__name__)
     context.logger.info("Starting on port {}".format(port))
 
+    context.continuous = continuous
     context.channel = grpc.insecure_channel('{}:{}'.format(host, port))
     context.number_of_leases = number_of_leases
     context.parent = parent
@@ -71,7 +73,8 @@ def dummy(context):
                 context=context,
                 channel=context.channel,
                 parent=context.parent,
-                number_of_leases=context.number_of_leases)
+                number_of_leases=context.number_of_leases,
+                continuous=context.continuous)
 
     except KeyboardInterrupt:
         pass
@@ -105,7 +108,8 @@ def _work_buildbox(context, remote, port, server_cert, client_key, client_cert,
                 context=context,
                 channel=context.channel,
                 parent=context.parent,
-                number_of_leases=context.number_of_leases)
+                number_of_leases=context.number_of_leases,
+                continuous=context.continuous)
 
     except KeyboardInterrupt:
         pass
diff --git a/buildgrid/bot/bot.py b/buildgrid/bot/bot.py
index 5dab86ae9..16d7c1bdd 100644
--- a/buildgrid/bot/bot.py
+++ b/buildgrid/bot/bot.py
@@ -39,33 +39,31 @@ class Bot(object):
     Creates a local BotSession.
     """
 
-    def __init__(self, work, context, channel, parent, number_of_leases):
+    def __init__(self, work, context, channel, parent, number_of_leases, continuous=True):
         if not inspect.iscoroutinefunction(work):
             raise BotError("work function must be async")
 
+        print(type(context))
+
         self.interface = bot_interface.BotInterface(channel)
         self.logger = logging.getLogger(__name__)
 
         self._create_session(parent, number_of_leases)
         self._work_queue = queue.Queue(maxsize = number_of_leases)
 
-        try:
-            while True:
-                ## TODO: Leases independently finish
-                ## Allow leases to queue finished work independently instead
-                ## of waiting for all to finish
-                futures = [self._do_work(work, context, lease) for lease in self._get_work()]
-                if futures:
-                    loop = asyncio.new_event_loop()
-                    leases_complete, _ = loop.run_until_complete(asyncio.wait(futures))
-                    work_complete = [(lease.result().id, lease.result(),) for lease in leases_complete]
-                    self._work_complete(work_complete)
-                    loop.close()
-                self._update_bot_session()
-                time.sleep(2)
-        except Exception as e:
-            self.logger.error(e)
-            raise BotError(e)
+        while continuous:
+            ## TODO: Leases independently finish
+            ## Allow leases to queue finished work independently instead
+            ## of waiting for all to finish
+            futures = [self._do_work(work, context, lease) for lease in self._get_work()]
+            if futures:
+                loop = asyncio.new_event_loop()
+                leases_complete, _ = loop.run_until_complete(asyncio.wait(futures))
+                work_complete = [(lease.result().id, lease.result(),) for lease in leases_complete]
+                self._work_complete(work_complete)
+                loop.close()
+            self._update_bot_session()
+            time.sleep(2)
 
     @property
     def bot_session(self):
@@ -97,7 +95,6 @@ class Bot(object):
     def _get_work(self):
         while not self._work_queue.empty():
             yield self._work_queue.get()
-            self._work_queue.task_done()
 
     def _work_complete(self, leases_complete):
         """ Bot updates itself with any completed work.
diff --git a/tests/integration/bot_interface.py b/tests/integration/bot_interface.py
new file mode 100644
index 000000000..a3c844b4e
--- /dev/null
+++ b/tests/integration/bot_interface.py
@@ -0,0 +1,54 @@
+# Copyright (C) 2018 Codethink Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#  <http://www.apache.org/licenses/LICENSE-2.0>
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Authors:
+#        Finn Ball <finn.ball@codethink.co.uk>
+
+import copy
+import grpc
+import logging
+import mock
+import pytest
+import uuid
+
+from buildgrid.bot import bot, bot_interface
+
+async def _work_dummy(context, lease):
+    return lease
+
+class ContextMock():
+    def __init__(self):
+        self.logger = logging.getLogger(__name__)
+
+# GRPC context
+@pytest.fixture
+def context():
+    yield ContextMock()
+
+# GRPC context
+@pytest.fixture
+def channel():
+    yield mock.MagicMock(spec = grpc.insecure_channel(''))
+
+@pytest.fixture
+def instance(channel):
+    yield bot.Bot(work=_work_dummy,
+                  context=ContextMock(),
+                  channel=channel,
+                  parent='rach',
+                  number_of_leases=1,
+                  continuous=False)
+
+def test_create_job(instance):
+    instance.bot_session()
-- 
GitLab