Skip to content

Commit

Permalink
feat: Output test result in CircleCI
Browse files Browse the repository at this point in the history
  • Loading branch information
Kesin11 committed May 31, 2020
1 parent 82481e7 commit 61cccf4
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 6 deletions.
62 changes: 58 additions & 4 deletions src/analyzer/circleci_analyzer.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { sumBy, min, max, sortBy, first, last } from "lodash"
import { Status, diffSec, Analyzer, secRound } from "./analyzer"
import { WorkflowRun, SingleBuildResponse, CircleciStatus } from "../client/circleci_client"
import { Status, diffSec, Analyzer, secRound, TestReport } from "./analyzer"
import { WorkflowRun, SingleBuildResponse, CircleciStatus, TestResponse } from "../client/circleci_client"
import { RepositoryTagMap } from "../client/github_repository_client"
import { TestSuites, TestSuite, TestCase } from "junit2json"

type WorkflowReport = {
// workflow
Expand Down Expand Up @@ -57,6 +58,7 @@ export class CircleciAnalyzer implements Analyzer {
const lastJob = last(sortedJobs)!
const repository = `${firstJob.username}/${firstJob.reponame}`
const workflowName = workflowRun.workflow_name
const workflowId = `${repository}-${workflowName}`
const workflowBuildNumber = lastJob.build_num
const workflowRunId = `${repository}-${workflowName}-${workflowBuildNumber}`

Expand Down Expand Up @@ -99,7 +101,7 @@ export class CircleciAnalyzer implements Analyzer {
// workflow
return {
service: 'circleci',
workflowId: `${repository}-${workflowName}` ,
workflowId: workflowId,
buildNumber: workflowBuildNumber,
workflowRunId,
workflowName,
Expand Down Expand Up @@ -136,4 +138,56 @@ export class CircleciAnalyzer implements Analyzer {
return 'OTHER';
}
}
}

async createTestReports( workflowRun: WorkflowRun, jobs: SingleBuildResponse[], tests: TestResponse[]): Promise<TestReport[]> {
const sortedJobs = sortBy(jobs, 'build_num')
const firstJob = first(sortedJobs)!
const lastJob = last(sortedJobs)!
const repository = `${firstJob.username}/${firstJob.reponame}`
const workflowName = workflowRun.workflow_name
const workflowId = `${repository}-${workflowName}`
const workflowBuildNumber = lastJob.build_num
const workflowRunId = `${repository}-${workflowName}-${workflowBuildNumber}`

const testSuiteList: TestSuite[] = []
// Filter empty test witch comes from build that has not stored test results.
tests = tests.filter((test) => test.tests.length > 0)
if (tests.length === 0) return []

for (const test of tests) {
const testCases: TestCase[] = test.tests.map((test) => {
return {
classname: test.classname,
name: test.name,
time: test.run_time,
failure: (test.result === 'success') ? undefined : [{ inner: test.message }],
}
})
const testSuite: TestSuite = {
name: jobs.find((job) => job.build_num === test.run_id)?.workflows.job_name ?? '',
time: secRound(sumBy(testCases, 'time')),
tests: testCases.length,
failures: testCases.filter((testcase) => testcase.failure !== undefined).length,
timestamp: firstJob.start_time,
testcase: testCases,
}
testSuiteList.push(testSuite)
}

const testSuites: TestSuites = {
name: workflowName,
time: secRound(sumBy(testSuiteList, 'time')),
tests: sumBy(testSuiteList, 'tests'),
failures: sumBy(testSuiteList, 'failures'),
testsuite: testSuiteList,
}

return [{
workflowId,
workflowRunId,
buildNumber: workflowBuildNumber,
workflowName,
testSuites,
}]
}
}
25 changes: 24 additions & 1 deletion src/client/circleci_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ type Steps = {
run_time_millis: number,
}[]
}

export type SingleBuildResponse = RecentBuildResponse & {
steps: Steps[]
}
Expand All @@ -83,6 +82,22 @@ export type WorkflowRun = {
last_build_num: number
}

export type TestResponse = {
exceptions?: unknown[]
tests: {
classname: string
file?: string
name: string
result: "success" | "failure"
run_time: number
message?: string
source: string
source_type: string
}[]
run_id: number // Add for join workflow
}


export class CircleciClient {
private axios: AxiosInstance
constructor(token: string, baseUrl?: string) {
Expand Down Expand Up @@ -191,4 +206,12 @@ export class CircleciClient {
workflow_name: 'workflow'
}
}

async fetchTests(owner: string, repo: string, vcsType: string, runId: number) {
const res = await this.axios.get( `project/${vcsType}/${owner}/${repo}/${runId}/tests`)
return {
...res.data,
run_id: runId
} as TestResponse
}
}
17 changes: 16 additions & 1 deletion src/runner/circleci_runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { YamlConfig } from "../config/config"
import { CircleciClient } from "../client/circleci_client"
import { CircleciAnalyzer } from "../analyzer/circleci_analyzer"
import { CircleciConfig, parseConfig } from "../config/circleci_config"
import { WorkflowReport } from "../analyzer/analyzer"
import { WorkflowReport, TestReport } from "../analyzer/analyzer"
import { CompositExporter } from "../exporter/exporter"
import { LastRunStore } from "../last_run_store"
import { GithubRepositoryClient } from "../client/github_repository_client"
Expand Down Expand Up @@ -40,9 +40,11 @@ export class CircleciRunner implements Runner {
this.store = await LastRunStore.init(this.service, this.configDir, this.config.lastRunStore)

let reports: WorkflowReport[] = []
let testReports: TestReport[] = []
for (const repo of this.config.repos) {
console.info(`Fetching ${this.service} - ${repo.fullname} ...`)
const repoReports: WorkflowReport[] = []
let repoTestReports: TestReport[] = []

try {
const lastRunId = this.store.getLastRun(repo.fullname)
Expand All @@ -58,13 +60,24 @@ export class CircleciRunner implements Runner {
buildNum
)
}))
const tests = await Promise.all(workflowRun.build_nums.map((buildNum) => {
return this.client.fetchTests(
workflowRun.username,
workflowRun.reponame,
workflowRun.vcs_type,
buildNum
)
}))
const report = this.analyzer.createWorkflowReport(workflowRun, jobs, tagMap)
const testReports = await this.analyzer.createTestReports(workflowRun, jobs, tests)

repoReports.push(report)
repoTestReports = repoTestReports.concat(testReports)
}

this.setRepoLastRun(repo.fullname, repoReports)
reports = reports.concat(repoReports)
testReports = testReports.concat(repoTestReports)
}
catch (error) {
console.error(`Some error raised in '${repo.fullname}', so it skipped.`)
Expand All @@ -76,6 +89,8 @@ export class CircleciRunner implements Runner {
console.info(`Exporting ${this.service} workflow reports ...`)
const exporter = new CompositExporter(this.service, this.configDir, this.config.exporter)
await exporter.exportReports(reports)
await exporter.exportTestReports(testReports)


this.store.save()
console.info(`Success: done execute '${this.service}'`)
Expand Down

0 comments on commit 61cccf4

Please sign in to comment.