Skip to content

Switching to nose-json module #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,7 @@ staticfiles/
# npm
npm-debug.log

# nose
nosetests.json
nosetests.xml

6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: test fasttest run lint pep8 eslint manage
.PHONY: test fasttest run lint pep8 eslint manage report_failed_tests

# Project settings
LEVEL ?= development
Expand Down Expand Up @@ -115,3 +115,7 @@ devserver: clean
# Production Server
server: clean pep8
LEVEL=$(LEVEL) PYTHONPATH=$(PROJECT) $(GUNICORN) -b $(SERVER_HOST):$(SERVER_PORT) -w $(GUNICORN_WORKERS) -n $(GUNICORN_NAME) -t 60 --graceful-timeout 60 $(gunicorn_args) $(GUNICORN_ARGS) $(PROJECT).wsgi:application

# Reporting of failed cases:
jira_check_tests:
ezh-jira-test-checker run_check $(COMMAND_ARGS)
13 changes: 11 additions & 2 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ machine:
DATABASE_URL: postgres://ubuntu:@127.0.0.1:5432/circle_test
DJANGO_SETTINGS_MODULE: django_and_angular.settings.development
LEVEL: development

# JIRA integration
REPORT_FAILED_TEST_BRANCHES: develop,master,test1
JIRA_ISSUE_TYPE: Bug
JIRA_PROJECT_KEY: EZHOME
JIRA_SERVER: https://ezhome-test.atlassian.net
JIRA_USERNAME: jira_bot
JIRA_PASSWORD: P@ssw0rd
JIRA_DEFAULT_ASSIGNEE: [email protected]
timezone:
America/Los_Angeles
node:
Expand All @@ -25,13 +34,13 @@ dependencies:

test:
override:
- TEST_ARGS='--with-xunit --with-json-extended' make lint test
- TEST_ARGS='--with-xunit' make lint test

post:
# - coveralls
- mkdir -p $CIRCLE_TEST_REPORTS/junit/
- "[ -r nosetests.xml ] && mv nosetests.xml $CIRCLE_TEST_REPORTS/junit/ || :"
- "[ -r nosetests.json ] && mv nosetests.json $CIRCLE_TEST_REPORTS/junit/ || :"
- "COMMAND_ARGS='--target-branch ${CIRCLE_BRANCH} --test-results ${CIRCLE_TEST_REPORTS}/junit/nosetests.xml --branches $(REPORT_FAILED_TEST_BRANCHES) --issue-type $(JIRA_ISSUE_TYPE) --project-key $(JIRA_PROJECT_KEY) --jira-server $(JIRA_SERVER) --jira-username $(JIRA_USERNAME) --jira-password $(JIRA_PASSWORD) --jira-default-assignee $(JIRA_DEFAULT_ASSIGNEE)' make jira_check_tests || :"

# # Override /etc/hosts
# hosts:
Expand Down
1 change: 0 additions & 1 deletion nosetests.xml

This file was deleted.

8 changes: 6 additions & 2 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
--extra-index-url=http://ezhome-bot:[email protected]/simple/

coverage==4.0
coveralls==1.0
django-nose==1.4.2
# ezhinfra==0.2.? <TBD>
# temporary solution for being able to check the PR
-e [email protected]:ezhome/ezh-infrastructure-tools.git@d910af7fdbe2c04e1c258e718e5026992c1ac29d#egg=ezhinfra
flake8==2.3.0
flake8-import-order==0.5.3
flake8-pep257==1.0.3
https://github.com/zheller/flake8-quotes/tarball/aef86c4f8388e790332757e5921047ad53160a75#egg=flake8-quotes
flake8-quotes==0.2.4
nose==1.3.7
nosetests-json-extended==0.1.0
pep257==0.6.0
pep8==1.6.2
pep8-naming==0.3.3
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ drf-nested-routers==0.9.0
gunicorn==19.1.1
psycopg2==2.6.1
raven==5.10.2
six==1.8.0
six==1.9.0
static3==0.5.1
wsgiref==0.1.2
3 changes: 1 addition & 2 deletions src/authentication/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,4 @@ def test_success(self):
pass

def test_failure(self):
# self.fail('Failed test')
pass
self.fail('Failed test')
Empty file.
Empty file.
226 changes: 226 additions & 0 deletions src/django_and_angular/management/commands/report_failed_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
import re
from optparse import make_option
from xml.etree import cElementTree as ElementTree

from django.core.management.base import BaseCommand
from git import Repo
from jira import JIRA
from jira.exceptions import JIRAError


FAILURE_ROW_RE = re.compile(r'\s*File\s"(.*)",\sline\s(.*),.*')


class Command(BaseCommand):

help = 'Creates JIRA issues for every failed case for specified branch'

