Add a sparse_output flag to the histogram config in the registry.
Bug: 102478
Change-Id: I7d82b3b372eff9410d2038678bb62deeda413a40
Reviewed-on: https://fuchsia-review.googlesource.com/c/cobalt/+/691384
Reviewed-by: Laura Peskin <pesk@google.com>
Commit-Queue: Cameron Dale <camrdale@google.com>
diff --git a/src/bin/config_parser/src/config_validator/int_buckets.go b/src/bin/config_parser/src/config_validator/int_buckets.go
index 269b7c8..0101a39 100644
--- a/src/bin/config_parser/src/config_validator/int_buckets.go
+++ b/src/bin/config_parser/src/config_validator/int_buckets.go
@@ -55,16 +55,16 @@
if r.IntBuckets != nil {
// If it exists, the report bucket config takes priority.
- return validateIntBuckets(*r.IntBuckets)
+ return validateIntBuckets(*r.IntBuckets, r)
} else if m.IntBuckets != nil {
- return validateIntBuckets(*m.IntBuckets)
+ return validateIntBuckets(*m.IntBuckets, r)
}
return nil
}
// Validate the fields in an int_buckets.
-func validateIntBuckets(bucket config.IntegerBuckets) error {
+func validateIntBuckets(bucket config.IntegerBuckets, r config.ReportDefinition) error {
if bucket_value := bucket.GetLinear(); bucket_value != nil {
if bucket_value.NumBuckets < 1 {
return fmt.Errorf("Linear bucket must set a num_buckets greater than or equal to 1.")
@@ -86,5 +86,9 @@
return fmt.Errorf("int_config must define either a linear or exponential bucket definition.")
}
+ if bucket.SparseOutput && r.PrivacyLevel != config.ReportDefinition_PRIVACY_LEVEL_UNKNOWN && r.PrivacyLevel != config.ReportDefinition_NO_ADDED_PRIVACY {
+ return fmt.Errorf("int_config can not have sparse_output set if additional privacy is enabled on the report.")
+ }
+
return nil
}
diff --git a/src/bin/config_parser/src/config_validator/int_buckets_test.go b/src/bin/config_parser/src/config_validator/int_buckets_test.go
index 7d878c8..93cf58a 100644
--- a/src/bin/config_parser/src/config_validator/int_buckets_test.go
+++ b/src/bin/config_parser/src/config_validator/int_buckets_test.go
@@ -102,70 +102,93 @@
// Tests int_buckets validation.
func TestValidateIntBuckets(t *testing.T) {
+ r := makeValidReportWithType(config.ReportDefinition_UNIQUE_DEVICE_HISTOGRAMS)
+
// Test that an IntBuckets without a linear or exponential bucket defined returns an error.
bucket := config.IntegerBuckets{}
- if err := validateIntBuckets(bucket); err == nil {
+ if err := validateIntBuckets(bucket, r); err == nil {
t.Errorf("Uninitialized int_buckets should return an error.")
}
// Test that an empty Linear bucket config returns an error.
bucket.Buckets = &config.IntegerBuckets_Linear{}
- if err := validateIntBuckets(bucket); err == nil {
+ if err := validateIntBuckets(bucket, r); err == nil {
t.Errorf("int_buckets with an empty linear_bucket should return an error.")
}
// Test that a Linear bucket config with invalid num_buckets returns an error.
linear := &config.LinearIntegerBuckets{Floor: 0, NumBuckets: 0, StepSize: 10}
bucket.Buckets = &config.IntegerBuckets_Linear{Linear: linear}
- if err := validateIntBuckets(bucket); err == nil {
+ if err := validateIntBuckets(bucket, r); err == nil {
t.Errorf("A linear_bucket with 0 num_buckets should return an error.")
}
// Test that a Linear bucket config with invalid step_size returns an error.
linear = &config.LinearIntegerBuckets{Floor: 0, NumBuckets: 10, StepSize: 0}
bucket.Buckets = &config.IntegerBuckets_Linear{Linear: linear}
- if err := validateIntBuckets(bucket); err == nil {
+ if err := validateIntBuckets(bucket, r); err == nil {
t.Errorf("A linear_bucket with 0 step_size should return an error.")
}
// Test that a valid Linear bucket config passes.
linear = &config.LinearIntegerBuckets{Floor: 0, NumBuckets: 20, StepSize: 10}
bucket.Buckets = &config.IntegerBuckets_Linear{Linear: linear}
- if err := validateIntBuckets(bucket); err != nil {
+ if err := validateIntBuckets(bucket, r); err != nil {
t.Errorf("Rejected a valid linear int_config: %v", err)
}
// Test that an empty Exponential bucket config returns an error.
bucket.Buckets = &config.IntegerBuckets_Exponential{}
- if err := validateIntBuckets(bucket); err == nil {
+ if err := validateIntBuckets(bucket, r); err == nil {
t.Errorf("int_buckets with an empty exponential_bucket should return an error.")
}
// Test that an Exponential bucket config with invalid num_buckets returns an error.
exponential := &config.ExponentialIntegerBuckets{Floor: 0, NumBuckets: 0, InitialStep: 10, StepMultiplier: 2}
bucket.Buckets = &config.IntegerBuckets_Exponential{Exponential: exponential}
- if err := validateIntBuckets(bucket); err == nil {
+ if err := validateIntBuckets(bucket, r); err == nil {
t.Errorf("An exponential_bucket with 0 num_buckets should return an error.")
}
// Test that an Exponential bucket config with invalid initial_step returns an error.
exponential = &config.ExponentialIntegerBuckets{Floor: 0, NumBuckets: 10, InitialStep: 0, StepMultiplier: 2}
bucket.Buckets = &config.IntegerBuckets_Exponential{Exponential: exponential}
- if err := validateIntBuckets(bucket); err == nil {
+ if err := validateIntBuckets(bucket, r); err == nil {
t.Errorf("An exponential_bucket with 0 initial_step should return an error.")
}
// Test that an Exponential bucket config with invalid step_multiplier returns an error.
exponential = &config.ExponentialIntegerBuckets{Floor: 0, NumBuckets: 20, InitialStep: 2, StepMultiplier: 0}
bucket.Buckets = &config.IntegerBuckets_Exponential{Exponential: exponential}
- if err := validateIntBuckets(bucket); err == nil {
+ if err := validateIntBuckets(bucket, r); err == nil {
t.Errorf("An exponential_bucket with 0 step_multipier should return an error.")
}
// Test that a valid Exponential bucket config passes.
exponential = &config.ExponentialIntegerBuckets{Floor: 0, NumBuckets: 30, InitialStep: 2, StepMultiplier: 2}
bucket.Buckets = &config.IntegerBuckets_Exponential{Exponential: exponential}
- if err := validateIntBuckets(bucket); err != nil {
+ if err := validateIntBuckets(bucket, r); err != nil {
t.Errorf("Rejected a valid exponential int_config: %v", err)
}
+
+ // Test that sparse_output can be set on reports with no additional privacy.
+ linear = &config.LinearIntegerBuckets{Floor: 0, NumBuckets: 20, StepSize: 10}
+ bucket.Buckets = &config.IntegerBuckets_Linear{Linear: linear}
+ bucket.SparseOutput = true
+ if err := validateIntBuckets(bucket, r); err != nil {
+ t.Errorf("Rejected a valid sparse_output setting on a report with no privacy setting: %v", err)
+ }
+ r.PrivacyLevel = config.ReportDefinition_NO_ADDED_PRIVACY
+ if err := validateIntBuckets(bucket, r); err != nil {
+ t.Errorf("Rejected a valid sparse_output setting on a report with no added privacy: %v", err)
+ }
+ // Test that sparse_output can NOT be set on reports WITH additional privacy.
+ r.PrivacyLevel = config.ReportDefinition_LOW_PRIVACY
+ if err := validateIntBuckets(bucket, r); err == nil {
+ t.Errorf("An int_bucket with sparse_output on a report with low privacy should return an error.")
+ }
+ r.PrivacyLevel = config.ReportDefinition_HIGH_PRIVACY
+ if err := validateIntBuckets(bucket, r); err == nil {
+ t.Errorf("An int_bucket with sparse_output on a report with high privacy should return an error.")
+ }
}
diff --git a/src/registry/report_definition.proto b/src/registry/report_definition.proto
index 6a1a685..c2324c6 100644
--- a/src/registry/report_definition.proto
+++ b/src/registry/report_definition.proto
@@ -1101,6 +1101,12 @@
ExponentialIntegerBuckets exponential = 1;
LinearIntegerBuckets linear = 2;
}
+
+ // If set to true, empty buckets will not be added to the report data such
+ // that all histograms contain a row for every bucket. Buckets with a zero
+ // count may still occur if data is logged that contains a zero count. This
+ // field can not be set on reports with added privacy.
+ bool sparse_output = 3;
}
message StringSketchParameters {