blob: d790517d8b8d61caf0151d10f8cb5b402cd757c0 [file] [log] [blame]
package config_parser
import (
"config"
"privacy"
"testing"
)
var testParamRecords = [][]string{
{"1.0", "10000", "1", "0.0024182694032788277", "9"},
{"1.0", "20000", "1", "0.0013450710102915764", "10"},
{"1.0", "10000", "2", "0.004249398596584797", "7"},
{"1.0", "20000", "2", "0.002368534915149212", "9"},
{"1.0", "10000", "10", "0.019537480548024178", "4"},
{"1.0", "20000", "10", "0.011127800680696964", "5"},
{"5.0", "10000", "1", "0.000906200148165226", "12"},
{"5.0", "20000", "1", "0.000491277314722538", "15"},
{"5.0", "10000", "2", "0.0009743086993694305", "12"},
{"5.0", "20000", "2", "0.0005256505683064461", "14"},
{"5.0", "10000", "10", "0.0014028092846274376", "10"},
{"5.0", "20000", "10", "0.0007524723187088966", "13"},
{"10.0", "10000", "1", "0.00017590634524822235", "20"},
{"10.0", "20000", "1", "0.00013784877955913544", "22"},
{"10.0", "10000", "2", "0.00047429557889699936", "15"},
{"10.0", "20000", "2", "0.0002830149605870247", "17"},
{"10.0", "10000", "10", "0.0008652620017528534", "12"},
{"10.0", "20000", "10", "0.0004855785518884659", "15"},
}
// Test cases where the report's integer range size is larger than the number of index
// points given by |testParamRecords|.
func TestPopulateParamsForReport(t *testing.T) {
var eventCodeBufferMax uint64 = 10
var smallEventCodeBufferMax uint64 = 2
var stringBufferMax uint32 = 1
var minValue int64 = 0
var maxValue int64 = 100
var maxCount uint64 = 100
occurrenceMetric := config.MetricDefinition{
MetricName: "OccurrenceMetric",
MetricType: config.MetricDefinition_OCCURRENCE,
EventCodeBufferMax: eventCodeBufferMax,
}
stringMetric := config.MetricDefinition{
MetricName: "StringMetric",
MetricType: config.MetricDefinition_STRING,
EventCodeBufferMax: smallEventCodeBufferMax,
StringBufferMax: stringBufferMax,
}
highPrivacyFleetwideOccurrenceCountsReport := config.ReportDefinition{
ReportName: "HighPrivacyFleetwideOccurrenceCounts",
ReportType: config.ReportDefinition_FLEETWIDE_OCCURRENCE_COUNTS,
PrivacyLevel: config.ReportDefinition_HIGH_PRIVACY,
MinValue: minValue,
MaxValue: maxValue,
}
mediumPrivacyFleetwideOccurrenceCountsReport := config.ReportDefinition{
ReportName: "MediumPrivacyFleetwideOccurrenceCounts",
ReportType: config.ReportDefinition_FLEETWIDE_OCCURRENCE_COUNTS,
PrivacyLevel: config.ReportDefinition_MEDIUM_PRIVACY,
MinValue: minValue,
MaxValue: maxValue,
}
lowPrivacyFleetwideOccurrenceCountsReport := config.ReportDefinition{
ReportName: "LowPrivacyFleetwideOccurrenceCounts",
ReportType: config.ReportDefinition_FLEETWIDE_OCCURRENCE_COUNTS,
PrivacyLevel: config.ReportDefinition_LOW_PRIVACY,
MinValue: minValue,
MaxValue: maxValue,
}
noAddedPrivacyFleetwideOccurrenceCountsReport := config.ReportDefinition{
ReportName: "NoAddedPrivacyFleetwideOccurrenceCounts",
ReportType: config.ReportDefinition_FLEETWIDE_OCCURRENCE_COUNTS,
PrivacyLevel: config.ReportDefinition_NO_ADDED_PRIVACY,
}
highPrivacyStringCountsReport := config.ReportDefinition{
ReportName: "HighPrivacyStringCounts",
ReportType: config.ReportDefinition_STRING_COUNTS,
PrivacyLevel: config.ReportDefinition_HIGH_PRIVACY,
MaxCount: maxCount,
}
noAddedPrivacyStringCountsReport := config.ReportDefinition{
ReportName: "NoAddedPrivacyStringCounts",
ReportType: config.ReportDefinition_STRING_COUNTS,
PrivacyLevel: config.ReportDefinition_NO_ADDED_PRIVACY,
}
unsetPrivacyLevelReport := config.ReportDefinition{
ReportName: "UnsetPrivacyFleetwideOccurrenceCounts",
ReportType: config.ReportDefinition_FLEETWIDE_OCCURRENCE_COUNTS,
}
calc, err := privacy.NewPrivacyEncodingParamsCalculatorForTesting(testParamRecords)
if err != nil {
t.Errorf("Failed precondition: failed to create PrivacyEncodingParamsCalculator. Error: %v", err)
}
type args struct {
metric *config.MetricDefinition
report *config.ReportDefinition
}
type expectedParams struct {
probBitFlip float64
numIndexPoints uint32
}
var tests = []struct {
input args
expected expectedParams
}{
// Valid inputs:
// For each report, the sparsity is |eventCodeBufferMax| = 10 and the target
// population is 15000.
//
// The best-match record has key (1.0, 10000, 10).
{args{&occurrenceMetric, &highPrivacyFleetwideOccurrenceCountsReport}, expectedParams{0.019537480548024178, 4}},
// The best-match record has key (5.0, 10000, 10).
{args{&occurrenceMetric, &mediumPrivacyFleetwideOccurrenceCountsReport}, expectedParams{0.0014028092846274376, 10}},
// The best-match record has key (10.0, 10000, 10).
{args{&occurrenceMetric, &lowPrivacyFleetwideOccurrenceCountsReport}, expectedParams{0.0008652620017528534, 12}},
// The best-match record has key (1.0, 10000, 10).
{args{&stringMetric, &highPrivacyStringCountsReport}, expectedParams{0.019537480548024178, 4}},
// The reports have no added privacy, so both ProbBitFlip and NumIndexPoints should remain 0.
// STRING_COUNTS with no added privacy are supported.
{args{&occurrenceMetric, &noAddedPrivacyFleetwideOccurrenceCountsReport}, expectedParams{0.0, 0}},
{args{&stringMetric, &noAddedPrivacyStringCountsReport}, expectedParams{0.0, 0}},
// The report has no privacy level set, so both ProbBitFlip and NumIndexPoints should remain 0.
// (All real reports should have a privacy level, enforced by the config validator.
// However, we want to allow test reports with an unset privacy level.)
{args{&occurrenceMetric, &unsetPrivacyLevelReport}, expectedParams{0.0, 0}},
}
for _, test := range tests {
err := populateParamsForReport(calc, test.input.metric, test.input.report)
if err != nil {
t.Errorf("populateParamsForReport() failed for report %s: %v", test.input.report.ReportName, err)
} else {
if test.input.report.ProbBitFlip != test.expected.probBitFlip {
t.Errorf("populateParamsForReport() wrote incorrect ProbBitFlip for report %s: expected %f, got %f",
test.input.report.ReportName, test.expected.probBitFlip, test.input.report.ProbBitFlip)
}
if test.input.report.NumIndexPoints != test.expected.numIndexPoints {
t.Errorf("populateParamsForReport() wrote incorrect NumIndexPoints for report %s: expected %d, got %d",
test.input.report.ReportName, test.expected.numIndexPoints, test.input.report.NumIndexPoints)
}
}
}
}
// Test cases where the report's integer range size is smaller than the number of index
// points given by |testParamRecords|. In this case the integer range size should be used
// as the number of index points.
func TestPopulateParamsForSmallRangeReport(t *testing.T) {
var eventCodeBufferMax uint64 = 5
var minValue int64 = 0
var maxValue int64 = 1
var maxCount uint64 = 1
var numLinearBuckets uint32 = 2
linearBuckets := config.LinearIntegerBuckets{NumBuckets: numLinearBuckets}
buckets := config.IntegerBuckets{
Buckets: &config.IntegerBuckets_Linear{&linearBuckets},
}
occurrenceMetric := config.MetricDefinition{
MetricName: "OccurrenceMetric",
MetricType: config.MetricDefinition_OCCURRENCE,
EventCodeBufferMax: eventCodeBufferMax,
}
integerMetric := config.MetricDefinition{
MetricName: "IntegerMetric",
MetricType: config.MetricDefinition_INTEGER,
EventCodeBufferMax: eventCodeBufferMax,
}
highPrivacyFleetwideOccurrenceCountsReport := config.ReportDefinition{
ReportName: "HighPrivacyFleetwideOccurrenceCounts",
ReportType: config.ReportDefinition_FLEETWIDE_OCCURRENCE_COUNTS,
PrivacyLevel: config.ReportDefinition_HIGH_PRIVACY,
MinValue: minValue,
MaxValue: maxValue,
}
highPrivacyFleetwideHistogramsReport := config.ReportDefinition{
ReportName: "HighPrivacyFleetwideHistograms",
ReportType: config.ReportDefinition_FLEETWIDE_HISTOGRAMS,
IntBuckets: &buckets,
PrivacyLevel: config.ReportDefinition_HIGH_PRIVACY,
MaxCount: maxCount,
}
calc, err := privacy.NewPrivacyEncodingParamsCalculatorForTesting(testParamRecords)
if err != nil {
t.Errorf("Failed precondition: failed to create PrivacyEncodingParamsCalculator. Error: %v", err)
}
type args struct {
metric *config.MetricDefinition
report *config.ReportDefinition
}
type expectedParams struct {
probBitFlip float64
numIndexPoints uint32
}
var tests = []struct {
input args
valid bool
expected expectedParams
}{
// Valid inputs:
// For each report, the target population is 15000.
//
// The sparsity is |eventCodeBufferMax| = 5.
// The best-match record has key (1.0, 10000, 10).
{args{&occurrenceMetric, &highPrivacyFleetwideOccurrenceCountsReport}, true,
expectedParams{0.019537480548024178, uint32(maxValue + 1)}},
// The sparsity is |eventCodeBufferMax| * |numLinearBuckets| = 10.
// The best-match record has key (1.0, 10000, 10).
{args{&integerMetric, &highPrivacyFleetwideHistogramsReport}, true,
expectedParams{0.019537480548024178, uint32(maxCount + 1)}},
}
for _, test := range tests {
err := populateParamsForReport(calc, test.input.metric, test.input.report)
if test.valid && err != nil {
t.Errorf("populateParamsForReport() failed for report %s: %v", test.input.report.ReportName, err)
} else if !test.valid && err == nil {
t.Errorf("populateParamsForReport() accepted invalid input: metric %s, report %s", test.input.metric.MetricName, test.input.report.ReportName)
} else {
if test.input.report.ProbBitFlip != test.expected.probBitFlip {
t.Errorf("populateParamsForReport() wrote incorrect ProbBitFlip for report %s: expected %f, got %f",
test.input.report.ReportName, test.expected.probBitFlip, test.input.report.ProbBitFlip)
}
if test.input.report.NumIndexPoints != test.expected.numIndexPoints {
t.Errorf("populateParamsForReport() wrote incorrect NumIndexPoints for report %s: expected %d, got %d",
test.input.report.ReportName, test.expected.numIndexPoints, test.input.report.NumIndexPoints)
}
}
}
}
func TestPopulateParamsForConfig(t *testing.T) {
cobalt10Report := config.ReportDefinition{
ReportName: "Cobalt1.0Report",
ReportType: config.ReportDefinition_EVENT_COMPONENT_OCCURRENCE_COUNT,
}
cobalt10Metric := config.MetricDefinition{
MetricName: "Cobalt1.0Metric",
MetricType: config.MetricDefinition_EVENT_COUNT,
Reports: []*config.ReportDefinition{&cobalt10Report},
}
cobalt11Report := config.ReportDefinition{
ReportName: "Cobalt1.1Report",
ReportType: config.ReportDefinition_FLEETWIDE_OCCURRENCE_COUNTS,
PrivacyLevel: config.ReportDefinition_HIGH_PRIVACY,
MinValue: 0,
MaxValue: 10,
}
cobalt11Metric := config.MetricDefinition{
MetricName: "Cobalt1.1Metric",
MetricType: config.MetricDefinition_OCCURRENCE,
EventCodeBufferMax: 10,
Reports: []*config.ReportDefinition{&cobalt11Report},
}
c := ProjectConfig{}
c.ProjectConfig = &config.ProjectConfigFile{MetricDefinitions: []*config.MetricDefinition{&cobalt10Metric, &cobalt11Metric}}
calc, err := privacy.NewPrivacyEncodingParamsCalculatorForTesting(testParamRecords)
if err != nil {
t.Errorf("Failed precondition: failed to create PrivacyEncodingParamsCalculator. Error: %v", err)
}
err = populateParamsForConfig(calc, &c)
if err != nil {
t.Errorf("Failed to populate params for valid config. Error: %v", err)
}
var cobalt11ReportExpectedProbBitFlip float64 = 0.019537480548024178
var cobalt11ReportExpectedNumIndexPoints uint32 = 4
if cobalt11Report.ProbBitFlip != cobalt11ReportExpectedProbBitFlip {
t.Errorf("Incorrect ProbBitFlip for report %s. Expected %f, got %f",
cobalt11Report.ReportName, cobalt11ReportExpectedProbBitFlip, cobalt11Report.ProbBitFlip)
}
if cobalt11Report.NumIndexPoints != cobalt11ReportExpectedNumIndexPoints {
t.Errorf("Incorrect NumIndexPoints for report %s. Expected %d, got %d",
cobalt11Report.ReportName, cobalt11ReportExpectedNumIndexPoints, cobalt11Report.NumIndexPoints)
}
}