Example #1
0
    def test_multi_phase(self):
        project = self.create_project()

        plan = self.create_plan()

        option = ItemOption(
            item_id=plan.id,
            name='build.expect-tests',
            value='1',
        )
        db.session.add(option)
        db.session.commit()

        build = self.create_build(project=project)
        job = self.create_job(build=build)
        jobplan = self.create_job_plan(job, plan)
        jobphase = self.create_jobphase(
            job=job,
            label='setup',
            # it's important that the date_created here is actually newer
            # than the second phase
            date_created=datetime(2013, 9, 19, 22, 17, 24),
            date_started=datetime(2013, 9, 19, 22, 15, 24),
        )
        jobphase2 = self.create_jobphase(
            job=job,
            label='test',
            date_created=datetime(2013, 9, 19, 22, 16, 24),
            date_started=datetime(2013, 9, 19, 22, 16, 24),
        )
        jobstep = self.create_jobstep(jobphase)
        jobstep2 = self.create_jobstep(jobphase2)

        assert not is_missing_tests(jobstep, jobplan)
        assert is_missing_tests(jobstep2, jobplan)

        testcase = TestCase(
            project_id=project.id,
            job_id=job.id,
            step_id=jobstep.id,
            name='test',
        )
        db.session.add(testcase)
        db.session.commit()

        assert not is_missing_tests(jobstep, jobplan)
        assert is_missing_tests(jobstep2, jobplan)

        testcase = TestCase(
            project_id=project.id,
            job_id=job.id,
            step_id=jobstep2.id,
            name='test2',
        )
        db.session.add(testcase)
        db.session.commit()

        assert not is_missing_tests(jobstep2, jobplan)
Example #2
0
    def test_single_phase(self):
        project = self.create_project()
        plan = self.create_plan()

        option = ItemOption(
            item_id=plan.id,
            name='build.expect-tests',
            value='0',
        )
        db.session.add(option)
        db.session.commit()

        build = self.create_build(project=project)
        job = self.create_job(build=build)
        jobplan = self.create_job_plan(job, plan)
        jobphase = self.create_jobphase(
            job=job,
            date_started=datetime(2013, 9, 19, 22, 15, 24),
        )
        jobstep = self.create_jobstep(jobphase)
        jobstep2 = self.create_jobstep(jobphase)

        assert not is_missing_tests(jobstep, jobplan)

        jobplan.data['snapshot']['options'][option.name] = '1'
        db.session.add(jobplan)
        db.session.commit()

        assert is_missing_tests(jobstep, jobplan)

        testcase = TestCase(
            project_id=project.id,
            job_id=job.id,
            step_id=jobstep2.id,
            name='test',
        )
        db.session.add(testcase)
        db.session.commit()

        assert is_missing_tests(jobstep, jobplan)

        testcase = TestCase(
            project_id=project.id,
            job_id=job.id,
            step_id=jobstep.id,
            name='test2',
        )
        db.session.add(testcase)
        db.session.commit()

        assert not is_missing_tests(jobstep, jobplan)
Example #3
0
    def save(self, test_list):
        step = self.step
        job = step.job
        project = job.project
        # agg_groups_by_id = {}

        # create all test cases
        for test in test_list:
            testcase = TestCase(job=job,
                                step=step,
                                name_sha=test.name_sha,
                                project=project,
                                suite=test.suite,
                                name=test.name,
                                duration=test.duration,
                                message=test.message,
                                result=test.result,
                                date_created=test.date_created,
                                reruns=test.reruns)
            db.session.add(testcase)

        db.session.commit()

        self._record_test_counts(test_list)
        self._record_test_duration(test_list)
        self._record_test_rerun_counts(test_list)
Example #4
0
    def save(self, test_list):
        if not test_list:
            return

        step = self.step
        job = step.job
        project = job.project
        # agg_groups_by_id = {}

        # create all test cases
        for test in test_list:
            testcase = TestCase(job=job,
                                step=step,
                                name_sha=test.name_sha,
                                project=project,
                                name=test.name,
                                duration=test.duration,
                                message=test.message,
                                result=test.result,
                                date_created=test.date_created,
                                reruns=test.reruns)
            db.session.add(testcase)

        db.session.commit()

        try:
            self._record_test_counts(test_list)
            self._record_test_failures(test_list)
            self._record_test_duration(test_list)
            self._record_test_rerun_counts(test_list)
        except Exception:
            logger.exception('Failed to record aggregate test statistics')
