Skip to content
Merged
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,5 @@ codecov.exe
.coverage
# File genrated as part of XCode tests
coverage-report-test.json

*.coverage.txt
1 change: 1 addition & 0 deletions src/helpers/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ const args: ICLIArgument[] = [
name: 'feature',
type: 'string',
description: `Toggle functionalities. Separate multiple ones by comma: -X network,search
-X fixes Enable file fixes to ignore common lines from coverage (e.g. blank lines or empty brackets)
-X network Disable uploading the file network
-X search Disable searching for coverage files`,
},
Expand Down
70 changes: 70 additions & 0 deletions src/helpers/fixes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import fs from 'fs'
import readline from 'readline'

import { getAllFiles } from './files'
import { UploadLogger } from './logger'

export const FIXES_HEADER = '# path=fixes\n'

export async function generateFixes(projectRoot: string): Promise<string> {
// Fake out the UploaderArgs as they are not needed
const allFiles = await getAllFiles(projectRoot, projectRoot, {
flags: '',
slug: '',
upstream: '',
})

const allAdjustments: string[] = []
const EMPTYLINE = /^\s*$/mg
// { or }
const SYNTAXBRACKET = /^\s*[{}]\s*(\/\/.*)?$/m
// [ or ]
const SYNTAXLIST = /^\s*[[\]]\s*(\/\/.*)?$/m

for (const file of allFiles) {
let lineAdjustments: string[] = []

if (
file.match(/\.c$/) ||
file.match(/\.cpp$/) ||
file.match(/\.h$/) ||
file.match(/\.hpp$/) ||
file.match(/\.m$/) ||
file.match(/\.swift$/) ||
file.match(/\.vala$/)
) {
lineAdjustments = await getMatchedLines(file, [EMPTYLINE, SYNTAXBRACKET])
} else if (
file.match(/\.php$/)
) {
lineAdjustments = await getMatchedLines(file, [SYNTAXBRACKET, SYNTAXLIST])
}

if (lineAdjustments.length > 0) {
UploadLogger.verbose(`Matched file ${file} for adjustments: ${lineAdjustments.join(',')}`)
allAdjustments.push(`${file}:${lineAdjustments.join(',')}\n`)
}
}
Comment on lines +24 to +47
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A future PR should move these promises into Promise.all or Promise.allSettled.

return allAdjustments.join('')
}

async function getMatchedLines(file: string, matchers: RegExp[]): Promise<string[]> {
const fileStream = fs.createReadStream(file)
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity
});

const matchedLines: string[] = []
let lineNumber = 1

for await (const line of rl) {
for (const matcher of matchers) {
if (line.match(matcher)) {
matchedLines.push(lineNumber.toString())
}
}
lineNumber++
}
return matchedLines
}
11 changes: 11 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
removeFile,
} from './helpers/files'
import { generateCoveragePyFile } from './helpers/coveragepy'
import { generateFixes, FIXES_HEADER } from './helpers/fixes'
import { generateGcovCoverageFiles } from './helpers/gcov'
import { generateXcodeCoverageFiles } from './helpers/xcode'
import { argAsArray } from './helpers/util'
Expand Down Expand Up @@ -322,6 +323,16 @@ export async function main(
uploadFileChunks.push(Buffer.from(MARKER_ENV_END))
}

// Fixes
if (args.feature && args.feature.split(',').includes('fixes') === true) {
info('Generating file fixes...')
const fixes = await generateFixes(projectRoot)
uploadFileChunks.push(Buffer.from(FIXES_HEADER))
uploadFileChunks.push(Buffer.from(fixes))
uploadFileChunks.push(Buffer.from(MARKER_ENV_END))
info('Finished generating file fixes')
}

const uploadFile = Buffer.concat(uploadFileChunks)
const gzippedFile = zlib.gzipSync(uploadFile)

Expand Down
20 changes: 20 additions & 0 deletions test/fixtures/fixes/example.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php namespace Example;

class Example
{
public static function go()
{
$test_array = [
1,
2,
3,
]

if (false) {
return true;
}

return false;

}
}
66 changes: 66 additions & 0 deletions test/helpers/fixes.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import td from 'testdouble'
import childProcess from 'child_process'

import * as fixesHelpers from '../../src/helpers/fixes'

describe('Fixes Helpers', () => {
afterEach(() => {
td.reset()
})

it('provides no fixes if none are applicable', async () => {
const files = ['package.json']
td.replace(childProcess, 'spawnSync', () => {
return {
stdout: files.join('\n'),
status: 0,
error: undefined,
}
})
expect(
await fixesHelpers.generateFixes('.')
).toBe('')
})

it('provides proper fixes for c-like files', async () => {
const files = ['test/fixtures/gcov/main.c']
td.replace(childProcess, 'spawnSync', () => {
return {
stdout: files.join('\n'),
status: 0,
error: undefined,
}
})
expect(
await fixesHelpers.generateFixes('.')
).toBe('test/fixtures/gcov/main.c:2,4,11,12,13,14,16,20\n')
})

it('provides proper fixes for php-like files', async () => {
const files = ['test/fixtures/fixes/example.php']
td.replace(childProcess, 'spawnSync', () => {
return {
stdout: files.join('\n'),
status: 0,
error: undefined,
}
})
expect(
await fixesHelpers.generateFixes('.')
).toBe('test/fixtures/fixes/example.php:4,6,11,15,19,20\n')
})

it('provides multiple fixes for files', async () => {
const files = ['test/fixtures/fixes/example.php', 'test/fixtures/gcov/main.c']
td.replace(childProcess, 'spawnSync', () => {
return {
stdout: files.join('\n'),
status: 0,
error: undefined,
}
})
expect(
await fixesHelpers.generateFixes('.')
).toBe('test/fixtures/fixes/example.php:4,6,11,15,19,20\ntest/fixtures/gcov/main.c:2,4,11,12,13,14,16,20\n')
})
})
16 changes: 16 additions & 0 deletions test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -509,4 +509,20 @@ describe('Uploader Core', () => {
expect.stringMatching(/<<<<<< network/),
)
})

it('Can create fixes', async () => {
await app.main({
name: 'customname',
token: 'abcdefg',
url: 'https://codecov.io',
dryRun: 'true',
feature: 'fixes',
flags: '',
slug: '',
upstream: ''
})
expect(console.log).toHaveBeenCalledWith(
expect.stringMatching(/# path=fixes\ntest\/fixtures\/fixes\/example.php:4,6,11,15,19,20\ntest\/fixtures\/gcov\/main.c:2,4,11,12,13,14,16,20/)
)
})
})