From 9629dbb4051968c498da437852761a2a52f15aa7 Mon Sep 17 00:00:00 2001 From: Mikhail Podgurskiy Date: Thu, 19 Jan 2012 17:34:34 +0700 Subject: [PATCH 1/5] Simple standalone ci server, for those who still curious about ci, and have no jenkins instance installed. Embedding simple ci into testing server instance seems good solution. django_jenkins.standalone not going to be whole jenkins replacement, but only cover most basic use cases. --- .gitignore | 1 + .../management/commands/__init__.py | 10 ++-- django_jenkins/management/commands/ci.py | 42 +++++++++++++++ django_jenkins/runner.py | 4 +- django_jenkins/standalone/__init__.py | 0 django_jenkins/standalone/storage.py | 38 ++++++++++++++ django_jenkins/standalone/urls.py | 8 +++ django_jenkins/standalone/views.py | 52 +++++++++++++++++++ .../templates/django_jenkins/base_ci.html | 10 ++++ .../templates/django_jenkins/index.html | 5 ++ tests/requirements.pip | 2 +- tests/settings.py | 2 + tests/test_app/urls.py | 6 +-- 13 files changed, 170 insertions(+), 10 deletions(-) create mode 100644 django_jenkins/management/commands/ci.py create mode 100644 django_jenkins/standalone/__init__.py create mode 100644 django_jenkins/standalone/storage.py create mode 100644 django_jenkins/standalone/urls.py create mode 100644 django_jenkins/standalone/views.py create mode 100644 django_jenkins/templates/django_jenkins/base_ci.html create mode 100644 django_jenkins/templates/django_jenkins/index.html diff --git a/.gitignore b/.gitignore index 9c43ee52..98aa82a4 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,6 @@ .coverage *.pyc reports +tests/media diff --git a/django_jenkins/management/commands/__init__.py b/django_jenkins/management/commands/__init__.py index 6d5a5e90..51830182 100644 --- a/django_jenkins/management/commands/__init__.py +++ b/django_jenkins/management/commands/__init__.py @@ -47,7 +47,7 @@ def __init__(self): super(TaskListCommand, self).__init__() self.tasks_cls = [import_module(module_name).Task for module_name in self.get_task_list()] - def handle(self, *test_labels, **options): + def initialize(self, *test_labels, **options): # instantiate tasks self.tasks = [task_cls(test_labels, options) for task_cls in self.tasks_cls] @@ -58,16 +58,18 @@ def handle(self, *test_labels, **options): if signal_handler: signal.connect(signal_handler) - # run + # setup test runner test_runner_cls = get_runner() - test_runner = test_runner_cls( + self.test_runner = test_runner_cls( output_dir=options['output_dir'], interactive=options['interactive'], debug=options['debug'], verbosity=int(options.get('verbosity', 1)), with_reports=options.get('with_reports', True)) - if test_runner.run_tests(test_labels): + def handle(self, *test_labels, **options): + self.initialize(*test_labels, **options) + if self.test_runner.run_tests(test_labels): sys.exit(1) def get_task_list(self): diff --git a/django_jenkins/management/commands/ci.py b/django_jenkins/management/commands/ci.py new file mode 100644 index 00000000..87a6e36a --- /dev/null +++ b/django_jenkins/management/commands/ci.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8; mode: django -*- +import os +import sys +from django_jenkins.standalone.storage import Storage +from django_jenkins.management.commands.jenkins import Command as BaseCICommand + + +class Command(BaseCICommand): + help = "Run CI process, and collect data for django_jenkins.standalon" + args = '[appname ...]' + + def handle(self, *test_labels, **options): + storage = Storage.open() + build_id = storage['last_build_id'] + 1 + + # substitute output_dir + options['output_dir'] = os.path.join(Storage.ci_root(), 'build-%d' % build_id) + + # run + self.initialize(*test_labels, **options) + result = self.test_runner.run_tests(test_labels) + + # store results and exit + build_data = {} + + # TODO Here jenkins-task views come to play + + # tests + test_result = self.test_runner.result + build_data['tests-successes'] = len(test_result.successes) + build_data['tests-failures'] = len(test_result.failures) + build_data['tests-errors'] = len(test_result.errors) + + # End jenkins-task views + + storage['build-%d' % build_id] = build_data + storage['last_build_id'] = build_id + storage.close() + + if result: + sys.exit(1) + diff --git a/django_jenkins/runner.py b/django_jenkins/runner.py index b14dfb5a..fe54a922 100644 --- a/django_jenkins/runner.py +++ b/django_jenkins/runner.py @@ -340,10 +340,10 @@ def run_tests(self, test_labels, extra_tests=None, **kwargs): suite = self.build_suite(test_labels, extra_tests=extra_tests) if suite.countTestCases(): old_config = self.setup_databases() - result = self.run_suite(suite) + self.result = self.run_suite(suite) self.teardown_databases(old_config) self.teardown_test_environment() - return self.suite_result(suite, result) + return self.suite_result(suite, self.result) else: self.teardown_test_environment() return 0 diff --git a/django_jenkins/standalone/__init__.py b/django_jenkins/standalone/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/django_jenkins/standalone/storage.py b/django_jenkins/standalone/storage.py new file mode 100644 index 00000000..52fec15a --- /dev/null +++ b/django_jenkins/standalone/storage.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8; mode: django -*- +import os +import shelve +from django.conf import settings + + +class Storage(object): + def __init__(self, storage): + self.storage = storage + + @staticmethod + def ci_root(): + ci_root = getattr(settings, 'CI_ROOT', os.path.join(settings.MEDIA_ROOT, 'ci')) + if not os.path.exists(ci_root): + os.makedirs(ci_root) + return ci_root + + @staticmethod + def open(): + storage = shelve.open(os.path.join(Storage.ci_root(), 'cidata.shelve')) + if 'version' not in storage: + storage['version'] = '1.0' + if 'last_build_id' not in storage: + storage['last_build_id'] = 0 + return Storage(storage=storage) + + def __getitem__(self, key): + return self.storage[key] + + def __setitem__(self, key, value): + self.storage[key] = value + + def __contains__(self, item): + return item in self.storage + + def close(self): + self.storage.close() + diff --git a/django_jenkins/standalone/urls.py b/django_jenkins/standalone/urls.py new file mode 100644 index 00000000..52ed1f89 --- /dev/null +++ b/django_jenkins/standalone/urls.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8; mode: django -*- +from django.conf.urls.defaults import patterns, url + + +urlpatterns = patterns('django_jenkins.standalone.views', + url(r'^$', 'index', name="ci_index"), +) + diff --git a/django_jenkins/standalone/views.py b/django_jenkins/standalone/views.py new file mode 100644 index 00000000..ecf662a3 --- /dev/null +++ b/django_jenkins/standalone/views.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8; mode: django -*- +from pygooglechart import Axis, SimpleLineChart +from django.shortcuts import render +from django_jenkins.standalone.storage import Storage + + +def index(request): + try: + storage = Storage.open() + + # TODO Here jenkins-task views come to play + + # tests + last_build_id = storage['last_build_id'] + successes, failures, errors = [], [], [] + for build_id in xrange(1, last_build_id+1): + build_data = storage['build-%d' % build_id] + successes.append(build_data['tests-successes']) + failures.append(build_data['tests-failures']) + errors.append(build_data['tests-errors']) + + + tests_chart = SimpleLineChart(400, 250) + # axis + max_tests = max([s+f+e for s,f,e in zip(successes,failures,errors)]) + step = round(max_tests/10, 1-len(str(max_tests/10))) + tests_chart.set_axis_labels(Axis.LEFT, xrange(0, step*11, step)) + tests_chart.set_axis_labels(Axis.BOTTOM, xrange(1, last_build_id+1)) + tests_chart.set_grid(0, step/2, 5, 5) + + # First value - allowed maximum + tests_chart.add_data([step*10] * 2) + # All tests, failures and errors, errors + tests_chart.add_data([s+f+e for s,f,e in zip(successes,failures,errors)]) + tests_chart.add_data([f+e for f,e in zip(failures,errors)]) + tests_chart.add_data(errors) + # Last value is the lowest in the Y axis. + tests_chart.add_data([0] * 2) + + # Fill colors + tests_chart.set_colours(['FFFFFF', '00FF00', 'FF0000', '0000FF']) + tests_chart.add_fill_range('00FF00', 1, 2) + tests_chart.add_fill_range('FF0000', 2, 3) + tests_chart.add_fill_range('0000FF', 3, 4) + + # End jenkins-task views + + return render(request, 'django_jenkins/index.html', + { 'tests_chart' : tests_chart }) + finally: + storage.close() + diff --git a/django_jenkins/templates/django_jenkins/base_ci.html b/django_jenkins/templates/django_jenkins/base_ci.html new file mode 100644 index 00000000..1e9a08b3 --- /dev/null +++ b/django_jenkins/templates/django_jenkins/base_ci.html @@ -0,0 +1,10 @@ + + + Continous integration results + + +