Example #5
0
    def _normalize_test_segments(cls, test_name):
        sep = TestCase(name=test_name).sep
        segments = test_name.split(sep)

        # kill the file extension
        if sep is '/' and '.' in segments[-1]:
            segments[-1] = segments[-1].rsplit('.', 1)[0]

        return tuple(segments)
Example #6
0
    def test_simple(self):
        project = self.create_project()

        previous_build = self.create_build(project)
        previous_job = self.create_job(previous_build)

        changed_test = TestCase(
            job=previous_job,
            project=previous_job.project,
            name='unchanged test',
        )
        removed_test = TestCase(
            job=previous_job,
            project=previous_job.project,
            name='removed test',
        )
        db.session.add(removed_test)
        db.session.add(changed_test)

        current_build = self.create_build(project)
        current_job = self.create_job(current_build)
        added_test = TestCase(
            job=current_job,
            project=current_job.project,
            name='added test',
        )

        db.session.add(added_test)
        db.session.add(
            TestCase(
                job=current_job,
                project=current_job.project,
                name='unchanged test',
            ))
        db.session.commit()

        results = find_changed_tests(current_build, previous_build)

        assert results['total'] == 2

        assert ('-', removed_test) in results['changes']
        assert ('+', added_test) in results['changes']

        assert len(results['changes']) == 2
Example #7
0
    def create_test(self, job, **kwargs):
        kwargs.setdefault('name', uuid4().hex)

        case = TestCase(job=job,
                        project=job.project,
                        project_id=job.project_id,
                        job_id=job.id,
                        **kwargs)
        db.session.add(case)
        db.session.commit()

        return case
Example #8
0
    def get_test_stats(cls, project_slug):
        response = api_client.get(
            '/projects/{project}/'.format(project=project_slug))
        last_build = response['lastPassingBuild']

        if not last_build:
            # use a failing build if no build has passed yet
            last_build = response['lastBuild']

        if not last_build:
            return {}, 0

        # XXX(dcramer): ideally this would be abstracted via an API
        job_list = db.session.query(Job.id).filter(
            Job.build_id == last_build['id'], )

        test_durations = dict(
            db.session.query(TestCase.name, TestCase.duration).filter(
                TestCase.job_id.in_(job_list), ))
        test_names = []
        total_count, total_duration = 0, 0
        for test in test_durations:
            test_names.append(test)
            total_duration += test_durations[test]
            total_count += 1

        test_stats = {}
        if test_names:
            sep = TestCase(name=test_names[0]).sep
            tree = build_flat_tree(test_names, sep=sep)
            for group_name, group_tests in tree.iteritems():
                segments = cls._normalize_test_segments(group_name)
                test_stats[segments] = sum(test_durations[t]
                                           for t in group_tests)

        # the build report can contain different test suites so this isnt the
        # most accurate
        if total_duration > 0:
            avg_test_time = int(total_duration / total_count)
        else:
            avg_test_time = 0

        return test_stats, avg_test_time
    def get(self, project_id):
        project = Project.get(project_id)
        if not project:
            return '', 404

        args = self.parser.parse_args()

        latest_job = Job.query.join(
            Source, Source.id == Job.source_id,
        ).filter(
            Source.patch_id == None,  # NOQA
            Job.project_id == project.id,
            Job.result == Result.passed,
            Job.status == Status.finished,
        ).order_by(
            Job.date_created.desc(),
        ).limit(1).first()

        if not latest_job:
            return self.respond([])

        # use the most recent test
        test_list = db.session.query(
            TestCase.name, TestCase.duration
        ).filter(
            TestCase.project_id == project_id,
            TestCase.job_id == latest_job.id,
        )
        if args.parent:
            test_list = test_list.filter(
                TestCase.name.startswith(args.parent),
            )
        test_list = list(test_list)

        if test_list:
            sep = TestCase(name=test_list[0][0]).sep

            groups = build_tree(
                [t[0] for t in test_list],
                sep=sep,
                min_children=2,
                parent=args.parent,
            )

            results = []
            for group in groups:
                num_tests = 0
                total_duration = 0
                for name, duration in test_list:
                    if name == group or name.startswith(group + sep):
                        num_tests += 1
                        total_duration += duration

                if args.parent:
                    name = group[len(args.parent) + len(sep):]
                else:
                    name = group
                data = {
                    'name': name,
                    'path': group,
                    'totalDuration': total_duration,
                    'numTests': num_tests,
                }
                results.append(data)
            results.sort(key=lambda x: x['totalDuration'], reverse=True)

            trail = []
            context = []
            if args.parent:
                for chunk in args.parent.split(sep):
                    context.append(chunk)
                    trail.append({
                        'path': sep.join(context),
                        'name': chunk,
                    })
        else:
            results = []
            trail = []

        data = {
            'groups': results,
            'trail': trail,
        }

        return self.respond(data, serialize=False)