option_list = BaseCommand.option_list + (
make_option(
'--branches',
action='store',
dest='branches',
help='Affected branches',
),
make_option(
'--target-branch',
action='store',
dest='target_branch',
help='Target branch',
),
make_option(
'--issue-type',
action='store',
dest='issue_type',
default='Bug',
help='Issue type',
),
make_option(
'--project-key',
action='store',
dest='project_key',
default='EZHOME',
help='Project key',
),
make_option(
'--jira-server',
action='store',
dest='jira_server',
help='JIRA server',
),
make_option(
'--jira-username',
action='store',
dest='jira_username',
help='Username for JIRA account',
),
make_option(
'--jira-password',
action='store',
dest='jira_password',
help='Password for JIRA account',
),
make_option(
'--test-results',
action='store',
dest='test_results',
default='nosetests.xml',
help='Location of test results',
),
make_option(
'--jira-default-assignee',
action='store',
dest='jira_default_assignee',
default='',
help='Default assignee for new user',
)
)

def handle(self, *args, **options):
self.issue_type = options['issue_type']
self.project_key = options['project_key']
self.jira_server = options['jira_server']
self.jira_username = options['jira_username']
self.jira_password = options['jira_password']
self.jira_default_assignee = options['jira_default_assignee']
test_results = options['test_results']

self.repo = Repo()
branches = []
try:
for branch in options['branches'].split(','):
branches.append(self.repo.heads[branch])
except IndexError:
return 'Cannot find branch "{0}"'.format(branch)
try:
self.target_branch = self.repo.heads[options['target_branch']]
except IndexError:
return 'Cannot find branch "{0}"'.format(options['target_branch'])
if self.target_branch != self.repo.head.ref:
return (
'Current branch "{0}" does not match '
'provided CircleCI branch "{1}"'
.format(self.repo.head.ref, self.target_branch)
)
elif self.target_branch not in branches:
return 'Skipping check for branch "{0}"'.format(self.repo.head.ref)

try:
root = ElementTree.parse(test_results).getroot()
if root.attrib['errors']:
results = []
for testcase in root:
if testcase:
result = self.handle_testcase(testcase)
if result is not None:
results.append(result)
if results:
return '\n'.join(results)
else:
return 'No errors in tests'
else:
return 'No errors in tests'
except IOError:
return 'File "{0}" does not exist'.format(test_results)

@staticmethod
def parse_test_path(path):
path, classname = path.rsplit('.', 1)
path = path.replace('.', '/')
return path, classname

def handle_testcase(self, testcase):
path, classname = self.parse_test_path(
testcase.attrib['classname']
)
for (file_path, line_number) in re.findall(
FAILURE_ROW_RE, testcase[0].text
):
if path in file_path:
# Finding the line of testcase definition
authors = {}
commit, line = self.repo.blame(
'-L/def {}/'.format(testcase.attrib['name']), file_path
)[0]
if commit.author not in authors:
authors['function'] = commit.author
# Finding the line of failure
commit, line = self.repo.blame(
'-L{0},{0}'.format(line_number), file_path
)[0]
if commit.author not in authors:
authors['failure'] = commit.author
return self.handle_jira(
path=path,
authors=authors,
classname=classname,
testcase=testcase,
)

def handle_jira(self, path, authors, classname, testcase):
try:
jira = JIRA(
server=self.jira_server,
basic_auth=(
self.jira_username,
self.jira_password,
)
)
summary = (
'Fail: {path}:{classname}.{testcase}, '
'branch: {branch}'.format(
path=path,
classname=classname,
testcase=testcase.attrib['name'],
branch=self.target_branch,
)
)
open_issues = jira.search_issues(
'summary ~ "{summary}" AND '
'resolution=unresolved'.format(
summary=summary
),
maxResults=1
)
if open_issues:
# Update priority
issue = open_issues[0]
new_priority = '1'
if int(issue.fields.priority.id) > 1:
new_priority = str(int(issue.fields.priority.id) - 1)
issue.update(priority={'id': new_priority})
return (
'Priority of issue "{issue}" '
'has been set to "{priority}"'.format(
issue=issue, priority=jira.priority(new_priority)
)
)
else:
# Create issue
assignees = []
assignees.extend(jira.search_users(
user=authors['function'].email,
maxResults=1
))
assignees.extend(jira.search_users(
user=authors['failure'].email,
maxResults=1
))
if self.jira_default_assignee:
assignees.extend(jira.search_users(
user=self.jira_default_assignee,
maxResults=1
))
issue_dict = dict(
project={'key': self.project_key},
summary=summary,
issuetype={'name': self.issue_type},
priority={'id': jira.priorities()[-1].id},
description='Description here',
)
if assignees:
issue_dict['assignee'] = {'name': assignees[0].name}
new_issue = jira.create_issue(fields=issue_dict)
return 'New issue "{0}" has been created'.format(new_issue)
except JIRAError as e:
return 'JIRA ERROR: {}'.format(e.text)
1 change: 1 addition & 0 deletions src/django_and_angular/settings/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
'compressor',
'authentication',
'posts',
'django_and_angular',
)

MIDDLEWARE_CLASSES = (
Expand Down