Skip to content

Commit

Permalink
Show information gain in header validator output
Browse files Browse the repository at this point in the history
  • Loading branch information
apasel422 committed Feb 1, 2024
1 parent 72daf75 commit ab00ae3
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 13 deletions.
11 changes: 10 additions & 1 deletion ts/src/header-validator/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,16 @@ export type Issue = {
export type ValidationResult = {
errors: Issue[]
warnings: Issue[]
notes: Issue[]
}

export class Context {
private readonly path: PathComponent[] = []
private readonly result: ValidationResult = { errors: [], warnings: [] }
private readonly result: ValidationResult = {
errors: [],
warnings: [],
notes: [],
}

scope<T>(c: PathComponent, f: () => T): T {
this.path.push(c)
Expand All @@ -33,6 +38,10 @@ export class Context {
this.result.warnings.push(this.issue(msg))
}

note(msg: string): void {
this.result.notes.push(this.issue(msg))
}

finish(topLevelError?: string): ValidationResult {
if (typeof topLevelError !== 'undefined') {
this.result.errors.push({ msg: topLevelError })
Expand Down
2 changes: 2 additions & 0 deletions ts/src/header-validator/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ <h1>Attribution Reporting Header Validation</h1>
<ul id=errors></ul>
<p>Warnings:
<ul id=warnings></ul>
<p>Notes:
<ul id=notes></ul>
</output>
</fieldset>
</div>
Expand Down
5 changes: 4 additions & 1 deletion ts/src/header-validator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const sourceTypeRadios = form.elements.namedItem(
)! as RadioNodeList
const errorList = document.querySelector('#errors')!
const warningList = document.querySelector('#warnings')!
const noteList = document.querySelector('#note')!
const successDiv = document.querySelector('#success')!
const sourceTypeFieldset = document.querySelector(
'#source-type'
Expand Down Expand Up @@ -69,7 +70,8 @@ function validate(): void {
input.value,
vsv.Chromium,
sourceType(),
flexCheckbox.checked
flexCheckbox.checked,
/*noteInfoGain=*/true,
)[0]
break
case 'trigger':
Expand Down Expand Up @@ -103,6 +105,7 @@ function validate(): void {

errorList.replaceChildren(...result.errors.map(makeLi))
warningList.replaceChildren(...result.warnings.map(makeLi))
noteList.replaceChildren(...result.notes.map(makeLi))
}

form.addEventListener('input', validate)
Expand Down
58 changes: 53 additions & 5 deletions ts/src/header-validator/source.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import * as jsontest from './validate-json.test'

type TestCase = jsontest.TestCase<Source> & {
sourceType?: SourceType
noteInfoGain?: boolean
}

const testCases: TestCase[] = [
Expand Down Expand Up @@ -1220,7 +1221,7 @@ const testCases: TestCase[] = [
},

{
name: 'channel-capacity-default-event',
name: 'channel-capacity-default-event-custom-max',
json: `{"destination": "https://a.test"}`,
sourceType: SourceType.event,
vsv: {
Expand All @@ -1233,12 +1234,12 @@ const testCases: TestCase[] = [
expectedErrors: [
{
path: [],
msg: 'exceeds max event-level channel capacity per event source (1.58 > 0.00)',
msg: 'exceeds max event-level channel capacity per event source (1.585 > 0.000)',
},
],
},
{
name: 'channel-capacity-default-navigation',
name: 'channel-capacity-default-navigation-custom-max',
json: `{"destination": "https://a.test"}`,
sourceType: SourceType.navigation,
vsv: {
Expand All @@ -1251,7 +1252,53 @@ const testCases: TestCase[] = [
expectedErrors: [
{
path: [],
msg: 'exceeds max event-level channel capacity per navigation source (11.46 > 0.00)',
msg: 'exceeds max event-level channel capacity per navigation source (11.462 > 0.000)',
},
],
},
{
name: 'channel-capacity-default-event-notes',
json: `{"destination": "https://a.test"}`,
sourceType: SourceType.event,
noteInfoGain: true,
vsv: {
maxSettableEventLevelEpsilon: 14,
},
expectedNotes: [
{
path: [],
msg: 'number of possible output states: 3',
},
{
path: [],
msg: 'information gain: 1.585',
},
{
path: [],
msg: 'randomized response rate: 0.00025%',
},
],
},
{
name: 'channel-capacity-default-navigation-notes',
json: `{"destination": "https://a.test"}`,
sourceType: SourceType.navigation,
noteInfoGain: true,
vsv: {
maxSettableEventLevelEpsilon: 14,
},
expectedNotes: [
{
path: [],
msg: 'number of possible output states: 2925',
},
{
path: [],
msg: 'information gain: 11.462',
},
{
path: [],
msg: 'randomized response rate: 0.24263%',
},
],
},
Expand Down Expand Up @@ -1833,7 +1880,8 @@ testCases.forEach((tc) =>
tc.json,
{ ...vsv.Chromium, ...tc.vsv },
tc.sourceType ?? SourceType.navigation,
tc.parseFullFlex ?? false
tc.parseFullFlex ?? false,
tc.noteInfoGain ?? false
)
)
)
2 changes: 2 additions & 0 deletions ts/src/header-validator/util.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as context from './context'
export type TestCase = {
expectedWarnings?: context.Issue[]
expectedErrors?: context.Issue[]
expectedNotes?: context.Issue[]
}

export function run(
Expand All @@ -17,6 +18,7 @@ export function run(
assert.deepEqual(result, {
errors: tc.expectedErrors ?? [],
warnings: tc.expectedWarnings ?? [],
notes: tc.expectedNotes ?? [],
})
})
}
18 changes: 12 additions & 6 deletions ts/src/header-validator/validate-json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ class SourceContext extends RegistrationContext {
constructor(
vsv: Readonly<VendorSpecificValues>,
parseFullFlex: boolean,
readonly sourceType: SourceType
readonly sourceType: SourceType,
readonly noteInfoGain: boolean
) {
super(vsv, parseFullFlex)
}
Expand Down Expand Up @@ -832,18 +833,22 @@ function channelCapacity(ctx: SourceContext, s: Source): void {
perTriggerDataConfigs
)

const { infoGain } = config.computeConfigData(
const out = config.computeConfigData(
s.eventLevelEpsilon,
ctx.vsv.maxEventLevelChannelCapacityPerSource[ctx.sourceType]
)

const max = ctx.vsv.maxEventLevelChannelCapacityPerSource[ctx.sourceType]
if (infoGain > max) {
if (out.infoGain > max) {
ctx.error(
`exceeds max event-level channel capacity per ${
ctx.sourceType
} source (${infoGain.toFixed(2)} > ${max.toFixed(2)})`
} source (${out.infoGain.toFixed(3)} > ${max.toFixed(3)})`
)
} else if (ctx.noteInfoGain) {
ctx.note(`number of possible output states: ${out.numStates}`)
ctx.note(`information gain: ${out.infoGain.toFixed(3)}`)
ctx.note(`randomized response rate: ${(100 * out.flipProb).toFixed(5)}%`)
}
}

Expand Down Expand Up @@ -1421,10 +1426,11 @@ export function validateSource(
json: string,
vsv: Readonly<VendorSpecificValues>,
sourceType: SourceType,
parseFullFlex: boolean = false
parseFullFlex: boolean = false,
noteInfoGain: boolean = false
): [ValidationResult, Maybe<Source>] {
return validateJSON(
new SourceContext(vsv, parseFullFlex, sourceType),
new SourceContext(vsv, parseFullFlex, sourceType, noteInfoGain),
json,
source
)
Expand Down

0 comments on commit ab00ae3

Please sign in to comment.