Example #10
0
 def name_sha(self):
     return TestCase.calculate_name_sha(self.name)
Example #11
0
    def test_finished(self, get_implementation, queue_delay):
        implementation = mock.Mock()
        get_implementation.return_value = implementation

        def mark_finished(step):
            step.status = Status.finished
            step.result = Result.failed

        implementation.update_step.side_effect = mark_finished

        project = self.create_project()
        build = self.create_build(project=project)
        job = self.create_job(build=build)

        plan = self.create_plan()
        self.create_step(plan, implementation='test', order=0)
        self.create_job_plan(job, plan)

        phase = self.create_jobphase(job)
        step = self.create_jobstep(phase)
        task = self.create_task(
            parent_id=job.id,
            task_id=step.id,
            task_name='sync_job_step',
            status=Status.finished,
        )

        db.session.add(
            TestCase(
                name='test',
                step_id=step.id,
                job_id=job.id,
                project_id=project.id,
                result=Result.failed,
            ))

        db.session.add(
            FileCoverage(
                job=job,
                step=step,
                project=job.project,
                filename='foo.py',
                data='CCCUUUCCCUUNNN',
                lines_covered=6,
                lines_uncovered=5,
                diff_lines_covered=3,
                diff_lines_uncovered=2,
            ))
        db.session.commit()

        sync_job_step(
            step_id=step.id.hex,
            task_id=step.id.hex,
            parent_task_id=job.id.hex,
        )

        get_implementation.assert_called_once_with()

        implementation.update_step.assert_called_once_with(step=step)

        db.session.expire(step)
        db.session.expire(task)

        step = JobStep.query.get(step.id)

        assert step.status == Status.finished

        task = Task.query.get(task.id)

        assert task.status == Status.finished

        assert len(queue_delay.mock_calls) == 0

        stat = ItemStat.query.filter(
            ItemStat.item_id == step.id,
            ItemStat.name == 'tests_missing',
        ).first()
        assert stat.value == 0

        stat = ItemStat.query.filter(
            ItemStat.item_id == step.id,
            ItemStat.name == 'lines_covered',
        ).first()
        assert stat.value == 6

        stat = ItemStat.query.filter(
            ItemStat.item_id == step.id,
            ItemStat.name == 'lines_uncovered',
        ).first()
        assert stat.value == 5

        stat = ItemStat.query.filter(
            ItemStat.item_id == step.id,
            ItemStat.name == 'diff_lines_covered',
        ).first()
        assert stat.value == 3

        stat = ItemStat.query.filter(
            ItemStat.item_id == step.id,
            ItemStat.name == 'diff_lines_uncovered',
        ).first()
        assert stat.value == 2

        assert FailureReason.query.filter(
            FailureReason.step_id == step.id,
            FailureReason.reason == 'test_failures',
        )
