diff --git a/internal/cmd/miniooni/oonirun.go b/internal/cmd/miniooni/oonirun.go index 420ddcc0b..ce41e58fc 100644 --- a/internal/cmd/miniooni/oonirun.go +++ b/internal/cmd/miniooni/oonirun.go @@ -53,6 +53,8 @@ func ooniRunMain(ctx context.Context, logger.Warnf("oonirun: parsing OONI Run v2 descriptor failed: %s", err.Error()) continue } + logger.Infof("oonirun: running '%s'", descr.Name) + logger.Infof("oonirun: link authored by '%s'", descr.Author) if err := oonirun.V2MeasureDescriptor(ctx, cfg, &descr); err != nil { logger.Warnf("oonirun: running link failed: %s", err.Error()) continue diff --git a/internal/registry/factory.go b/internal/registry/factory.go index 2a704740e..937c22e97 100644 --- a/internal/registry/factory.go +++ b/internal/registry/factory.go @@ -7,6 +7,7 @@ package registry import ( "errors" "fmt" + "math" "os" "reflect" "strconv" @@ -107,6 +108,12 @@ func (b *Factory) setOptionBool(field reflect.Value, value any) error { } } +// With JSON we're limited by the 52 bits in the mantissa +const ( + jsonMaxInteger = 1<<53 - 1 + jsonMinInteger = -1<<53 + 1 +) + // setOptionInt sets an int option func (b *Factory) setOptionInt(field reflect.Value, value any) error { switch v := value.(type) { @@ -132,6 +139,18 @@ func (b *Factory) setOptionInt(field reflect.Value, value any) error { } field.SetInt(number) return nil + case float64: + if math.IsNaN(v) || math.IsInf(v, 0) { + return fmt.Errorf("%w from: %v", ErrCannotSetIntegerOption, value) + } + if math.Trunc(v) != v { + return fmt.Errorf("%w from: %v", ErrCannotSetIntegerOption, value) + } + if v > jsonMaxInteger || v < jsonMinInteger { + return fmt.Errorf("%w from: %v", ErrCannotSetIntegerOption, value) + } + field.SetInt(int64(v)) + return nil default: return fmt.Errorf("%w from a value of type %T", ErrCannotSetIntegerOption, value) } diff --git a/internal/registry/factory_test.go b/internal/registry/factory_test.go index 49f11338a..b407f074e 100644 --- a/internal/registry/factory_test.go +++ b/internal/registry/factory_test.go @@ -3,6 +3,7 @@ package registry import ( "errors" "fmt" + "math" "os" "testing" @@ -251,6 +252,48 @@ func TestExperimentBuilderSetOptionAny(t *testing.T) { FieldValue: make(chan any), ExpectErr: ErrCannotSetIntegerOption, ExpectConfig: &fakeExperimentConfig{}, + }, { + TestCaseName: "[int] for NaN", + InitialConfig: &fakeExperimentConfig{}, + FieldName: "Value", + FieldValue: math.NaN(), + ExpectErr: ErrCannotSetIntegerOption, + ExpectConfig: &fakeExperimentConfig{}, + }, { + TestCaseName: "[int] for +Inf", + InitialConfig: &fakeExperimentConfig{}, + FieldName: "Value", + FieldValue: math.Inf(1), + ExpectErr: ErrCannotSetIntegerOption, + ExpectConfig: &fakeExperimentConfig{}, + }, { + TestCaseName: "[int] for -Inf", + InitialConfig: &fakeExperimentConfig{}, + FieldName: "Value", + FieldValue: math.Inf(-1), + ExpectErr: ErrCannotSetIntegerOption, + ExpectConfig: &fakeExperimentConfig{}, + }, { + TestCaseName: "[int] for too large value", + InitialConfig: &fakeExperimentConfig{}, + FieldName: "Value", + FieldValue: float64(jsonMaxInteger + 1), + ExpectErr: ErrCannotSetIntegerOption, + ExpectConfig: &fakeExperimentConfig{}, + }, { + TestCaseName: "[int] for too small value", + InitialConfig: &fakeExperimentConfig{}, + FieldName: "Value", + FieldValue: float64(jsonMinInteger - 1), + ExpectErr: ErrCannotSetIntegerOption, + ExpectConfig: &fakeExperimentConfig{}, + }, { + TestCaseName: "[int] for float64 with nonzero fractional value", + InitialConfig: &fakeExperimentConfig{}, + FieldName: "Value", + FieldValue: 1.11, + ExpectErr: ErrCannotSetIntegerOption, + ExpectConfig: &fakeExperimentConfig{}, }, { TestCaseName: "[string] for serialized bool value while setting a string value", InitialConfig: &fakeExperimentConfig{},