CI results

+ {% block content %} + {% endblock %} + + diff --git a/django_jenkins/templates/django_jenkins/index.html b/django_jenkins/templates/django_jenkins/index.html new file mode 100644 index 00000000..ecbd9bec --- /dev/null +++ b/django_jenkins/templates/django_jenkins/index.html @@ -0,0 +1,5 @@ +{% extends 'django_jenkins/base_ci.html' %} + +{% block content %} + +{% endblock %}' diff --git a/tests/requirements.pip b/tests/requirements.pip index 34c535c5..ea0d4a59 100644 --- a/tests/requirements.pip +++ b/tests/requirements.pip @@ -1,10 +1,10 @@ django>=1.2 pylint>=0.23 coverage>=3.4 -windmill pyflakes pep8 lettuce +pygooglechart # development only ipdb diff --git a/tests/settings.py b/tests/settings.py index 99b0476f..ba84b660 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -28,6 +28,8 @@ } } +MEDIA_ROOT = os.path.join(PROJECT_ROOT, 'media') + JENKINS_TASKS = ( 'django_jenkins.tasks.with_coverage', 'django_jenkins.tasks.django_tests', diff --git a/tests/test_app/urls.py b/tests/test_app/urls.py index d0107b0d..9aa2ac97 100644 --- a/tests/test_app/urls.py +++ b/tests/test_app/urls.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -from django.conf.urls.defaults import * +from django.conf.urls.defaults import patterns, url, include urlpatterns = patterns('', - url(r'^wm_test_click/$', 'django.views.generic.simple.direct_to_template', - {'template': 'test_app/wm_test_click.html'}, name='wm_test_click') + url(r'^ci/$', include('django_jenkins.standalone.urls')), ) + From b5586a542765ee7879469e55e88ffd5a48b8597b Mon Sep 17 00:00:00 2001 From: Mikhail Podgurskiy Date: Thu, 19 Jan 2012 18:12:37 +0700 Subject: [PATCH 2/5] Fix left axis --- django_jenkins/standalone/views.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/django_jenkins/standalone/views.py b/django_jenkins/standalone/views.py index ecf662a3..6faa11e9 100644 --- a/django_jenkins/standalone/views.py +++ b/django_jenkins/standalone/views.py @@ -1,4 +1,5 @@ # -*- coding: utf-8; mode: django -*- +import math from pygooglechart import Axis, SimpleLineChart from django.shortcuts import render from django_jenkins.standalone.storage import Storage @@ -24,12 +25,13 @@ def index(request): # axis max_tests = max([s+f+e for s,f,e in zip(successes,failures,errors)]) step = round(max_tests/10, 1-len(str(max_tests/10))) - tests_chart.set_axis_labels(Axis.LEFT, xrange(0, step*11, step)) + total_steps = math.ceil(1.0 * max_tests/step) + tests_chart.set_axis_labels(Axis.LEFT, xrange(0, step*(total_steps+1), step)) tests_chart.set_axis_labels(Axis.BOTTOM, xrange(1, last_build_id+1)) tests_chart.set_grid(0, step/2, 5, 5) # First value - allowed maximum - tests_chart.add_data([step*10] * 2) + tests_chart.add_data([step*total_steps] * 2) # All tests, failures and errors, errors tests_chart.add_data([s+f+e for s,f,e in zip(successes,failures,errors)]) tests_chart.add_data([f+e for f,e in zip(failures,errors)]) From 537f6ef3711b474e91b076741dd1a61a069988d4 Mon Sep 17 00:00:00 2001 From: Mikhail Podgurskiy Date: Mon, 7 May 2012 11:40:54 +0700 Subject: [PATCH 3/5] Dump standalone changes --- django_jenkins/standalone/taskviews.py | 82 ++++++++++++++++++++++++++ django_jenkins/standalone/views.py | 1 + django_jenkins/tasks/__init__.py | 1 + django_jenkins/tasks/django_tests.py | 2 + 4 files changed, 86 insertions(+) create mode 100644 django_jenkins/standalone/taskviews.py diff --git a/django_jenkins/standalone/taskviews.py b/django_jenkins/standalone/taskviews.py new file mode 100644 index 00000000..255efd1a --- /dev/null +++ b/django_jenkins/standalone/taskviews.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8; mode: django -*- +""" +Store and view task results data in standalone ci +""" +import math +from pygooglechart import Axis, SimpleLineChart + + +class BaseView(object): + """ + Base class for standalone.ci view + """ + def get_build_data(self, tasks, runner): + """ + Extrats current build ci data + """ + return {} + + def collect_build_data(self, build_data): + """ + Collect build data for view + """ + + def build_chart(self, start_build_id, end_build_id): + """ + Returns pygooglechart instance with build data + """ + + +class TestView(BaseView): + def __init__(self): + self.successes = [] + self.failures = [] + self.errors = [] + + def get_build_data(self, tasks, runner): + test_result = self.test_runner.result + + result = {} + result['tests-successes'] = len(test_result.successes) + result['tests-failures'] = len(test_result.failures) + result['tests-errors'] = len(test_result.errors) + return result + + def collect_build_data(self, build_data): + self.successes.append(build_data['tests-successes']) + self.failures.append(build_data['tests-failures']) + self.errors.append(build_data['tests-errors']) + + def build_chart(self, start_build_id, end_build_id): + tests_chart = SimpleLineChart(400, 250) + all_fails = [f+e for f,e in zip(self.failures, self.errors)] + all_results = [s+af for s,af in zip(self.successes, all_fails)] + + # axis + max_tests = max(all_results) + step = round(max_tests/10, 1-len(str(max_tests/10))) + total_steps = math.ceil(1.0 * max_tests/step) + tests_chart.set_axis_labels(Axis.LEFT, xrange(0, step*(total_steps+1), step)) + tests_chart.set_axis_labels(Axis.BOTTOM, xrange(start_build_id, end_build_id+1)) + tests_chart.set_grid(0, step/2, 5, 5) + + # First value - allowed maximum + tests_chart.add_data([step*total_steps] * 2) + # All tests, failures and errors, errors + tests_chart.add_data(all_results) + tests_chart.add_data(all_fails) + tests_chart.add_data(self.errors) + # Last value is the lowest in the Y axis. + tests_chart.add_data([0] * 2) + + # Fill colors + tests_chart.set_colours(['FFFFFF', '00FF00', 'FF0000', '0000FF']) + tests_chart.add_fill_range('00FF00', 1, 2) + tests_chart.add_fill_range('FF0000', 2, 3) + tests_chart.add_fill_range('0000FF', 3, 4) + + return tests_chart + + +class ViolationsView(BaseView): + pass diff --git a/django_jenkins/standalone/views.py b/django_jenkins/standalone/views.py index 6faa11e9..5c2f5c91 100644 --- a/django_jenkins/standalone/views.py +++ b/django_jenkins/standalone/views.py @@ -22,6 +22,7 @@ def index(request): tests_chart = SimpleLineChart(400, 250) + tests_chart.BASE_URL = 'http://chart.googleapis.com/chart?' # axis max_tests = max([s+f+e for s,f,e in zip(successes,failures,errors)]) step = round(max_tests/10, 1-len(str(max_tests/10))) diff --git a/django_jenkins/tasks/__init__.py b/django_jenkins/tasks/__init__.py index 9ecbe682..32a38a18 100644 --- a/django_jenkins/tasks/__init__.py +++ b/django_jenkins/tasks/__init__.py @@ -8,6 +8,7 @@ class BaseTask(object): """ Base interface for ci tasks """ + view = None option_list = [] def __init__(self, test_labels, options): diff --git a/django_jenkins/tasks/django_tests.py b/django_jenkins/tasks/django_tests.py index 9580913e..3d935637 100644 --- a/django_jenkins/tasks/django_tests.py +++ b/django_jenkins/tasks/django_tests.py @@ -11,6 +11,8 @@ class Task(BaseTask): + view = 'django_jenkins.standalone.taskviews.TestView' + def __init__(self, test_labels, options): super(Task, self).__init__(test_labels, options) if not self.test_labels: From dc3f20f0afdc017d60b2a5b0d98ab546b19c8596 Mon Sep 17 00:00:00 2001 From: Mikhail Podgurskiy Date: Tue, 8 May 2012 11:27:32 +0700 Subject: [PATCH 4/5] New standalone views interface --- django_jenkins/management/commands/ci.py | 22 ++++++-- .../{taskviews.py => taskviews/__init__.py} | 56 +++++++++++++------ .../standalone/taskviews/tests_view.py | 40 +++++++++++++ 3 files changed, 94 insertions(+), 24 deletions(-) rename django_jenkins/standalone/{taskviews.py => taskviews/__init__.py} (66%) create mode 100644 django_jenkins/standalone/taskviews/tests_view.py diff --git a/django_jenkins/management/commands/ci.py b/django_jenkins/management/commands/ci.py index 87a6e36a..0eae2a0b 100644 --- a/django_jenkins/management/commands/ci.py +++ b/django_jenkins/management/commands/ci.py @@ -1,9 +1,14 @@ # -*- coding: utf-8; mode: django -*- import os import sys +from itertools import groupby +from django.utils.importlib import import_module from django_jenkins.standalone.storage import Storage from django_jenkins.management.commands.jenkins import Command as BaseCICommand +def task_view(task): + return getattr(task, 'view', None) + class Command(BaseCICommand): help = "Run CI process, and collect data for django_jenkins.standalon" @@ -23,13 +28,19 @@ def handle(self, *test_labels, **options): # store results and exit build_data = {} - # TODO Here jenkins-task views come to play + # here is jenkins-task views come to play + build_data['views'] = [] + self.tasks.sort(key=task_view) + for view_name, tasks in groupby(self.tasks, task_view): + view = import_module(view_name) + build_data[view_name] = view.get_build_data(tasks) + build_data['views'].append(view_name) # tests - test_result = self.test_runner.result - build_data['tests-successes'] = len(test_result.successes) - build_data['tests-failures'] = len(test_result.failures) - build_data['tests-errors'] = len(test_result.errors) + #test_result = self.test_runner.result + #build_data['tests-successes'] = len(test_result.successes) + #build_data['tests-failures'] = len(test_result.failures) + #build_data['tests-errors'] = len(test_result.errors) # End jenkins-task views @@ -39,4 +50,3 @@ def handle(self, *test_labels, **options): if result: sys.exit(1) - diff --git a/django_jenkins/standalone/taskviews.py b/django_jenkins/standalone/taskviews/__init__.py similarity index 66% rename from django_jenkins/standalone/taskviews.py rename to django_jenkins/standalone/taskviews/__init__.py index 255efd1a..8ad41902 100644 --- a/django_jenkins/standalone/taskviews.py +++ b/django_jenkins/standalone/taskviews/__init__.py @@ -4,42 +4,62 @@ """ import math from pygooglechart import Axis, SimpleLineChart +from django.views.generic import View -class BaseView(object): - """ - Base class for standalone.ci view - """ - def get_build_data(self, tasks, runner): +class BaseTaskDataExtract(object): + def extract_build_data(self, tasks): """ Extrats current build ci data """ return {} - def collect_build_data(self, build_data): + def extract_details_data(self, tasks): """ - Collect build data for view + Extract details view data """ + return None + + + +class BaseTaskDataView(object): + """ + Renters html part of ci index page + """ + def add_build_data(self, build_data_id, build_data): + pass + + def render_part(self, request): + pass + + +class TaskDetailView(View): + """ + Renders build task detail page + """ + def view(request, build_id, build_data): + pass - def build_chart(self, start_build_id, end_build_id): - """ - Returns pygooglechart instance with build data - """ -class TestView(BaseView): +class TestView(object): # TODO def __init__(self): self.successes = [] self.failures = [] self.errors = [] - def get_build_data(self, tasks, runner): - test_result = self.test_runner.result - + def get_build_data(self, tasks): result = {} - result['tests-successes'] = len(test_result.successes) - result['tests-failures'] = len(test_result.failures) - result['tests-errors'] = len(test_result.errors) + result['tests-successes'] = 0 + result['tests-failures'] = 0 + result['tests-errors'] = 0 + + for task in tasks: + test_result = task.test_runner.result + result['tests-successes'] += len(test_result.successes) + result['tests-failures'] += len(test_result.failures) + result['tests-errors'] += len(test_result.errors) + return result def collect_build_data(self, build_data): diff --git a/django_jenkins/standalone/taskviews/tests_view.py b/django_jenkins/standalone/taskviews/tests_view.py new file mode 100644 index 00000000..09e434c2 --- /dev/null +++ b/django_jenkins/standalone/taskviews/tests_view.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8; mode: django -*- +""" +View for tasks with tests runner +""" +from django_jenkins.standalone import taskviews + + +class TaskDataExtract(taskviews.BaseTaskDataExtract): + def extract_build_data(self, tasks): + """ + Extrats current build ci data + """ + return {} + + def extract_details_data(self, tasks): + """ + Extract details view data + """ + return None + + + +class TaskDataView(taskviews.BaseTaskDataView): + """ + Renters html part of ci index page + """ + def add_build_data(self, build_data_id, build_data): + pass + + def render_part(self, request): + pass + + +class TaskDetailView(taskviews.BaseTaskDetailView): + """ + Renders build task detail page + """ + def view(request, build_id, build_data): + pass + From 7d15cdffbee9008f3785c5453c515170706482a0 Mon Sep 17 00:00:00 2001 From: Mikhail Podgurskiy Date: Wed, 9 May 2012 14:32:02 +0700 Subject: [PATCH 5/5] Starting tests view --- .../standalone/taskviews/__init__.py | 65 +--------------- .../standalone/taskviews/tests_view.py | 76 +++++++++++++++++-- django_jenkins/tasks/dir_tests.py | 2 + django_jenkins/tasks/django_tests.py | 2 +- django_jenkins/tasks/lettuce_tests.py | 2 + .../templates/django_jenkins/part.html | 4 + .../templates/django_jenkins/tests_part.html | 1 + 7 files changed, 81 insertions(+), 71 deletions(-) create mode 100644 django_jenkins/templates/django_jenkins/part.html create mode 100644 django_jenkins/templates/django_jenkins/tests_part.html diff --git a/django_jenkins/standalone/taskviews/__init__.py b/django_jenkins/standalone/taskviews/__init__.py index 8ad41902..ef85f2b7 100644 --- a/django_jenkins/standalone/taskviews/__init__.py +++ b/django_jenkins/standalone/taskviews/__init__.py @@ -2,8 +2,6 @@ """ Store and view task results data in standalone ci """ -import math -from pygooglechart import Axis, SimpleLineChart from django.views.generic import View @@ -26,7 +24,7 @@ class BaseTaskDataView(object): """ Renters html part of ci index page """ - def add_build_data(self, build_data_id, build_data): + def add_build_data(self, build_id, build_data): pass def render_part(self, request): @@ -39,64 +37,3 @@ class TaskDetailView(View): """ def view(request, build_id, build_data): pass - - - -class TestView(object): # TODO - def __init__(self): - self.successes = [] - self.failures = [] - self.errors = [] - - def get_build_data(self, tasks): - result = {} - result['tests-successes'] = 0 - result['tests-failures'] = 0 - result['tests-errors'] = 0 - - for task in tasks: - test_result = task.test_runner.result - result['tests-successes'] += len(test_result.successes) - result['tests-failures'] += len(test_result.failures) - result['tests-errors'] += len(test_result.errors) - - return result - - def collect_build_data(self, build_data): - self.successes.append(build_data['tests-successes']) - self.failures.append(build_data['tests-failures']) - self.errors.append(build_data['tests-errors']) - - def build_chart(self, start_build_id, end_build_id): - tests_chart = SimpleLineChart(400, 250) - all_fails = [f+e for f,e in zip(self.failures, self.errors)] - all_results = [s+af for s,af in zip(self.successes, all_fails)] - - # axis - max_tests = max(all_results) - step = round(max_tests/10, 1-len(str(max_tests/10))) - total_steps = math.ceil(1.0 * max_tests/step) - tests_chart.set_axis_labels(Axis.LEFT, xrange(0, step*(total_steps+1), step)) - tests_chart.set_axis_labels(Axis.BOTTOM, xrange(start_build_id, end_build_id+1)) - tests_chart.set_grid(0, step/2, 5, 5) - - # First value - allowed maximum - tests_chart.add_data([step*total_steps] * 2) - # All tests, failures and errors, errors - tests_chart.add_data(all_results) - tests_chart.add_data(all_fails) - tests_chart.add_data(self.errors) - # Last value is the lowest in the Y axis. - tests_chart.add_data([0] * 2) - - # Fill colors - tests_chart.set_colours(['FFFFFF', '00FF00', 'FF0000', '0000FF']) - tests_chart.add_fill_range('00FF00', 1, 2) - tests_chart.add_fill_range('FF0000', 2, 3) - tests_chart.add_fill_range('0000FF', 3, 4) - - return tests_chart - - -class ViolationsView(BaseView): - pass diff --git a/django_jenkins/standalone/taskviews/tests_view.py b/django_jenkins/standalone/taskviews/tests_view.py index 09e434c2..16f2af03 100644 --- a/django_jenkins/standalone/taskviews/tests_view.py +++ b/django_jenkins/standalone/taskviews/tests_view.py @@ -2,33 +2,97 @@ """ View for tasks with tests runner """ +import math +import sys +from pygooglechart import Axis, SimpleLineChart +from django.template.loader import render_to_string from django_jenkins.standalone import taskviews class TaskDataExtract(taskviews.BaseTaskDataExtract): + """ + Extract data from test runner tasks. + + Each task should have test_runner and output_file attributes + """ def extract_build_data(self, tasks): """ Extrats current build ci data """ - return {} + result = {} + result['tests-successes'] = 0 + result['tests-failures'] = 0 + result['tests-errors'] = 0 + + for task in tasks: + test_result = task.test_runner.result + result['tests-successes'] += len(test_result.successes) + result['tests-failures'] += len(test_result.failures) + result['tests-errors'] += len(test_result.errors) + + return result def extract_details_data(self, tasks): """ Extract details view data """ - return None + output = [] + for task in tasks: + output.append(task.output_file) + return { 'output' : output } - class TaskDataView(taskviews.BaseTaskDataView): """ Renters html part of ci index page """ - def add_build_data(self, build_data_id, build_data): - pass + def __init__(self): + self.min_build_id, self.max_build_id = sys.max_int, 0 + self.successes = [] + self.failures = [] + self.errors = [] + + def add_build_data(self, build_id, build_data): + self.min_build_id = min(build_id, self.min_build_id) + self.max_build_id = max(build_id, self.max_build_id) + self.successes.append(build_data['tests-successes']) + self.failures.append(build_data['tests-failures']) + self.errors.append(build_data['tests-errors']) + + def build_chart(self): + tests_chart = SimpleLineChart(400, 250) + all_fails = [f+e for f,e in zip(self.failures, self.errors)] + all_results = [s+af for s,af in zip(self.successes, all_fails)] + + # axis + max_tests = max(all_results) + step = round(max_tests/10, 1-len(str(max_tests/10))) + total_steps = math.ceil(1.0 * max_tests/step) + tests_chart.set_axis_labels(Axis.LEFT, xrange(0, step*(total_steps+1), step)) + tests_chart.set_axis_labels(Axis.BOTTOM, xrange(self.min_build_id, self.max_build_id+1)) + tests_chart.set_grid(0, step/2, 5, 5) + + # First value - allowed maximum + tests_chart.add_data([step*total_steps] * 2) + # All tests, failures and errors, errors + tests_chart.add_data(all_results) + tests_chart.add_data(all_fails) + tests_chart.add_data(self.errors) + # Last value is the lowest in the Y axis. + tests_chart.add_data([0] * 2) + + # Fill colors + tests_chart.set_colours(['FFFFFF', '00FF00', 'FF0000', '0000FF']) + tests_chart.add_fill_range('00FF00', 1, 2) + tests_chart.add_fill_range('FF0000', 2, 3) + tests_chart.add_fill_range('0000FF', 3, 4) + + return tests_chart def render_part(self, request): - pass + return render_to_string('django_jenkins/tests_part.html', + { 'chart' : self.build_chart(), + 'title' : 'Tests result' }) class TaskDetailView(taskviews.BaseTaskDetailView): diff --git a/django_jenkins/tasks/dir_tests.py b/django_jenkins/tasks/dir_tests.py index 427cddd6..50304b46 100644 --- a/django_jenkins/tasks/dir_tests.py +++ b/django_jenkins/tasks/dir_tests.py @@ -20,6 +20,8 @@ def build_suite(app): class Task(BaseTask): + view = 'django_jenkins.standalone.taskviews.tests_view' + def __init__(self, test_labels, options): super(Task, self).__init__(test_labels, options) if not self.test_labels: diff --git a/django_jenkins/tasks/django_tests.py b/django_jenkins/tasks/django_tests.py index 3d935637..4bf2ee97 100644 --- a/django_jenkins/tasks/django_tests.py +++ b/django_jenkins/tasks/django_tests.py @@ -11,7 +11,7 @@ class Task(BaseTask): - view = 'django_jenkins.standalone.taskviews.TestView' + view = 'django_jenkins.standalone.taskviews.tests_view' def __init__(self, test_labels, options): super(Task, self).__init__(test_labels, options) diff --git a/django_jenkins/tasks/lettuce_tests.py b/django_jenkins/tasks/lettuce_tests.py index 8ebf35e8..29985568 100644 --- a/django_jenkins/tasks/lettuce_tests.py +++ b/django_jenkins/tasks/lettuce_tests.py @@ -10,6 +10,8 @@ class Task(BaseTask): + view = 'django_jenkins.standalone.taskviews.tests_view' + option_list = [ make_option("--lettuce-server", dest="lettuce-server", diff --git a/django_jenkins/templates/django_jenkins/part.html b/django_jenkins/templates/django_jenkins/part.html new file mode 100644 index 00000000..e7c48f6c --- /dev/null +++ b/django_jenkins/templates/django_jenkins/part.html @@ -0,0 +1,4 @@ +{% block content %} +

{{ title }} + +{% endblock %}' diff --git a/django_jenkins/templates/django_jenkins/tests_part.html b/django_jenkins/templates/django_jenkins/tests_part.html new file mode 100644 index 00000000..5d3aa7bd --- /dev/null +++ b/django_jenkins/templates/django_jenkins/tests_part.html @@ -0,0 +1 @@ +{% extends 'django_jenkins/part.html' %}