Example #12
0
 def name_sha(self):
     return TestCase.calculate_name_sha(self.name)
    def get(self, project_id):
        project = Project.get(project_id)
        if not project:
            return '', 404

        args = self.parser.parse_args()

        latest_build = Build.query.join(
            Source,
            Source.id == Build.source_id,
        ).filter(
            Source.patch_id == None,  # NOQA
            Build.project_id == project.id,
            Build.result == Result.passed,
            Build.status == Status.finished,
        ).order_by(Build.date_created.desc(), ).limit(1).first()

        if not latest_build:
            return self.respond({})

        job_list = db.session.query(Job.id).filter(
            Job.build_id == latest_build.id, )

        # use the most recent test
        test_list = db.session.query(TestCase.name, TestCase.duration).filter(
            TestCase.job_id.in_(job_list), )
        if args.parent:
            test_list = test_list.filter(TestCase.name.startswith(
                args.parent), )
        test_list = list(test_list)

        if test_list:
            sep = TestCase(name=test_list[0][0]).sep

            groups = build_tree(
                [t[0] for t in test_list],
                sep=sep,
                min_children=2,
                parent=args.parent,
            )

            results = []
            for group in groups:
                num_tests = 0
                total_duration = 0
                for name, duration in test_list:
                    if name == group or name.startswith(group + sep):
                        num_tests += 1
                        total_duration += duration

                if args.parent:
                    name = group[len(args.parent) + len(sep):]
                else:
                    name = group
                data = {
                    'name': name,
                    'path': group,
                    'totalDuration': total_duration,
                    'numTests': num_tests,
                }
                results.append(data)
            results.sort(key=lambda x: x['totalDuration'], reverse=True)

            trail = []
            context = []
            if args.parent:
                for chunk in args.parent.split(sep):
                    context.append(chunk)
                    trail.append({
                        'path': sep.join(context),
                        'name': chunk,
                    })
        else:
            results = []
            trail = []

        options = dict((o.name, o.value) for o in ProjectOption.query.filter(
            ProjectOption.project_id == project.id,
            ProjectOption.name == 'build.test-duration-warning',
        ))

        over_threshold_duration = options.get('build.test-duration-warning')
        if over_threshold_duration:
            over_threshold_count = TestCase.query.filter(
                TestCase.project_id == project_id,
                TestCase.job_id.in_(job_list),
                TestCase.duration >= over_threshold_duration,
            ).count()
        else:
            over_threshold_count = 0

        data = {
            'groups': results,
            'trail': trail,
            'overThreshold': {
                'count': over_threshold_count,
                'duration': over_threshold_duration,
            }
        }

        return self.respond(data, serialize=False)
