Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
5e51951
Add support for using timezone aware datetime objects with Github
Kami Feb 13, 2020
a3e1834
Merge branch 'master' into support_tz_aware_datetime_objects
Kami Feb 16, 2020
c247e27
Fix how default branch is determined - currectly it resulted in default
Kami Feb 17, 2020
393d0cd
Add tests for getcomparisonexes() function and verify it correctly
Kami Feb 17, 2020
2877ec4
Make maximum length values for the executable names which are used in
Kami Feb 17, 2020
36007a0
Move name truncating functionality in a utility function and add tests
Kami Feb 17, 2020
cb7ed5f
Merge branch 'master' of github.com:tobami/codespeed into support_tz_…
Kami Feb 18, 2020
4ea7d40
Fix lint issue.
Kami Feb 18, 2020
7a0091d
Merge branch 'support_tz_aware_datetime_objects' of github.com:kami/c…
Kami Feb 18, 2020
8268c52
Add section on provider specific settings to the readme.
Kami Feb 18, 2020
e89a614
Merge pull request #279 from Kami/support_tz_aware_datetime_objects
tobami Feb 18, 2020
aaab180
Merge pull request #282 from Kami/add_provider_specific_settings_docs
tobami Feb 18, 2020
2de039c
Merge pull request #281 from Kami/make_maxlen_configurable
tobami Feb 18, 2020
7484c27
Merge branch 'master' into fix_default_branch_handling
Kami Feb 19, 2020
650856d
Trim extra whitespace.
Kami Feb 19, 2020
2f6694a
Add new "display_on_comparison_page" field to the Branch model which
Kami Feb 24, 2020
adc0fba
Update help string.
Kami Feb 24, 2020
74ce67d
Add migration file.
Kami Feb 24, 2020
7450893
Merge pull request #280 from Kami/fix_default_branch_handling
tobami Feb 24, 2020
f972a25
Merge branch 'master' of github.com:tobami/codespeed into configurabl…
Kami Feb 25, 2020
fac243d
Add a test case for comparision view branch filtering.
Kami Feb 25, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,15 @@ COMP_EXECUTABLES = [
]
```

### VCS Provider Specific Settings

#### Github

* ``GITHUB_OAUTH_TOKEN`` - Github oAuth token to use for authenticating against
the Github API. If not provided, it will default to unauthenticated API requests
which have low rate limits so an exception may be thrown when retrieving info
from the Github API due to the rate limit being reached.

## Getting help

For help regarding the configuration of Codespeed, or to share any ideas or
Expand Down
2 changes: 1 addition & 1 deletion codespeed/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class ProjectAdmin(admin.ModelAdmin):

@admin.register(Branch)
class BranchAdmin(admin.ModelAdmin):
list_display = ('name', 'project')
list_display = ('name', 'project', 'display_on_comparison_page')
list_filter = ('project',)


Expand Down
14 changes: 8 additions & 6 deletions codespeed/commits/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
import json

import isodate
from django.conf import settings
from django.core.cache import cache
from django.conf import settings

from .exceptions import CommitLogError

Expand Down Expand Up @@ -103,11 +103,13 @@ def retrieve_revision(commit_id, username, project, revision=None):
if revision:
# Overwrite any existing data we might have for this revision since
# we never want our records to be out of sync with the actual VCS:

# We need to convert the timezone-aware date to a naive (i.e.
# timezone-less) date in UTC to avoid killing MySQL:
revision.date = date.astimezone(
isodate.tzinfo.Utc()).replace(tzinfo=None)
if not getattr(settings, 'USE_TZ_AWARE_DATES', False):
# We need to convert the timezone-aware date to a naive (i.e.
# timezone-less) date in UTC to avoid killing MySQL:
logger.debug('USE_TZ_AWARE_DATES setting is set to False, '
'converting datetime object to a naive one')
revision.date = date.astimezone(
isodate.tzinfo.Utc()).replace(tzinfo=None)
revision.author = commit_json['author']['name']
revision.message = commit_json['message']
revision.full_clean()
Expand Down
18 changes: 18 additions & 0 deletions codespeed/migrations/0004_branch_display_on_comparison_page.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 2.1.15 on 2020-02-24 11:26

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('codespeed', '0003_project_default_branch'),
]

operations = [
migrations.AddField(
model_name='branch',
name='display_on_comparison_page',
field=models.BooleanField(default=True, verbose_name='True to display this branch on the comparison page'),
),
]
3 changes: 3 additions & 0 deletions codespeed/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ class Branch(models.Model):
name = models.CharField(max_length=32)
project = models.ForeignKey(
Project, on_delete=models.CASCADE, related_name="branches")
display_on_comparison_page = models.BooleanField(
"True to display this branch on the comparison page",
default=True)

def __str__(self):
return self.project.name + ":" + self.name
Expand Down
10 changes: 10 additions & 0 deletions codespeed/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,22 @@
# ('myexe', '21df2423ra'),
# ('myexe', 'L'),]

TIMELINE_EXECUTABLE_NAME_MAX_LEN = 22 # Maximum length of the executable name used in the
# Changes and Timeline view. If the name is longer, the name
# will be truncated and "..." will be added at the end.

COMPARISON_EXECUTABLE_NAME_MAX_LEN = 20 # Maximum length of the executable name used in the
# Coomparison view. If the name is longer, the name

USE_MEDIAN_BANDS = True # True to enable median bands on Timeline view


ALLOW_ANONYMOUS_POST = True # Whether anonymous users can post results
REQUIRE_SECURE_AUTH = True # Whether auth needs to be over a secure channel

US_TZ_AWARE_DATES = False # True to use timezone aware datetime objects with Github provider.
# NOTE: Some database backends may not support tz aware dates.

GITHUB_OAUTH_TOKEN = None # Github oAuth token to use when using Github repo type. If not
# specified, it will utilize unauthenticated requests which have
# low rate limits.
159 changes: 159 additions & 0 deletions codespeed/tests/test_views_data.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
# -*- coding: utf-8 -*-
from django.test import TestCase
from django.test import override_settings

from codespeed.models import Project, Executable, Branch, Revision
from codespeed.views import getbaselineexecutables
from codespeed.views import getcomparisonexes
from codespeed.views_data import get_sanitized_executable_name_for_timeline_view
from codespeed.views_data import get_sanitized_executable_name_for_comparison_view


class TestGetBaselineExecutables(TestCase):
Expand Down Expand Up @@ -38,3 +42,158 @@ def test_get_baseline_executables(self):
Revision.objects.create(commitid='3', branch=self.branch)
result = getbaselineexecutables()
self.assertEqual(len(result), 3)


class TestGetComparisonExes(TestCase):
def setUp(self):
self.project = Project.objects.create(name='Test')
self.executable_1 = Executable.objects.create(
name='TestExecutable1', project=self.project)
self.executable_2 = Executable.objects.create(
name='TestExecutable2', project=self.project)
self.branch_master = Branch.objects.create(name='master',
project=self.project)
self.branch_custom = Branch.objects.create(name='custom',
project=self.project)

self.revision_1_master = Revision.objects.create(
branch=self.branch_master, commitid='1')
self.revision_1_custom = Revision.objects.create(
branch=self.branch_custom, commitid='1')

def test_get_comparisionexes_master_default_branch(self):
# Standard "master" default branch is used
self.project.default_branch = 'master'
self.project.save()

executables, exe_keys = getcomparisonexes()
self.assertEqual(len(executables), 1)
self.assertEqual(len(executables[self.project]), 4)
self.assertEqual(len(exe_keys), 4)

self.assertEqual(executables[self.project][0]['executable'],
self.executable_1)
self.assertEqual(executables[self.project][0]['revision'],
self.revision_1_master)
self.assertEqual(executables[self.project][0]['key'],
'1+L+master')
self.assertEqual(executables[self.project][0]['name'],
'TestExecutable1 latest')
self.assertEqual(executables[self.project][0]['revision'],
self.revision_1_master)

self.assertEqual(executables[self.project][1]['key'],
'2+L+master')
self.assertEqual(executables[self.project][1]['name'],
'TestExecutable2 latest')

self.assertEqual(executables[self.project][2]['key'],
'1+L+custom')
self.assertEqual(executables[self.project][2]['name'],
'TestExecutable1 latest in branch \'custom\'')

self.assertEqual(executables[self.project][3]['key'],
'2+L+custom')
self.assertEqual(executables[self.project][3]['name'],
'TestExecutable2 latest in branch \'custom\'')

self.assertEqual(exe_keys[0], '1+L+master')
self.assertEqual(exe_keys[1], '2+L+master')

def test_get_comparisionexes_custom_default_branch(self):
# Custom default branch is used
self.project.default_branch = 'custom'
self.project.save()

executables, exe_keys = getcomparisonexes()
self.assertEqual(len(executables), 1)
self.assertEqual(len(executables[self.project]), 4)
self.assertEqual(len(exe_keys), 4)

self.assertEqual(executables[self.project][0]['executable'],
self.executable_1)
self.assertEqual(executables[self.project][0]['revision'],
self.revision_1_master)
self.assertEqual(executables[self.project][0]['key'],
'1+L+master')
self.assertEqual(executables[self.project][0]['name'],
'TestExecutable1 latest in branch \'master\'')
self.assertEqual(executables[self.project][0]['revision'],
self.revision_1_master)

self.assertEqual(executables[self.project][1]['key'],
'2+L+master')
self.assertEqual(executables[self.project][1]['name'],
'TestExecutable2 latest in branch \'master\'')

self.assertEqual(executables[self.project][2]['key'],
'1+L+custom')
self.assertEqual(executables[self.project][2]['name'],
'TestExecutable1 latest')

self.assertEqual(executables[self.project][3]['key'],
'2+L+custom')
self.assertEqual(executables[self.project][3]['name'],
'TestExecutable2 latest')

self.assertEqual(exe_keys[0], '1+L+master')
self.assertEqual(exe_keys[1], '2+L+master')
self.assertEqual(exe_keys[2], '1+L+custom')
self.assertEqual(exe_keys[3], '2+L+custom')

def test_get_comparisionexes_branch_filtering(self):
# branch1 and branch3 have display_on_comparison_page flag set to False
# so they shouldn't be included in the result
branch1 = Branch.objects.create(name='branch1', project=self.project,
display_on_comparison_page=False)
branch2 = Branch.objects.create(name='branch2', project=self.project,
display_on_comparison_page=True)
branch3 = Branch.objects.create(name='branch3', project=self.project,
display_on_comparison_page=False)

Revision.objects.create(
branch=branch1, commitid='1')
Revision.objects.create(
branch=branch2, commitid='1')
Revision.objects.create(
branch=branch3, commitid='1')

executables, exe_keys = getcomparisonexes()
self.assertEqual(len(executables), 1)
self.assertEqual(len(executables[self.project]), 6)
self.assertEqual(len(exe_keys), 6)

expected_exe_keys = [
'1+L+master',
'2+L+master',
'1+L+custom',
'2+L+custom',
'1+L+branch2',
'2+L+branch2'
]
self.assertEqual(exe_keys, expected_exe_keys)

for index, exe_key in enumerate(expected_exe_keys):
self.assertEqual(executables[self.project][index]['key'], exe_key)


class UtilityFunctionsTestCase(TestCase):
@override_settings(TIMELINE_EXECUTABLE_NAME_MAX_LEN=22)
def test_get_sanitized_executable_name_for_timeline_view(self):
executable = Executable(name='a' * 22)
name = get_sanitized_executable_name_for_timeline_view(executable)
self.assertEqual(name, 'a' * 22)

executable = Executable(name='a' * 25)
name = get_sanitized_executable_name_for_timeline_view(executable)
self.assertEqual(name, 'a' * 22 + '...')

@override_settings(COMPARISON_EXECUTABLE_NAME_MAX_LEN=20)
def test_get_sanitized_executable_name_for_comparision_view(self):
executable = Executable(name='b' * 20)
name = get_sanitized_executable_name_for_comparison_view(executable)
self.assertEqual(name, 'b' * 20)

executable = Executable(name='b' * 25)
name = get_sanitized_executable_name_for_comparison_view(executable)
self.assertEqual(name, 'b' * 20 + '...')
2 changes: 1 addition & 1 deletion codespeed/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ def comparison(request):
else:
rev = Revision.objects.get(commitid=rev)
key += str(rev.id)
key += "+default"
key += "+%s" % (exe.project.default_branch)
if key in exekeys:
checkedexecutables.append(key)
else:
Expand Down
58 changes: 48 additions & 10 deletions codespeed/views_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,10 @@ def getbaselineexecutables():
}]
executables = Executable.objects.select_related('project')
revs = Revision.objects.exclude(tag="").select_related('branch__project')
maxlen = 22
for rev in revs:
# Add executables that correspond to each tagged revision.
for exe in [e for e in executables if e.project == rev.branch.project]:
exestring = str(exe)
if len(exestring) > maxlen:
exestring = str(exe)[0:maxlen] + "..."
exestring = get_sanitized_executable_name_for_timeline_view(exe)
name = exestring + " " + rev.tag
key = str(exe.id) + "+" + str(rev.id)
baseline.append({
Expand Down Expand Up @@ -116,15 +113,14 @@ def getcomparisonexes():
for proj in Project.objects.all():
executables = []
executablekeys = []
maxlen = 20
# add all tagged revs for any project
for exe in baselines:
if exe['key'] != "none" and exe['executable'].project == proj:
executablekeys.append(exe['key'])
executables.append(exe)

# add latest revs of the project
branches = Branch.objects.filter(project=proj)
branches = Branch.objects.filter(project=proj, display_on_comparison_page=True)
for branch in branches:
try:
rev = Revision.objects.filter(branch=branch).latest('date')
Expand All @@ -134,11 +130,9 @@ def getcomparisonexes():
# because we already added tagged revisions
if rev.tag == "":
for exe in Executable.objects.filter(project=proj):
exestring = str(exe)
if len(exestring) > maxlen:
exestring = str(exe)[0:maxlen] + "..."
exestring = get_sanitized_executable_name_for_comparison_view(exe)
name = exestring + " latest"
if branch.name != 'default':
if branch.name != proj.default_branch:
name += " in branch '" + branch.name + "'"
key = str(exe.id) + "+L+" + branch.name
executablekeys.append(key)
Expand Down Expand Up @@ -260,3 +254,47 @@ def get_stats_with_defaults(res):
if res.q3 is not None:
q3 = res.q3
return q1, q3, val_max, val_min


def get_sanitized_executable_name_for_timeline_view(executable):
"""
Return sanitized executable name which is used in the sidebar in the
Timeline and Changes view.

If the name is longer than settings.TIMELINE_EXECUTABLE_NAME_MAX_LEN,
the name will be truncated to that length and "..." appended to it.

:param executable: Executable object.
:type executable: :class:``codespeed.models.Executable``

:return: ``str``
"""
maxlen = getattr(settings, 'TIMELINE_EXECUTABLE_NAME_MAX_LEN', 20)

exestring = str(executable)
if len(exestring) > maxlen:
exestring = str(executable)[0:maxlen] + "..."

return exestring


def get_sanitized_executable_name_for_comparison_view(executable):
"""
Return sanitized executable name which is used in the sidebar in the
comparision view.

If the name is longer than settings.COMPARISON_EXECUTABLE_NAME_MAX_LEN,
the name will be truncated to that length and "..." appended to it.

:param executable: Executable object.
:type executable: :class:``codespeed.models.Executable``

:return: ``str``
"""
maxlen = getattr(settings, 'COMPARISON_EXECUTABLE_NAME_MAX_LEN', 22)

exestring = str(executable)
if len(exestring) > maxlen:
exestring = str(executable)[0:maxlen] + "..."

return exestring