-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Generate reports on private API usage
This uses the data gathered in 2dcf4ed to generate some CSV reports about private API usage in the test suite. We’ll use these reports as a starting point for deciding how to remove this private API usage when reusing the test suite as a unified test suite for all our client libraries. Resolves ECO-4834.
- Loading branch information
1 parent
2dcf4ed
commit f770124
Showing
15 changed files
with
727 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
export type TestPrivateApiContextDto = { | ||
type: 'test'; | ||
title: string; | ||
/** | ||
* null means that either the test isn’t parameterised or that this usage is unique to the specific parameter | ||
*/ | ||
parameterisedTestTitle: string | null; | ||
helperStack: string[]; | ||
file: string; | ||
suite: string[]; | ||
}; | ||
|
||
export type HookPrivateApiContextDto = { | ||
type: 'hook'; | ||
title: string; | ||
helperStack: string[]; | ||
file: string; | ||
suite: string[]; | ||
}; | ||
|
||
export type RootHookPrivateApiContextDto = { | ||
type: 'hook'; | ||
title: string; | ||
helperStack: string[]; | ||
file: null; | ||
suite: null; | ||
}; | ||
|
||
export type TestDefinitionPrivateApiContextDto = { | ||
type: 'definition'; | ||
label: string; | ||
helperStack: string[]; | ||
file: string; | ||
suite: string[]; | ||
}; | ||
|
||
export type PrivateApiContextDto = | ||
| TestPrivateApiContextDto | ||
| HookPrivateApiContextDto | ||
| RootHookPrivateApiContextDto | ||
| TestDefinitionPrivateApiContextDto; | ||
|
||
export type PrivateApiUsageDto = { | ||
context: PrivateApiContextDto; | ||
privateAPIIdentifier: string; | ||
}; | ||
|
||
export type TestStartRecord = { | ||
context: TestPrivateApiContextDto; | ||
privateAPIIdentifier: null; | ||
}; | ||
|
||
export type Record = PrivateApiUsageDto | TestStartRecord; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { PrivateApiUsageDto } from './dto'; | ||
|
||
type ExclusionRule = { | ||
privateAPIIdentifier: string; | ||
// i.e. only ignore when called from within this helper | ||
helper?: string; | ||
}; | ||
|
||
/** | ||
* This exclusions mechanism is not currently being used on `main`, but I will use it on a separate unified test suite branch in order to exclude some private API usage that can currently be disregarded in the context of the unified test suite. | ||
*/ | ||
export function applyingExclusions(usageDtos: PrivateApiUsageDto[]) { | ||
const exclusionRules: ExclusionRule[] = []; | ||
|
||
return usageDtos.filter( | ||
(usageDto) => | ||
!exclusionRules.some( | ||
(exclusionRule) => | ||
exclusionRule.privateAPIIdentifier === usageDto.privateAPIIdentifier && | ||
(!('helper' in exclusionRule) || usageDto.context.helperStack.includes(exclusionRule.helper!)), | ||
), | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import { PrivateApiUsageDto } from './dto'; | ||
|
||
export type Group<Key, Value> = { | ||
key: Key; | ||
values: Value[]; | ||
}; | ||
|
||
export function grouped<Key, Value>( | ||
values: Value[], | ||
keyForValue: (value: Value) => Key, | ||
areKeysEqual: (key1: Key, key2: Key) => boolean, | ||
) { | ||
const result: Group<Key, Value>[] = []; | ||
|
||
for (const value of values) { | ||
const key = keyForValue(value); | ||
|
||
let existingGroup = result.find((group) => areKeysEqual(group.key, key)); | ||
|
||
if (existingGroup === undefined) { | ||
existingGroup = { key, values: [] }; | ||
result.push(existingGroup); | ||
} | ||
|
||
existingGroup.values.push(value); | ||
} | ||
|
||
return result; | ||
} | ||
|
||
/** | ||
* Makes sure that each private API is only listed once in a given context. | ||
*/ | ||
function dedupeUsages<Key>(contextGroups: Group<Key, PrivateApiUsageDto>[]) { | ||
for (const contextGroup of contextGroups) { | ||
const newUsages: typeof contextGroup.values = []; | ||
|
||
for (const usage of contextGroup.values) { | ||
const existing = newUsages.find((otherUsage) => otherUsage.privateAPIIdentifier === usage.privateAPIIdentifier); | ||
if (existing === undefined) { | ||
newUsages.push(usage); | ||
} | ||
} | ||
|
||
contextGroup.values = newUsages; | ||
} | ||
} | ||
|
||
export function groupedAndDeduped<Key>( | ||
usages: PrivateApiUsageDto[], | ||
keyForUsage: (usage: PrivateApiUsageDto) => Key, | ||
areKeysEqual: (key1: Key, key2: Key) => boolean, | ||
) { | ||
const result = grouped(usages, keyForUsage, areKeysEqual); | ||
dedupeUsages(result); | ||
return result; | ||
} | ||
|
||
/** | ||
* Return value is sorted in decreasing order of usage of a given private API identifer | ||
*/ | ||
export function groupedAndSortedByPrivateAPIIdentifier<Key>( | ||
groupedByKey: Group<Key, PrivateApiUsageDto>[], | ||
): Group<string, Key>[] { | ||
const flattened = groupedByKey.flatMap((group) => group.values.map((value) => ({ key: group.key, value }))); | ||
|
||
const groupedByPrivateAPIIdentifier = grouped( | ||
flattened, | ||
(value) => value.value.privateAPIIdentifier, | ||
(id1, id2) => id1 === id2, | ||
).map((group) => ({ key: group.key, values: group.values.map((value) => value.key) })); | ||
|
||
groupedByPrivateAPIIdentifier.sort((group1, group2) => group2.values.length - group1.values.length); | ||
|
||
return groupedByPrivateAPIIdentifier; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { readFileSync } from 'fs'; | ||
import { applyingExclusions } from './exclusions'; | ||
import { splittingRecords, stripFilePrefix } from './utils'; | ||
import { Record } from './dto'; | ||
|
||
export function load(jsonFilePath: string) { | ||
let records = JSON.parse(readFileSync(jsonFilePath).toString('utf-8')) as Record[]; | ||
|
||
stripFilePrefix(records); | ||
|
||
let { usageDtos, testStartRecords } = splittingRecords(records); | ||
|
||
usageDtos = applyingExclusions(usageDtos); | ||
|
||
return { usageDtos, testStartRecords }; | ||
} |
Oops, something went wrong.