Example #14
0
    def get(self, project_id):
        project = Project.get(project_id)
        if not project:
            return '', 404

        args = self.parser.parse_args()

        latest_job = Job.query.join(
            Source,
            Source.id == Job.source_id,
        ).filter(
            Source.patch_id == None,  # NOQA
            Job.project_id == project.id,
            Job.result == Result.passed,
            Job.status == Status.finished,
        ).order_by(Job.date_created.desc(), ).limit(1).first()

        if not latest_job:
            return self.respond([])

        # use the most recent test
        test_list = db.session.query(TestCase.name, TestCase.duration).filter(
            TestCase.project_id == project_id,
            TestCase.job_id == latest_job.id,
        )
        if args.parent:
            test_list = test_list.filter(TestCase.name.startswith(
                args.parent), )
        test_list = list(test_list)

        if test_list:
            sep = TestCase(name=test_list[0][0]).sep

            groups = build_tree(
                [t[0] for t in test_list],
                sep=sep,
                min_children=2,
                parent=args.parent,
            )

            results = []
            for group in groups:
                num_tests = 0
                total_duration = 0
                for name, duration in test_list:
                    if name == group or name.startswith(group + sep):
                        num_tests += 1
                        total_duration += duration

                if args.parent:
                    name = group[len(args.parent) + len(sep):]
                else:
                    name = group
                data = {
                    'name': name,
                    'path': group,
                    'totalDuration': total_duration,
                    'numTests': num_tests,
                }
                results.append(data)
            results.sort(key=lambda x: x['totalDuration'], reverse=True)

            trail = []
            context = []
            if args.parent:
                for chunk in args.parent.split(sep):
                    context.append(chunk)
                    trail.append({
                        'path': sep.join(context),
                        'name': chunk,
                    })
        else:
            results = []
            trail = []

        data = {
            'groups': results,
            'trail': trail,
        }

        return self.respond(data, serialize=False)
    def test_simple(self):
        fake_id = uuid4()

        project = self.create_project()
        build = self.create_build(project, result=Result.failed)
        job = self.create_job(build, status=Status.finished)

        test = TestCase(
            job=job,
            project=project,
            name='foo',
            name_sha='a' * 40,
        )
        db.session.add(test)

        path = '/api/0/builds/{0}/tests/'.format(fake_id.hex)

        resp = self.client.get(path)
        assert resp.status_code == 404

        # test each sort option just to ensure it doesnt straight up fail
        path = '/api/0/builds/{0}/tests/?sort=duration'.format(build.id.hex)

        resp = self.client.get(path)
        assert resp.status_code == 200
        data = self.unserialize(resp)
        assert len(data) == 1
        assert data[0]['id'] == test.id.hex

        path = '/api/0/builds/{0}/tests/?sort=name'.format(build.id.hex)

        resp = self.client.get(path)
        assert resp.status_code == 200
        data = self.unserialize(resp)
        assert len(data) == 1
        assert data[0]['id'] == test.id.hex

        path = '/api/0/builds/{0}/tests/?sort=retries'.format(build.id.hex)

        resp = self.client.get(path)
        assert resp.status_code == 200
        data = self.unserialize(resp)
        assert len(data) == 1
        assert data[0]['id'] == test.id.hex

        path = '/api/0/builds/{0}/tests/?per_page='.format(build.id.hex)

        resp = self.client.get(path)
        assert resp.status_code == 200
        data = self.unserialize(resp)
        assert len(data) == 1
        assert data[0]['id'] == test.id.hex

        path = '/api/0/builds/{0}/tests/?query=foo'.format(build.id.hex)

        resp = self.client.get(path)
        assert resp.status_code == 200
        data = self.unserialize(resp)
        assert len(data) == 1
        assert data[0]['id'] == test.id.hex

        path = '/api/0/builds/{0}/tests/?query=bar'.format(build.id.hex)

        resp = self.client.get(path)
        assert resp.status_code == 200
        data = self.unserialize(resp)
        assert len(data) == 0

        path = '/api/0/builds/{0}/tests/?result='.format(build.id.hex)

        resp = self.client.get(path)
        assert resp.status_code == 200
        data = self.unserialize(resp)
        assert len(data) == 1

        path = '/api/0/builds/{0}/tests/?result=passed'.format(build.id.hex)

        resp = self.client.get(path)
        assert resp.status_code == 200
        data = self.unserialize(resp)
        assert len(data) == 0
    def get(self, project_id):
        project = Project.get(project_id)
        if not project:
            return '', 404

        args = self.parser.parse_args()

        latest_build = Build.query.join(
            Source, Source.id == Build.source_id,
        ).filter(
            Source.patch_id == None,  # NOQA
            Build.project_id == project.id,
            Build.result == Result.passed,
            Build.status == Status.finished,
        ).order_by(
            Build.date_created.desc(),
        ).limit(1).first()

        if not latest_build:
            return self.respond({})

        job_list = db.session.query(Job.id).filter(
            Job.build_id == latest_build.id,
        )

        # use the most recent test
        test_list = db.session.query(
            TestCase.name, TestCase.duration
        ).filter(
            TestCase.job_id.in_(job_list),
        )
        if args.parent:
            test_list = test_list.filter(
                TestCase.name.startswith(args.parent),
            )
        test_list = list(test_list)

        if test_list:
            sep = TestCase(name=test_list[0][0]).sep

            groups = build_tree(
                [t[0] for t in test_list],
                sep=sep,
                min_children=2,
                parent=args.parent,
            )

            results = []
            for group in groups:
                num_tests = 0
                total_duration = 0
                for name, duration in test_list:
                    if name == group or name.startswith(group + sep):
                        num_tests += 1
                        total_duration += duration

                if args.parent:
                    name = group[len(args.parent) + len(sep):]
                else:
                    name = group
                data = {
                    'name': name,
                    'path': group,
                    'totalDuration': total_duration,
                    'numTests': num_tests,
                }
                results.append(data)
            results.sort(key=lambda x: x['totalDuration'], reverse=True)

            trail = []
            context = []
            if args.parent:
                for chunk in args.parent.split(sep):
                    context.append(chunk)
                    trail.append({
                        'path': sep.join(context),
                        'name': chunk,
                    })
        else:
            results = []
            trail = []

        options = dict(
            (o.name, o.value) for o in ProjectOption.query.filter(
                ProjectOption.project_id == project.id,
                ProjectOption.name == 'build.test-duration-warning',
            )
        )

        over_threshold_duration = options.get('build.test-duration-warning')
        if over_threshold_duration:
            over_threshold_count = TestCase.query.filter(
                TestCase.project_id == project_id,
                TestCase.job_id.in_(job_list),
                TestCase.duration >= over_threshold_duration,
            ).count()
        else:
            over_threshold_count = 0

        data = {
            'groups': results,
            'trail': trail,
            'overThreshold': {
                'count': over_threshold_count,
                'duration': over_threshold_duration,
            }
        }

        return self.respond(data, serialize=False)