| 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 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, |
| } |
| 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 |
| valid bool |
| 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}, true, expectedParams{0.019537480548024178, 4}}, |
| // The best-match record has key (5.0, 10000, 10). |
| {args{&occurrenceMetric, &mediumPrivacyFleetwideOccurrenceCountsReport}, true, expectedParams{0.0014028092846274376, 10}}, |
| // The best-match record has key (10.0, 10000, 10). |
| {args{&occurrenceMetric, &lowPrivacyFleetwideOccurrenceCountsReport}, true, expectedParams{0.0008652620017528534, 12}}, |
| // 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}, true, expectedParams{0.0, 0}}, |
| {args{&stringMetric, &noAddedPrivacyStringCountsReport}, true, 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}, true, expectedParams{0.0, 0}}, |
| // |
| // Invalid inputs: |
| // STRING_COUNTS reports with added privacy are not supported yet. |
| {args{&stringMetric, &highPrivacyStringCountsReport}, false, expectedParams{}}, |
| } |
| 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) |
| } |
| } |
| } |
| } |
| |
| // 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)}}, |
| // 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)}}, |
| } |
| 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) |
| } |
| } |