summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorCharles Oliveira <charles.oliveira@linaro.org>2020-03-11 16:48:21 -0300
committerCharles Oliveira <charles.oliveira@linaro.org>2020-03-23 19:25:31 -0300
commit381a89d7517cd7e4f70591d4ea053d72aceedb80 (patch)
tree4393364fb012cbe4184cc204095a9b02fd4f4776 /tests
parent3a1aab9581b2ae714f7032d8450f355847a63c83 (diff)
tests: start local squad server to support testing
Start a local instance of SQUAD so that Squad-Client tests can run against it. Add fixtures file, which provides all necessary data to be tested in squad-client. The mechanism is pretty simple, before every call to `./manage.py test`, a fresh squad instance is started up and run that file so that all data is available thru the api
Diffstat (limited to 'tests')
-rw-r--r--tests/__init__.py35
-rw-r--r--tests/fixtures.py38
-rw-r--r--tests/settings.py12
-rw-r--r--tests/squad_service.py142
-rw-r--r--tests/test_api.py6
-rw-r--r--tests/test_models.py17
-rw-r--r--tests/test_shortcuts.py7
-rw-r--r--tests/test_squad_service.py69
8 files changed, 306 insertions, 20 deletions
diff --git a/tests/__init__.py b/tests/__init__.py
index 4b43dfe..159726e 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -1,9 +1,30 @@
-import unittest
-import os
+import subprocess as sp
-def run():
- loader = unittest.TestLoader()
- tests = loader.discover(os.path.dirname(os.path.abspath(__file__)))
- testRunner = unittest.runner.TextTestRunner()
- testRunner.run(tests)
+from squad_client.core.api import SquadApi
+from .squad_service import SquadService
+
+
+def run(coverage=False, tests=['discover'], verbose=False):
+ squad_service = SquadService()
+ if not squad_service.start() or not squad_service.apply_fixtures('tests/fixtures.py'):
+ print('Aborting tests!')
+ return False
+
+ SquadApi.configure(url=squad_service.host)
+
+ argv = ['-m', 'unittest'] + tests
+ if len(tests) == 0 and verbose:
+ argv += ['discover', '-v']
+
+ if coverage:
+ print('\t --coverage is enabled, run `coverage report -m` to view coverage report')
+ argv = ['coverage', 'run', '--source', 'squad_client'] + argv
+ else:
+ argv = ['python3'] + argv
+
+ proc = sp.Popen(argv)
+ proc.wait()
+
+ squad_service.stop()
+ return proc.returncode == 0
diff --git a/tests/fixtures.py b/tests/fixtures.py
new file mode 100644
index 0000000..53da607
--- /dev/null
+++ b/tests/fixtures.py
@@ -0,0 +1,38 @@
+# This file is supposed to run in a squad instance for squad-client tests
+#
+# Some guidance to maintain this file:
+# - try to keep all values here
+# - do not delete any of the data anywhere (if deletion tests are needed, create one for the specific test)
+#
+
+from squad.core import models as m
+from squad.ci import models as mci
+
+group = m.Group.objects.create(slug='my_group')
+group2 = m.Group.objects.create(slug='my_other_group')
+
+project = group.projects.create(slug='my_project')
+
+build = project.builds.create(version='my_build')
+build2 = project.builds.create(version='my_build2')
+build3 = project.builds.create(version='my_build3')
+build4 = project.builds.create(version='my_build4')
+build5 = project.builds.create(version='my_build5')
+build6 = project.builds.create(version='my_build6')
+
+environment = project.environments.create(slug='my_env')
+suite = project.suites.create(slug='my_suite')
+
+testrun = build.test_runs.create(environment=environment)
+passed_test = testrun.tests.create(suite=suite, result=True, name='my_passed_test')
+failed_test = testrun.tests.create(suite=suite, result=False, name='my_failed_test')
+xfailed_test = testrun.tests.create(suite=suite, result=True, name='my_xfailed_test', has_known_issues=True)
+skipped_test = testrun.tests.create(suite=suite, result=None, name='my_skipped_test')
+
+backend = mci.Backend.objects.create()
+testjob = testrun.test_jobs.create(backend=backend, target=project, target_build=build)
+
+emailtemplate = m.EmailTemplate.objects.create(name='my_emailtemplate')
+suitemetadata = m.SuiteMetadata.objects.create(name='my_suitemetadata')
+metricthreshold = m.MetricThreshold.objects.create(project=project, value=42)
+report = build.delayed_reports.create()
diff --git a/tests/settings.py b/tests/settings.py
new file mode 100644
index 0000000..ba2153b
--- /dev/null
+++ b/tests/settings.py
@@ -0,0 +1,12 @@
+import os
+
+
+from squad_client.settings import * # noqa
+
+
+tests_dir = os.path.dirname(os.path.abspath(__file__))
+
+
+DEFAULT_SQUAD_PORT = 9000
+DEFAULT_SQUAD_DATABASE_NAME = os.path.join(tests_dir, 'squad.sqlite3')
+DEFAULT_SQUAD_DATABASE_CONFIG = 'ENGINE=django.db.backends.sqlite3:NAME=%s' % DEFAULT_SQUAD_DATABASE_NAME
diff --git a/tests/squad_service.py b/tests/squad_service.py
new file mode 100644
index 0000000..9a89c32
--- /dev/null
+++ b/tests/squad_service.py
@@ -0,0 +1,142 @@
+import logging
+import os
+import subprocess as sp
+import time
+import requests
+import socket
+
+
+from . import settings
+
+
+# Possible outcomes of running squad-admin process
+OK = 0 # all good, exited with 0
+TIMEOUT = 1 # as name says, squad-admin timed out
+ERROR = 2 # squad-admin didn't time out but returned non-zero status
+
+
+class SquadAdmin:
+ def __init__(self, env={'DATABASE': settings.DEFAULT_SQUAD_DATABASE_CONFIG}):
+ self.cmd = ['squad-admin']
+ self.env = os.environ.copy()
+ self.env.update(env)
+ self.__truncate_database__()
+ self.logger = logging.getLogger('squad-admin')
+ self.daemons = []
+
+ def __del__(self):
+ if len(self.daemons):
+ self.logger.info('Terminating daemons')
+
+ for daemon in self.daemons:
+ if daemon.poll() is None:
+ daemon.kill()
+
+ def __truncate_database__(self):
+ if os.path.isfile(settings.DEFAULT_SQUAD_DATABASE_NAME):
+ os.remove(settings.DEFAULT_SQUAD_DATABASE_NAME)
+
+ def __run_process__(self, args, timeout=10, daemon=False, input=None, stdin=sp.DEVNULL, stdout=sp.DEVNULL, stderr=sp.DEVNULL):
+ proc = sp.Popen(self.cmd + args, env=self.env, stdin=stdin, stdout=stdout, stderr=stderr)
+ proc.ok = False
+
+ if not daemon:
+ try:
+ proc.out, proc.err = proc.communicate(input=input, timeout=timeout)
+ proc.ok = (proc.returncode == 0)
+ except sp.TimeoutExpired:
+ self.logger.error('Running "%s" time out after %i seconds!' % (' '.join(self.cmd + args), timeout))
+ proc.kill()
+ proc.out, proc.err = proc.communicate()
+ else:
+ self.daemons.append(proc)
+
+ return proc
+
+ def migrate(self):
+ proc = self.__run_process__(['migrate'])
+ if not proc.ok:
+ self.logger.error('Failed to migrate!')
+ return proc
+
+ def runserver(self, port=8000):
+ # --noreload forces single threaded server. Ref: https://docs.djangoproject.com/en/dev/ref/django-admin/#cmdoption-runserver-noreload
+ proc = self.__run_process__(['runserver', '--noreload', str(port)], daemon=True)
+
+ attempts = 5
+ while attempts > 0:
+ attempts -= 1
+ try:
+ response = requests.get('http://localhost:%s' % str(port))
+ if response.ok:
+ self.logger.debug('`squad-admin runserver` has started successfully!')
+ proc.ok = True
+ except requests.exceptions.ConnectionError:
+ self.logger.debug('Checking if `squad-admin runserver` is running... attempt %s' % str(attempts))
+ time.sleep(1)
+
+ if not proc.ok:
+ self.logger.error('Failed to start `squad-admin runserver`!')
+ return proc
+
+ def shell(self, input=None):
+ proc = self.__run_process__(['shell'], input=input, stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE)
+ return proc
+
+
+class SquadService:
+
+ def __init__(self, port=settings.DEFAULT_SQUAD_PORT):
+ self.port = port
+ self.host = 'http://localhost:%s' % str(self.port)
+ self.service = None
+ self.squad_admin = SquadAdmin()
+ self.logger = logging.getLogger('squad-service')
+
+ def __del__(self):
+ self.stop()
+
+ def __port_in_use__(self):
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
+ return s.connect_ex(('localhost', self.port)) == 0
+
+ def start(self):
+ self.logger.info('Starting a fresh squad instance on port %s' % self.port)
+
+ if self.__port_in_use__():
+ self.logger.error('... port already in use!')
+ return False
+
+ self.logger.info('Creating database schema')
+ proc = self.squad_admin.migrate()
+ if not proc.ok:
+ return False
+
+ self.service = self.squad_admin.runserver(port=self.port)
+ if not self.service.ok:
+ self.logger.error('Failed to start squad service "%s"' % self.service.stderr.read().decode())
+ return False
+
+ return True
+
+ def is_running(self):
+ return self.service is not None and self.service.poll() is None
+
+ def apply_fixtures(self, fixtures_path):
+ self.logger.info('Applying %s' % fixtures_path)
+
+ with open(fixtures_path, 'rb') as f:
+ fixtures_content = f.read()
+
+ proc = self.squad_admin.shell(fixtures_content)
+ if not proc.ok:
+ error_message = proc.out + proc.err
+ self.logger.error(error_message.decode())
+
+ return proc.ok
+
+ def stop(self):
+ if self.is_running():
+ self.logger.debug('Terminating Squad')
+ self.service.kill()
+ self.service.wait(timeout=5)
diff --git a/tests/test_api.py b/tests/test_api.py
index 82f0656..f2c8763 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -1,13 +1,13 @@
from unittest import TestCase
-
+from . import settings
from squad_client.core.api import SquadApi, ApiException
def is_test_server_running():
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- result = sock.connect_ex(('localhost', 8000))
+ result = sock.connect_ex(('localhost', settings.DEFAULT_SQUAD_PORT))
sock.close()
return result == 0
@@ -15,7 +15,7 @@ def is_test_server_running():
class SquadApiTest(TestCase):
def setUp(self):
- SquadApi.configure(url='http://localhost:8000')
+ SquadApi.configure(url='http://localhost:%s' % settings.DEFAULT_SQUAD_PORT)
def test_malformed_url(self):
with self.assertRaises(ApiException):
diff --git a/tests/test_models.py b/tests/test_models.py
index 072b08a..25bd597 100644
--- a/tests/test_models.py
+++ b/tests/test_models.py
@@ -1,12 +1,12 @@
import unittest
-
+from . import settings
from squad_client.core.api import SquadApi
-from squad_client.core.models import Squad
+from squad_client.core.models import Squad, ALL
from squad_client.utils import first
-SquadApi.configure(url='http://localhost:8000')
+SquadApi.configure(url='http://localhost:%s' % settings.DEFAULT_SQUAD_PORT)
class SquadTest(unittest.TestCase):
@@ -23,16 +23,19 @@ class SquadTest(unittest.TestCase):
self.assertEqual(0, len(groups))
def test_groups_with_count(self):
- four_groups = self.squad.groups(count=4)
- self.assertEqual(4, len(four_groups))
+ all_groups = self.squad.groups(count=ALL)
+ self.assertEqual(2, len(all_groups))
+
+ one_groups = self.squad.groups(count=1)
+ self.assertEqual(1, len(one_groups))
def test_not_found_group(self):
not_found_group = self.squad.group('this-group-does-not-really-exist')
self.assertEqual(None, not_found_group)
def test_group(self):
- lkft_group = self.squad.group('lkft')
- self.assertTrue(lkft_group is not None)
+ group = self.squad.group('my_group')
+ self.assertTrue(group is not None)
def test_projects(self):
projects = self.squad.projects()
diff --git a/tests/test_shortcuts.py b/tests/test_shortcuts.py
index 4d5342a..b635a4b 100644
--- a/tests/test_shortcuts.py
+++ b/tests/test_shortcuts.py
@@ -1,6 +1,7 @@
from unittest import TestCase
+from . import settings
from squad_client.core.api import SquadApi
from squad_client.shortcuts import retrieve_latest_builds, retrieve_build_results
@@ -8,12 +9,12 @@ from squad_client.shortcuts import retrieve_latest_builds, retrieve_build_result
class ShortcutsTest(TestCase):
def setUp(self):
- SquadApi.configure('http://localhost:8000')
+ SquadApi.configure('http://localhost:%s' % settings.DEFAULT_SQUAD_PORT)
def test_retrieve_latest_builds(self):
- builds = retrieve_latest_builds('lkft/linux-stable-rc-4.14-oe', count=5)
+ builds = retrieve_latest_builds('my_group/my_project', count=5)
self.assertEqual(5, len(builds))
def test_retrieve_build_results(self):
- results = retrieve_build_results('lkft/linux-stable-rc-4.14-oe-sanity/build/v4.14.74')
+ results = retrieve_build_results('my_group/my_project/build/my_build')
self.assertIsNotNone(results)
diff --git a/tests/test_squad_service.py b/tests/test_squad_service.py
new file mode 100644
index 0000000..4d3aa30
--- /dev/null
+++ b/tests/test_squad_service.py
@@ -0,0 +1,69 @@
+from unittest import TestCase
+import uuid
+
+
+from .squad_service import SquadAdmin, SquadService
+
+
+DB_NAME = str(uuid.uuid4())
+
+
+class SquadServiceTest(TestCase):
+
+ def setUp(self):
+ self.squad_service = SquadService(port=9001)
+ self.squad_service.squad_admin.env['DATABASE'] = 'ENGINE=django.db.backends.sqlite3:NAME=/tmp/%s.sqlite3' % DB_NAME
+
+ def test_start(self):
+ self.squad_service.start()
+ self.assertTrue(self.squad_service.is_running())
+
+ self.squad_service.stop()
+ self.assertFalse(self.squad_service.is_running())
+
+ def test_fixtures(self):
+ self.squad_service.start()
+ self.assertTrue(self.squad_service.is_running())
+
+ ok = self.squad_service.apply_fixtures('tests/fixtures.py')
+ self.assertTrue(ok)
+
+ self.squad_service.stop()
+ self.assertFalse(self.squad_service.is_running())
+
+
+class SquadAdminTest(TestCase):
+
+ def setUp(self):
+ self.squad_admin = SquadAdmin()
+ self.squad_admin.env['DATABASE'] = 'ENGINE=django.db.backends.sqlite3:NAME=/tmp/%s.sqlite3' % DB_NAME
+
+ def tearDown(self):
+ self.squad_admin.__truncate_database__()
+
+ def test_migration(self):
+ status = self.squad_admin.migrate()
+ self.assertTrue(status.ok)
+
+ def test_runserver(self):
+ migrate_status = self.squad_admin.migrate()
+ self.assertTrue(migrate_status.ok)
+
+ service = self.squad_admin.runserver(port=9002)
+ self.assertTrue(service.ok)
+
+ service.kill()
+ service.wait(timeout=5)
+
+ def test_shell(self):
+ migrate_status = self.squad_admin.migrate()
+ self.assertTrue(migrate_status.ok)
+
+ shell_status = self.squad_admin.shell(b'print(')
+ self.assertFalse(shell_status.ok)
+
+ shell_status = self.squad_admin.shell(b'print()')
+ self.assertTrue(shell_status.ok)
+
+ shell_status = self.squad_admin.shell(b'from squad.core.models import Group; Group.objects.create(slug="agroup")')
+ self.assertTrue(shell_status.ok)