From 98880b5474971d5e8e651575885fda7ff34a9871 Mon Sep 17 00:00:00 2001 From: Tom Coupland Date: Wed, 22 Aug 2018 11:00:04 +0100 Subject: [PATCH] Add App,Trigger,Fn Equality and Clone Testing (#1159) Creates a test that aims to assert that the Equals and Clone functions for our three entity structs actually work. The bulk of the code is spent creating gopter generators for the entities. See information of generative or property based testing for explainations on that topic, but basically it's an object that is capable of creating a stream of unique instances of the given struct. With the generator we then make three assertions: 1) Entities are always equal to themselves. 2) A .Clone() of an entity is Equal to the original entity. 3) A .Clone() of an entity that has a field modified is not equal to the orignal. The third property is the worse for implementation, as it does not generate the field to modify, it simply loops all fields for each generated entity, and checks Equals always breaks. Break testing shows that this would have caught earlier bugs in Equals due to field addition. It will add to the work to add further fields, generators have to be manually specified for each field, but that seems a worthy cost. --- Gopkg.lock | 12 +- api/models/app.go | 2 +- api/models/app_test.go | 143 +++++++ api/models/fn_test.go | 109 +++++ api/models/trigger_test.go | 85 ++++ vendor/github.com/leanovate/gopter/.gitignore | 30 ++ .../github.com/leanovate/gopter/.travis.yml | 12 + .../github.com/leanovate/gopter/CHANGELOG.md | 65 +++ vendor/github.com/leanovate/gopter/LICENSE | 21 + vendor/github.com/leanovate/gopter/Makefile | 41 ++ vendor/github.com/leanovate/gopter/README.md | 44 ++ .../leanovate/gopter/arbitrary/arbitraries.go | 41 ++ .../leanovate/gopter/arbitrary/doc.go | 32 ++ .../example_arbitrary_struct_test.go | 64 +++ .../gopter/arbitrary/example_parseint_test.go | 28 ++ .../arbitrary/example_quadratic_test.go | 78 ++++ .../leanovate/gopter/arbitrary/forall.go | 33 ++ .../gopter/arbitrary/gen_for_kind.go | 333 +++++++++++++++ .../arbitrary/gen_for_kind_slice_test.go | 30 ++ .../arbitrary/gen_for_kind_struct_test.go | 52 +++ .../gopter/arbitrary/gen_for_kind_test.go | 135 ++++++ .../github.com/leanovate/gopter/bi_mapper.go | 111 +++++ .../leanovate/gopter/bi_mapper_test.go | 27 ++ .../leanovate/gopter/commands/actions.go | 140 +++++++ .../leanovate/gopter/commands/command.go | 72 ++++ .../leanovate/gopter/commands/commands.go | 84 ++++ .../gopter/commands/commands_test.go | 112 +++++ .../leanovate/gopter/commands/doc.go | 11 + .../commands/example_circularqueue_test.go | 256 ++++++++++++ .../gopter/commands/example_commands_test.go | 125 ++++++ .../github.com/leanovate/gopter/convey/doc.go | 5 + .../leanovate/gopter/convey/should_forall.go | 42 ++ .../gopter/convey/should_forall_test.go | 80 ++++ .../leanovate/gopter/derived_gen.go | 122 ++++++ .../leanovate/gopter/derived_gen_test.go | 186 +++++++++ vendor/github.com/leanovate/gopter/doc.go | 35 ++ .../leanovate/gopter/example_fizzbuzz_test.go | 78 ++++ .../leanovate/gopter/example_labels_test.go | 59 +++ .../leanovate/gopter/example_sqrt_test.go | 37 ++ vendor/github.com/leanovate/gopter/flag.go | 23 ++ .../github.com/leanovate/gopter/flag_test.go | 22 + .../leanovate/gopter/formated_reporter.go | 140 +++++++ .../gopter/formated_reporter_test.go | 80 ++++ vendor/github.com/leanovate/gopter/gen.go | 251 ++++++++++++ .../github.com/leanovate/gopter/gen/bool.go | 10 + .../leanovate/gopter/gen/bool_test.go | 14 + .../leanovate/gopter/gen/complex.go | 49 +++ .../leanovate/gopter/gen/complex_shrink.go | 27 ++ .../gopter/gen/complex_shrink_test.go | 91 ++++ .../leanovate/gopter/gen/complex_test.go | 46 +++ .../github.com/leanovate/gopter/gen/const.go | 11 + .../leanovate/gopter/gen/const_test.go | 14 + vendor/github.com/leanovate/gopter/gen/doc.go | 4 + .../github.com/leanovate/gopter/gen/fail.go | 15 + .../leanovate/gopter/gen/fail_test.go | 18 + .../github.com/leanovate/gopter/gen/floats.go | 69 ++++ .../leanovate/gopter/gen/floats_shrink.go | 49 +++ .../gopter/gen/floats_shrink_test.go | 154 +++++++ .../leanovate/gopter/gen/floats_test.go | 48 +++ .../leanovate/gopter/gen/frequency.go | 33 ++ .../leanovate/gopter/gen/frequency_test.go | 48 +++ .../leanovate/gopter/gen/helper_test.go | 33 ++ .../leanovate/gopter/gen/integers.go | 225 ++++++++++ .../leanovate/gopter/gen/integers_shrink.go | 95 +++++ .../gopter/gen/integers_shrink_test.go | 127 ++++++ .../leanovate/gopter/gen/integers_test.go | 130 ++++++ .../github.com/leanovate/gopter/gen/map_of.go | 87 ++++ .../leanovate/gopter/gen/map_of_test.go | 96 +++++ .../leanovate/gopter/gen/map_shrink.go | 149 +++++++ .../leanovate/gopter/gen/map_shrink_test.go | 57 +++ .../github.com/leanovate/gopter/gen/one_of.go | 29 ++ .../leanovate/gopter/gen/one_of_test.go | 53 +++ .../github.com/leanovate/gopter/gen/ptr_of.go | 41 ++ .../leanovate/gopter/gen/ptr_of_test.go | 54 +++ .../leanovate/gopter/gen/ptr_shrink.go | 41 ++ .../leanovate/gopter/gen/ptr_shrink_test.go | 17 + .../github.com/leanovate/gopter/gen/regex.go | 78 ++++ .../leanovate/gopter/gen/regex_test.go | 36 ++ .../leanovate/gopter/gen/retry_until.go | 21 + .../leanovate/gopter/gen/retry_until_test.go | 33 ++ .../leanovate/gopter/gen/slice_of.go | 89 ++++ .../leanovate/gopter/gen/slice_of_test.go | 172 ++++++++ .../leanovate/gopter/gen/slice_shrink.go | 101 +++++ .../leanovate/gopter/gen/slice_shrink_test.go | 86 ++++ .../leanovate/gopter/gen/string_shrink.go | 11 + .../leanovate/gopter/gen/strings.go | 158 +++++++ .../leanovate/gopter/gen/strings_test.go | 161 ++++++++ .../github.com/leanovate/gopter/gen/struct.go | 69 ++++ .../leanovate/gopter/gen/struct_test.go | 96 +++++ .../github.com/leanovate/gopter/gen/time.go | 37 ++ .../leanovate/gopter/gen/time_shrink.go | 27 ++ .../leanovate/gopter/gen/time_shrink_test.go | 27 ++ .../leanovate/gopter/gen/time_test.go | 60 +++ .../leanovate/gopter/gen/weighted.go | 44 ++ .../leanovate/gopter/gen/weighted_test.go | 41 ++ .../leanovate/gopter/gen_parameter_test.go | 63 +++ .../leanovate/gopter/gen_parameters.go | 68 +++ .../github.com/leanovate/gopter/gen_result.go | 55 +++ .../leanovate/gopter/gen_result_test.go | 26 ++ .../github.com/leanovate/gopter/gen_test.go | 387 ++++++++++++++++++ .../leanovate/gopter/locked_source.go | 77 ++++ vendor/github.com/leanovate/gopter/prop.go | 111 +++++ .../gopter/prop/check_condition_func.go | 40 ++ .../gopter/prop/check_condition_func_test.go | 60 +++ .../leanovate/gopter/prop/convert_result.go | 37 ++ .../gopter/prop/convert_result_test.go | 51 +++ .../github.com/leanovate/gopter/prop/doc.go | 4 + .../github.com/leanovate/gopter/prop/error.go | 14 + .../leanovate/gopter/prop/error_test.go | 18 + .../gopter/prop/example_invalidconcat_test.go | 42 ++ .../gopter/prop/example_quadratic_test.go | 66 +++ .../gopter/prop/example_shrink_test.go | 36 ++ .../gopter/prop/example_time_test.go | 55 +++ .../leanovate/gopter/prop/forall.go | 123 ++++++ .../leanovate/gopter/prop/forall_no_shrink.go | 59 +++ .../gopter/prop/forall_no_shrink_test.go | 54 +++ .../leanovate/gopter/prop/forall_test.go | 63 +++ .../github.com/leanovate/gopter/prop_arg.go | 32 ++ .../leanovate/gopter/prop_arg_test.go | 19 + .../leanovate/gopter/prop_result.go | 108 +++++ .../leanovate/gopter/prop_result_test.go | 76 ++++ .../github.com/leanovate/gopter/prop_test.go | 175 ++++++++ .../github.com/leanovate/gopter/properties.go | 59 +++ .../leanovate/gopter/properties_test.go | 47 +++ .../github.com/leanovate/gopter/reporter.go | 7 + vendor/github.com/leanovate/gopter/runner.go | 77 ++++ .../leanovate/gopter/runner_test.go | 189 +++++++++ vendor/github.com/leanovate/gopter/shrink.go | 185 +++++++++ .../leanovate/gopter/shrink_test.go | 155 +++++++ .../leanovate/gopter/test_parameters.go | 39 ++ .../leanovate/gopter/test_result.go | 51 +++ .../leanovate/gopter/test_result_test.go | 49 +++ 132 files changed, 9544 insertions(+), 2 deletions(-) create mode 100644 api/models/app_test.go create mode 100644 api/models/fn_test.go create mode 100644 vendor/github.com/leanovate/gopter/.gitignore create mode 100644 vendor/github.com/leanovate/gopter/.travis.yml create mode 100644 vendor/github.com/leanovate/gopter/CHANGELOG.md create mode 100644 vendor/github.com/leanovate/gopter/LICENSE create mode 100644 vendor/github.com/leanovate/gopter/Makefile create mode 100644 vendor/github.com/leanovate/gopter/README.md create mode 100644 vendor/github.com/leanovate/gopter/arbitrary/arbitraries.go create mode 100644 vendor/github.com/leanovate/gopter/arbitrary/doc.go create mode 100644 vendor/github.com/leanovate/gopter/arbitrary/example_arbitrary_struct_test.go create mode 100644 vendor/github.com/leanovate/gopter/arbitrary/example_parseint_test.go create mode 100644 vendor/github.com/leanovate/gopter/arbitrary/example_quadratic_test.go create mode 100644 vendor/github.com/leanovate/gopter/arbitrary/forall.go create mode 100644 vendor/github.com/leanovate/gopter/arbitrary/gen_for_kind.go create mode 100644 vendor/github.com/leanovate/gopter/arbitrary/gen_for_kind_slice_test.go create mode 100644 vendor/github.com/leanovate/gopter/arbitrary/gen_for_kind_struct_test.go create mode 100644 vendor/github.com/leanovate/gopter/arbitrary/gen_for_kind_test.go create mode 100644 vendor/github.com/leanovate/gopter/bi_mapper.go create mode 100644 vendor/github.com/leanovate/gopter/bi_mapper_test.go create mode 100644 vendor/github.com/leanovate/gopter/commands/actions.go create mode 100644 vendor/github.com/leanovate/gopter/commands/command.go create mode 100644 vendor/github.com/leanovate/gopter/commands/commands.go create mode 100644 vendor/github.com/leanovate/gopter/commands/commands_test.go create mode 100644 vendor/github.com/leanovate/gopter/commands/doc.go create mode 100644 vendor/github.com/leanovate/gopter/commands/example_circularqueue_test.go create mode 100644 vendor/github.com/leanovate/gopter/commands/example_commands_test.go create mode 100644 vendor/github.com/leanovate/gopter/convey/doc.go create mode 100644 vendor/github.com/leanovate/gopter/convey/should_forall.go create mode 100644 vendor/github.com/leanovate/gopter/convey/should_forall_test.go create mode 100644 vendor/github.com/leanovate/gopter/derived_gen.go create mode 100644 vendor/github.com/leanovate/gopter/derived_gen_test.go create mode 100644 vendor/github.com/leanovate/gopter/doc.go create mode 100644 vendor/github.com/leanovate/gopter/example_fizzbuzz_test.go create mode 100644 vendor/github.com/leanovate/gopter/example_labels_test.go create mode 100644 vendor/github.com/leanovate/gopter/example_sqrt_test.go create mode 100644 vendor/github.com/leanovate/gopter/flag.go create mode 100644 vendor/github.com/leanovate/gopter/flag_test.go create mode 100644 vendor/github.com/leanovate/gopter/formated_reporter.go create mode 100644 vendor/github.com/leanovate/gopter/formated_reporter_test.go create mode 100644 vendor/github.com/leanovate/gopter/gen.go create mode 100644 vendor/github.com/leanovate/gopter/gen/bool.go create mode 100644 vendor/github.com/leanovate/gopter/gen/bool_test.go create mode 100644 vendor/github.com/leanovate/gopter/gen/complex.go create mode 100644 vendor/github.com/leanovate/gopter/gen/complex_shrink.go create mode 100644 vendor/github.com/leanovate/gopter/gen/complex_shrink_test.go create mode 100644 vendor/github.com/leanovate/gopter/gen/complex_test.go create mode 100644 vendor/github.com/leanovate/gopter/gen/const.go create mode 100644 vendor/github.com/leanovate/gopter/gen/const_test.go create mode 100644 vendor/github.com/leanovate/gopter/gen/doc.go create mode 100644 vendor/github.com/leanovate/gopter/gen/fail.go create mode 100644 vendor/github.com/leanovate/gopter/gen/fail_test.go create mode 100644 vendor/github.com/leanovate/gopter/gen/floats.go create mode 100644 vendor/github.com/leanovate/gopter/gen/floats_shrink.go create mode 100644 vendor/github.com/leanovate/gopter/gen/floats_shrink_test.go create mode 100644 vendor/github.com/leanovate/gopter/gen/floats_test.go create mode 100644 vendor/github.com/leanovate/gopter/gen/frequency.go create mode 100644 vendor/github.com/leanovate/gopter/gen/frequency_test.go create mode 100644 vendor/github.com/leanovate/gopter/gen/helper_test.go create mode 100644 vendor/github.com/leanovate/gopter/gen/integers.go create mode 100644 vendor/github.com/leanovate/gopter/gen/integers_shrink.go create mode 100644 vendor/github.com/leanovate/gopter/gen/integers_shrink_test.go create mode 100644 vendor/github.com/leanovate/gopter/gen/integers_test.go create mode 100644 vendor/github.com/leanovate/gopter/gen/map_of.go create mode 100644 vendor/github.com/leanovate/gopter/gen/map_of_test.go create mode 100644 vendor/github.com/leanovate/gopter/gen/map_shrink.go create mode 100644 vendor/github.com/leanovate/gopter/gen/map_shrink_test.go create mode 100644 vendor/github.com/leanovate/gopter/gen/one_of.go create mode 100644 vendor/github.com/leanovate/gopter/gen/one_of_test.go create mode 100644 vendor/github.com/leanovate/gopter/gen/ptr_of.go create mode 100644 vendor/github.com/leanovate/gopter/gen/ptr_of_test.go create mode 100644 vendor/github.com/leanovate/gopter/gen/ptr_shrink.go create mode 100644 vendor/github.com/leanovate/gopter/gen/ptr_shrink_test.go create mode 100644 vendor/github.com/leanovate/gopter/gen/regex.go create mode 100644 vendor/github.com/leanovate/gopter/gen/regex_test.go create mode 100644 vendor/github.com/leanovate/gopter/gen/retry_until.go create mode 100644 vendor/github.com/leanovate/gopter/gen/retry_until_test.go create mode 100644 vendor/github.com/leanovate/gopter/gen/slice_of.go create mode 100644 vendor/github.com/leanovate/gopter/gen/slice_of_test.go create mode 100644 vendor/github.com/leanovate/gopter/gen/slice_shrink.go create mode 100644 vendor/github.com/leanovate/gopter/gen/slice_shrink_test.go create mode 100644 vendor/github.com/leanovate/gopter/gen/string_shrink.go create mode 100644 vendor/github.com/leanovate/gopter/gen/strings.go create mode 100644 vendor/github.com/leanovate/gopter/gen/strings_test.go create mode 100644 vendor/github.com/leanovate/gopter/gen/struct.go create mode 100644 vendor/github.com/leanovate/gopter/gen/struct_test.go create mode 100644 vendor/github.com/leanovate/gopter/gen/time.go create mode 100644 vendor/github.com/leanovate/gopter/gen/time_shrink.go create mode 100644 vendor/github.com/leanovate/gopter/gen/time_shrink_test.go create mode 100644 vendor/github.com/leanovate/gopter/gen/time_test.go create mode 100644 vendor/github.com/leanovate/gopter/gen/weighted.go create mode 100644 vendor/github.com/leanovate/gopter/gen/weighted_test.go create mode 100644 vendor/github.com/leanovate/gopter/gen_parameter_test.go create mode 100644 vendor/github.com/leanovate/gopter/gen_parameters.go create mode 100644 vendor/github.com/leanovate/gopter/gen_result.go create mode 100644 vendor/github.com/leanovate/gopter/gen_result_test.go create mode 100644 vendor/github.com/leanovate/gopter/gen_test.go create mode 100644 vendor/github.com/leanovate/gopter/locked_source.go create mode 100644 vendor/github.com/leanovate/gopter/prop.go create mode 100644 vendor/github.com/leanovate/gopter/prop/check_condition_func.go create mode 100644 vendor/github.com/leanovate/gopter/prop/check_condition_func_test.go create mode 100644 vendor/github.com/leanovate/gopter/prop/convert_result.go create mode 100644 vendor/github.com/leanovate/gopter/prop/convert_result_test.go create mode 100644 vendor/github.com/leanovate/gopter/prop/doc.go create mode 100644 vendor/github.com/leanovate/gopter/prop/error.go create mode 100644 vendor/github.com/leanovate/gopter/prop/error_test.go create mode 100644 vendor/github.com/leanovate/gopter/prop/example_invalidconcat_test.go create mode 100644 vendor/github.com/leanovate/gopter/prop/example_quadratic_test.go create mode 100644 vendor/github.com/leanovate/gopter/prop/example_shrink_test.go create mode 100644 vendor/github.com/leanovate/gopter/prop/example_time_test.go create mode 100644 vendor/github.com/leanovate/gopter/prop/forall.go create mode 100644 vendor/github.com/leanovate/gopter/prop/forall_no_shrink.go create mode 100644 vendor/github.com/leanovate/gopter/prop/forall_no_shrink_test.go create mode 100644 vendor/github.com/leanovate/gopter/prop/forall_test.go create mode 100644 vendor/github.com/leanovate/gopter/prop_arg.go create mode 100644 vendor/github.com/leanovate/gopter/prop_arg_test.go create mode 100644 vendor/github.com/leanovate/gopter/prop_result.go create mode 100644 vendor/github.com/leanovate/gopter/prop_result_test.go create mode 100644 vendor/github.com/leanovate/gopter/prop_test.go create mode 100644 vendor/github.com/leanovate/gopter/properties.go create mode 100644 vendor/github.com/leanovate/gopter/properties_test.go create mode 100644 vendor/github.com/leanovate/gopter/reporter.go create mode 100644 vendor/github.com/leanovate/gopter/runner.go create mode 100644 vendor/github.com/leanovate/gopter/runner_test.go create mode 100644 vendor/github.com/leanovate/gopter/shrink.go create mode 100644 vendor/github.com/leanovate/gopter/shrink_test.go create mode 100644 vendor/github.com/leanovate/gopter/test_parameters.go create mode 100644 vendor/github.com/leanovate/gopter/test_result.go create mode 100644 vendor/github.com/leanovate/gopter/test_result_test.go diff --git a/Gopkg.lock b/Gopkg.lock index b2a395689..067bd220c 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -221,6 +221,16 @@ ] revision = "0dae4fefe7c0e190f7b5a78dac28a1c82cc8d849" +[[projects]] + name = "github.com/leanovate/gopter" + packages = [ + ".", + "gen", + "prop" + ] + revision = "1f4d0ba27bd5df5390eb622bcb458f8f7ac2573c" + version = "v0.2.2" + [[projects]] branch = "master" name = "github.com/lib/pq" @@ -494,6 +504,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "643d4a3862aeaa6f1115edc26fa3f1f3a6822bb17c01d60fcbdf9ce98bf183ac" + inputs-digest = "6c6cfaf48ee2f7f926ed2d063af6a65a505f927e7354fe4613ead8c8a6efb203" solver-name = "gps-cdcl" solver-version = 1 diff --git a/api/models/app.go b/api/models/app.go index 7a4d06805..ced10dabd 100644 --- a/api/models/app.go +++ b/api/models/app.go @@ -115,7 +115,7 @@ func (a *App) Clone() *App { clone.Config[k] = v } } - clone.ID = a.ID + return clone } diff --git a/api/models/app_test.go b/api/models/app_test.go new file mode 100644 index 000000000..e4ea53cf2 --- /dev/null +++ b/api/models/app_test.go @@ -0,0 +1,143 @@ +package models + +import ( + "reflect" + "testing" + "time" + + "github.com/fnproject/fn/api/common" + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/gen" + "github.com/leanovate/gopter/prop" +) + +var stringType = reflect.TypeOf("") +var intType = reflect.TypeOf(0) + +func appReflectType() reflect.Type { + app := App{} + return reflect.TypeOf(app) +} + +func configGenerator() gopter.Gen { + return gen.MapOf(gen.AlphaString(), gen.AlphaString()) +} + +func annotationGenerator() gopter.Gen { + annotation1, _ := EmptyAnnotations().With("anAnnotation1", "value1") + annotation2, _ := EmptyAnnotations().With("anAnnotation2", "value2") + + return gen.OneConstOf(annotation1, annotation2) +} + +func datetimeGenerator() gopter.Gen { + return gen.Time().Map(func(t time.Time) common.DateTime { + return common.DateTime(t) + }) +} + +func appFieldGenerators(t *testing.T) map[string]gopter.Gen { + fieldGens := make(map[string]gopter.Gen) + fieldGens["ID"] = gen.AlphaString() + fieldGens["Name"] = gen.AlphaString() + fieldGens["Config"] = configGenerator() + fieldGens["Annotations"] = annotationGenerator() + fieldGens["SyslogURL"] = gen.AlphaString().Map(func(s string) *string { + return &s + }) + fieldGens["CreatedAt"] = datetimeGenerator() + fieldGens["UpdatedAt"] = datetimeGenerator() + + appFieldCount := appReflectType().NumField() + + if appFieldCount != len(fieldGens) { + t.Fatalf("App struct field count, %d, does not match app generator field count, %d", appFieldCount, len(fieldGens)) + } + + return fieldGens +} + +func appGenerator(t *testing.T) gopter.Gen { + return gen.Struct(appReflectType(), appFieldGenerators(t)) +} + +func novelValue(t *testing.T, originalInstance reflect.Value, fieldName string, fieldGen gopter.Gen) (interface{}, reflect.Value) { + newValue, result := fieldGen.Sample() + if !result { + t.Fatalf("Error sampling field generator, %s, %v", fieldName, result) + } + + field := originalInstance.FieldByName(fieldName) + currentValue := field.Interface() + + for i := 0; i < 100; i++ { + if fieldName == "Annotations" { + if !newValue.(Annotations).Equals(currentValue.(Annotations)) { + break + } + } else { + if newValue != currentValue { + break + } + } + newValue, result = fieldGen.Sample() + if !result { + t.Fatalf("Error sampling field generator, %s, %v", fieldName, result) + } + + if i == 99 { + t.Fatalf("Failed to generate a novel value for field, %s", fieldName) + } + } + return currentValue, reflect.ValueOf(newValue) +} + +func TestAppEquality(t *testing.T) { + properties := gopter.NewProperties(nil) + + properties.Property("An app should always equal itself", prop.ForAll( + func(app App) bool { + return app.Equals(&app) + }, + appGenerator(t), + )) + + properties.Property("An app should always equal a clone of itself", prop.ForAll( + func(app App) bool { + clone := app.Clone() + return app.Equals(clone) + }, + appGenerator(t), + )) + + appFieldGens := appFieldGenerators(t) + + properties.Property("An app should never match a modified version of itself", prop.ForAll( + func(app App) bool { + for fieldName, fieldGen := range appFieldGens { + + if fieldName == "CreatedAt" || + fieldName == "UpdatedAt" { + continue + } + + currentValue, newValue := novelValue(t, reflect.ValueOf(app), fieldName, fieldGen) + + clone := app.Clone() + s := reflect.ValueOf(clone).Elem() + field := s.FieldByName(fieldName) + + field.Set(newValue) + + if app.Equals(clone) { + t.Errorf("Changed field, %s, from {%v} to {%v}, but still equal.", fieldName, currentValue, newValue) + return false + } + } + return true + }, + appGenerator(t), + )) + + properties.TestingRun(t) +} diff --git a/api/models/fn_test.go b/api/models/fn_test.go new file mode 100644 index 000000000..28598ecb6 --- /dev/null +++ b/api/models/fn_test.go @@ -0,0 +1,109 @@ +package models + +import ( + "reflect" + "testing" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/gen" + "github.com/leanovate/gopter/prop" +) + +func fnReflectType() reflect.Type { + fn := Fn{} + return reflect.TypeOf(fn) +} + +func resourceConfigGenerator(t *testing.T) gopter.Gen { + fieldGens := make(map[string]gopter.Gen) + + fieldGens["Memory"] = gen.UInt64() + fieldGens["Timeout"] = gen.Int32() + fieldGens["IdleTimeout"] = gen.Int32() + + resourceConfig := ResourceConfig{} + resourceConfigFieldCount := reflect.TypeOf(resourceConfig).NumField() + + if resourceConfigFieldCount != len(fieldGens) { + t.Fatalf("Fn struct field count, %d, does not match fn generator field count, %d", resourceConfigFieldCount, len(fieldGens)) + } + + return gen.Struct(reflect.TypeOf(resourceConfig), fieldGens) +} + +func fnFieldGenerators(t *testing.T) map[string]gopter.Gen { + fieldGens := make(map[string]gopter.Gen) + + fieldGens["ID"] = gen.AlphaString() + fieldGens["Name"] = gen.AlphaString() + fieldGens["AppID"] = gen.AlphaString() + fieldGens["Image"] = gen.AlphaString() + fieldGens["Config"] = configGenerator() + fieldGens["ResourceConfig"] = resourceConfigGenerator(t) + fieldGens["Annotations"] = annotationGenerator() + fieldGens["CreatedAt"] = datetimeGenerator() + fieldGens["UpdatedAt"] = datetimeGenerator() + fieldGens["Format"] = gen.AlphaString() + + fnFieldCount := fnReflectType().NumField() + + if fnFieldCount != len(fieldGens) { + t.Fatalf("Fn struct field count, %d, does not match fn generator field count, %d", fnFieldCount, len(fieldGens)) + } + + return fieldGens +} + +func fnGenerator(t *testing.T) gopter.Gen { + return gen.Struct(fnReflectType(), fnFieldGenerators(t)) +} + +func TestFnEquality(t *testing.T) { + properties := gopter.NewProperties(nil) + + properties.Property("A fn should always equal itself", prop.ForAll( + func(fn Fn) bool { + return fn.Equals(&fn) + }, + fnGenerator(t), + )) + + properties.Property("A fn should always equal a clone of itself", prop.ForAll( + func(fn Fn) bool { + clone := fn.Clone() + return fn.Equals(clone) + }, + fnGenerator(t), + )) + + fnFieldGens := fnFieldGenerators(t) + + properties.Property("A fn should never match a modified version of itself", prop.ForAll( + func(fn Fn) bool { + for fieldName, fieldGen := range fnFieldGens { + + if fieldName == "CreatedAt" || + fieldName == "UpdatedAt" { + continue + } + + currentValue, newValue := novelValue(t, reflect.ValueOf(fn), fieldName, fieldGen) + + clone := fn.Clone() + s := reflect.ValueOf(clone).Elem() + field := s.FieldByName(fieldName) + + field.Set(newValue) + + if fn.Equals(clone) { + t.Errorf("Changed field, %s, from {%v} to {%v}, but still equal.", fieldName, currentValue, newValue) + return false + } + } + return true + }, + fnGenerator(t), + )) + + properties.TestingRun(t) +} diff --git a/api/models/trigger_test.go b/api/models/trigger_test.go index f81dac3d3..8fc3cf1ce 100644 --- a/api/models/trigger_test.go +++ b/api/models/trigger_test.go @@ -2,7 +2,12 @@ package models import ( "encoding/json" + "reflect" "testing" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/gen" + "github.com/leanovate/gopter/prop" ) var openEmptyJSON = `{"id":"","name":"","app_id":"","fn_id":"","created_at":"0001-01-01T00:00:00.000Z","updated_at":"0001-01-01T00:00:00.000Z","type":"","source":""` @@ -62,3 +67,83 @@ func TestTriggerValidate(t *testing.T) { } } } + +func triggerReflectType() reflect.Type { + trigger := Trigger{} + return reflect.TypeOf(trigger) +} + +func triggerFieldGenerators(t *testing.T) map[string]gopter.Gen { + fieldGens := make(map[string]gopter.Gen) + fieldGens["ID"] = gen.AlphaString() + fieldGens["Name"] = gen.AlphaString() + fieldGens["AppID"] = gen.AlphaString() + fieldGens["FnID"] = gen.AlphaString() + fieldGens["CreatedAt"] = datetimeGenerator() + fieldGens["UpdatedAt"] = datetimeGenerator() + fieldGens["Type"] = gen.AlphaString() + fieldGens["Source"] = gen.AlphaString() + fieldGens["Annotations"] = annotationGenerator() + + triggerFieldCount := triggerReflectType().NumField() + + if triggerFieldCount != len(fieldGens) { + t.Fatalf("Trigger struct field count, %d, does not match trigger generator field count, %d", triggerFieldCount, len(fieldGens)) + } + + return fieldGens +} + +func triggerGenerator(t *testing.T) gopter.Gen { + return gen.Struct(triggerReflectType(), triggerFieldGenerators(t)) +} + +func TestTriggerEquality(t *testing.T) { + properties := gopter.NewProperties(nil) + + properties.Property("A trigger should always equal itself", prop.ForAll( + func(trigger Trigger) bool { + return trigger.Equals(&trigger) + }, + triggerGenerator(t), + )) + + properties.Property("A trigger should always equal a clone of itself", prop.ForAll( + func(trigger Trigger) bool { + clone := trigger.Clone() + return trigger.Equals(clone) + }, + triggerGenerator(t), + )) + + triggerFieldGens := triggerFieldGenerators(t) + + properties.Property("A trigger should never match a modified version of itself", prop.ForAll( + func(trigger Trigger) bool { + for fieldName, fieldGen := range triggerFieldGens { + + if fieldName == "CreatedAt" || + fieldName == "UpdatedAt" { + continue + } + + currentValue, newValue := novelValue(t, reflect.ValueOf(trigger), fieldName, fieldGen) + + clone := trigger.Clone() + s := reflect.ValueOf(clone).Elem() + field := s.FieldByName(fieldName) + + field.Set(newValue) + + if trigger.Equals(clone) { + t.Errorf("Changed field, %s, from {%v} to {%v}, but still equal.", fieldName, currentValue, newValue) + return false + } + } + return true + }, + triggerGenerator(t), + )) + + properties.TestingRun(t) +} diff --git a/vendor/github.com/leanovate/gopter/.gitignore b/vendor/github.com/leanovate/gopter/.gitignore new file mode 100644 index 000000000..45371a9fa --- /dev/null +++ b/vendor/github.com/leanovate/gopter/.gitignore @@ -0,0 +1,30 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof + +bin/ +*.iml +.idea/ +coverage.txt +.pkg.coverage diff --git a/vendor/github.com/leanovate/gopter/.travis.yml b/vendor/github.com/leanovate/gopter/.travis.yml new file mode 100644 index 000000000..954d52609 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/.travis.yml @@ -0,0 +1,12 @@ +sudo: false +language: go +go: +- 1.x +script: make all coverage refreshGodoc +before_install: + - pip install --user codecov +after_success: + - codecov +notifications: + slack: + secure: M0PgOUB0Kzn0maWtd6NNtiKYINxMY/7zgbbDpb8mAa6NTPYuypEYkUgmo6HC74BzDWSjkJaLQOeZrumrOuJUKbGdT+eEYR1pXColp2qb/WxnSCAwlL9iM/k7pj6nIRUdlP7l6WX0QB/DNh+BC/9STHrcSKjBpUu38oO9CwT7klSj2hfPMjzcx7EO4f8pjSfwCrIyYbANKxLzP0lr4PcbdY/ZeGbc8R5/m9torzPjS2YXDl0tQQ7pvSS8UVToLfL0m+omp9A/lOu0n6FpdNIkof2Eu9qWJqsI7jy+Pi+8DGbfEyxSLKAhDiTn0nfO/5nwqWIBhUaVACBDxpaH6ewpiuMbs4RO+wNaEEuVEH8QMKZOx9PGgnzNJ3zZ5Hfm+FP8zBrwrKlsjUoy31waGFjgua2ne4X0wa+Ld4iFEsj+XoMKa1oxRKRXYFhyEywalwgBVjXH2+ZCMlFGV3QxaV5gVuYcfEuNQ4pOlJpk+WSgm7yfXEX2qosOk2p91yGyX2Msbe3B7Ov3PXVzs2CshIsYasHr46pLplMvG6Z+712TPsrFS0zhb8FAsm/Vd7xX2xxmNS/uffh3RgFzeZxg8S9/ObVq+JBkZAtK4j0SwLVsOkjI4W3yUVgfxvhnAM1iLzzeSyD64BSo1VyUZu1eSJ9YxJ1+K6ldo0u0hj2VHwO1vUE= diff --git a/vendor/github.com/leanovate/gopter/CHANGELOG.md b/vendor/github.com/leanovate/gopter/CHANGELOG.md new file mode 100644 index 000000000..40e7cc3c8 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/CHANGELOG.md @@ -0,0 +1,65 @@ +# Change log + +## [Unreleased] +### Additions +- `gopter.GenParameters` now has a `CloneWithSeed(seed int64)` function to + temparary copies to create rerunable sections of code. +- Added `gopter.Gen.MapResult` for power-user mappings +- Added `gopter.DeriveGen` to derive a generator and it's shrinker from a + bi-directional mapping (`gopter.BiMapper`) + +### Changed +- Refactored `commands` package under the hood to allow the use of mutable state. + Re-runability of commands is provided by invoking the `commands.GenInitialState` + generator with the same `gopter.GenParameters`. Of course `commands.GenInitialState` + is supposed to create the same state for the same parameters every time. +- Fixed a bug in `commands` that might lead to shrinked command sequences not + satisfying the precondtions. +- `commands.Command.PostCondition` was called with the state before running the command. It makes + much more sense to first do `commands.Command.NextState` and then `commands.Command.PostCondition` +- `commands.Commands.NewSystemUnderTest` now takes has an argument `initialState commands.State` to + allow implementators to create/bootstrap a system under test based on an arbitrary initial state. + So far examples were just using a constant initial state ... which is a bit boring. +- Fixed: Actually use `commands.Commands.InitialPreCondition` as sieve for + `commands.Commands.GenInitialState` +- Gen.Map and Shrink.Map now accept `interface{}` instead of `func (interface{}) interface{}` + + This allows cleaner mapping functions without type conversion. E.g. instead of + + ```Go + gen.AnyString().Map(function (v interface{}) interface{} { + return strings.ToUpper(v.(string)) + }) + ``` + you can (and should) now write + + ```Go + gen.AnyString().Map(function (v string) string { + return strings.ToUpper(v) + }) + ``` +- Correspondingly Gen.SuchThat now also ccept `interface{}` instead of `func (interface{}) bool` + + This allows cleaner sieve functions without type conversion. E.g. instead of + + ```Go + gen.AnyString().SuchThat(function (v interface{}) bool { + return HasPrefix(v.(string), "P") + }) + ``` + you can (and should) now write + + ```Go + gen.AnyString().SuchThat(function (v string) bool { + return HasPrefix(v, "P") + }) + ``` +- Gen.FlatMap now has a second parameter `resultType reflect.Type` defining the result type of the mapped generator +- Reason for these changes: The original `Map` and `FlatMap` had a recurring issue with empty results. If the original generator created an empty result there was no clean way to determine the result type of the mapped generator. The new version fixes this by extracting the return type of the mapping functions. + +## [0.1] - 2016-04-30 +### Added +- Initial implementation. + +[Unreleased]: https://github.com/leanovate/gopter/compare/v0.1...HEAD +[0.1]: https://github.com/leanovate/gopter/tree/v0.1 diff --git a/vendor/github.com/leanovate/gopter/LICENSE b/vendor/github.com/leanovate/gopter/LICENSE new file mode 100644 index 000000000..072084c00 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 leanovate + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/leanovate/gopter/Makefile b/vendor/github.com/leanovate/gopter/Makefile new file mode 100644 index 000000000..e06116ca4 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/Makefile @@ -0,0 +1,41 @@ +PACKAGES=$(shell go list ./...) + +all: format + @go get github.com/smartystreets/goconvey + @go build -v ./... + +format: + @echo "--> Running go fmt" + @go fmt ./... + +test: + @echo "--> Running tests" + @go test -v ./... + @$(MAKE) vet + +coverage: + @echo "--> Running tests with coverage" + @echo "" > coverage.txt + for pkg in $(shell go list ./...); do \ + (go test -coverprofile=.pkg.coverage -covermode=atomic -v $$pkg && \ + cat .pkg.coverage >> coverage.txt) || exit 1; \ + done + @rm .pkg.coverage + @$(MAKE) vet + +vet: + @go tool vet 2>/dev/null ; if [ $$? -eq 3 ]; then \ + go get golang.org/x/tools/cmd/vet; \ + fi + @echo "--> Running go tool vet $(VETARGS)" + @find . -name "*.go" | grep -v "./Godeps/" | xargs go tool vet $(VETARGS); if [ $$? -eq 1 ]; then \ + echo ""; \ + echo "Vet found suspicious constructs. Please check the reported constructs"; \ + echo "and fix them if necessary before submitting the code for reviewal."; \ + fi + +refreshGodoc: + @echo "--> Refreshing godoc.org" + for pkg in $(shell go list ./...); do \ + curl -d "path=$$pkg" https://godoc.org/-/refresh ; \ + done diff --git a/vendor/github.com/leanovate/gopter/README.md b/vendor/github.com/leanovate/gopter/README.md new file mode 100644 index 000000000..44cfbfdb8 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/README.md @@ -0,0 +1,44 @@ +# GOPTER + +... the GOlang Property TestER +[![Build Status](https://travis-ci.org/leanovate/gopter.svg?branch=master)](https://travis-ci.org/leanovate/gopter) +[![codecov](https://codecov.io/gh/leanovate/gopter/branch/master/graph/badge.svg)](https://codecov.io/gh/leanovate/gopter) +[![GoDoc](https://godoc.org/github.com/leanovate/gopter?status.png)](https://godoc.org/github.com/leanovate/gopter) + +[Change Log](CHANGELOG.md) + +## Synopsis + +Gopter tries to bring the goodness of [ScalaCheck](https://www.scalacheck.org/) (and implicitly, the goodness of [QuickCheck](http://hackage.haskell.org/package/QuickCheck)) to Go. +It can also be seen as a more sophisticated version of the testing/quick package. + +Main differences to ScalaCheck: + +* It is Go ... duh +* ... nevertheless: Do not expect the same typesafety and elegance as in ScalaCheck. +* For simplicity [Shrink](https://www.scalacheck.org/files/scalacheck_2.11-1.14.0-api/index.html#org.scalacheck.Shrink) has become part of the generators. They can still be easily changed if necessary. +* There is no [Pretty](https://www.scalacheck.org/files/scalacheck_2.11-1.14.0-api/index.html#org.scalacheck.util.Pretty) ... so far gopter feels quite comfortable being ugly. +* A generator for regex matches +* No parallel commands ... yet? + +Main differences to the testing/quick package: + +* Much tighter control over generators +* Shrinkers, i.e. automatically find the minimum value falsifying a property +* A generator for regex matches (already mentioned that ... but it's cool) +* Support for stateful tests + +## Documentation + +Current godocs: + +* [gopter](https://godoc.org/github.com/leanovate/gopter): Main interfaces +* [gopter/gen](https://godoc.org/github.com/leanovate/gopter/gen): All commonly used generators +* [gopter/prop](https://godoc.org/github.com/leanovate/gopter/prop): Common helpers to create properties from a condition function and specific generators +* [gopter/arbitrary](https://godoc.org/github.com/leanovate/gopter/arbitrary): Helpers automatically combine generators for arbitrary types +* [gopter/commands](https://godoc.org/github.com/leanovate/gopter/commands): Helpers to create stateful tests based on arbitrary commands +* [gopter/convey](https://godoc.org/github.com/leanovate/gopter/convey): Helpers used by gopter inside goconvey tests + +## License + +[MIT Licence](http://opensource.org/licenses/MIT) diff --git a/vendor/github.com/leanovate/gopter/arbitrary/arbitraries.go b/vendor/github.com/leanovate/gopter/arbitrary/arbitraries.go new file mode 100644 index 000000000..f6983212a --- /dev/null +++ b/vendor/github.com/leanovate/gopter/arbitrary/arbitraries.go @@ -0,0 +1,41 @@ +package arbitrary + +import ( + "reflect" + "time" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/gen" +) + +// Arbitraries defines a context to generate arbitrary values of any kind. +// Values are generated by either providing a generator for a specific type +// or by creating a generator on the fly using golang reflection. +type Arbitraries struct { + generators map[reflect.Type]gopter.Gen +} + +// DefaultArbitraries creates a default arbitrary context with the widest +// possible ranges for all types. +func DefaultArbitraries() *Arbitraries { + return &Arbitraries{ + generators: map[reflect.Type]gopter.Gen{ + reflect.TypeOf(time.Now()): gen.Time(), + }, + } +} + +// GenForType gets a generator for a generator for a type +func (a *Arbitraries) GenForType(rt reflect.Type) gopter.Gen { + if gen, ok := a.generators[rt]; ok { + return gen + } + return a.genForKind(rt) +} + +// RegisterGen registers a generator +func (a *Arbitraries) RegisterGen(gen gopter.Gen) { + result := gen(gopter.DefaultGenParameters()) + rt := result.ResultType + a.generators[rt] = gen +} diff --git a/vendor/github.com/leanovate/gopter/arbitrary/doc.go b/vendor/github.com/leanovate/gopter/arbitrary/doc.go new file mode 100644 index 000000000..1d134d0a3 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/arbitrary/doc.go @@ -0,0 +1,32 @@ +/* +Package arbitrary contains helpers to create contexts of arbitrary values, i.e. +automatically combine generators as needed using reflection. + +A simple example might look like this: + + func TestIntParse(t *testing.T) { + properties := gopter.NewProperties(nil) + arbitraries := arbitrary.DefaultArbitraries() + + properties.Property("printed integers can be parsed", arbitraries.ForAll( + func(a int64) bool { + str := fmt.Sprintf("%d", a) + parsed, err := strconv.ParseInt(str, 10, 64) + return err == nil && parsed == a + })) + + properties.TestingRun(t) + } + +Be aware that by default always the most generic generators are used. I.e. in +the example above the gen.Int64 generator will be used and the condition will +be tested for the full range of int64 numbers. + +To adapt this one might register a generator for a specific type in an +arbitraries context. I.e. by adding + + arbitraries.RegisterGen(gen.Int64Range(-1000, 1000)) + +any generated int64 number will be between -1000 and 1000. +*/ +package arbitrary diff --git a/vendor/github.com/leanovate/gopter/arbitrary/example_arbitrary_struct_test.go b/vendor/github.com/leanovate/gopter/arbitrary/example_arbitrary_struct_test.go new file mode 100644 index 000000000..dc63c6fe7 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/arbitrary/example_arbitrary_struct_test.go @@ -0,0 +1,64 @@ +package arbitrary_test + +import ( + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/arbitrary" +) + +type MyStringType string +type MyInt8Type int8 +type MyInt16Type int16 +type MyInt32Type int32 +type MyInt64Type int64 +type MyUInt8Type uint8 +type MyUInt16Type uint16 +type MyUInt32Type uint32 +type MyUInt64Type uint64 + +type Foo struct { + Name MyStringType + Id1 MyInt8Type + Id2 MyInt16Type + Id3 MyInt32Type + Id4 MyInt64Type + Id5 MyUInt8Type + Id6 MyUInt16Type + Id7 MyUInt32Type + Id8 MyUInt64Type +} + +func Example_arbitrary_structs() { + parameters := gopter.DefaultTestParametersWithSeed(1234) // Example should generate reproducable results, otherwise DefaultTestParameters() will suffice + + arbitraries := arbitrary.DefaultArbitraries() + + properties := gopter.NewProperties(parameters) + + properties.Property("MyInt64", arbitraries.ForAll( + func(id MyInt64Type) bool { + return id > -1000 + })) + properties.Property("MyUInt32Type", arbitraries.ForAll( + func(id MyUInt32Type) bool { + return id < 2000 + })) + properties.Property("Foo", arbitraries.ForAll( + func(foo *Foo) bool { + return true + })) + properties.Property("Foo2", arbitraries.ForAll( + func(foo Foo) bool { + return true + })) + + properties.Run(gopter.ConsoleReporter(false)) + // Output: + // ! MyInt64: Falsified after 6 passed tests. + // ARG_0: -1000 + // ARG_0_ORIGINAL (54 shrinks): -1601066829744837253 + // ! MyUInt32Type: Falsified after 0 passed tests. + // ARG_0: 2000 + // ARG_0_ORIGINAL (23 shrinks): 2161922319 + // + Foo: OK, passed 100 tests. + // + Foo2: OK, passed 100 tests. +} diff --git a/vendor/github.com/leanovate/gopter/arbitrary/example_parseint_test.go b/vendor/github.com/leanovate/gopter/arbitrary/example_parseint_test.go new file mode 100644 index 000000000..932c552af --- /dev/null +++ b/vendor/github.com/leanovate/gopter/arbitrary/example_parseint_test.go @@ -0,0 +1,28 @@ +package arbitrary_test + +import ( + "fmt" + "strconv" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/arbitrary" +) + +func Example_parseint() { + parameters := gopter.DefaultTestParametersWithSeed(1234) // Example should generate reproducable results, otherwise DefaultTestParameters() will suffice + + arbitraries := arbitrary.DefaultArbitraries() + properties := gopter.NewProperties(parameters) + + properties.Property("printed integers can be parsed", arbitraries.ForAll( + func(a int64) bool { + str := fmt.Sprintf("%d", a) + parsed, err := strconv.ParseInt(str, 10, 64) + return err == nil && parsed == a + })) + + // When using testing.T you might just use: properties.TestingRun(t) + properties.Run(gopter.ConsoleReporter(false)) + // Output: + // + printed integers can be parsed: OK, passed 100 tests. +} diff --git a/vendor/github.com/leanovate/gopter/arbitrary/example_quadratic_test.go b/vendor/github.com/leanovate/gopter/arbitrary/example_quadratic_test.go new file mode 100644 index 000000000..9c8f91922 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/arbitrary/example_quadratic_test.go @@ -0,0 +1,78 @@ +package arbitrary_test + +import ( + "errors" + "math/cmplx" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/arbitrary" + "github.com/leanovate/gopter/gen" +) + +type QudraticEquation struct { + A, B, C complex128 +} + +func (q *QudraticEquation) Eval(x complex128) complex128 { + return q.A*x*x + q.B*x + q.C +} + +func (q *QudraticEquation) Solve() (complex128, complex128, error) { + if q.A == 0 { + return 0, 0, errors.New("No solution") + } + v := q.B*q.B - 4*q.A*q.C + v = cmplx.Sqrt(v) + return (-q.B + v) / 2 / q.A, (-q.B - v) / 2 / q.A, nil +} + +func Example_quadratic() { + parameters := gopter.DefaultTestParametersWithSeed(1234) // Example should generate reproducable results, otherwise DefaultTestParameters() will suffice + + arbitraries := arbitrary.DefaultArbitraries() + arbitraries.RegisterGen(gen.Complex128Box(-1e8-1e8i, 1e8+1e8i)) // Only use complex values within a range + + properties := gopter.NewProperties(parameters) + + properties.Property("Quadratic equations can be solved (as pointer)", arbitraries.ForAll( + func(quadratic *QudraticEquation) bool { + x1, x2, err := quadratic.Solve() + if err != nil { + return true + } + + return cmplx.Abs(quadratic.Eval(x1)) < 1e-5 && cmplx.Abs(quadratic.Eval(x2)) < 1e-5 + })) + + properties.Property("Quadratic equations can be solved (as struct)", arbitraries.ForAll( + func(quadratic QudraticEquation) bool { + x1, x2, err := quadratic.Solve() + if err != nil { + return true + } + + return cmplx.Abs(quadratic.Eval(x1)) < 1e-5 && cmplx.Abs(quadratic.Eval(x2)) < 1e-5 + })) + + properties.Property("Quadratic equations can be solved alternative", arbitraries.ForAll( + func(a, b, c complex128) bool { + quadratic := &QudraticEquation{ + A: a, + B: b, + C: c, + } + x1, x2, err := quadratic.Solve() + if err != nil { + return true + } + + return cmplx.Abs(quadratic.Eval(x1)) < 1e-5 && cmplx.Abs(quadratic.Eval(x2)) < 1e-5 + })) + + // When using testing.T you might just use: properties.TestingRun(t) + properties.Run(gopter.ConsoleReporter(false)) + // Output: + // + Quadratic equations can be solved (as pointer): OK, passed 100 tests. + // + Quadratic equations can be solved (as struct): OK, passed 100 tests. + // + Quadratic equations can be solved alternative: OK, passed 100 tests. +} diff --git a/vendor/github.com/leanovate/gopter/arbitrary/forall.go b/vendor/github.com/leanovate/gopter/arbitrary/forall.go new file mode 100644 index 000000000..22061bd59 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/arbitrary/forall.go @@ -0,0 +1,33 @@ +package arbitrary + +import ( + "fmt" + "reflect" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/prop" +) + +/* +ForAll creates a property that requires the check condition to be true for all +values, if the condition falsiies the generated values will be shrinked. + +"condition" has to be a function with the any number of parameters that can +generated in context of the Arbitraries. The function may return a simple bool, +a *PropResult, a boolean with error or a *PropResult with error. +*/ +func (a *Arbitraries) ForAll(condition interface{}) gopter.Prop { + conditionVal := reflect.ValueOf(condition) + conditionType := conditionVal.Type() + + if conditionType.Kind() != reflect.Func { + return prop.ErrorProp(fmt.Errorf("Param of ForrAll has to be a func: %v", conditionType.Kind())) + } + + gens := make([]gopter.Gen, conditionType.NumIn()) + for i := 0; i < conditionType.NumIn(); i++ { + gens[i] = a.GenForType(conditionType.In(i)) + } + + return prop.ForAll(condition, gens...) +} diff --git a/vendor/github.com/leanovate/gopter/arbitrary/gen_for_kind.go b/vendor/github.com/leanovate/gopter/arbitrary/gen_for_kind.go new file mode 100644 index 000000000..f02bac6f5 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/arbitrary/gen_for_kind.go @@ -0,0 +1,333 @@ +package arbitrary + +import ( + "reflect" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/gen" +) + +func mapBoolish(to reflect.Type, v interface{}) interface{} { + value := reflect.ValueOf(v) + result := reflect.New(to).Elem() + result.SetBool(value.Bool()) + return result.Interface() +} + +func mapIntish(to reflect.Type, v interface{}) interface{} { + value := reflect.ValueOf(v) + result := reflect.New(to).Elem() + result.SetInt(value.Int()) + return result.Interface() +} + +func mapUintish(to reflect.Type, v interface{}) interface{} { + value := reflect.ValueOf(v) + result := reflect.New(to).Elem() + result.SetUint(value.Uint()) + return result.Interface() +} + +func mapFloatish(to reflect.Type, v interface{}) interface{} { + value := reflect.ValueOf(v) + result := reflect.New(to).Elem() + result.SetFloat(value.Float()) + return result.Interface() +} + +func mapComplexish(to reflect.Type, v interface{}) interface{} { + value := reflect.ValueOf(v) + result := reflect.New(to).Elem() + result.SetComplex(value.Complex()) + return result.Interface() +} + +func mapStringish(to reflect.Type, v interface{}) interface{} { + value := reflect.ValueOf(v) + result := reflect.New(to).Elem() + result.SetString(value.String()) + return result.Interface() +} + +func (a *Arbitraries) genForKind(rt reflect.Type) gopter.Gen { + switch rt.Kind() { + case reflect.Bool: + return gen.Bool().MapResult(func(result *gopter.GenResult) *gopter.GenResult { + return &gopter.GenResult{ + Labels: result.Labels, + ResultType: rt, + Result: mapBoolish(rt, result.Result), + Sieve: func(v interface{}) bool { + return result.Sieve == nil || result.Sieve(mapBoolish(reflect.TypeOf(bool(false)), v)) + }, + Shrinker: gopter.NoShrinker, + } + }) + case reflect.Int: + return gen.Int().MapResult(func(result *gopter.GenResult) *gopter.GenResult { + return &gopter.GenResult{ + Labels: result.Labels, + ResultType: rt, + Result: mapIntish(rt, result.Result), + Sieve: func(v interface{}) bool { + return result.Sieve == nil || result.Sieve(mapIntish(reflect.TypeOf(int(0)), v)) + }, + Shrinker: func(v interface{}) gopter.Shrink { + return result.Shrinker(mapIntish(reflect.TypeOf(int(0)), v)).Map(func(s interface{}) interface{} { + return mapIntish(rt, s) + }) + }, + } + }) + case reflect.Uint: + return gen.UInt().MapResult(func(result *gopter.GenResult) *gopter.GenResult { + return &gopter.GenResult{ + Labels: result.Labels, + ResultType: rt, + Result: mapUintish(rt, result.Result), + Sieve: func(v interface{}) bool { + return result.Sieve == nil || result.Sieve(mapUintish(reflect.TypeOf(uint(0)), v)) + }, + Shrinker: func(v interface{}) gopter.Shrink { + return result.Shrinker(mapUintish(reflect.TypeOf(uint(0)), v)).Map(func(s interface{}) interface{} { + return mapUintish(rt, s) + }) + }, + } + }) + case reflect.Int8: + return gen.Int8().MapResult(func(result *gopter.GenResult) *gopter.GenResult { + return &gopter.GenResult{ + Labels: result.Labels, + ResultType: rt, + Result: mapIntish(rt, result.Result), + Sieve: func(v interface{}) bool { + return result.Sieve == nil || result.Sieve(mapIntish(reflect.TypeOf(int8(0)), v)) + }, + Shrinker: func(v interface{}) gopter.Shrink { + return result.Shrinker(mapIntish(reflect.TypeOf(int8(0)), v)).Map(func(s interface{}) interface{} { + return mapIntish(rt, s) + }) + }, + } + }) + case reflect.Uint8: + return gen.UInt8().MapResult(func(result *gopter.GenResult) *gopter.GenResult { + return &gopter.GenResult{ + Labels: result.Labels, + ResultType: rt, + Result: mapUintish(rt, result.Result), + Sieve: func(v interface{}) bool { + return result.Sieve == nil || result.Sieve(mapUintish(reflect.TypeOf(uint8(0)), v)) + }, + Shrinker: func(v interface{}) gopter.Shrink { + return result.Shrinker(mapUintish(reflect.TypeOf(uint8(0)), v)).Map(func(s interface{}) interface{} { + return mapUintish(rt, s) + }) + }, + } + }) + case reflect.Int16: + return gen.Int16().MapResult(func(result *gopter.GenResult) *gopter.GenResult { + return &gopter.GenResult{ + Labels: result.Labels, + ResultType: rt, + Result: mapIntish(rt, result.Result), + Sieve: func(v interface{}) bool { + return result.Sieve == nil || result.Sieve(mapIntish(reflect.TypeOf(int16(0)), v)) + }, + Shrinker: func(v interface{}) gopter.Shrink { + return result.Shrinker(mapIntish(reflect.TypeOf(int16(0)), v)).Map(func(s interface{}) interface{} { + return mapIntish(rt, s) + }) + }, + } + }) + case reflect.Uint16: + return gen.UInt16().MapResult(func(result *gopter.GenResult) *gopter.GenResult { + return &gopter.GenResult{ + Labels: result.Labels, + ResultType: rt, + Result: mapUintish(rt, result.Result), + Sieve: func(v interface{}) bool { + return result.Sieve == nil || result.Sieve(mapUintish(reflect.TypeOf(uint16(0)), v)) + }, + Shrinker: func(v interface{}) gopter.Shrink { + return result.Shrinker(mapUintish(reflect.TypeOf(uint16(0)), v)).Map(func(s interface{}) interface{} { + return mapUintish(rt, s) + }) + }, + } + }) + case reflect.Int32: + return gen.Int32().MapResult(func(result *gopter.GenResult) *gopter.GenResult { + return &gopter.GenResult{ + Labels: result.Labels, + ResultType: rt, + Result: mapIntish(rt, result.Result), + Sieve: func(v interface{}) bool { + return result.Sieve == nil || result.Sieve(mapIntish(reflect.TypeOf(int32(0)), v)) + }, + Shrinker: func(v interface{}) gopter.Shrink { + return result.Shrinker(mapIntish(reflect.TypeOf(int32(0)), v)).Map(func(s interface{}) interface{} { + return mapIntish(rt, s) + }) + }, + } + }) + case reflect.Uint32: + return gen.UInt32().MapResult(func(result *gopter.GenResult) *gopter.GenResult { + return &gopter.GenResult{ + Labels: result.Labels, + ResultType: rt, + Result: mapUintish(rt, result.Result), + Sieve: func(v interface{}) bool { + return result.Sieve == nil || result.Sieve(mapUintish(reflect.TypeOf(uint32(0)), v)) + }, + Shrinker: func(v interface{}) gopter.Shrink { + return result.Shrinker(mapUintish(reflect.TypeOf(uint32(0)), v)).Map(func(s interface{}) interface{} { + return mapUintish(rt, s) + }) + }, + } + }) + case reflect.Int64: + return gen.Int64().MapResult(func(result *gopter.GenResult) *gopter.GenResult { + return &gopter.GenResult{ + Labels: result.Labels, + ResultType: rt, + Result: mapIntish(rt, result.Result), + Sieve: func(v interface{}) bool { + return result.Sieve == nil || result.Sieve(mapIntish(reflect.TypeOf(int32(0)), v)) + }, + Shrinker: func(v interface{}) gopter.Shrink { + return result.Shrinker(mapIntish(reflect.TypeOf(int64(0)), v)).Map(func(s interface{}) interface{} { + return mapIntish(rt, s) + }) + }, + } + }) + case reflect.Uint64: + return gen.UInt64().MapResult(func(result *gopter.GenResult) *gopter.GenResult { + return &gopter.GenResult{ + Labels: result.Labels, + ResultType: rt, + Result: mapUintish(rt, result.Result), + Sieve: func(v interface{}) bool { + return result.Sieve == nil || result.Sieve(mapUintish(reflect.TypeOf(uint64(0)), v)) + }, + Shrinker: func(v interface{}) gopter.Shrink { + return result.Shrinker(mapUintish(reflect.TypeOf(uint64(0)), v)).Map(func(s interface{}) interface{} { + return mapUintish(rt, s) + }) + }, + } + }) + case reflect.Float32: + return gen.Float32().MapResult(func(result *gopter.GenResult) *gopter.GenResult { + return &gopter.GenResult{ + Labels: result.Labels, + ResultType: rt, + Result: mapFloatish(rt, result.Result), + Sieve: func(v interface{}) bool { + return result.Sieve == nil || result.Sieve(mapFloatish(reflect.TypeOf(float32(0)), v)) + }, + Shrinker: func(v interface{}) gopter.Shrink { + return result.Shrinker(mapFloatish(reflect.TypeOf(float32(0)), v)).Map(func(s interface{}) interface{} { + return mapFloatish(rt, s) + }) + }, + } + }) + case reflect.Float64: + return gen.Float64().MapResult(func(result *gopter.GenResult) *gopter.GenResult { + return &gopter.GenResult{ + Labels: result.Labels, + ResultType: rt, + Result: mapFloatish(rt, result.Result), + Sieve: func(v interface{}) bool { + return result.Sieve == nil || result.Sieve(mapFloatish(reflect.TypeOf(float64(0)), v)) + }, + Shrinker: func(v interface{}) gopter.Shrink { + return result.Shrinker(mapFloatish(reflect.TypeOf(float64(0)), v)).Map(func(s interface{}) interface{} { + return mapFloatish(rt, s) + }) + }, + } + }) + case reflect.Complex64: + return gen.Complex64().MapResult(func(result *gopter.GenResult) *gopter.GenResult { + return &gopter.GenResult{ + Labels: result.Labels, + ResultType: rt, + Result: mapComplexish(rt, result.Result), + Sieve: func(v interface{}) bool { + return result.Sieve == nil || result.Sieve(mapComplexish(reflect.TypeOf(complex64(0)), v)) + }, + Shrinker: func(v interface{}) gopter.Shrink { + return result.Shrinker(mapComplexish(reflect.TypeOf(complex64(0)), v)).Map(func(s interface{}) interface{} { + return mapComplexish(rt, s) + }) + }, + } + }) + case reflect.Complex128: + return gen.Complex128().MapResult(func(result *gopter.GenResult) *gopter.GenResult { + return &gopter.GenResult{ + Labels: result.Labels, + ResultType: rt, + Result: mapComplexish(rt, result.Result), + Sieve: func(v interface{}) bool { + return result.Sieve == nil || result.Sieve(mapComplexish(reflect.TypeOf(complex128(0)), v)) + }, + Shrinker: func(v interface{}) gopter.Shrink { + return result.Shrinker(mapComplexish(reflect.TypeOf(complex128(0)), v)).Map(func(s interface{}) interface{} { + return mapComplexish(rt, s) + }) + }, + } + }) + case reflect.String: + return gen.AnyString().MapResult(func(result *gopter.GenResult) *gopter.GenResult { + return &gopter.GenResult{ + Labels: result.Labels, + ResultType: rt, + Result: mapStringish(rt, result.Result), + Sieve: func(v interface{}) bool { + return result.Sieve == nil || result.Sieve(mapStringish(reflect.TypeOf(string("")), v)) + }, + Shrinker: func(v interface{}) gopter.Shrink { + return result.Shrinker(mapStringish(reflect.TypeOf(string("")), v)).Map(func(s interface{}) interface{} { + return mapStringish(rt, s) + }) + }, + } + }) + case reflect.Slice: + if elementGen := a.GenForType(rt.Elem()); elementGen != nil { + return gen.SliceOf(elementGen) + } + case reflect.Ptr: + if rt.Elem().Kind() == reflect.Struct { + gens := make(map[string]gopter.Gen) + for i := 0; i < rt.Elem().NumField(); i++ { + field := rt.Elem().Field(i) + if gen := a.GenForType(field.Type); gen != nil { + gens[field.Name] = gen + } + } + return gen.StructPtr(rt, gens) + } + return gen.PtrOf(a.GenForType(rt.Elem())) + case reflect.Struct: + gens := make(map[string]gopter.Gen) + for i := 0; i < rt.NumField(); i++ { + field := rt.Field(i) + if gen := a.GenForType(field.Type); gen != nil { + gens[field.Name] = gen + } + } + return gen.Struct(rt, gens) + } + return nil +} diff --git a/vendor/github.com/leanovate/gopter/arbitrary/gen_for_kind_slice_test.go b/vendor/github.com/leanovate/gopter/arbitrary/gen_for_kind_slice_test.go new file mode 100644 index 000000000..a5b0b01e8 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/arbitrary/gen_for_kind_slice_test.go @@ -0,0 +1,30 @@ +package arbitrary_test + +import ( + "reflect" + "testing" + + "github.com/leanovate/gopter/arbitrary" +) + +func TestArbitrariesSlices(t *testing.T) { + arbitraries := arbitrary.DefaultArbitraries() + + gen := arbitraries.GenForType(reflect.TypeOf([]bool{})) + value, ok := gen.Sample() + if !ok { + t.Errorf("Invalid value %#v", value) + } + if _, ok = value.([]bool); !ok { + t.Errorf("Invalid value %#v", value) + } + + gen = arbitraries.GenForType(reflect.TypeOf([]*int64{})) + value, ok = gen.Sample() + if !ok { + t.Errorf("Invalid value %#v", value) + } + if _, ok = value.([]*int64); !ok { + t.Errorf("Invalid value %#v", value) + } +} diff --git a/vendor/github.com/leanovate/gopter/arbitrary/gen_for_kind_struct_test.go b/vendor/github.com/leanovate/gopter/arbitrary/gen_for_kind_struct_test.go new file mode 100644 index 000000000..6afbe8f9b --- /dev/null +++ b/vendor/github.com/leanovate/gopter/arbitrary/gen_for_kind_struct_test.go @@ -0,0 +1,52 @@ +package arbitrary_test + +import ( + "reflect" + "testing" + "unicode" + + "github.com/leanovate/gopter/arbitrary" + "github.com/leanovate/gopter/gen" +) + +type DemoStruct struct { + Value1 int64 + Value2 string + Value3 []uint + Value4 int32 +} + +func TestArbitrariesStructs(t *testing.T) { + arbitraries := arbitrary.DefaultArbitraries() + + arbitraries.RegisterGen(gen.Int64Range(10, 100)) + arbitraries.RegisterGen(gen.Int32Range(1, 10)) + arbitraries.RegisterGen(gen.Const([]uint{1, 2, 3})) + arbitraries.RegisterGen(gen.AlphaString()) + + gen := arbitraries.GenForType(reflect.TypeOf(&DemoStruct{})) + for i := 0; i < 100; i++ { + raw, ok := gen.Sample() + if !ok { + t.Errorf("Invalid value: %#v", raw) + } + value, ok := raw.(*DemoStruct) + if !ok { + t.Errorf("Invalid value: %#v", raw) + } + if value.Value1 < 10 || value.Value1 > 100 { + t.Errorf("Invalid value.Value1 out of bounds: %#v", raw) + } + for _, ch := range value.Value2 { + if !unicode.IsLetter(ch) { + t.Errorf("Invalid value.Value2: %#v", raw) + } + } + if !reflect.DeepEqual(value.Value3, []uint{1, 2, 3}) { + t.Errorf("Invalid value.Value3: %#v", raw) + } + if value.Value4 < 1 || value.Value4 > 10 { + t.Errorf("Invalid value.Value4 out of bounds: %#v", raw) + } + } +} diff --git a/vendor/github.com/leanovate/gopter/arbitrary/gen_for_kind_test.go b/vendor/github.com/leanovate/gopter/arbitrary/gen_for_kind_test.go new file mode 100644 index 000000000..3920e50f1 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/arbitrary/gen_for_kind_test.go @@ -0,0 +1,135 @@ +package arbitrary_test + +import ( + "reflect" + "testing" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/arbitrary" +) + +func commonGeneratorTest(t *testing.T, name string, gen gopter.Gen, valueCheck func(interface{}) bool) { + for i := 0; i < 100; i++ { + value, ok := gen.Sample() + + if !ok || value == nil { + t.Errorf("Invalid generator result (%s): %#v", name, value) + } else if !valueCheck(value) { + t.Errorf("Invalid value (%s): %#v", name, value) + } + + genResult := gen(gopter.DefaultGenParameters()) + if genResult.Shrinker != nil { + value, ok := genResult.Retrieve() + if !ok || value == nil { + t.Errorf("Invalid generator result (%s): %#v", name, value) + } else { + shrink := genResult.Shrinker(value).Filter(genResult.Sieve) + shrunkValue, ok := shrink() + if ok && !valueCheck(shrunkValue) { + t.Errorf("Invalid shrunk value (%s): %#v -> %#v", name, value, shrunkValue) + } + } + } + } +} + +func TestArbitrariesSimple(t *testing.T) { + arbitraries := arbitrary.DefaultArbitraries() + + gen := arbitraries.GenForType(reflect.TypeOf(true)) + commonGeneratorTest(t, "bool", gen, func(value interface{}) bool { + _, ok := value.(bool) + return ok + }) + + gen = arbitraries.GenForType(reflect.TypeOf(0)) + commonGeneratorTest(t, "int", gen, func(value interface{}) bool { + _, ok := value.(int) + return ok + }) + + gen = arbitraries.GenForType(reflect.TypeOf(uint(0))) + commonGeneratorTest(t, "uint", gen, func(value interface{}) bool { + _, ok := value.(uint) + return ok + }) + + gen = arbitraries.GenForType(reflect.TypeOf(int8(0))) + commonGeneratorTest(t, "int8", gen, func(value interface{}) bool { + _, ok := value.(int8) + return ok + }) + + gen = arbitraries.GenForType(reflect.TypeOf(uint8(0))) + commonGeneratorTest(t, "uint8", gen, func(value interface{}) bool { + _, ok := value.(uint8) + return ok + }) + + gen = arbitraries.GenForType(reflect.TypeOf(int16(0))) + commonGeneratorTest(t, "int16", gen, func(value interface{}) bool { + _, ok := value.(int16) + return ok + }) + + gen = arbitraries.GenForType(reflect.TypeOf(uint16(0))) + commonGeneratorTest(t, "uint16", gen, func(value interface{}) bool { + _, ok := value.(uint16) + return ok + }) + + gen = arbitraries.GenForType(reflect.TypeOf(int32(0))) + commonGeneratorTest(t, "int32", gen, func(value interface{}) bool { + _, ok := value.(int32) + return ok + }) + + gen = arbitraries.GenForType(reflect.TypeOf(uint32(0))) + commonGeneratorTest(t, "uint32", gen, func(value interface{}) bool { + _, ok := value.(uint32) + return ok + }) + + gen = arbitraries.GenForType(reflect.TypeOf(int64(0))) + commonGeneratorTest(t, "int64", gen, func(value interface{}) bool { + _, ok := value.(int64) + return ok + }) + + gen = arbitraries.GenForType(reflect.TypeOf(uint64(0))) + commonGeneratorTest(t, "uint64", gen, func(value interface{}) bool { + _, ok := value.(uint64) + return ok + }) + + gen = arbitraries.GenForType(reflect.TypeOf(float32(0))) + commonGeneratorTest(t, "float32", gen, func(value interface{}) bool { + _, ok := value.(float32) + return ok + }) + + gen = arbitraries.GenForType(reflect.TypeOf(float64(0))) + commonGeneratorTest(t, "float64", gen, func(value interface{}) bool { + _, ok := value.(float64) + return ok + }) + + gen = arbitraries.GenForType(reflect.TypeOf(complex128(0))) + commonGeneratorTest(t, "complex128", gen, func(value interface{}) bool { + _, ok := value.(complex128) + return ok + }) + + gen = arbitraries.GenForType(reflect.TypeOf(complex64(0))) + commonGeneratorTest(t, "complex64", gen, func(value interface{}) bool { + _, ok := value.(complex64) + return ok + }) + + gen = arbitraries.GenForType(reflect.TypeOf("")) + commonGeneratorTest(t, "string", gen, func(value interface{}) bool { + _, ok := value.(string) + return ok + }) +} diff --git a/vendor/github.com/leanovate/gopter/bi_mapper.go b/vendor/github.com/leanovate/gopter/bi_mapper.go new file mode 100644 index 000000000..9df112985 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/bi_mapper.go @@ -0,0 +1,111 @@ +package gopter + +import ( + "fmt" + "reflect" +) + +// BiMapper is a bi-directional (or bijective) mapper of a tuple of values (up) +// to another tuple of values (down). +type BiMapper struct { + UpTypes []reflect.Type + DownTypes []reflect.Type + Downstream reflect.Value + Upstream reflect.Value +} + +// NewBiMapper creates a BiMapper of two functions `downstream` and its +// inverse `upstream`. +// That is: The return values of `downstream` must match the parameters of +// `upstream` and vice versa. +func NewBiMapper(downstream interface{}, upstream interface{}) *BiMapper { + downstreamVal := reflect.ValueOf(downstream) + if downstreamVal.Kind() != reflect.Func { + panic("downstream has to be a function") + } + upstreamVal := reflect.ValueOf(upstream) + if upstreamVal.Kind() != reflect.Func { + panic("upstream has to be a function") + } + + downstreamType := downstreamVal.Type() + upTypes := make([]reflect.Type, downstreamType.NumIn()) + for i := 0; i < len(upTypes); i++ { + upTypes[i] = downstreamType.In(i) + } + downTypes := make([]reflect.Type, downstreamType.NumOut()) + for i := 0; i < len(downTypes); i++ { + downTypes[i] = downstreamType.Out(i) + } + + upstreamType := upstreamVal.Type() + if len(upTypes) != upstreamType.NumOut() { + panic(fmt.Sprintf("upstream is expected to have %d return values", len(upTypes))) + } + for i, upType := range upTypes { + if upstreamType.Out(i) != upType { + panic(fmt.Sprintf("upstream has wrong return type %d: %v != %v", i, upstreamType.Out(i), upType)) + } + } + if len(downTypes) != upstreamType.NumIn() { + panic(fmt.Sprintf("upstream is expected to have %d parameters", len(downTypes))) + } + for i, downType := range downTypes { + if upstreamType.In(i) != downType { + panic(fmt.Sprintf("upstream has wrong parameter type %d: %v != %v", i, upstreamType.In(i), downType)) + } + } + + return &BiMapper{ + UpTypes: upTypes, + DownTypes: downTypes, + Downstream: downstreamVal, + Upstream: upstreamVal, + } +} + +// ConvertUp calls the Upstream function on the arguments in the down array +// and returns the results. +func (b *BiMapper) ConvertUp(down []interface{}) []interface{} { + if len(down) != len(b.DownTypes) { + panic(fmt.Sprintf("Expected %d values != %d", len(b.DownTypes), len(down))) + } + downVals := make([]reflect.Value, len(b.DownTypes)) + for i, val := range down { + if val == nil { + downVals[i] = reflect.Zero(b.DownTypes[i]) + } else { + downVals[i] = reflect.ValueOf(val) + } + } + upVals := b.Upstream.Call(downVals) + up := make([]interface{}, len(upVals)) + for i, upVal := range upVals { + up[i] = upVal.Interface() + } + + return up +} + +// ConvertDown calls the Downstream function on the elements of the up array +// and returns the results. +func (b *BiMapper) ConvertDown(up []interface{}) []interface{} { + if len(up) != len(b.UpTypes) { + panic(fmt.Sprintf("Expected %d values != %d", len(b.UpTypes), len(up))) + } + upVals := make([]reflect.Value, len(b.UpTypes)) + for i, val := range up { + if val == nil { + upVals[i] = reflect.Zero(b.UpTypes[i]) + } else { + upVals[i] = reflect.ValueOf(val) + } + } + downVals := b.Downstream.Call(upVals) + down := make([]interface{}, len(downVals)) + for i, downVal := range downVals { + down[i] = downVal.Interface() + } + + return down +} diff --git a/vendor/github.com/leanovate/gopter/bi_mapper_test.go b/vendor/github.com/leanovate/gopter/bi_mapper_test.go new file mode 100644 index 000000000..5a0166c52 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/bi_mapper_test.go @@ -0,0 +1,27 @@ +package gopter_test + +import ( + "testing" + + "github.com/leanovate/gopter" +) + +func TestBiMapperParamNotMatch(t *testing.T) { + defer expectPanic(t, "upstream has wrong parameter type 0: string != int") + gopter.NewBiMapper(func(int) int { return 0 }, func(string) int { return 0 }) +} + +func TestBiMapperReturnNotMatch(t *testing.T) { + defer expectPanic(t, "upstream has wrong return type 0: string != int") + gopter.NewBiMapper(func(int) int { return 0 }, func(int) string { return "" }) +} + +func TestBiMapperInvalidDownstream(t *testing.T) { + defer expectPanic(t, "downstream has to be a function") + gopter.NewBiMapper(1, 2) +} + +func TestBiMapperInvalidUpstream(t *testing.T) { + defer expectPanic(t, "upstream has to be a function") + gopter.NewBiMapper(func(int) int { return 0 }, 2) +} diff --git a/vendor/github.com/leanovate/gopter/commands/actions.go b/vendor/github.com/leanovate/gopter/commands/actions.go new file mode 100644 index 000000000..b3d8d856b --- /dev/null +++ b/vendor/github.com/leanovate/gopter/commands/actions.go @@ -0,0 +1,140 @@ +package commands + +import ( + "fmt" + "reflect" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/gen" +) + +type shrinkableCommand struct { + command Command + shrinker gopter.Shrinker +} + +func (s shrinkableCommand) shrink() gopter.Shrink { + return s.shrinker(s.command).Map(func(command Command) shrinkableCommand { + return shrinkableCommand{ + command: command, + shrinker: s.shrinker, + } + }) +} + +func (s shrinkableCommand) String() string { + return fmt.Sprintf("%v", s.command) +} + +type actions struct { + // initialStateProvider has to reset/recreate the initial state exactly the + // same every time. + initialStateProvider func() State + sequentialCommands []shrinkableCommand + // parallel commands will come later +} + +func (a *actions) String() string { + return fmt.Sprintf("initialState=%v sequential=%s", a.initialStateProvider(), a.sequentialCommands) +} + +func (a *actions) run(systemUnderTest SystemUnderTest) (*gopter.PropResult, error) { + state := a.initialStateProvider() + propResult := &gopter.PropResult{Status: gopter.PropTrue} + for _, shrinkableCommand := range a.sequentialCommands { + if !shrinkableCommand.command.PreCondition(state) { + return &gopter.PropResult{Status: gopter.PropFalse}, nil + } + result := shrinkableCommand.command.Run(systemUnderTest) + state = shrinkableCommand.command.NextState(state) + propResult = propResult.And(shrinkableCommand.command.PostCondition(state, result)) + } + return propResult, nil +} + +type sizedCommands struct { + state State + commands []shrinkableCommand +} + +func actionsShrinker(v interface{}) gopter.Shrink { + a := v.(*actions) + elementShrinker := gopter.Shrinker(func(v interface{}) gopter.Shrink { + return v.(shrinkableCommand).shrink() + }) + return gen.SliceShrinker(elementShrinker)(a.sequentialCommands).Map(func(v []shrinkableCommand) *actions { + return &actions{ + initialStateProvider: a.initialStateProvider, + sequentialCommands: v, + } + }) +} + +func genActions(commands Commands) gopter.Gen { + genInitialState := commands.GenInitialState() + genInitialStateProvider := gopter.Gen(func(params *gopter.GenParameters) *gopter.GenResult { + seed := params.NextInt64() + return gopter.NewGenResult(func() State { + paramsWithSeed := params.CloneWithSeed(seed) + if initialState, ok := genInitialState(paramsWithSeed).Retrieve(); ok { + return initialState + } + return nil + }, gopter.NoShrinker) + }).SuchThat(func(initialStateProvoder func() State) bool { + state := initialStateProvoder() + return state != nil && commands.InitialPreCondition(state) + }) + return genInitialStateProvider.FlatMap(func(v interface{}) gopter.Gen { + initialStateProvider := v.(func() State) + return genSizedCommands(commands, initialStateProvider).Map(func(v sizedCommands) *actions { + return &actions{ + initialStateProvider: initialStateProvider, + sequentialCommands: v.commands, + } + }).SuchThat(func(actions *actions) bool { + state := actions.initialStateProvider() + for _, shrinkableCommand := range actions.sequentialCommands { + if !shrinkableCommand.command.PreCondition(state) { + return false + } + state = shrinkableCommand.command.NextState(state) + } + return true + }).WithShrinker(actionsShrinker) + }, reflect.TypeOf((*actions)(nil))) +} + +func genSizedCommands(commands Commands, initialStateProvider func() State) gopter.Gen { + return func(genParams *gopter.GenParameters) *gopter.GenResult { + sizedCommandsGen := gen.Const(sizedCommands{ + state: initialStateProvider(), + commands: make([]shrinkableCommand, 0, genParams.MaxSize), + }) + for i := 0; i < genParams.MaxSize; i++ { + sizedCommandsGen = sizedCommandsGen.FlatMap(func(v interface{}) gopter.Gen { + prev := v.(sizedCommands) + return gen.RetryUntil(commands.GenCommand(prev.state), func(command Command) bool { + return command.PreCondition(prev.state) + }, 100).MapResult(func(result *gopter.GenResult) *gopter.GenResult { + value, ok := result.Retrieve() + if !ok { + return gopter.NewEmptyResult(reflect.TypeOf(sizedCommands{})) + } + command := value.(Command) + return gopter.NewGenResult( + sizedCommands{ + state: command.NextState(prev.state), + commands: append(prev.commands, shrinkableCommand{ + command: command, + shrinker: result.Shrinker, + }), + }, + gopter.NoShrinker, + ) + }) + }, reflect.TypeOf(sizedCommands{})) + } + return sizedCommandsGen(genParams) + } +} diff --git a/vendor/github.com/leanovate/gopter/commands/command.go b/vendor/github.com/leanovate/gopter/commands/command.go new file mode 100644 index 000000000..258840789 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/commands/command.go @@ -0,0 +1,72 @@ +package commands + +import "github.com/leanovate/gopter" + +// SystemUnderTest resembles the system under test, which may be any kind +// of stateful unit of code +type SystemUnderTest interface{} + +// State resembles the state the system under test is expected to be in +type State interface{} + +// Result resembles the result of a command that may or may not be checked +type Result interface{} + +// Command is any kind of command that may be applied to the system under test +type Command interface { + // Run applies the command to the system under test + Run(systemUnderTest SystemUnderTest) Result + // NextState calculates the next expected state if the command is applied + NextState(state State) State + // PreCondition checks if the state is valid before the command is applied + PreCondition(state State) bool + // PostCondition checks if the state is valid after the command is applied + PostCondition(state State, result Result) *gopter.PropResult + // String gets a (short) string representation of the command + String() string +} + +// ProtoCommand is a prototype implementation of the Command interface +type ProtoCommand struct { + Name string + RunFunc func(systemUnderTest SystemUnderTest) Result + NextStateFunc func(state State) State + PreConditionFunc func(state State) bool + PostConditionFunc func(state State, result Result) *gopter.PropResult +} + +// Run applies the command to the system under test +func (p *ProtoCommand) Run(systemUnderTest SystemUnderTest) Result { + if p.RunFunc != nil { + return p.RunFunc(systemUnderTest) + } + return nil +} + +// NextState calculates the next expected state if the command is applied +func (p *ProtoCommand) NextState(state State) State { + if p.NextStateFunc != nil { + return p.NextStateFunc(state) + } + return state +} + +// PreCondition checks if the state is valid before the command is applied +func (p *ProtoCommand) PreCondition(state State) bool { + if p.PreConditionFunc != nil { + return p.PreConditionFunc(state) + } + return true +} + +// PostCondition checks if the state is valid after the command is applied +func (p *ProtoCommand) PostCondition(state State, result Result) *gopter.PropResult { + if p.PostConditionFunc != nil { + return p.PostConditionFunc(state, result) + } + return &gopter.PropResult{Status: gopter.PropTrue} +} + +func (p *ProtoCommand) String() string { + return p.Name +} diff --git a/vendor/github.com/leanovate/gopter/commands/commands.go b/vendor/github.com/leanovate/gopter/commands/commands.go new file mode 100644 index 000000000..bb9b1f945 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/commands/commands.go @@ -0,0 +1,84 @@ +package commands + +import ( + "reflect" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/gen" + "github.com/leanovate/gopter/prop" +) + +// Commands provide an entry point for testing a stateful system +type Commands interface { + // NewSystemUnderTest should create a new/isolated system under test + NewSystemUnderTest(initialState State) SystemUnderTest + // DestroySystemUnderTest may perform any cleanup tasks to destroy a system + DestroySystemUnderTest(SystemUnderTest) + // GenInitialState provides a generator for the initial State. + // IMPORTANT: The generated state itself may be mutable, but this generator + // is supposed to generate a clean and reproductable state every time. + // Do not use an external random generator and be especially vary about + // `gen.Const()`. + GenInitialState() gopter.Gen + // GenCommand provides a generator for applicable commands to for a state + GenCommand(state State) gopter.Gen + // InitialPreCondition checks if the initial state is valid + InitialPreCondition(state State) bool +} + +// ProtoCommands is a prototype implementation of the Commands interface +type ProtoCommands struct { + NewSystemUnderTestFunc func(initialState State) SystemUnderTest + DestroySystemUnderTestFunc func(SystemUnderTest) + InitialStateGen gopter.Gen + GenCommandFunc func(State) gopter.Gen + InitialPreConditionFunc func(State) bool +} + +// NewSystemUnderTest should create a new/isolated system under test +func (p *ProtoCommands) NewSystemUnderTest(initialState State) SystemUnderTest { + if p.NewSystemUnderTestFunc != nil { + return p.NewSystemUnderTestFunc(initialState) + } + return nil +} + +// DestroySystemUnderTest may perform any cleanup tasks to destroy a system +func (p *ProtoCommands) DestroySystemUnderTest(systemUnderTest SystemUnderTest) { + if p.DestroySystemUnderTestFunc != nil { + p.DestroySystemUnderTestFunc(systemUnderTest) + } +} + +// GenCommand provides a generator for applicable commands to for a state +func (p *ProtoCommands) GenCommand(state State) gopter.Gen { + if p.GenCommandFunc != nil { + return p.GenCommandFunc(state) + } + return gen.Fail(reflect.TypeOf((*Command)(nil)).Elem()) +} + +// GenInitialState provides a generator for the initial State +func (p *ProtoCommands) GenInitialState() gopter.Gen { + return p.InitialStateGen.SuchThat(func(state State) bool { + return p.InitialPreCondition(state) + }) +} + +// InitialPreCondition checks if the initial state is valid +func (p *ProtoCommands) InitialPreCondition(state State) bool { + if p.InitialPreConditionFunc != nil { + return p.InitialPreConditionFunc(state) + } + return true +} + +// Prop creates a gopter.Prop from Commands +func Prop(commands Commands) gopter.Prop { + return prop.ForAll(func(actions *actions) (*gopter.PropResult, error) { + systemUnderTest := commands.NewSystemUnderTest(actions.initialStateProvider()) + defer commands.DestroySystemUnderTest(systemUnderTest) + + return actions.run(systemUnderTest) + }, genActions(commands)) +} diff --git a/vendor/github.com/leanovate/gopter/commands/commands_test.go b/vendor/github.com/leanovate/gopter/commands/commands_test.go new file mode 100644 index 000000000..906590ba0 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/commands/commands_test.go @@ -0,0 +1,112 @@ +package commands_test + +import ( + "testing" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/commands" + "github.com/leanovate/gopter/gen" +) + +type counter struct { + value int +} + +func (c *counter) Get() int { + return c.value +} + +func (c *counter) Inc() int { + c.value++ + return c.value +} + +func (c *counter) Dec() int { + c.value-- + return c.value +} + +var GetCommand = &commands.ProtoCommand{ + Name: "GET", + RunFunc: func(systemUnderTest commands.SystemUnderTest) commands.Result { + return systemUnderTest.(*counter).Get() + }, + PreConditionFunc: func(state commands.State) bool { + _, ok := state.(int) + return ok + }, + PostConditionFunc: func(state commands.State, result commands.Result) *gopter.PropResult { + if state.(int) != result.(int) { + return &gopter.PropResult{Status: gopter.PropFalse} + } + return &gopter.PropResult{Status: gopter.PropTrue} + }, +} + +var IncCommand = &commands.ProtoCommand{ + Name: "INC", + RunFunc: func(systemUnderTest commands.SystemUnderTest) commands.Result { + return systemUnderTest.(*counter).Inc() + }, + NextStateFunc: func(state commands.State) commands.State { + return state.(int) + 1 + }, + PostConditionFunc: func(state commands.State, result commands.Result) *gopter.PropResult { + if state.(int) != result.(int) { + return &gopter.PropResult{Status: gopter.PropFalse} + } + return &gopter.PropResult{Status: gopter.PropTrue} + }, +} + +var DecCommand = &commands.ProtoCommand{ + Name: "DEC", + RunFunc: func(systemUnderTest commands.SystemUnderTest) commands.Result { + return systemUnderTest.(*counter).Dec() + }, + PreConditionFunc: func(state commands.State) bool { + return state.(int) > 0 + }, + NextStateFunc: func(state commands.State) commands.State { + return state.(int) - 1 + }, + PostConditionFunc: func(state commands.State, result commands.Result) *gopter.PropResult { + if state.(int) != result.(int) { + return &gopter.PropResult{Status: gopter.PropFalse} + } + return &gopter.PropResult{Status: gopter.PropTrue} + }, +} + +type counterCommands struct { +} + +func (c *counterCommands) NewSystemUnderTest(initialState commands.State) commands.SystemUnderTest { + return &counter{value: initialState.(int)} +} + +func (c *counterCommands) DestroySystemUnderTest(commands.SystemUnderTest) { +} + +func (c *counterCommands) GenInitialState() gopter.Gen { + return gen.Int() +} + +func (c *counterCommands) InitialPreCondition(state commands.State) bool { + return state.(int) >= 0 +} + +func (c *counterCommands) GenCommand(state commands.State) gopter.Gen { + return gen.OneConstOf(GetCommand, IncCommand, DecCommand) +} + +func TestCommands(t *testing.T) { + parameters := gopter.DefaultTestParameters() + + prop := commands.Prop(&counterCommands{}) + + result := prop.Check(parameters) + if !result.Passed() { + t.Errorf("Invalid result: %v", result) + } +} diff --git a/vendor/github.com/leanovate/gopter/commands/doc.go b/vendor/github.com/leanovate/gopter/commands/doc.go new file mode 100644 index 000000000..627d9a819 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/commands/doc.go @@ -0,0 +1,11 @@ +/* +Package commands conains helpers to create stateful tests based on commands. + +Tester have to implement the Commands interface providing generators for the +initial state and the commands. For convenience testers may also use the +ProtoCommands as prototype. + +The commands themself have to implement the Command interface, whereas +testers might choose to use ProtoCommand as prototype. +*/ +package commands diff --git a/vendor/github.com/leanovate/gopter/commands/example_circularqueue_test.go b/vendor/github.com/leanovate/gopter/commands/example_circularqueue_test.go new file mode 100644 index 000000000..ad9afa0a8 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/commands/example_circularqueue_test.go @@ -0,0 +1,256 @@ +package commands_test + +import ( + "fmt" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/commands" + "github.com/leanovate/gopter/gen" +) + +// ***************************************** +// Production code (i.e. the implementation) +// ***************************************** + +type Queue struct { + inp int + outp int + size int + buf []int +} + +func New(n int) *Queue { + return &Queue{ + inp: 0, + outp: 0, + size: n + 1, + buf: make([]int, n+1), + } +} + +func (q *Queue) Put(n int) int { + if q.inp == 4 && n > 0 { // Intentional spooky bug + q.buf[q.size-1] *= n + } + q.buf[q.inp] = n + q.inp = (q.inp + 1) % q.size + return n +} + +func (q *Queue) Get() int { + ans := q.buf[q.outp] + q.outp = (q.outp + 1) % q.size + return ans +} + +func (q *Queue) Size() int { + return (q.inp - q.outp + q.size) % q.size +} + +func (q *Queue) Init() { + q.inp = 0 + q.outp = 0 +} + +// ***************************************** +// Test code +// ***************************************** + +// cbState holds the expected state (i.e. its the commands.State) +type cbState struct { + size int + elements []int + takenElement int +} + +func (st *cbState) TakeFront() { + st.takenElement = st.elements[0] + st.elements = append(st.elements[:0], st.elements[1:]...) +} + +func (st *cbState) PushBack(value int) { + st.elements = append(st.elements, value) +} + +func (st *cbState) String() string { + return fmt.Sprintf("State(size=%d, elements=%v)", st.size, st.elements) +} + +// Get command simply invokes the Get function on the queue and compares the +// result with the expected state. +var genGetCommand = gen.Const(&commands.ProtoCommand{ + Name: "Get", + RunFunc: func(q commands.SystemUnderTest) commands.Result { + return q.(*Queue).Get() + }, + NextStateFunc: func(state commands.State) commands.State { + state.(*cbState).TakeFront() + return state + }, + // The implementation implicitly assumes that Get is never called on an + // empty Queue, therefore the command requires a corresponding pre-condition + PreConditionFunc: func(state commands.State) bool { + return len(state.(*cbState).elements) > 0 + }, + PostConditionFunc: func(state commands.State, result commands.Result) *gopter.PropResult { + if result.(int) != state.(*cbState).takenElement { + return &gopter.PropResult{Status: gopter.PropFalse} + } + return &gopter.PropResult{Status: gopter.PropTrue} + }, +}) + +// Put command puts a value into the queue by using the Put function. Since +// the Put function has an int argument the Put command should have a +// corresponding parameter. +type putCommand int + +func (value putCommand) Run(q commands.SystemUnderTest) commands.Result { + return q.(*Queue).Put(int(value)) +} + +func (value putCommand) NextState(state commands.State) commands.State { + state.(*cbState).PushBack(int(value)) + return state +} + +// The implementation implicitly assumes that that Put is never called if +// the capacity is exhausted, therefore the command requires a corresponding +// pre-condition. +func (putCommand) PreCondition(state commands.State) bool { + s := state.(*cbState) + return len(s.elements) < s.size +} + +func (putCommand) PostCondition(state commands.State, result commands.Result) *gopter.PropResult { + st := state.(*cbState) + if result.(int) != st.elements[len(st.elements)-1] { + return &gopter.PropResult{Status: gopter.PropFalse} + } + return &gopter.PropResult{Status: gopter.PropTrue} +} + +func (value putCommand) String() string { + return fmt.Sprintf("Put(%d)", value) +} + +// We want to have a generator for put commands for arbitrary int values. +// In this case the command is actually shrinkable, e.g. if the property fails +// by putting a 1000, it might already fail for a 500 as well ... +var genPutCommand = gen.Int().Map(func(value int) commands.Command { + return putCommand(value) +}).WithShrinker(func(v interface{}) gopter.Shrink { + return gen.IntShrinker(int(v.(putCommand))).Map(func(value int) putCommand { + return putCommand(value) + }) +}) + +// Size command is simple again, it just invokes the Size function and +// compares compares the result with the expected state. +// The Size function can be called any time, therefore this command does not +// require a pre-condition. +var genSizeCommand = gen.Const(&commands.ProtoCommand{ + Name: "Size", + RunFunc: func(q commands.SystemUnderTest) commands.Result { + return q.(*Queue).Size() + }, + PostConditionFunc: func(state commands.State, result commands.Result) *gopter.PropResult { + if result.(int) != len(state.(*cbState).elements) { + return &gopter.PropResult{Status: gopter.PropFalse} + } + return &gopter.PropResult{Status: gopter.PropTrue} + }, +}) + +// cbCommands implements the command.Commands interface, i.e. is +// responsible for creating/destroying the system under test and generating +// commands and initial states (cbState) +var cbCommands = &commands.ProtoCommands{ + NewSystemUnderTestFunc: func(initialState commands.State) commands.SystemUnderTest { + s := initialState.(*cbState) + q := New(s.size) + for e := range s.elements { + q.Put(e) + } + return q + }, + DestroySystemUnderTestFunc: func(sut commands.SystemUnderTest) { + sut.(*Queue).Init() + }, + InitialStateGen: gen.IntRange(1, 30).Map(func(size int) *cbState { + return &cbState{ + size: size, + elements: make([]int, 0, size), + } + }), + InitialPreConditionFunc: func(state commands.State) bool { + s := state.(*cbState) + return len(s.elements) >= 0 && len(s.elements) <= s.size + }, + GenCommandFunc: func(state commands.State) gopter.Gen { + return gen.OneGenOf(genGetCommand, genPutCommand, genSizeCommand) + }, +} + +// Kudos to @jamesd for providing this real world example. +// ... of course he did not implemented the bug, that was evil me +// +// The bug only occures on the following conditions: +// - the queue size has to be greater than 4 +// - the queue has to be filled entirely once +// - Get operations have to be at least 5 elements behind put +// - The Put at the end of the queue and 5 elements later have to be non-zero +// +// Lets see what gopter has to say: +// +// The output of this example will be +// ! circular buffer: Falsified after 96 passed tests. +// ARG_0: initialState=State(size=7, elements=[]) sequential=[Put(0) Put(0) +// Get Put(0) Get Put(0) Put(0) Get Put(0) Get Put(0) Get Put(-1) Put(0) +// Put(0) Put(0) Put(0) Get Get Put(2) Get] +// ARG_0_ORIGINAL (85 shrinks): initialState=State(size=7, elements=[]) +// sequential=[Put(-1855365712) Put(-1591723498) Get Size Size +// Put(-1015561691) Get Put(397128011) Size Get Put(1943174048) Size +// Put(1309500770) Size Get Put(-879438231) Size Get Put(-1644094687) Get +// Put(-1818606323) Size Put(488620313) Size Put(-1219794505) +// Put(1166147059) Get Put(11390361) Get Size Put(-1407993944) Get Get Size +// Put(1393923085) Get Put(1222853245) Size Put(2070918543) Put(1741323168) +// Size Get Get Size Put(2019939681) Get Put(-170089451) Size Get Get Size +// Size Put(-49249034) Put(1229062846) Put(642598551) Get Put(1183453167) +// Size Get Get Get Put(1010460728) Put(6828709) Put(-185198587) Size Size +// Get Put(586459644) Get Size Put(-1802196502) Get Size Put(2097590857) Get +// Get Get Get Size Put(-474576011) Size Get Size Size Put(771190414) Size +// Put(-1509199920) Get Put(967212411) Size Get Put(578995532) Size Get Size +// Get] +// +// Though this is not the minimal possible combination of command, its already +// pretty close. +func Example_circularqueue() { + parameters := gopter.DefaultTestParametersWithSeed(1234) // Example should generate reproducable results, otherwise DefaultTestParameters() will suffice + + properties := gopter.NewProperties(parameters) + + properties.Property("circular buffer", commands.Prop(cbCommands)) + + // When using testing.T you might just use: properties.TestingRun(t) + properties.Run(gopter.ConsoleReporter(false)) + // Output: + // ! circular buffer: Falsified after 96 passed tests. + // ARG_0: initialState=State(size=7, elements=[]) sequential=[Put(0) Put(0) + // Get Put(0) Get Put(0) Put(0) Get Put(0) Get Put(0) Get Put(-1) Put(0) + // Put(0) Put(0) Put(0) Get Get Put(2) Get] + // ARG_0_ORIGINAL (85 shrinks): initialState=State(size=7, elements=[]) + // sequential=[Put(-1855365712) Put(-1591723498) Get Size Size + // Put(-1015561691) Get Put(397128011) Size Get Put(1943174048) Size + // Put(1309500770) Size Get Put(-879438231) Size Get Put(-1644094687) Get + // Put(-1818606323) Size Put(488620313) Size Put(-1219794505) + // Put(1166147059) Get Put(11390361) Get Size Put(-1407993944) Get Get Size + // Put(1393923085) Get Put(1222853245) Size Put(2070918543) Put(1741323168) + // Size Get Get Size Put(2019939681) Get Put(-170089451) Size Get Get Size + // Size Put(-49249034) Put(1229062846) Put(642598551) Get Put(1183453167) + // Size Get Get Get Put(1010460728) Put(6828709) Put(-185198587) Size Size + // Get Put(586459644) Get Size Put(-1802196502) Get Size Put(2097590857) Get + // Get Get Get Size Put(-474576011) Size Get Size Size Put(771190414) Size + // Put(-1509199920) Get Put(967212411) Size Get Put(578995532) Size Get Size + // Get] +} diff --git a/vendor/github.com/leanovate/gopter/commands/example_commands_test.go b/vendor/github.com/leanovate/gopter/commands/example_commands_test.go new file mode 100644 index 000000000..8bc3a75c5 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/commands/example_commands_test.go @@ -0,0 +1,125 @@ +package commands_test + +import ( + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/commands" + "github.com/leanovate/gopter/gen" +) + +type BuggyCounter struct { + n int +} + +func (c *BuggyCounter) Inc() { + c.n++ +} + +func (c *BuggyCounter) Dec() { + if c.n > 3 { + // Intentional error + c.n -= 2 + } else { + c.n-- + } +} + +func (c *BuggyCounter) Get() int { + return c.n +} + +func (c *BuggyCounter) Reset() { + c.n = 0 +} + +var GetBuggyCommand = &commands.ProtoCommand{ + Name: "GET", + RunFunc: func(systemUnderTest commands.SystemUnderTest) commands.Result { + return systemUnderTest.(*BuggyCounter).Get() + }, + PostConditionFunc: func(state commands.State, result commands.Result) *gopter.PropResult { + if state.(int) != result.(int) { + return &gopter.PropResult{Status: gopter.PropFalse} + } + return &gopter.PropResult{Status: gopter.PropTrue} + }, +} + +var IncBuggyCommand = &commands.ProtoCommand{ + Name: "INC", + RunFunc: func(systemUnderTest commands.SystemUnderTest) commands.Result { + systemUnderTest.(*BuggyCounter).Inc() + return nil + }, + NextStateFunc: func(state commands.State) commands.State { + return state.(int) + 1 + }, +} + +var DecBuggyCommand = &commands.ProtoCommand{ + Name: "DEC", + RunFunc: func(systemUnderTest commands.SystemUnderTest) commands.Result { + systemUnderTest.(*BuggyCounter).Dec() + return nil + }, + NextStateFunc: func(state commands.State) commands.State { + return state.(int) - 1 + }, +} + +var ResetBuggyCommand = &commands.ProtoCommand{ + Name: "RESET", + RunFunc: func(systemUnderTest commands.SystemUnderTest) commands.Result { + systemUnderTest.(*BuggyCounter).Reset() + return nil + }, + NextStateFunc: func(state commands.State) commands.State { + return 0 + }, +} + +var buggyCounterCommands = &commands.ProtoCommands{ + NewSystemUnderTestFunc: func(initialState commands.State) commands.SystemUnderTest { + return &BuggyCounter{} + }, + InitialStateGen: gen.Const(0), + InitialPreConditionFunc: func(state commands.State) bool { + return state.(int) == 0 + }, + GenCommandFunc: func(state commands.State) gopter.Gen { + return gen.OneConstOf(GetBuggyCommand, IncBuggyCommand, DecBuggyCommand, ResetBuggyCommand) + }, +} + +// Demonstrates the usage of the commands package to find a bug in a counter +// implementation that only occures if the counter is above 3. +// +// The output of this example will be +// ! buggy counter: Falsified after 45 passed tests. +// ARG_0: initial=0 sequential=[INC INC INC INC DEC GET] +// ARG_0_ORIGINAL (9 shrinks): initial=0 sequential=[DEC RESET GET GET GET +// RESET DEC DEC INC INC RESET RESET DEC INC RESET INC INC GET INC INC DEC +// DEC GET RESET INC INC DEC INC INC INC RESET RESET INC INC GET INC DEC GET +// DEC GET INC RESET INC INC RESET] +// I.e. gopter found an invalid state with a rather long sequence of arbitrary +// commonds/function calls, and then shrinkted that sequence down to +// INC INC INC INC DEC GET +// which is indeed the minimal set of commands one has to perform to find the +// bug. +func Example_buggyCounter() { + parameters := gopter.DefaultTestParameters() + parameters.Rng.Seed(1234) // Just for this example to generate reproducable results + + properties := gopter.NewProperties(parameters) + + properties.Property("buggy counter", commands.Prop(buggyCounterCommands)) + + // When using testing.T you might just use: properties.TestingRun(t) + properties.Run(gopter.ConsoleReporter(false)) + // Output: + // ! buggy counter: Falsified after 43 passed tests. + // ARG_0: initialState=0 sequential=[INC INC INC INC DEC GET] + // ARG_0_ORIGINAL (8 shrinks): initialState=0 sequential=[RESET GET GET GET + // RESET DEC DEC INC INC RESET RESET DEC INC RESET INC INC GET INC INC DEC + // DEC GET RESET INC INC DEC INC INC INC RESET RESET INC INC GET INC DEC GET + // DEC GET INC RESET INC INC] +} diff --git a/vendor/github.com/leanovate/gopter/convey/doc.go b/vendor/github.com/leanovate/gopter/convey/doc.go new file mode 100644 index 000000000..9ca18b282 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/convey/doc.go @@ -0,0 +1,5 @@ +/* +Package convey contains special assertion that come handy when using groper +properties with goconvey. +*/ +package convey diff --git a/vendor/github.com/leanovate/gopter/convey/should_forall.go b/vendor/github.com/leanovate/gopter/convey/should_forall.go new file mode 100644 index 000000000..ed18ee50e --- /dev/null +++ b/vendor/github.com/leanovate/gopter/convey/should_forall.go @@ -0,0 +1,42 @@ +package convey + +import ( + "bytes" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/arbitrary" + "github.com/leanovate/gopter/prop" +) + +func ShouldSucceedForAll(condition interface{}, params ...interface{}) string { + var arbitraries *arbitrary.Arbitraries + parameters := gopter.DefaultTestParameters() + gens := make([]gopter.Gen, 0) + for _, param := range params { + switch param.(type) { + case *arbitrary.Arbitraries: + arbitraries = param.(*arbitrary.Arbitraries) + case *gopter.TestParameters: + parameters = param.(*gopter.TestParameters) + case gopter.Gen: + gens = append(gens, param.(gopter.Gen)) + } + } + + var property gopter.Prop + if arbitraries != nil { + property = arbitraries.ForAll(condition) + } else { + property = prop.ForAll(condition, gens...) + } + result := property.Check(parameters) + + if !result.Passed() { + buffer := bytes.NewBufferString("") + reporter := gopter.NewFormatedReporter(true, 75, buffer) + reporter.ReportTestResult("", result) + + return buffer.String() + } + return "" +} diff --git a/vendor/github.com/leanovate/gopter/convey/should_forall_test.go b/vendor/github.com/leanovate/gopter/convey/should_forall_test.go new file mode 100644 index 000000000..d1b61bec0 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/convey/should_forall_test.go @@ -0,0 +1,80 @@ +package convey_test + +import ( + "errors" + "math" + "reflect" + "testing" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/arbitrary" + . "github.com/leanovate/gopter/convey" + "github.com/leanovate/gopter/gen" + . "github.com/smartystreets/goconvey/convey" +) + +type QudraticEquation struct { + A, B, C float64 +} + +func (q *QudraticEquation) Eval(x float64) float64 { + return q.A*x*x + q.B*x + q.C +} + +func (q *QudraticEquation) Solve() (float64, float64, error) { + if q.A == 0 { + return 0, 0, errors.New("No solution") + } + v := q.B*q.B - 4*q.A*q.C + if v < 0 { + return 0, 0, errors.New("No solution") + } + v = math.Sqrt(v) + return (-q.B + v) / 2 / q.A, (-q.B - v) / 2 / q.A, nil +} + +func TestShouldSucceedForAll(t *testing.T) { + Convey("Given a check for quadratic equations", t, func() { + checkSolve := func(quadratic *QudraticEquation) bool { + x1, x2, err := quadratic.Solve() + if err != nil { + return true + } + + return math.Abs(quadratic.Eval(x1)) < 1e-5 && math.Abs(quadratic.Eval(x2)) < 1e-5 + } + + Convey("Then check with arbitraries succeeds", func() { + arbitraries := arbitrary.DefaultArbitraries() + arbitraries.RegisterGen(gen.Float64Range(-1e5, 1e5)) + + So(checkSolve, ShouldSucceedForAll, arbitraries) + + Convey("And test parameters may be modified", func() { + parameters := gopter.DefaultTestParameters() + parameters.MinSuccessfulTests = 200 + + So(checkSolve, ShouldSucceedForAll, arbitraries, parameters) + }) + }) + + Convey("Then check with explicit generator succeeds", func() { + anyQudraticEquation := gen.StructPtr(reflect.TypeOf(QudraticEquation{}), map[string]gopter.Gen{ + "A": gen.Float64Range(-1e5, 1e5), + "B": gen.Float64Range(-1e5, 1e5), + "C": gen.Float64Range(-1e5, 1e5), + }) + + So(checkSolve, ShouldSucceedForAll, anyQudraticEquation) + }) + + Convey("Expect fail", func() { + parameters := gopter.DefaultTestParametersWithSeed(1234) // Example should generate reproducable results, otherwise DefaultTestParameters() will suffice + result := ShouldSucceedForAll(func(i int) bool { + return i > 500 + }, gen.Int(), parameters) + + So(result, ShouldStartWith, "! : Falsified after 1 passed tests.\nARG_0: 0\nARG_0_ORIGINAL (1 shrinks): -642623569") + }) + }) +} diff --git a/vendor/github.com/leanovate/gopter/derived_gen.go b/vendor/github.com/leanovate/gopter/derived_gen.go new file mode 100644 index 000000000..62fd09a76 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/derived_gen.go @@ -0,0 +1,122 @@ +package gopter + +import ( + "fmt" + "reflect" +) + +type derivedGen struct { + biMapper *BiMapper + upGens []Gen + upSieves []func(interface{}) bool + upShrinker Shrinker + resultType reflect.Type +} + +func (d *derivedGen) Generate(genParams *GenParameters) *GenResult { + labels := []string{} + up := make([]interface{}, len(d.upGens)) + shrinkers := make([]Shrinker, len(d.upGens)) + sieves := make([]func(v interface{}) bool, len(d.upGens)) + + var ok bool + for i, gen := range d.upGens { + result := gen(genParams) + labels = append(labels, result.Labels...) + shrinkers[i] = result.Shrinker + sieves[i] = result.Sieve + up[i], ok = result.Retrieve() + if !ok { + return &GenResult{ + Shrinker: d.Shrinker, + Result: nil, + Labels: result.Labels, + ResultType: d.resultType, + Sieve: d.Sieve, + } + } + } + down := d.biMapper.ConvertDown(up) + if len(down) == 1 { + return &GenResult{ + Shrinker: d.Shrinker, + Result: down[0], + Labels: labels, + ResultType: reflect.TypeOf(down[0]), + Sieve: d.Sieve, + } + } + return &GenResult{ + Shrinker: d.Shrinker, + Result: down, + Labels: labels, + ResultType: reflect.TypeOf(down), + Sieve: d.Sieve, + } +} + +func (d *derivedGen) Sieve(down interface{}) bool { + if down == nil { + return false + } + downs, ok := down.([]interface{}) + if !ok { + downs = []interface{}{down} + } + ups := d.biMapper.ConvertUp(downs) + for i, up := range ups { + if d.upSieves[i] != nil && !d.upSieves[i](up) { + return false + } + } + return true +} + +func (d *derivedGen) Shrinker(down interface{}) Shrink { + downs, ok := down.([]interface{}) + if !ok { + downs = []interface{}{down} + } + ups := d.biMapper.ConvertUp(downs) + upShrink := d.upShrinker(ups) + + return upShrink.Map(func(shrinkedUps []interface{}) interface{} { + downs := d.biMapper.ConvertDown(shrinkedUps) + if len(downs) == 1 { + return downs[0] + } + return downs + }) +} + +// DeriveGen derives a generator with shrinkers from a sequence of other +// generators mapped by a bijective function (BiMapper) +func DeriveGen(downstream interface{}, upstream interface{}, gens ...Gen) Gen { + biMapper := NewBiMapper(downstream, upstream) + + if len(gens) != len(biMapper.UpTypes) { + panic(fmt.Sprintf("Expected %d generators != %d", len(biMapper.UpTypes), len(gens))) + } + + resultType := reflect.TypeOf([]interface{}{}) + if len(biMapper.DownTypes) == 1 { + resultType = biMapper.DownTypes[0] + } + + sieves := make([]func(interface{}) bool, len(gens)) + shrinkers := make([]Shrinker, len(gens)) + for i, gen := range gens { + result := gen(DefaultGenParams) + sieves[i] = result.Sieve + shrinkers[i] = result.Shrinker + } + + derived := &derivedGen{ + biMapper: biMapper, + upGens: gens, + upSieves: sieves, + upShrinker: CombineShrinker(shrinkers...), + resultType: resultType, + } + return derived.Generate +} diff --git a/vendor/github.com/leanovate/gopter/derived_gen_test.go b/vendor/github.com/leanovate/gopter/derived_gen_test.go new file mode 100644 index 000000000..af7f89be7 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/derived_gen_test.go @@ -0,0 +1,186 @@ +package gopter_test + +import ( + "reflect" + "testing" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/gen" +) + +type downStruct struct { + a int + b string + c bool +} + +func TestDeriveGenSingleDown(t *testing.T) { + gen := gopter.DeriveGen( + func(a int, b string, c bool) *downStruct { + return &downStruct{a: a, b: b, c: c} + }, + func(d *downStruct) (int, string, bool) { + return d.a, d.b, d.c + }, + gen.Int(), + gen.AnyString(), + gen.Bool(), + ) + + sample, ok := gen.Sample() + if !ok { + t.Error("Sample not ok") + } + _, ok = sample.(*downStruct) + if !ok { + t.Errorf("%#v is not a downStruct", sample) + } + + shrinker := gen(gopter.DefaultGenParameters()).Shrinker + shrink := shrinker(&downStruct{a: 10, b: "abcd", c: false}) + + shrinkedStructs := make([]*downStruct, 0) + value, next := shrink() + for next { + shrinkedStruct, ok := value.(*downStruct) + if !ok { + t.Errorf("Invalid shrinked value: %#v", value) + } + shrinkedStructs = append(shrinkedStructs, shrinkedStruct) + value, next = shrink() + } + + expected := []*downStruct{ + &downStruct{a: 0, b: "abcd", c: false}, + &downStruct{a: 5, b: "abcd", c: false}, + &downStruct{a: -5, b: "abcd", c: false}, + &downStruct{a: 8, b: "abcd", c: false}, + &downStruct{a: -8, b: "abcd", c: false}, + &downStruct{a: 9, b: "abcd", c: false}, + &downStruct{a: -9, b: "abcd", c: false}, + &downStruct{a: 10, b: "cd", c: false}, + &downStruct{a: 10, b: "ab", c: false}, + &downStruct{a: 10, b: "bcd", c: false}, + &downStruct{a: 10, b: "acd", c: false}, + &downStruct{a: 10, b: "abd", c: false}, + &downStruct{a: 10, b: "abc", c: false}, + } + if !reflect.DeepEqual(shrinkedStructs, expected) { + t.Errorf("%v does not equal %v", shrinkedStructs, expected) + } +} + +func TestDeriveGenSingleDownWithSieves(t *testing.T) { + gen := gopter.DeriveGen( + func(a int, b string, c bool) *downStruct { + return &downStruct{a: a, b: b, c: c} + }, + func(d *downStruct) (int, string, bool) { + return d.a, d.b, d.c + }, + gen.Int().SuchThat(func(i int) bool { + return i%2 == 0 + }), + gen.AnyString(), + gen.Bool(), + ) + + parameters := gopter.DefaultGenParameters() + parameters.Rng.Seed(1234) + + hasNoValue := false + for i := 0; i < 100; i++ { + result := gen(parameters) + _, ok := result.Retrieve() + if !ok { + hasNoValue = true + break + } + } + if !hasNoValue { + t.Error("Sieve is not applied") + } + + sieve := gen(parameters).Sieve + + if !sieve(&downStruct{a: 2, b: "something", c: false}) { + t.Error("Sieve did not pass even") + } + + if sieve(&downStruct{a: 3, b: "something", c: false}) { + t.Error("Sieve did pass odd") + } +} + +func TestDeriveGenMultiDown(t *testing.T) { + gen := gopter.DeriveGen( + func(a int, b string, c bool, d int32) (*downStruct, int64) { + return &downStruct{a: a, b: b, c: c}, int64(a) + int64(d) + }, + func(d *downStruct, diff int64) (int, string, bool, int32) { + return d.a, d.b, d.c, int32(diff - int64(d.a)) + }, + gen.Int(), + gen.AnyString(), + gen.Bool(), + gen.Int32(), + ) + + sample, ok := gen.Sample() + if !ok { + t.Error("Sample not ok") + } + values, ok := sample.([]interface{}) + if !ok || len(values) != 2 { + t.Errorf("%#v is not a slice of interface", sample) + } + _, ok = values[0].(*downStruct) + if !ok { + t.Errorf("%#v is not a downStruct", values[0]) + } + _, ok = values[1].(int64) + if !ok { + t.Errorf("%#v is not a int64", values[1]) + } + + shrinker := gen(gopter.DefaultGenParameters()).Shrinker + shrink := shrinker([]interface{}{&downStruct{a: 10, b: "abcd", c: false}, int64(20)}) + + value, next := shrink() + shrinkedValues := make([][]interface{}, 0) + for next { + shrinked, ok := value.([]interface{}) + if !ok || len(values) != 2 { + t.Errorf("%#v is not a slice of interface", sample) + } + shrinkedValues = append(shrinkedValues, shrinked) + value, next = shrink() + } + + expected := [][]interface{}{ + []interface{}{&downStruct{a: 0, b: "abcd", c: false}, int64(10)}, + []interface{}{&downStruct{a: 5, b: "abcd", c: false}, int64(15)}, + []interface{}{&downStruct{a: -5, b: "abcd", c: false}, int64(5)}, + []interface{}{&downStruct{a: 8, b: "abcd", c: false}, int64(18)}, + []interface{}{&downStruct{a: -8, b: "abcd", c: false}, int64(2)}, + []interface{}{&downStruct{a: 9, b: "abcd", c: false}, int64(19)}, + []interface{}{&downStruct{a: -9, b: "abcd", c: false}, int64(1)}, + []interface{}{&downStruct{a: 10, b: "cd", c: false}, int64(20)}, + []interface{}{&downStruct{a: 10, b: "ab", c: false}, int64(20)}, + []interface{}{&downStruct{a: 10, b: "bcd", c: false}, int64(20)}, + []interface{}{&downStruct{a: 10, b: "acd", c: false}, int64(20)}, + []interface{}{&downStruct{a: 10, b: "abd", c: false}, int64(20)}, + []interface{}{&downStruct{a: 10, b: "abc", c: false}, int64(20)}, + []interface{}{&downStruct{a: 10, b: "abcd", c: false}, int64(10)}, + []interface{}{&downStruct{a: 10, b: "abcd", c: false}, int64(15)}, + []interface{}{&downStruct{a: 10, b: "abcd", c: false}, int64(5)}, + []interface{}{&downStruct{a: 10, b: "abcd", c: false}, int64(18)}, + []interface{}{&downStruct{a: 10, b: "abcd", c: false}, int64(2)}, + []interface{}{&downStruct{a: 10, b: "abcd", c: false}, int64(19)}, + []interface{}{&downStruct{a: 10, b: "abcd", c: false}, int64(1)}, + } + + if !reflect.DeepEqual(shrinkedValues, expected) { + t.Errorf("%v does not equal %v", shrinkedValues, expected) + } +} diff --git a/vendor/github.com/leanovate/gopter/doc.go b/vendor/github.com/leanovate/gopter/doc.go new file mode 100644 index 000000000..cbb5f973c --- /dev/null +++ b/vendor/github.com/leanovate/gopter/doc.go @@ -0,0 +1,35 @@ +/* +Package gopter contain the main interfaces of the GOlang Property TestER. + +A simple property test might look like this: + + func TestSqrt(t *testing.T) { + properties := gopter.NewProperties(nil) + + properties.Property("greater one of all greater one", prop.ForAll( + func(v float64) bool { + return math.Sqrt(v) >= 1 + }, + gen.Float64Range(1, math.MaxFloat64), + )) + + properties.Property("squared is equal to value", prop.ForAll( + func(v float64) bool { + r := math.Sqrt(v) + return math.Abs(r*r-v) < 1e-10*v + }, + gen.Float64Range(0, math.MaxFloat64), + )) + + properties.TestingRun(t) + } + +Generally a property is just a function that takes GenParameters and produces +a PropResult: + + type Prop func(*GenParameters) *PropResult + +but usually you will use prop.ForAll, prop.ForAllNoShrink or arbitrary.ForAll. +There is also the commands package, which can be helpful for stateful testing. +*/ +package gopter diff --git a/vendor/github.com/leanovate/gopter/example_fizzbuzz_test.go b/vendor/github.com/leanovate/gopter/example_fizzbuzz_test.go new file mode 100644 index 000000000..5f11da826 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/example_fizzbuzz_test.go @@ -0,0 +1,78 @@ +package gopter_test + +import ( + "errors" + "math" + "strconv" + "strings" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/gen" + "github.com/leanovate/gopter/prop" +) + +// Fizzbuzz: See https://wikipedia.org/wiki/Fizz_buzz +func fizzbuzz(number int) (string, error) { + if number <= 0 { + return "", errors.New("Undefined") + } + switch { + case number%15 == 0: + return "FizzBuzz", nil + case number%3 == 0: + return "Fizz", nil + case number%5 == 0: + return "Buzz", nil + } + return strconv.Itoa(number), nil +} + +func Example_fizzbuzz() { + properties := gopter.NewProperties(nil) + + properties.Property("Undefined for all <= 0", prop.ForAll( + func(number int) bool { + result, err := fizzbuzz(number) + return err != nil && result == "" + }, + gen.IntRange(math.MinInt32, 0), + )) + + properties.Property("Start with Fizz for all multiples of 3", prop.ForAll( + func(i int) bool { + result, err := fizzbuzz(i * 3) + return err == nil && strings.HasPrefix(result, "Fizz") + }, + gen.IntRange(1, math.MaxInt32/3), + )) + + properties.Property("End with Buzz for all multiples of 5", prop.ForAll( + func(i int) bool { + result, err := fizzbuzz(i * 5) + return err == nil && strings.HasSuffix(result, "Buzz") + }, + gen.IntRange(1, math.MaxInt32/5), + )) + + properties.Property("Int as string for all non-divisible by 3 or 5", prop.ForAll( + func(number int) bool { + result, err := fizzbuzz(number) + if err != nil { + return false + } + parsed, err := strconv.ParseInt(result, 10, 64) + return err == nil && parsed == int64(number) + }, + gen.IntRange(1, math.MaxInt32).SuchThat(func(v interface{}) bool { + return v.(int)%3 != 0 && v.(int)%5 != 0 + }), + )) + + // When using testing.T you might just use: properties.TestingRun(t) + properties.Run(gopter.ConsoleReporter(false)) + // Output: + // + Undefined for all <= 0: OK, passed 100 tests. + // + Start with Fizz for all multiples of 3: OK, passed 100 tests. + // + End with Buzz for all multiples of 5: OK, passed 100 tests. + // + Int as string for all non-divisible by 3 or 5: OK, passed 100 tests. +} diff --git a/vendor/github.com/leanovate/gopter/example_labels_test.go b/vendor/github.com/leanovate/gopter/example_labels_test.go new file mode 100644 index 000000000..09f24a39d --- /dev/null +++ b/vendor/github.com/leanovate/gopter/example_labels_test.go @@ -0,0 +1,59 @@ +package gopter_test + +import ( + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/gen" + "github.com/leanovate/gopter/prop" +) + +func spookyCalculation(a, b int) int { + if a < 0 { + a = -a + } + if b < 0 { + b = -b + } + return 2*b + 3*(2+(a+1)+b*(b+1)) +} + +// Example_labels demonstrates how labels may help, in case of more complex +// conditions. +// The output will be: +// ! Check spooky: Falsified after 0 passed tests. +// > Labels of failing property: even result +// a: 3 +// a_ORIGINAL (44 shrinks): 861384713 +// b: 0 +// b_ORIGINAL (1 shrinks): -642623569 +func Example_labels() { + parameters := gopter.DefaultTestParameters() + parameters.Rng.Seed(1234) // Just for this example to generate reproducable results + parameters.MinSuccessfulTests = 10000 + + properties := gopter.NewProperties(parameters) + + properties.Property("Check spooky", prop.ForAll( + func(a, b int) string { + result := spookyCalculation(a, b) + if result < 0 { + return "negative result" + } + if result%2 == 0 { + return "even result" + } + return "" + }, + gen.Int().WithLabel("a"), + gen.Int().WithLabel("b"), + )) + + // When using testing.T you might just use: properties.TestingRun(t) + properties.Run(gopter.ConsoleReporter(false)) + // Output: + // ! Check spooky: Falsified after 0 passed tests. + // > Labels of failing property: even result + // a: 3 + // a_ORIGINAL (44 shrinks): 861384713 + // b: 0 + // b_ORIGINAL (1 shrinks): -642623569 +} diff --git a/vendor/github.com/leanovate/gopter/example_sqrt_test.go b/vendor/github.com/leanovate/gopter/example_sqrt_test.go new file mode 100644 index 000000000..51a0e80ae --- /dev/null +++ b/vendor/github.com/leanovate/gopter/example_sqrt_test.go @@ -0,0 +1,37 @@ +package gopter_test + +import ( + "math" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/gen" + "github.com/leanovate/gopter/prop" +) + +func Example_sqrt() { + parameters := gopter.DefaultTestParameters() + parameters.Rng.Seed(1234) // Just for this example to generate reproducable results + + properties := gopter.NewProperties(parameters) + + properties.Property("greater one of all greater one", prop.ForAll( + func(v float64) bool { + return math.Sqrt(v) >= 1 + }, + gen.Float64().SuchThat(func(x float64) bool { return x >= 1.0 }), + )) + + properties.Property("squared is equal to value", prop.ForAll( + func(v float64) bool { + r := math.Sqrt(v) + return math.Abs(r*r-v) < 1e-10*v + }, + gen.Float64().SuchThat(func(x float64) bool { return x >= 0.0 }), + )) + + // When using testing.T you might just use: properties.TestingRun(t) + properties.Run(gopter.ConsoleReporter(false)) + // Output: + // + greater one of all greater one: OK, passed 100 tests. + // + squared is equal to value: OK, passed 100 tests. +} diff --git a/vendor/github.com/leanovate/gopter/flag.go b/vendor/github.com/leanovate/gopter/flag.go new file mode 100644 index 000000000..b32b5743b --- /dev/null +++ b/vendor/github.com/leanovate/gopter/flag.go @@ -0,0 +1,23 @@ +package gopter + +import "sync/atomic" + +// Flag is a convenient helper for an atomic boolean +type Flag struct { + flag int32 +} + +// Get the value of the flag +func (f *Flag) Get() bool { + return atomic.LoadInt32(&f.flag) > 0 +} + +// Set the the flag +func (f *Flag) Set() { + atomic.StoreInt32(&f.flag, 1) +} + +// Unset the flag +func (f *Flag) Unset() { + atomic.StoreInt32(&f.flag, 0) +} diff --git a/vendor/github.com/leanovate/gopter/flag_test.go b/vendor/github.com/leanovate/gopter/flag_test.go new file mode 100644 index 000000000..37dec9ee9 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/flag_test.go @@ -0,0 +1,22 @@ +package gopter_test + +import ( + "testing" + + "github.com/leanovate/gopter" +) + +func TestFlag(t *testing.T) { + flag := &gopter.Flag{} + if flag.Get() { + t.Errorf("Flag should be initially unset: %#v", flag) + } + flag.Set() + if !flag.Get() { + t.Errorf("Flag should be set: %#v", flag) + } + flag.Unset() + if flag.Get() { + t.Errorf("Flag should be unset: %#v", flag) + } +} diff --git a/vendor/github.com/leanovate/gopter/formated_reporter.go b/vendor/github.com/leanovate/gopter/formated_reporter.go new file mode 100644 index 000000000..b09ff2fe3 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/formated_reporter.go @@ -0,0 +1,140 @@ +package gopter + +import ( + "fmt" + "io" + "os" + "strings" + "unicode" +) + +const newLine = "\n" + +// FormatedReporter reports test results in a human readable manager. +type FormatedReporter struct { + verbose bool + width int + output io.Writer +} + +// NewFormatedReporter create a new formated reporter +// verbose toggles verbose output of the property results +// width is the maximal width per line +// output is the writer were the report will be written to +func NewFormatedReporter(verbose bool, width int, output io.Writer) Reporter { + return &FormatedReporter{ + verbose: verbose, + width: width, + output: output, + } +} + +// ConsoleReporter creates a FormatedReporter writing to the console (i.e. stdout) +func ConsoleReporter(verbose bool) Reporter { + return NewFormatedReporter(verbose, 75, os.Stdout) +} + +// ReportTestResult reports a single property result +func (r *FormatedReporter) ReportTestResult(propName string, result *TestResult) { + if result.Passed() { + fmt.Fprintln(r.output, r.formatLines(fmt.Sprintf("+ %s: %s", propName, r.reportResult(result)), "", "")) + } else { + fmt.Fprintln(r.output, r.formatLines(fmt.Sprintf("! %s: %s", propName, r.reportResult(result)), "", "")) + } +} + +func (r *FormatedReporter) reportResult(result *TestResult) string { + status := "" + switch result.Status { + case TestProved: + status = "OK, proved property.\n" + r.reportPropArgs(result.Args) + case TestPassed: + status = fmt.Sprintf("OK, passed %d tests.", result.Succeeded) + case TestFailed: + status = fmt.Sprintf("Falsified after %d passed tests.\n%s%s", result.Succeeded, r.reportLabels(result.Labels), r.reportPropArgs(result.Args)) + case TestExhausted: + status = fmt.Sprintf("Gave up after only %d passed tests. %d tests were discarded.", result.Succeeded, result.Discarded) + case TestError: + status = fmt.Sprintf("Error on property evaluation after %d passed tests: %s\n%s", result.Succeeded, result.Error.Error(), r.reportPropArgs(result.Args)) + } + + if r.verbose { + return concatLines(status, fmt.Sprintf("Elapsed time: %s", result.Time.String())) + } + return status +} + +func (r *FormatedReporter) reportLabels(labels []string) string { + if labels != nil && len(labels) > 0 { + return fmt.Sprintf("> Labels of failing property: %s\n", strings.Join(labels, newLine)) + } + return "" +} + +func (r *FormatedReporter) reportPropArgs(p PropArgs) string { + result := "" + for i, arg := range p { + if result != "" { + result += newLine + } + result += r.reportPropArg(i, arg) + } + return result +} + +func (r *FormatedReporter) reportPropArg(idx int, propArg *PropArg) string { + label := propArg.Label + if label == "" { + label = fmt.Sprintf("ARG_%d", idx) + } + result := fmt.Sprintf("%s: %v", label, propArg.Arg) + if propArg.Shrinks > 0 { + result += fmt.Sprintf("\n%s_ORIGINAL (%d shrinks): %v", label, propArg.Shrinks, propArg.OrigArg) + } + + return result +} + +func (r *FormatedReporter) formatLines(str, lead, trail string) string { + result := "" + for _, line := range strings.Split(str, "\n") { + if result != "" { + result += newLine + } + result += r.breakLine(lead+line+trail, " ") + } + return result +} + +func (r *FormatedReporter) breakLine(str, lead string) string { + if len(str) <= r.width { + return str + } + + result := "" + for len(str) > r.width { + idx := strings.LastIndexFunc(str[0:r.width], func(ch rune) bool { + return unicode.IsSpace(ch) + }) + if idx <= 0 { + idx = r.width + } + result += str[0:idx] + "\n" + lead + str = str[idx:] + } + result += str + return result +} + +func concatLines(strs ...string) string { + result := "" + for _, str := range strs { + if str != "" { + if result != "" { + result += "\n" + } + result += str + } + } + return result +} diff --git a/vendor/github.com/leanovate/gopter/formated_reporter_test.go b/vendor/github.com/leanovate/gopter/formated_reporter_test.go new file mode 100644 index 000000000..3159c8133 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/formated_reporter_test.go @@ -0,0 +1,80 @@ +package gopter + +import ( + "bytes" + "errors" + "testing" + "time" +) + +func TestConsoleReporter(t *testing.T) { + var buffer bytes.Buffer + reporter := &FormatedReporter{ + verbose: false, + width: 75, + output: &buffer, + } + + reporter.ReportTestResult("test property", &TestResult{Status: TestPassed, Succeeded: 50}) + if buffer.String() != "+ test property: OK, passed 50 tests.\n" { + t.Errorf("Invalid output: %#v", buffer.String()) + } + buffer.Reset() + + reporter.ReportTestResult("test property", &TestResult{ + Status: TestFailed, + Succeeded: 50, + Args: PropArgs([]*PropArg{&PropArg{ + Arg: "0", + }}), + }) + if buffer.String() != "! test property: Falsified after 50 passed tests.\nARG_0: 0\n" { + t.Errorf("Invalid output: %#v", buffer.String()) + } + buffer.Reset() + + reporter.ReportTestResult("test property", &TestResult{ + Status: TestProved, + Succeeded: 50, + Args: PropArgs([]*PropArg{&PropArg{ + Arg: "0", + Label: "somehing", + OrigArg: "10", + Shrinks: 6, + }}), + }) + if buffer.String() != "+ test property: OK, proved property.\nsomehing: 0\nsomehing_ORIGINAL (6 shrinks): 10\n" { + t.Errorf("Invalid output: %#v", buffer.String()) + } + buffer.Reset() + + reporter.ReportTestResult("test property", &TestResult{ + Status: TestExhausted, + Succeeded: 50, + Discarded: 40, + }) + if buffer.String() != "! test property: Gave up after only 50 passed tests. 40 tests were\n discarded.\n" { + t.Errorf("Invalid output: %#v", buffer.String()) + } + buffer.Reset() + + reporter.ReportTestResult("test property", &TestResult{ + Status: TestError, + Error: errors.New("Poop"), + Succeeded: 50, + Args: PropArgs([]*PropArg{&PropArg{ + Arg: "0", + }}), + }) + if buffer.String() != "! test property: Error on property evaluation after 50 passed tests: Poop\nARG_0: 0\n" { + t.Errorf("Invalid output: %#v", buffer.String()) + } + buffer.Reset() + + reporter.verbose = true + reporter.ReportTestResult("test property", &TestResult{Status: TestPassed, Succeeded: 50, Time: time.Minute}) + if buffer.String() != "+ test property: OK, passed 50 tests.\nElapsed time: 1m0s\n" { + t.Errorf("Invalid output: %#v", buffer.String()) + } + buffer.Reset() +} diff --git a/vendor/github.com/leanovate/gopter/gen.go b/vendor/github.com/leanovate/gopter/gen.go new file mode 100644 index 000000000..5402934bb --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen.go @@ -0,0 +1,251 @@ +package gopter + +import ( + "fmt" + "reflect" +) + +// Gen generator of arbitrary values. +// Usually properties are checked by verifing a condition holds true for +// arbitrary input parameters generated by a Gen. +// +// IMPORTANT: Even though a generator is supposed to generate random values, it +// should do this in a reproducable way. Therefore a generator has to create the +// same result for the same GenParameters, i.e. ensure that you just use the +// RNG provided by GenParameters and no external one. +// If you just plug generators together you do not have to worry about this. +type Gen func(*GenParameters) *GenResult + +var ( + // DefaultGenParams can be used as default für *GenParameters + DefaultGenParams = DefaultGenParameters() +) + +// Sample generate a sample value. +// Depending on the state of the RNG the generate might fail to provide a sample +func (g Gen) Sample() (interface{}, bool) { + return g(DefaultGenParameters()).Retrieve() +} + +// WithLabel adds a label to a generated value. +// Labels are usually used for reporting for the arguments of a property check. +func (g Gen) WithLabel(label string) Gen { + return func(genParams *GenParameters) *GenResult { + result := g(genParams) + result.Labels = append(result.Labels, label) + return result + } +} + +// SuchThat creates a derived generator by adding a sieve. +// f: has to be a function with one parameter (matching the generated value) returning a bool. +// All generated values are expected to satisfy +// f(value) == true. +// Use this care, if the sieve to to fine the generator will have many misses which results +// in an undecided property. +func (g Gen) SuchThat(f interface{}) Gen { + checkVal := reflect.ValueOf(f) + checkType := checkVal.Type() + + if checkVal.Kind() != reflect.Func { + panic(fmt.Sprintf("Param of SuchThat has to be a func, but is %v", checkType.Kind())) + } + if checkType.NumIn() != 1 { + panic(fmt.Sprintf("Param of SuchThat has to be a func with one param, but is %v", checkType.NumIn())) + } else { + genResultType := g(DefaultGenParams).ResultType + if !genResultType.AssignableTo(checkType.In(0)) { + panic(fmt.Sprintf("Param of SuchThat has to be a func with one param assignable to %v, but is %v", genResultType, checkType.In(0))) + } + } + if checkType.NumOut() != 1 { + panic(fmt.Sprintf("Param of SuchThat has to be a func with one return value, but is %v", checkType.NumOut())) + } else if checkType.Out(0).Kind() != reflect.Bool { + panic(fmt.Sprintf("Param of SuchThat has to be a func with one return value of bool, but is %v", checkType.Out(0).Kind())) + } + sieve := func(v interface{}) bool { + return checkVal.Call([]reflect.Value{reflect.ValueOf(v)})[0].Bool() + } + + return func(genParams *GenParameters) *GenResult { + result := g(genParams) + prevSieve := result.Sieve + if prevSieve == nil { + result.Sieve = sieve + } else { + result.Sieve = func(value interface{}) bool { + return prevSieve(value) && sieve(value) + } + } + return result + } +} + +// WithShrinker creates a derived generator with a specific shrinker +func (g Gen) WithShrinker(shrinker Shrinker) Gen { + return func(genParams *GenParameters) *GenResult { + result := g(genParams) + if shrinker == nil { + result.Shrinker = NoShrinker + } else { + result.Shrinker = shrinker + } + return result + } +} + +// Map creates a derived generators by mapping all generatored values with a given function. +// f: has to be a function with one parameter (matching the generated value) and a single return. +// Note: The derived generator will not have a sieve or shrinker. +// Note: The mapping function may have a second parameter "*GenParameters" +// Note: The first parameter of the mapping function and its return may be a *GenResult (this makes MapResult obsolete) +func (g Gen) Map(f interface{}) Gen { + mapperVal := reflect.ValueOf(f) + mapperType := mapperVal.Type() + needsGenParameters := false + genResultInput := false + genResultOutput := false + + if mapperVal.Kind() != reflect.Func { + panic(fmt.Sprintf("Param of Map has to be a func, but is %v", mapperType.Kind())) + } + if mapperType.NumIn() != 1 && mapperType.NumIn() != 2 { + panic(fmt.Sprintf("Param of Map has to be a func with one or two params, but is %v", mapperType.NumIn())) + } else { + if mapperType.NumIn() == 2 { + if !reflect.TypeOf(&GenParameters{}).AssignableTo(mapperType.In(1)) { + panic("Second parameter of mapper function has to be a *GenParameters") + } + needsGenParameters = true + } + genResultType := g(DefaultGenParams).ResultType + if reflect.TypeOf(&GenResult{}).AssignableTo(mapperType.In(0)) { + genResultInput = true + } else if !genResultType.AssignableTo(mapperType.In(0)) { + panic(fmt.Sprintf("Param of Map has to be a func with one param assignable to %v, but is %v", genResultType, mapperType.In(0))) + } + } + if mapperType.NumOut() != 1 { + panic(fmt.Sprintf("Param of Map has to be a func with one return value, but is %v", mapperType.NumOut())) + } else if reflect.TypeOf(&GenResult{}).AssignableTo(mapperType.Out(0)) { + genResultOutput = true + } + + return func(genParams *GenParameters) *GenResult { + result := g(genParams) + if genResultInput { + var mapped reflect.Value + if needsGenParameters { + mapped = mapperVal.Call([]reflect.Value{reflect.ValueOf(result), reflect.ValueOf(genParams)})[0] + } else { + mapped = mapperVal.Call([]reflect.Value{reflect.ValueOf(result)})[0] + } + if genResultOutput { + return mapped.Interface().(*GenResult) + } + return &GenResult{ + Shrinker: NoShrinker, + Result: mapped.Interface(), + Labels: result.Labels, + ResultType: mapperType.Out(0), + } + } + value, ok := result.RetrieveAsValue() + if ok { + var mapped reflect.Value + if needsGenParameters { + mapped = mapperVal.Call([]reflect.Value{value, reflect.ValueOf(genParams)})[0] + } else { + mapped = mapperVal.Call([]reflect.Value{value})[0] + } + if genResultOutput { + return mapped.Interface().(*GenResult) + } + return &GenResult{ + Shrinker: NoShrinker, + Result: mapped.Interface(), + Labels: result.Labels, + ResultType: mapperType.Out(0), + } + } + return &GenResult{ + Shrinker: NoShrinker, + Result: nil, + Labels: result.Labels, + ResultType: mapperType.Out(0), + } + } +} + +// FlatMap creates a derived generator by passing a generated value to a function which itself +// creates a generator. +func (g Gen) FlatMap(f func(interface{}) Gen, resultType reflect.Type) Gen { + return func(genParams *GenParameters) *GenResult { + result := g(genParams) + value, ok := result.Retrieve() + if ok { + return f(value)(genParams) + } + return &GenResult{ + Shrinker: NoShrinker, + Result: nil, + Labels: result.Labels, + ResultType: resultType, + } + } +} + +// MapResult creates a derived generator by mapping the GenResult directly. +// Contrary to `Map` and `FlatMap` this also allow the conversion of +// shrinkers and sieves, but implementation is more cumbersome. +// Deprecation note: Map now has the same functionality +func (g Gen) MapResult(f func(*GenResult) *GenResult) Gen { + return func(genParams *GenParameters) *GenResult { + return f(g(genParams)) + } +} + +// CombineGens creates a generators from a list of generators. +// The result type will be a []interface{} containing the generated values of each generators in +// the list. +// Note: The combined generator will not have a sieve or shrinker. +func CombineGens(gens ...Gen) Gen { + return func(genParams *GenParameters) *GenResult { + labels := []string{} + values := make([]interface{}, len(gens)) + shrinkers := make([]Shrinker, len(gens)) + sieves := make([]func(v interface{}) bool, len(gens)) + + var ok bool + for i, gen := range gens { + result := gen(genParams) + labels = append(labels, result.Labels...) + shrinkers[i] = result.Shrinker + sieves[i] = result.Sieve + values[i], ok = result.Retrieve() + if !ok { + return &GenResult{ + Shrinker: NoShrinker, + Result: nil, + Labels: result.Labels, + ResultType: reflect.TypeOf(values), + } + } + } + return &GenResult{ + Shrinker: CombineShrinker(shrinkers...), + Result: values, + Labels: labels, + ResultType: reflect.TypeOf(values), + Sieve: func(v interface{}) bool { + values := v.([]interface{}) + for i, value := range values { + if sieves[i] != nil && !sieves[i](value) { + return false + } + } + return true + }, + } + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/bool.go b/vendor/github.com/leanovate/gopter/gen/bool.go new file mode 100644 index 000000000..019f93bc1 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/bool.go @@ -0,0 +1,10 @@ +package gen + +import "github.com/leanovate/gopter" + +// Bool generates an arbitrary bool value +func Bool() gopter.Gen { + return func(genParams *gopter.GenParameters) *gopter.GenResult { + return gopter.NewGenResult(genParams.NextBool(), gopter.NoShrinker) + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/bool_test.go b/vendor/github.com/leanovate/gopter/gen/bool_test.go new file mode 100644 index 000000000..e14f05002 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/bool_test.go @@ -0,0 +1,14 @@ +package gen_test + +import ( + "testing" + + "github.com/leanovate/gopter/gen" +) + +func TestBool(t *testing.T) { + commonGeneratorTest(t, "bool", gen.Bool(), func(value interface{}) bool { + _, ok := value.(bool) + return ok + }) +} diff --git a/vendor/github.com/leanovate/gopter/gen/complex.go b/vendor/github.com/leanovate/gopter/gen/complex.go new file mode 100644 index 000000000..2bc57a088 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/complex.go @@ -0,0 +1,49 @@ +package gen + +import "github.com/leanovate/gopter" + +// Complex128Box generate complex128 numbers within a rectangle/box in the complex plane +func Complex128Box(min, max complex128) gopter.Gen { + return gopter.CombineGens( + Float64Range(real(min), real(max)), + Float64Range(imag(min), imag(max)), + ).Map(func(values []interface{}) complex128 { + return complex(values[0].(float64), values[1].(float64)) + }).SuchThat(func(v complex128) bool { + return real(v) >= real(min) && real(v) <= real(max) && + imag(v) >= imag(min) && imag(v) <= imag(max) + }).WithShrinker(Complex128Shrinker) +} + +// Complex128 generate arbitrary complex128 numbers +func Complex128() gopter.Gen { + return gopter.CombineGens( + Float64(), + Float64(), + ).Map(func(values []interface{}) complex128 { + return complex(values[0].(float64), values[1].(float64)) + }).WithShrinker(Complex128Shrinker) +} + +// Complex64Box generate complex64 numbers within a rectangle/box in the complex plane +func Complex64Box(min, max complex64) gopter.Gen { + return gopter.CombineGens( + Float32Range(real(min), real(max)), + Float32Range(imag(min), imag(max)), + ).Map(func(values []interface{}) complex64 { + return complex(values[0].(float32), values[1].(float32)) + }).SuchThat(func(v complex64) bool { + return real(v) >= real(min) && real(v) <= real(max) && + imag(v) >= imag(min) && imag(v) <= imag(max) + }).WithShrinker(Complex64Shrinker) +} + +// Complex64 generate arbitrary complex64 numbers +func Complex64() gopter.Gen { + return gopter.CombineGens( + Float32(), + Float32(), + ).Map(func(values []interface{}) complex64 { + return complex(values[0].(float32), values[1].(float32)) + }).WithShrinker(Complex64Shrinker) +} diff --git a/vendor/github.com/leanovate/gopter/gen/complex_shrink.go b/vendor/github.com/leanovate/gopter/gen/complex_shrink.go new file mode 100644 index 000000000..68ec8bdc2 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/complex_shrink.go @@ -0,0 +1,27 @@ +package gen + +import "github.com/leanovate/gopter" + +// Complex128Shrinker is a shrinker for complex128 numbers +func Complex128Shrinker(v interface{}) gopter.Shrink { + c := v.(complex128) + realShrink := Float64Shrinker(real(c)).Map(func(r float64) complex128 { + return complex(r, imag(c)) + }) + imagShrink := Float64Shrinker(imag(c)).Map(func(i float64) complex128 { + return complex(real(c), i) + }) + return realShrink.Interleave(imagShrink) +} + +// Complex64Shrinker is a shrinker for complex64 numbers +func Complex64Shrinker(v interface{}) gopter.Shrink { + c := v.(complex64) + realShrink := Float64Shrinker(float64(real(c))).Map(func(r float64) complex64 { + return complex(float32(r), imag(c)) + }) + imagShrink := Float64Shrinker(float64(imag(c))).Map(func(i float64) complex64 { + return complex(real(c), float32(i)) + }) + return realShrink.Interleave(imagShrink) +} diff --git a/vendor/github.com/leanovate/gopter/gen/complex_shrink_test.go b/vendor/github.com/leanovate/gopter/gen/complex_shrink_test.go new file mode 100644 index 000000000..b2ad573f6 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/complex_shrink_test.go @@ -0,0 +1,91 @@ +package gen_test + +import ( + "reflect" + "testing" + + "github.com/leanovate/gopter/gen" +) + +func TestComplex128Shrinker(t *testing.T) { + zeroShrinks := gen.Complex128Shrinker(0 + 0i).All() + if !reflect.DeepEqual(zeroShrinks, []interface{}{}) { + t.Errorf("Invalid zeroShrinks: %#v", zeroShrinks) + } + + oneShrink := gen.Complex128Shrinker(1 + 0i).All() + if !reflect.DeepEqual(oneShrink, []interface{}{ + (0 + 0i), (0.5 + 0i), (-0.5 + 0i), (0.75 + 0i), (-0.75 + 0i), (0.875 + 0i), (-0.875 + 0i), + (0.9375 + 0i), (-0.9375 + 0i), (0.96875 + 0i), (-0.96875 + 0i), (0.984375 + 0i), + (-0.984375 + 0i), (0.9921875 + 0i), (-0.9921875 + 0i), (0.99609375 + 0i), + (-0.99609375 + 0i), (0.998046875 + 0i), (-0.998046875 + 0i), (0.9990234375 + 0i), + (-0.9990234375 + 0i), (0.99951171875 + 0i), (-0.99951171875 + 0i), (0.999755859375 + 0i), + (-0.999755859375 + 0i), (0.9998779296875 + 0i), (-0.9998779296875 + 0i), + (0.99993896484375 + 0i), (-0.99993896484375 + 0i), (0.999969482421875 + 0i), + (-0.999969482421875 + 0i), (0.9999847412109375 + 0i), (-0.9999847412109375 + 0i), + }) { + t.Errorf("Invalid oneShrink: %#v", oneShrink) + } + + iShrink := gen.Complex128Shrinker(1i).All() + if !reflect.DeepEqual(iShrink, []interface{}{ + (0 + 0i), (0 + 0.5i), (0 - 0.5i), (0 + 0.75i), (0 - 0.75i), (0 + 0.875i), (0 - 0.875i), + (0 + 0.9375i), (0 - 0.9375i), (0 + 0.96875i), (0 - 0.96875i), (0 + 0.984375i), + (0 - 0.984375i), (0 + 0.9921875i), (0 - 0.9921875i), (0 + 0.99609375i), (0 - 0.99609375i), + (0 + 0.998046875i), (0 - 0.998046875i), (0 + 0.9990234375i), (0 - 0.9990234375i), + (0 + 0.99951171875i), (0 - 0.99951171875i), (0 + 0.999755859375i), (0 - 0.999755859375i), + (0 + 0.9998779296875i), (0 - 0.9998779296875i), (0 + 0.99993896484375i), + (0 - 0.99993896484375i), (0 + 0.999969482421875i), (0 - 0.999969482421875i), + (0 + 0.9999847412109375i), (0 - 0.9999847412109375i), + }) { + t.Errorf("Invalid iShrink: %#v", iShrink) + } + + teniShrink := gen.Complex128Shrinker(10 + 1i).All() + if !reflect.DeepEqual(teniShrink, []interface{}{(0 + 1i), (10 + 0i), (5 + 1i), (10 + 0.5i), + (-5 + 1i), (10 - 0.5i), (7.5 + 1i), (10 + 0.75i), (-7.5 + 1i), (10 - 0.75i), (8.75 + 1i), + (10 + 0.875i), (-8.75 + 1i), (10 - 0.875i), (9.375 + 1i), (10 + 0.9375i), (-9.375 + 1i), + (10 - 0.9375i), (9.6875 + 1i), (10 + 0.96875i), (-9.6875 + 1i), (10 - 0.96875i), + (9.84375 + 1i), (10 + 0.984375i), (-9.84375 + 1i), (10 - 0.984375i), (9.921875 + 1i), + (10 + 0.9921875i), (-9.921875 + 1i), (10 - 0.9921875i), (9.9609375 + 1i), + (10 + 0.99609375i), (-9.9609375 + 1i), (10 - 0.99609375i), (9.98046875 + 1i), + (10 + 0.998046875i), (-9.98046875 + 1i), (10 - 0.998046875i), (9.990234375 + 1i), + (10 + 0.9990234375i), (-9.990234375 + 1i), (10 - 0.9990234375i), (9.9951171875 + 1i), + (10 + 0.99951171875i), (-9.9951171875 + 1i), (10 - 0.99951171875i), (9.99755859375 + 1i), + (10 + 0.999755859375i), (-9.99755859375 + 1i), (10 - 0.999755859375i), + (9.998779296875 + 1i), (10 + 0.9998779296875i), (-9.998779296875 + 1i), + (10 - 0.9998779296875i), (9.9993896484375 + 1i), (10 + 0.99993896484375i), + (-9.9993896484375 + 1i), (10 - 0.99993896484375i), (9.99969482421875 + 1i), + (10 + 0.999969482421875i), (-9.99969482421875 + 1i), (10 - 0.999969482421875i), + (9.999847412109375 + 1i), (10 + 0.9999847412109375i), (-9.999847412109375 + 1i), + (10 - 0.9999847412109375i), (9.999923706054688 + 1i), (-9.999923706054688 + 1i), + (9.999961853027344 + 1i), (-9.999961853027344 + 1i), (9.999980926513672 + 1i), + (-9.999980926513672 + 1i), + }) { + t.Errorf("Invalid teniShrink: %#v", teniShrink) + } +} + +func TestComplex64Shrinker(t *testing.T) { + zeroShrinks := gen.Complex64Shrinker(complex64(0 + 0i)).All() + if !reflect.DeepEqual(zeroShrinks, []interface{}{}) { + t.Errorf("Invalid zeroShrinks: %#v", zeroShrinks) + } + + oneShrink := gen.Complex64Shrinker(complex64(1 + 0i)).All() + if !reflect.DeepEqual(oneShrink, []interface{}{ + complex64(0 + 0i), complex64(0.5 + 0i), complex64(-0.5 + 0i), complex64(0.75 + 0i), + complex64(-0.75 + 0i), complex64(0.875 + 0i), complex64(-0.875 + 0i), + complex64(0.9375 + 0i), complex64(-0.9375 + 0i), complex64(0.96875 + 0i), + complex64(-0.96875 + 0i), complex64(0.984375 + 0i), complex64(-0.984375 + 0i), + complex64(0.9921875 + 0i), complex64(-0.9921875 + 0i), complex64(0.99609375 + 0i), + complex64(-0.99609375 + 0i), complex64(0.9980469 + 0i), complex64(-0.9980469 + 0i), + complex64(0.99902344 + 0i), complex64(-0.99902344 + 0i), complex64(0.9995117 + 0i), + complex64(-0.9995117 + 0i), complex64(0.99975586 + 0i), complex64(-0.99975586 + 0i), + complex64(0.9998779 + 0i), complex64(-0.9998779 + 0i), complex64(0.99993896 + 0i), + complex64(-0.99993896 + 0i), complex64(0.9999695 + 0i), complex64(-0.9999695 + 0i), + complex64(0.99998474 + 0i), complex64(-0.99998474 + 0i), + }) { + t.Errorf("Invalid oneShrink: %#v", oneShrink) + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/complex_test.go b/vendor/github.com/leanovate/gopter/gen/complex_test.go new file mode 100644 index 000000000..fdb95de8f --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/complex_test.go @@ -0,0 +1,46 @@ +package gen_test + +import ( + "math" + "testing" + + "github.com/leanovate/gopter/gen" +) + +func TestComplex128Box(t *testing.T) { + minReal := -12345.67 + maxReal := 2345.78 + minImag := -5432.8 + maxImag := 8764.6 + complexs := gen.Complex128Box(complex(minReal, minImag), complex(maxReal, maxImag)) + commonGeneratorTest(t, "complex 128 box", complexs, func(value interface{}) bool { + v, ok := value.(complex128) + return ok && real(v) >= minReal && real(v) < maxReal && imag(v) >= minImag && imag(v) < maxImag + }) +} + +func TestComplex128(t *testing.T) { + commonGeneratorTest(t, "complex 128", gen.Complex128(), func(value interface{}) bool { + v, ok := value.(complex128) + return ok && !math.IsNaN(real(v)) && !math.IsNaN(imag(v)) && !math.IsInf(real(v), 0) && !math.IsInf(imag(v), 0) + }) +} + +func TestComplex64Box(t *testing.T) { + minReal := float32(-12345.67) + maxReal := float32(2345.78) + minImag := float32(-5432.8) + maxImag := float32(8764.6) + complexs := gen.Complex64Box(complex(minReal, minImag), complex(maxReal, maxImag)) + commonGeneratorTest(t, "complex 64 box", complexs, func(value interface{}) bool { + v, ok := value.(complex64) + return ok && real(v) >= minReal && real(v) < maxReal && imag(v) >= minImag && imag(v) < maxImag + }) +} + +func TestComplex64(t *testing.T) { + commonGeneratorTest(t, "complex 64", gen.Complex64(), func(value interface{}) bool { + _, ok := value.(complex64) + return ok + }) +} diff --git a/vendor/github.com/leanovate/gopter/gen/const.go b/vendor/github.com/leanovate/gopter/gen/const.go new file mode 100644 index 000000000..c213e1672 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/const.go @@ -0,0 +1,11 @@ +package gen + +import "github.com/leanovate/gopter" + +// Const creates a generator for a constant value +// Not the most exciting generator, but can be helpful from time to time +func Const(value interface{}) gopter.Gen { + return func(*gopter.GenParameters) *gopter.GenResult { + return gopter.NewGenResult(value, gopter.NoShrinker) + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/const_test.go b/vendor/github.com/leanovate/gopter/gen/const_test.go new file mode 100644 index 000000000..b72ac1fa3 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/const_test.go @@ -0,0 +1,14 @@ +package gen_test + +import ( + "testing" + + "github.com/leanovate/gopter/gen" +) + +func TestConstGen(t *testing.T) { + commonGeneratorTest(t, "const", gen.Const("some constant"), func(value interface{}) bool { + v, ok := value.(string) + return ok && v == "some constant" + }) +} diff --git a/vendor/github.com/leanovate/gopter/gen/doc.go b/vendor/github.com/leanovate/gopter/gen/doc.go new file mode 100644 index 000000000..485923920 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/doc.go @@ -0,0 +1,4 @@ +/* +Package gen contains all commonly used generators and shrinkers. +*/ +package gen diff --git a/vendor/github.com/leanovate/gopter/gen/fail.go b/vendor/github.com/leanovate/gopter/gen/fail.go new file mode 100644 index 000000000..9d0efc7ee --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/fail.go @@ -0,0 +1,15 @@ +package gen + +import ( + "reflect" + + "github.com/leanovate/gopter" +) + +// Fail is a generator that always fails to generate a value +// Useful as fallback +func Fail(resultType reflect.Type) gopter.Gen { + return func(*gopter.GenParameters) *gopter.GenResult { + return gopter.NewEmptyResult(resultType) + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/fail_test.go b/vendor/github.com/leanovate/gopter/gen/fail_test.go new file mode 100644 index 000000000..aeafba523 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/fail_test.go @@ -0,0 +1,18 @@ +package gen_test + +import ( + "reflect" + "testing" + + "github.com/leanovate/gopter/gen" +) + +func TestFail(t *testing.T) { + fail := gen.Fail(reflect.TypeOf("")) + + value, ok := fail.Sample() + + if value != nil || ok { + t.Fail() + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/floats.go b/vendor/github.com/leanovate/gopter/gen/floats.go new file mode 100644 index 000000000..901cac606 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/floats.go @@ -0,0 +1,69 @@ +package gen + +import ( + "math" + "reflect" + + "github.com/leanovate/gopter" +) + +// Float64Range generates float64 numbers within a given range +func Float64Range(min, max float64) gopter.Gen { + d := max - min + if d < 0 || d > math.MaxFloat64 { + return Fail(reflect.TypeOf(float64(0))) + } + + return func(genParams *gopter.GenParameters) *gopter.GenResult { + genResult := gopter.NewGenResult(min+genParams.Rng.Float64()*d, Float64Shrinker) + genResult.Sieve = func(v interface{}) bool { + return v.(float64) >= min && v.(float64) <= max + } + return genResult + } +} + +// Float64 generates arbitrary float64 numbers that do not contain NaN or Inf +func Float64() gopter.Gen { + return gopter.CombineGens( + Int64Range(0, 1), + Int64Range(0, 0x7fe), + Int64Range(0, 0xfffffffffffff), + ).Map(func(values []interface{}) float64 { + sign := uint64(values[0].(int64)) + exponent := uint64(values[1].(int64)) + mantissa := uint64(values[2].(int64)) + + return math.Float64frombits((sign << 63) | (exponent << 52) | mantissa) + }).WithShrinker(Float64Shrinker) +} + +// Float32Range generates float32 numbers within a given range +func Float32Range(min, max float32) gopter.Gen { + d := max - min + if d < 0 || d > math.MaxFloat32 { + return Fail(reflect.TypeOf(float32(0))) + } + return func(genParams *gopter.GenParameters) *gopter.GenResult { + genResult := gopter.NewGenResult(min+genParams.Rng.Float32()*d, Float32Shrinker) + genResult.Sieve = func(v interface{}) bool { + return v.(float32) >= min && v.(float32) <= max + } + return genResult + } +} + +// Float32 generates arbitrary float32 numbers that do not contain NaN or Inf +func Float32() gopter.Gen { + return gopter.CombineGens( + Int32Range(0, 1), + Int32Range(0, 0xfe), + Int32Range(0, 0x7fffff), + ).Map(func(values []interface{}) float32 { + sign := uint32(values[0].(int32)) + exponent := uint32(values[1].(int32)) + mantissa := uint32(values[2].(int32)) + + return math.Float32frombits((sign << 31) | (exponent << 23) | mantissa) + }).WithShrinker(Float32Shrinker) +} diff --git a/vendor/github.com/leanovate/gopter/gen/floats_shrink.go b/vendor/github.com/leanovate/gopter/gen/floats_shrink.go new file mode 100644 index 000000000..00cd540e9 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/floats_shrink.go @@ -0,0 +1,49 @@ +package gen + +import ( + "math" + + "github.com/leanovate/gopter" +) + +type float64Shrink struct { + original float64 + half float64 +} + +func (s *float64Shrink) isZeroOrVeryClose() bool { + if s.half == 0 { + return true + } + muliple := s.half * 100000 + return math.Abs(muliple) < 1 && muliple != 0 +} + +func (s *float64Shrink) Next() (interface{}, bool) { + if s.isZeroOrVeryClose() { + return nil, false + } + value := s.original - s.half + s.half /= 2 + return value, true +} + +// Float64Shrinker is a shrinker for float64 numbers +func Float64Shrinker(v interface{}) gopter.Shrink { + negShrink := float64Shrink{ + original: -v.(float64), + half: -v.(float64), + } + posShrink := float64Shrink{ + original: v.(float64), + half: v.(float64) / 2, + } + return gopter.Shrink(negShrink.Next).Interleave(gopter.Shrink(posShrink.Next)) +} + +// Float32Shrinker is a shrinker for float32 numbers +func Float32Shrinker(v interface{}) gopter.Shrink { + return Float64Shrinker(float64(v.(float32))).Map(func(e float64) float32 { + return float32(e) + }) +} diff --git a/vendor/github.com/leanovate/gopter/gen/floats_shrink_test.go b/vendor/github.com/leanovate/gopter/gen/floats_shrink_test.go new file mode 100644 index 000000000..53221749e --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/floats_shrink_test.go @@ -0,0 +1,154 @@ +package gen_test + +import ( + "reflect" + "testing" + + "github.com/leanovate/gopter/gen" +) + +func TestFloat64Shrinker(t *testing.T) { + zeroShrinks := gen.Float64Shrinker(float64(0)).All() + if !reflect.DeepEqual(zeroShrinks, []interface{}{}) { + t.Errorf("Invalid zeroShrinks: %#v", zeroShrinks) + } + + oneShrinks := gen.Float64Shrinker(float64(1)).All() + if !reflect.DeepEqual(oneShrinks, []interface{}{ + 0.0, + 0.5, + -0.5, + 0.75, + -0.75, + 0.875, + -0.875, + 0.9375, + -0.9375, + 0.96875, + -0.96875, + 0.984375, + -0.984375, + 0.9921875, + -0.9921875, + 0.99609375, + -0.99609375, + 0.998046875, + -0.998046875, + 0.9990234375, + -0.9990234375, + 0.99951171875, + -0.99951171875, + 0.999755859375, + -0.999755859375, + 0.9998779296875, + -0.9998779296875, + 0.99993896484375, + -0.99993896484375, + 0.999969482421875, + -0.999969482421875, + 0.9999847412109375, + -0.9999847412109375, + }) { + t.Errorf("Invalid tenShrinks: %#v", oneShrinks) + } + + hundretShrinks := gen.Float64Shrinker(float64(100)).All() + if !reflect.DeepEqual(hundretShrinks, []interface{}{ + 0.0, + 50.0, + -50.0, + 75.0, + -75.0, + 87.5, + -87.5, + 93.75, + -93.75, + 96.875, + -96.875, + 98.4375, + -98.4375, + 99.21875, + -99.21875, + 99.609375, + -99.609375, + 99.8046875, + -99.8046875, + 99.90234375, + -99.90234375, + 99.951171875, + -99.951171875, + 99.9755859375, + -99.9755859375, + 99.98779296875, + -99.98779296875, + 99.993896484375, + -99.993896484375, + 99.9969482421875, + -99.9969482421875, + 99.99847412109375, + -99.99847412109375, + 99.99923706054688, + -99.99923706054688, + 99.99961853027344, + -99.99961853027344, + 99.99980926513672, + -99.99980926513672, + 99.99990463256836, + -99.99990463256836, + 99.99995231628418, + -99.99995231628418, + 99.99997615814209, + -99.99997615814209, + 99.99998807907104, + -99.99998807907104, + }) { + t.Errorf("Invalid hundretShrinks: %#v", hundretShrinks) + } +} + +func TestFloat32Shrinker(t *testing.T) { + zeroShrinks := gen.Float32Shrinker(float32(0)).All() + if !reflect.DeepEqual(zeroShrinks, []interface{}{}) { + t.Errorf("Invalid zeroShrinks: %#v", zeroShrinks) + } + + oneShrinks := gen.Float32Shrinker(float32(1)).All() + if !reflect.DeepEqual(oneShrinks, []interface{}{ + float32(0), + float32(0.5), + float32(-0.5), + float32(0.75), + float32(-0.75), + float32(0.875), + float32(-0.875), + float32(0.9375), + float32(-0.9375), + float32(0.96875), + float32(-0.96875), + float32(0.984375), + float32(-0.984375), + float32(0.9921875), + float32(-0.9921875), + float32(0.99609375), + float32(-0.99609375), + float32(0.9980469), + float32(-0.9980469), + float32(0.99902344), + float32(-0.99902344), + float32(0.9995117), + float32(-0.9995117), + float32(0.99975586), + float32(-0.99975586), + float32(0.9998779), + float32(-0.9998779), + float32(0.99993896), + float32(-0.99993896), + float32(0.9999695), + float32(-0.9999695), + float32(0.99998474), + float32(-0.99998474), + }) { + t.Errorf("Invalid tenShrinks: %#v", oneShrinks) + } + +} diff --git a/vendor/github.com/leanovate/gopter/gen/floats_test.go b/vendor/github.com/leanovate/gopter/gen/floats_test.go new file mode 100644 index 000000000..d3bbf9980 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/floats_test.go @@ -0,0 +1,48 @@ +package gen_test + +import ( + "math" + "testing" + + "github.com/leanovate/gopter/gen" +) + +func TestFloat64(t *testing.T) { + commonGeneratorTest(t, "float 64", gen.Float64(), func(value interface{}) bool { + v, ok := value.(float64) + return ok && !math.IsNaN(v) && !math.IsInf(v, 0) + }) +} + +func TestFloat64Range(t *testing.T) { + fail := gen.Float64Range(200, 100) + + if value, ok := fail.Sample(); value != nil || ok { + t.Fail() + } + + commonGeneratorTest(t, "float 64 range", gen.Float64Range(-1234.5, 56789.123), func(value interface{}) bool { + v, ok := value.(float64) + return ok && !math.IsNaN(v) && !math.IsInf(v, 0) && v >= -1234.5 && v <= 56789.123 + }) +} + +func TestFloat32(t *testing.T) { + commonGeneratorTest(t, "float 32", gen.Float32(), func(value interface{}) bool { + _, ok := value.(float32) + return ok + }) +} + +func TestFloat32Range(t *testing.T) { + fail := gen.Float32Range(200, 100) + + if value, ok := fail.Sample(); value != nil || ok { + t.Fail() + } + + commonGeneratorTest(t, "float 32 range", gen.Float32Range(-1234.5, 56789.123), func(value interface{}) bool { + v, ok := value.(float32) + return ok && v >= -1234.5 && v <= 56789.123 + }) +} diff --git a/vendor/github.com/leanovate/gopter/gen/frequency.go b/vendor/github.com/leanovate/gopter/gen/frequency.go new file mode 100644 index 000000000..5f5c7075e --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/frequency.go @@ -0,0 +1,33 @@ +package gen + +import ( + "sort" + + "github.com/leanovate/gopter" +) + +// Frequency combines multiple weighted generators of the the same result type +// The generators from weightedGens will be used accrding to the weight, i.e. generators +// with a hight weight will be used more often than generators with a low weight. +func Frequency(weightedGens map[int]gopter.Gen) gopter.Gen { + if len(weightedGens) == 0 { + return Fail(nil) + } + weights := make(sort.IntSlice, 0, len(weightedGens)) + max := 0 + for weight := range weightedGens { + if weight > max { + max = weight + } + weights = append(weights, weight) + } + weights.Sort() + return func(genParams *gopter.GenParameters) *gopter.GenResult { + idx := weights.Search(genParams.Rng.Intn(max + 1)) + gen := weightedGens[weights[idx]] + + result := gen(genParams) + result.Sieve = nil + return result + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/frequency_test.go b/vendor/github.com/leanovate/gopter/gen/frequency_test.go new file mode 100644 index 000000000..9a93a32dd --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/frequency_test.go @@ -0,0 +1,48 @@ +package gen_test + +import ( + "math/rand" + "testing" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/gen" +) + +type fixedSeed struct { + fixed int64 +} + +func (f fixedSeed) Int63() int64 { return f.fixed } +func (f fixedSeed) Seed(seed int64) {} + +func fixedParameters(size int, fixed int64) *gopter.GenParameters { + return &gopter.GenParameters{ + MaxSize: size, + Rng: rand.New(fixedSeed{ + fixed: fixed, + }), + } +} + +func TestFrequency(t *testing.T) { + zeroNine := gen.Frequency(map[int]gopter.Gen{ + 0: gen.Const("zero"), + 9: gen.Const("nine"), + }) + value, ok := zeroNine(fixedParameters(10, 0)).Retrieve() + if !ok { + t.FailNow() + } + if value.(string) != "zero" { + t.Errorf("Invalid value for 0: %#v", value) + } + for i := int64(1); i < int64(10); i++ { + value, ok = zeroNine(fixedParameters(10, i<<32)).Retrieve() + if !ok { + t.FailNow() + } + if value.(string) != "nine" { + t.Errorf("Invalid value for %d: %#v", i, value) + } + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/helper_test.go b/vendor/github.com/leanovate/gopter/gen/helper_test.go new file mode 100644 index 000000000..8f857afcc --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/helper_test.go @@ -0,0 +1,33 @@ +package gen_test + +import ( + "testing" + + "github.com/leanovate/gopter" +) + +func commonGeneratorTest(t *testing.T, name string, gen gopter.Gen, valueCheck func(interface{}) bool) { + for i := 0; i < 100; i++ { + value, ok := gen.Sample() + + if !ok || value == nil { + t.Errorf("Invalid generator result (%s): %#v", name, value) + } else if !valueCheck(value) { + t.Errorf("Invalid value (%s): %#v", name, value) + } + + genResult := gen(gopter.DefaultGenParameters()) + if genResult.Shrinker != nil { + value, ok := genResult.Retrieve() + if !ok || value == nil { + t.Errorf("Invalid generator result (%s): %#v", name, value) + } else { + shrink := genResult.Shrinker(value).Filter(genResult.Sieve) + shrunkValue, ok := shrink() + if ok && !valueCheck(shrunkValue) { + t.Errorf("Invalid shrunk value (%s): %#v -> %#v", name, value, shrunkValue) + } + } + } + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/integers.go b/vendor/github.com/leanovate/gopter/gen/integers.go new file mode 100644 index 000000000..518530ab5 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/integers.go @@ -0,0 +1,225 @@ +package gen + +import ( + "math" + "reflect" + + "github.com/leanovate/gopter" +) + +// Int64Range generates int64 numbers within a given range +func Int64Range(min, max int64) gopter.Gen { + if max < min { + return Fail(reflect.TypeOf(int64(0))) + } + if max == math.MaxInt64 && min == math.MinInt64 { // Check for range overflow + return func(genParams *gopter.GenParameters) *gopter.GenResult { + return gopter.NewGenResult(genParams.NextInt64(), Int64Shrinker) + } + } + + rangeSize := uint64(max - min + 1) + return func(genParams *gopter.GenParameters) *gopter.GenResult { + var nextResult = uint64(min) + (genParams.NextUint64() % rangeSize) + genResult := gopter.NewGenResult(int64(nextResult), Int64Shrinker) + genResult.Sieve = func(v interface{}) bool { + return v.(int64) >= min && v.(int64) <= max + } + return genResult + } +} + +// UInt64Range generates uint64 numbers within a given range +func UInt64Range(min, max uint64) gopter.Gen { + if max < min { + return Fail(reflect.TypeOf(uint64(0))) + } + d := max - min + 1 + if d == 0 { // Check overflow (i.e. max = MaxInt64, min = MinInt64) + return func(genParams *gopter.GenParameters) *gopter.GenResult { + return gopter.NewGenResult(genParams.NextUint64(), UInt64Shrinker) + } + } + return func(genParams *gopter.GenParameters) *gopter.GenResult { + genResult := gopter.NewGenResult(min+genParams.NextUint64()%d, UInt64Shrinker) + genResult.Sieve = func(v interface{}) bool { + return v.(uint64) >= min && v.(uint64) <= max + } + return genResult + } +} + +// Int64 generates an arbitrary int64 number +func Int64() gopter.Gen { + return Int64Range(math.MinInt64, math.MaxInt64) +} + +// UInt64 generates an arbitrary Uint64 number +func UInt64() gopter.Gen { + return UInt64Range(0, math.MaxUint64) +} + +// Int32Range generates int32 numbers within a given range +func Int32Range(min, max int32) gopter.Gen { + return Int64Range(int64(min), int64(max)). + Map(int64To32). + WithShrinker(Int32Shrinker). + SuchThat(func(v int32) bool { + return v >= min && v <= max + }) +} + +// UInt32Range generates uint32 numbers within a given range +func UInt32Range(min, max uint32) gopter.Gen { + return UInt64Range(uint64(min), uint64(max)). + Map(uint64To32). + WithShrinker(UInt32Shrinker). + SuchThat(func(v uint32) bool { + return v >= min && v <= max + }) +} + +// Int32 generate arbitrary int32 numbers +func Int32() gopter.Gen { + return Int32Range(math.MinInt32, math.MaxInt32) +} + +// UInt32 generate arbitrary int32 numbers +func UInt32() gopter.Gen { + return UInt32Range(0, math.MaxUint32) +} + +// Int16Range generates int16 numbers within a given range +func Int16Range(min, max int16) gopter.Gen { + return Int64Range(int64(min), int64(max)). + Map(int64To16). + WithShrinker(Int16Shrinker). + SuchThat(func(v int16) bool { + return v >= min && v <= max + }) +} + +// UInt16Range generates uint16 numbers within a given range +func UInt16Range(min, max uint16) gopter.Gen { + return UInt64Range(uint64(min), uint64(max)). + Map(uint64To16). + WithShrinker(UInt16Shrinker). + SuchThat(func(v uint16) bool { + return v >= min && v <= max + }) +} + +// Int16 generate arbitrary int16 numbers +func Int16() gopter.Gen { + return Int16Range(math.MinInt16, math.MaxInt16) +} + +// UInt16 generate arbitrary uint16 numbers +func UInt16() gopter.Gen { + return UInt16Range(0, math.MaxUint16) +} + +// Int8Range generates int8 numbers within a given range +func Int8Range(min, max int8) gopter.Gen { + return Int64Range(int64(min), int64(max)). + Map(int64To8). + WithShrinker(Int8Shrinker). + SuchThat(func(v int8) bool { + return v >= min && v <= max + }) +} + +// UInt8Range generates uint8 numbers within a given range +func UInt8Range(min, max uint8) gopter.Gen { + return UInt64Range(uint64(min), uint64(max)). + Map(uint64To8). + WithShrinker(UInt8Shrinker). + SuchThat(func(v uint8) bool { + return v >= min && v <= max + }) +} + +// Int8 generate arbitrary int8 numbers +func Int8() gopter.Gen { + return Int8Range(math.MinInt8, math.MaxInt8) +} + +// UInt8 generate arbitrary uint8 numbers +func UInt8() gopter.Gen { + return UInt8Range(0, math.MaxUint8) +} + +// IntRange generates int numbers within a given range +func IntRange(min, max int) gopter.Gen { + return Int64Range(int64(min), int64(max)). + Map(int64ToInt). + WithShrinker(IntShrinker). + SuchThat(func(v int) bool { + return v >= min && v <= max + }) +} + +// Int generate arbitrary int numbers +func Int() gopter.Gen { + return Int64Range(math.MinInt32, math.MaxInt32). + Map(int64ToInt). + WithShrinker(IntShrinker) +} + +// UIntRange generates uint numbers within a given range +func UIntRange(min, max uint) gopter.Gen { + return UInt64Range(uint64(min), uint64(max)). + Map(uint64ToUint). + WithShrinker(UIntShrinker). + SuchThat(func(v uint) bool { + return v >= min && v <= max + }) +} + +// UInt generate arbitrary uint numbers +func UInt() gopter.Gen { + return UInt64Range(0, math.MaxUint32). + Map(uint64ToUint). + WithShrinker(UIntShrinker) +} + +// Size just extracts the MaxSize field of the GenParameters. +// This can be helpful to generate limited integer value in a more structued +// manner. +func Size() gopter.Gen { + return func(genParams *gopter.GenParameters) *gopter.GenResult { + return gopter.NewGenResult(genParams.MaxSize, IntShrinker) + } +} + +func int64To32(value int64) int32 { + return int32(value) +} + +func uint64To32(value uint64) uint32 { + return uint32(value) +} + +func int64To16(value int64) int16 { + return int16(value) +} + +func uint64To16(value uint64) uint16 { + return uint16(value) +} + +func int64To8(value int64) int8 { + return int8(value) +} + +func uint64To8(value uint64) uint8 { + return uint8(value) +} + +func int64ToInt(value int64) int { + return int(value) +} + +func uint64ToUint(value uint64) uint { + return uint(value) +} diff --git a/vendor/github.com/leanovate/gopter/gen/integers_shrink.go b/vendor/github.com/leanovate/gopter/gen/integers_shrink.go new file mode 100644 index 000000000..ede4189ac --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/integers_shrink.go @@ -0,0 +1,95 @@ +package gen + +import ( + "github.com/leanovate/gopter" +) + +type int64Shrink struct { + original int64 + half int64 +} + +func (s *int64Shrink) Next() (interface{}, bool) { + if s.half == 0 { + return nil, false + } + value := s.original - s.half + s.half /= 2 + return value, true +} + +type uint64Shrink struct { + original uint64 + half uint64 +} + +func (s *uint64Shrink) Next() (interface{}, bool) { + if s.half == 0 { + return nil, false + } + value := s.original - s.half + s.half >>= 1 + return value, true +} + +// Int64Shrinker is a shrinker for int64 numbers +func Int64Shrinker(v interface{}) gopter.Shrink { + negShrink := int64Shrink{ + original: -v.(int64), + half: -v.(int64), + } + posShrink := int64Shrink{ + original: v.(int64), + half: v.(int64) / 2, + } + return gopter.Shrink(negShrink.Next).Interleave(gopter.Shrink(posShrink.Next)) +} + +// UInt64Shrinker is a shrinker for uint64 numbers +func UInt64Shrinker(v interface{}) gopter.Shrink { + shrink := uint64Shrink{ + original: v.(uint64), + half: v.(uint64), + } + return shrink.Next +} + +// Int32Shrinker is a shrinker for int32 numbers +func Int32Shrinker(v interface{}) gopter.Shrink { + return Int64Shrinker(int64(v.(int32))).Map(int64To32) +} + +// UInt32Shrinker is a shrinker for uint32 numbers +func UInt32Shrinker(v interface{}) gopter.Shrink { + return UInt64Shrinker(uint64(v.(uint32))).Map(uint64To32) +} + +// Int16Shrinker is a shrinker for int16 numbers +func Int16Shrinker(v interface{}) gopter.Shrink { + return Int64Shrinker(int64(v.(int16))).Map(int64To16) +} + +// UInt16Shrinker is a shrinker for uint16 numbers +func UInt16Shrinker(v interface{}) gopter.Shrink { + return UInt64Shrinker(uint64(v.(uint16))).Map(uint64To16) +} + +// Int8Shrinker is a shrinker for int8 numbers +func Int8Shrinker(v interface{}) gopter.Shrink { + return Int64Shrinker(int64(v.(int8))).Map(int64To8) +} + +// UInt8Shrinker is a shrinker for uint8 numbers +func UInt8Shrinker(v interface{}) gopter.Shrink { + return UInt64Shrinker(uint64(v.(uint8))).Map(uint64To8) +} + +// IntShrinker is a shrinker for int numbers +func IntShrinker(v interface{}) gopter.Shrink { + return Int64Shrinker(int64(v.(int))).Map(int64ToInt) +} + +// UIntShrinker is a shrinker for uint numbers +func UIntShrinker(v interface{}) gopter.Shrink { + return UInt64Shrinker(uint64(v.(uint))).Map(uint64ToUint) +} diff --git a/vendor/github.com/leanovate/gopter/gen/integers_shrink_test.go b/vendor/github.com/leanovate/gopter/gen/integers_shrink_test.go new file mode 100644 index 000000000..750768e5a --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/integers_shrink_test.go @@ -0,0 +1,127 @@ +package gen_test + +import ( + "reflect" + "testing" + + "github.com/leanovate/gopter/gen" +) + +func TestInt64Shrink(t *testing.T) { + zeroShrinks := gen.Int64Shrinker(int64(0)).All() + if !reflect.DeepEqual(zeroShrinks, []interface{}{}) { + t.Errorf("Invalid zeroShrinks: %#v", zeroShrinks) + } + + tenShrinks := gen.Int64Shrinker(int64(10)).All() + if !reflect.DeepEqual(tenShrinks, []interface{}{int64(0), int64(5), int64(-5), int64(8), int64(-8), int64(9), int64(-9)}) { + t.Errorf("Invalid tenShrinks: %#v", tenShrinks) + } + + negTenShrinks := gen.Int64Shrinker(int64(-10)).All() + if !reflect.DeepEqual(negTenShrinks, []interface{}{int64(0), int64(-5), int64(5), int64(-8), int64(8), int64(-9), int64(9)}) { + t.Errorf("Invalid negTenShrinks: %#v", negTenShrinks) + } + + leetShrink := gen.Int64Shrinker(int64(1337)).All() + if !reflect.DeepEqual(leetShrink, []interface{}{ + int64(0), int64(669), int64(-669), int64(1003), int64(-1003), int64(1170), int64(-1170), + int64(1254), int64(-1254), int64(1296), int64(-1296), int64(1317), int64(-1317), + int64(1327), int64(-1327), int64(1332), int64(-1332), int64(1335), int64(-1335), + int64(1336), int64(-1336)}) { + t.Errorf("Invalid leetShrink: %#v", leetShrink) + } +} + +func TestUInt64Shrink(t *testing.T) { + zeroShrinks := gen.UInt64Shrinker(uint64(0)).All() + if !reflect.DeepEqual(zeroShrinks, []interface{}{}) { + t.Errorf("Invalid zeroShrinks: %#v", zeroShrinks) + } + + tenShrinks := gen.UInt64Shrinker(uint64(10)).All() + if !reflect.DeepEqual(tenShrinks, []interface{}{uint64(0), uint64(5), uint64(8), uint64(9)}) { + t.Errorf("Invalid tenShrinks: %#v", tenShrinks) + } + + leetShrink := gen.UInt64Shrinker(uint64(1337)).All() + if !reflect.DeepEqual(leetShrink, []interface{}{ + uint64(0), uint64(669), uint64(1003), uint64(1170), + uint64(1254), uint64(1296), uint64(1317), + uint64(1327), uint64(1332), uint64(1335), + uint64(1336)}) { + t.Errorf("Invalid leetShrink: %#v", leetShrink) + } +} + +func TestInt32Shrink(t *testing.T) { + zeroShrinks := gen.Int32Shrinker(int32(0)).All() + if !reflect.DeepEqual(zeroShrinks, []interface{}{}) { + t.Errorf("Invalid zeroShrinks: %#v", zeroShrinks) + } + + tenShrinks := gen.Int32Shrinker(int32(10)).All() + if !reflect.DeepEqual(tenShrinks, []interface{}{int32(0), int32(5), int32(-5), int32(8), int32(-8), int32(9), int32(-9)}) { + t.Errorf("Invalid tenShrinks: %#v", tenShrinks) + } +} + +func TestUInt32Shrink(t *testing.T) { + zeroShrinks := gen.UInt32Shrinker(uint32(0)).All() + if !reflect.DeepEqual(zeroShrinks, []interface{}{}) { + t.Errorf("Invalid zeroShrinks: %#v", zeroShrinks) + } + + tenShrinks := gen.UInt32Shrinker(uint32(10)).All() + if !reflect.DeepEqual(tenShrinks, []interface{}{uint32(0), uint32(5), uint32(8), uint32(9)}) { + t.Errorf("Invalid tenShrinks: %#v", tenShrinks) + } +} + +func TestInt16Shrink(t *testing.T) { + zeroShrinks := gen.Int16Shrinker(int16(0)).All() + if !reflect.DeepEqual(zeroShrinks, []interface{}{}) { + t.Errorf("Invalid zeroShrinks: %#v", zeroShrinks) + } + + tenShrinks := gen.Int16Shrinker(int16(10)).All() + if !reflect.DeepEqual(tenShrinks, []interface{}{int16(0), int16(5), int16(-5), int16(8), int16(-8), int16(9), int16(-9)}) { + t.Errorf("Invalid tenShrinks: %#v", tenShrinks) + } +} + +func TestUInt16Shrink(t *testing.T) { + zeroShrinks := gen.UInt16Shrinker(uint16(0)).All() + if !reflect.DeepEqual(zeroShrinks, []interface{}{}) { + t.Errorf("Invalid zeroShrinks: %#v", zeroShrinks) + } + + tenShrinks := gen.UInt16Shrinker(uint16(10)).All() + if !reflect.DeepEqual(tenShrinks, []interface{}{uint16(0), uint16(5), uint16(8), uint16(9)}) { + t.Errorf("Invalid tenShrinks: %#v", tenShrinks) + } +} + +func TestInt8Shrink(t *testing.T) { + zeroShrinks := gen.Int8Shrinker(int8(0)).All() + if !reflect.DeepEqual(zeroShrinks, []interface{}{}) { + t.Errorf("Invalid zeroShrinks: %#v", zeroShrinks) + } + + tenShrinks := gen.Int8Shrinker(int8(10)).All() + if !reflect.DeepEqual(tenShrinks, []interface{}{int8(0), int8(5), int8(-5), int8(8), int8(-8), int8(9), int8(-9)}) { + t.Errorf("Invalid tenShrinks: %#v", tenShrinks) + } +} + +func TestUInt8Shrink(t *testing.T) { + zeroShrinks := gen.UInt8Shrinker(uint8(0)).All() + if !reflect.DeepEqual(zeroShrinks, []interface{}{}) { + t.Errorf("Invalid zeroShrinks: %#v", zeroShrinks) + } + + tenShrinks := gen.UInt8Shrinker(uint8(10)).All() + if !reflect.DeepEqual(tenShrinks, []interface{}{uint8(0), uint8(5), uint8(8), uint8(9)}) { + t.Errorf("Invalid tenShrinks: %#v", tenShrinks) + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/integers_test.go b/vendor/github.com/leanovate/gopter/gen/integers_test.go new file mode 100644 index 000000000..47e9ae3e2 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/integers_test.go @@ -0,0 +1,130 @@ +package gen_test + +import ( + "math" + "testing" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/gen" +) + +func TestInt64Range(t *testing.T) { + fail := gen.Int64Range(200, 100) + + if value, ok := fail.Sample(); value != nil || ok { + t.Fail() + } + + commonGeneratorTest(t, "int 64 range", gen.Int64Range(-123456, 234567), func(value interface{}) bool { + v, ok := value.(int64) + return ok && v >= -123456 || v <= 234567 + }) + + commonGeneratorTest(t, "int 64 positive", gen.Int64Range(1, math.MaxInt64), func(value interface{}) bool { + v, ok := value.(int64) + return ok && v > 0 + }) + + commonGeneratorTest(t, "int 64 negative", gen.Int64Range(math.MinInt64, -1), func(value interface{}) bool { + v, ok := value.(int64) + return ok && v < 0 + }) + + commonGeneratorTest(t, "full int 64 range", gen.Int64Range(math.MinInt64, math.MaxInt64), func(value interface{}) bool { + _, ok := value.(int64) + return ok + }) +} + +func TestUInt64Range(t *testing.T) { + fail := gen.UInt64Range(200, 100) + + if value, ok := fail.Sample(); value != nil || ok { + t.Fail() + } + + commonGeneratorTest(t, "uint 64 range", gen.UInt64Range(0, 234567), func(value interface{}) bool { + v, ok := value.(uint64) + return ok && v <= 234567 + }) +} + +func TestInt64(t *testing.T) { + commonGeneratorTest(t, "int 64", gen.Int64(), func(value interface{}) bool { + _, ok := value.(int64) + return ok + }) + + commonGeneratorTest(t, "uint 64", gen.UInt64(), func(value interface{}) bool { + _, ok := value.(uint64) + return ok + }) +} + +func TestInt32(t *testing.T) { + commonGeneratorTest(t, "int 32", gen.Int32(), func(value interface{}) bool { + _, ok := value.(int32) + return ok + }) + + commonGeneratorTest(t, "uint 32", gen.UInt32(), func(value interface{}) bool { + _, ok := value.(uint32) + return ok + }) +} + +func TestInt16(t *testing.T) { + commonGeneratorTest(t, "int 16", gen.Int16(), func(value interface{}) bool { + _, ok := value.(int16) + return ok + }) + + commonGeneratorTest(t, "uint 16", gen.UInt16(), func(value interface{}) bool { + _, ok := value.(uint16) + return ok + }) +} + +func TestInt8(t *testing.T) { + commonGeneratorTest(t, "int 8", gen.Int8(), func(value interface{}) bool { + _, ok := value.(int8) + return ok + }) + + commonGeneratorTest(t, "uint 8", gen.UInt8(), func(value interface{}) bool { + _, ok := value.(uint8) + return ok + }) +} + +func TestInt(t *testing.T) { + commonGeneratorTest(t, "int", gen.Int(), func(value interface{}) bool { + _, ok := value.(int) + return ok + }) + commonGeneratorTest(t, "intrange", gen.IntRange(-1234, 5678), func(value interface{}) bool { + v, ok := value.(int) + return ok && v >= -1234 && v <= 5678 + }) + + commonGeneratorTest(t, "uint", gen.UInt(), func(value interface{}) bool { + _, ok := value.(uint) + return ok + }) + commonGeneratorTest(t, "uintrange", gen.UIntRange(1234, 5678), func(value interface{}) bool { + v, ok := value.(uint) + return ok && v >= 1234 && v <= 5678 + }) +} + +func TestGenSize(t *testing.T) { + params := gopter.DefaultGenParameters() + genSize := gen.Size() + for i := 0; i < 100; i++ { + result := genSize(params.WithSize(i)) + value, ok := result.Retrieve() + if !ok || value.(int) != i { + t.Errorf("Invalid gen size: %v", value) + } + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/map_of.go b/vendor/github.com/leanovate/gopter/gen/map_of.go new file mode 100644 index 000000000..17a3e2116 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/map_of.go @@ -0,0 +1,87 @@ +package gen + +import ( + "reflect" + + "github.com/leanovate/gopter" +) + +// MapOf generates an arbitrary map of generated kay values. +// genParams.MaxSize sets an (exclusive) upper limit on the size of the map +// genParams.MinSize sets an (inclusive) lower limit on the size of the map +func MapOf(keyGen, elementGen gopter.Gen) gopter.Gen { + return func(genParams *gopter.GenParameters) *gopter.GenResult { + len := 0 + if genParams.MaxSize > 0 || genParams.MinSize > 0 { + if genParams.MinSize > genParams.MaxSize { + panic("GenParameters.MinSize must be <= GenParameters.MaxSize") + } + + if genParams.MaxSize == genParams.MinSize { + len = genParams.MaxSize + } else { + len = genParams.Rng.Intn(genParams.MaxSize-genParams.MinSize) + genParams.MinSize + } + } + + result, keySieve, keyShrinker, elementSieve, elementShrinker := genMap(keyGen, elementGen, genParams, len) + + genResult := gopter.NewGenResult(result.Interface(), MapShrinker(keyShrinker, elementShrinker)) + if keySieve != nil || elementSieve != nil { + genResult.Sieve = forAllKeyValueSieve(keySieve, elementSieve) + } + return genResult + } +} + +func genMap(keyGen, elementGen gopter.Gen, genParams *gopter.GenParameters, len int) (reflect.Value, func(interface{}) bool, gopter.Shrinker, func(interface{}) bool, gopter.Shrinker) { + element := elementGen(genParams) + elementSieve := element.Sieve + elementShrinker := element.Shrinker + + key := keyGen(genParams) + keySieve := key.Sieve + keyShrinker := key.Shrinker + + result := reflect.MakeMapWithSize(reflect.MapOf(key.ResultType, element.ResultType), len) + + for i := 0; i < len; i++ { + keyValue, keyOk := key.Retrieve() + elementValue, elementOk := element.Retrieve() + + if keyOk && elementOk { + if key == nil { + if elementValue == nil { + result.SetMapIndex(reflect.Zero(key.ResultType), reflect.Zero(element.ResultType)) + } else { + result.SetMapIndex(reflect.Zero(key.ResultType), reflect.ValueOf(elementValue)) + } + } else { + if elementValue == nil { + result.SetMapIndex(reflect.ValueOf(keyValue), reflect.Zero(element.ResultType)) + } else { + result.SetMapIndex(reflect.ValueOf(keyValue), reflect.ValueOf(elementValue)) + } + } + } + key = keyGen(genParams) + element = elementGen(genParams) + } + + return result, keySieve, keyShrinker, elementSieve, elementShrinker +} + +func forAllKeyValueSieve(keySieve, elementSieve func(interface{}) bool) func(interface{}) bool { + return func(v interface{}) bool { + rv := reflect.ValueOf(v) + for _, key := range rv.MapKeys() { + if keySieve != nil && !keySieve(key.Interface()) { + return false + } + if elementSieve != nil && !elementSieve(rv.MapIndex(key).Interface()) { + return false + } + } + return true + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/map_of_test.go b/vendor/github.com/leanovate/gopter/gen/map_of_test.go new file mode 100644 index 000000000..4dc5191cb --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/map_of_test.go @@ -0,0 +1,96 @@ +package gen_test + +import ( + "testing" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/gen" +) + +func TestMapOf(t *testing.T) { + genParams := gopter.DefaultGenParameters() + genParams.MaxSize = 50 + keyGen := gen.Identifier() + elementGen := gen.Const("element") + mapGen := gen.MapOf(keyGen, elementGen) + + for i := 0; i < 100; i++ { + sample, ok := mapGen(genParams).Retrieve() + + if !ok { + t.Error("Sample was not ok") + } + strings, ok := sample.(map[string]string) + if !ok { + t.Errorf("Sample not slice of string: %#v", sample) + } else { + if len(strings) > 50 { + t.Errorf("Sample has invalid length: %#v", len(strings)) + } + for _, value := range strings { + if value != "element" { + t.Errorf("Sample contains invalid value: %#v", sample) + } + } + } + } + + genParams.MaxSize = 10 + + for i := 0; i < 100; i++ { + sample, ok := mapGen(genParams).Retrieve() + + if !ok { + t.Error("Sample was not ok") + } + strings, ok := sample.(map[string]string) + if !ok { + t.Errorf("Sample not slice of string: %#v", sample) + } else { + if len(strings) > 10 { + t.Errorf("Sample has invalid length: %#v", len(strings)) + } + for _, value := range strings { + if value != "element" { + t.Errorf("Sample contains invalid value: %#v", sample) + } + } + } + } + + genParams.MaxSize = 0 + genParams.MinSize = 0 + + for i := 0; i < 100; i++ { + sample, ok := mapGen(genParams).Retrieve() + + if !ok { + t.Error("Sample was not ok") + } + strings, ok := sample.(map[string]string) + if !ok { + t.Errorf("Sample not slice of string: %#v", sample) + } else { + if len(strings) != 0 { + t.Errorf("Sample has invalid length: %#v", len(strings)) + } + } + } +} + +func TestMapOfPanic(t *testing.T) { + genParams := gopter.DefaultGenParameters() + genParams.MaxSize = 0 + genParams.MinSize = 1 + keyGen := gen.Identifier() + elementGen := gen.Const("element") + mapGen := gen.MapOf(keyGen, elementGen) + + defer func() { + if r := recover(); r == nil { + t.Error("SliceOf did not panic when MinSize was > MaxSize") + } + }() + + mapGen(genParams).Retrieve() +} diff --git a/vendor/github.com/leanovate/gopter/gen/map_shrink.go b/vendor/github.com/leanovate/gopter/gen/map_shrink.go new file mode 100644 index 000000000..7948a3725 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/map_shrink.go @@ -0,0 +1,149 @@ +package gen + +import ( + "fmt" + "reflect" + + "github.com/leanovate/gopter" +) + +type mapShrinkOne struct { + original reflect.Value + key reflect.Value + keyShrink gopter.Shrink + elementShrink gopter.Shrink + state bool + keyExhausted bool + lastKey interface{} + elementExhausted bool + lastElement interface{} +} + +func (s *mapShrinkOne) nextKeyValue() (interface{}, interface{}, bool) { + for !s.keyExhausted && !s.elementExhausted { + s.state = !s.state + if s.state && !s.keyExhausted { + value, ok := s.keyShrink() + if ok { + s.lastKey = value + return s.lastKey, s.lastElement, true + } + s.keyExhausted = true + } else if !s.state && !s.elementExhausted { + value, ok := s.elementShrink() + if ok { + s.lastElement = value + return s.lastKey, s.lastElement, true + } + s.elementExhausted = true + } + } + return nil, nil, false +} + +func (s *mapShrinkOne) Next() (interface{}, bool) { + nextKey, nextValue, ok := s.nextKeyValue() + if !ok { + return nil, false + } + result := reflect.MakeMapWithSize(s.original.Type(), s.original.Len()) + for _, key := range s.original.MapKeys() { + if !reflect.DeepEqual(key.Interface(), s.key.Interface()) { + result.SetMapIndex(key, s.original.MapIndex(key)) + } + } + result.SetMapIndex(reflect.ValueOf(nextKey), reflect.ValueOf(nextValue)) + + return result.Interface(), true +} + +// MapShrinkerOne creates a map shrinker from a shrinker for the key values of a map. +// The length of the map will remain (mostly) unchanged, instead each key value pair is +// shrinked after the other. +func MapShrinkerOne(keyShrinker, elementShrinker gopter.Shrinker) gopter.Shrinker { + return func(v interface{}) gopter.Shrink { + rv := reflect.ValueOf(v) + if rv.Kind() != reflect.Map { + panic(fmt.Sprintf("%#v is not a map", v)) + } + + keys := rv.MapKeys() + shrinks := make([]gopter.Shrink, 0, len(keys)) + for _, key := range keys { + mapShrinkOne := &mapShrinkOne{ + original: rv, + key: key, + keyShrink: keyShrinker(key.Interface()), + lastKey: key.Interface(), + elementShrink: elementShrinker(rv.MapIndex(key).Interface()), + lastElement: rv.MapIndex(key).Interface(), + } + shrinks = append(shrinks, mapShrinkOne.Next) + } + return gopter.ConcatShrinks(shrinks...) + } +} + +type mapShrink struct { + original reflect.Value + originalKeys []reflect.Value + length int + offset int + chunkLength int +} + +func (s *mapShrink) Next() (interface{}, bool) { + if s.chunkLength == 0 { + return nil, false + } + keys := make([]reflect.Value, 0, s.length-s.chunkLength) + keys = append(keys, s.originalKeys[0:s.offset]...) + s.offset += s.chunkLength + if s.offset < s.length { + keys = append(keys, s.originalKeys[s.offset:s.length]...) + } else { + s.offset = 0 + s.chunkLength >>= 1 + } + + result := reflect.MakeMapWithSize(s.original.Type(), len(keys)) + for _, key := range keys { + result.SetMapIndex(key, s.original.MapIndex(key)) + } + + return result.Interface(), true +} + +// MapShrinker creates a map shrinker from shrinker for the key values. +// The length of the map will be shrinked as well +func MapShrinker(keyShrinker, elementShrinker gopter.Shrinker) gopter.Shrinker { + return func(v interface{}) gopter.Shrink { + rv := reflect.ValueOf(v) + if rv.Kind() != reflect.Map { + panic(fmt.Sprintf("%#v is not a Map", v)) + } + keys := rv.MapKeys() + mapShrink := &mapShrink{ + original: rv, + originalKeys: keys, + offset: 0, + length: rv.Len(), + chunkLength: rv.Len() >> 1, + } + + shrinks := make([]gopter.Shrink, 0, rv.Len()+1) + shrinks = append(shrinks, mapShrink.Next) + for _, key := range keys { + mapShrinkOne := &mapShrinkOne{ + original: rv, + key: key, + keyShrink: keyShrinker(key.Interface()), + lastKey: key.Interface(), + elementShrink: elementShrinker(rv.MapIndex(key).Interface()), + lastElement: rv.MapIndex(key).Interface(), + } + shrinks = append(shrinks, mapShrinkOne.Next) + } + return gopter.ConcatShrinks(shrinks...) + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/map_shrink_test.go b/vendor/github.com/leanovate/gopter/gen/map_shrink_test.go new file mode 100644 index 000000000..ee750b7bb --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/map_shrink_test.go @@ -0,0 +1,57 @@ +package gen_test + +import ( + "reflect" + "testing" + + "github.com/leanovate/gopter/gen" +) + +func TestMapShrinkerOne(t *testing.T) { + mapShrink := gen.MapShrinkerOne(gen.StringShrinker, gen.Int64Shrinker)(map[string]int64{ + "two": 2, + }).All() + if !reflect.DeepEqual(mapShrink, []interface{}{ + map[string]int64{"wo": 2}, + map[string]int64{"wo": 0}, + map[string]int64{"to": 0}, + map[string]int64{"to": 1}, + map[string]int64{"tw": 1}, + map[string]int64{"tw": -1}, + }) { + t.Errorf("Invalid mapShrink: %#v", mapShrink) + } +} + +func TestMapShrinker(t *testing.T) { + mapShrink := gen.MapShrinker(gen.StringShrinker, gen.Int64Shrinker)(map[string]int64{ + "two": 2, + }).All() + if !reflect.DeepEqual(mapShrink, []interface{}{ + map[string]int64{"wo": 2}, + map[string]int64{"wo": 0}, + map[string]int64{"to": 0}, + map[string]int64{"to": 1}, + map[string]int64{"tw": 1}, + map[string]int64{"tw": -1}, + }) { + t.Errorf("Invalid mapShrink: %#v", mapShrink) + } + + mapShrink2 := gen.MapShrinker(gen.StringShrinker, gen.Int64Shrinker)(map[string]int64{ + "one": 1, + "two": 2, + "three": 3, + "four": 3, + }).All() + + if len(mapShrink2) < 10 { + t.Errorf("mapShrink2 too short: %#v", mapShrink2) + } + for _, shrink := range mapShrink2 { + _, ok := shrink.(map[string]int64) + if !ok { + t.Errorf("mapShrink2 invalid type: %#v", mapShrink2) + } + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/one_of.go b/vendor/github.com/leanovate/gopter/gen/one_of.go new file mode 100644 index 000000000..9cb5370db --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/one_of.go @@ -0,0 +1,29 @@ +package gen + +import ( + "reflect" + + "github.com/leanovate/gopter" +) + +// OneConstOf generate one of a list of constant values +func OneConstOf(consts ...interface{}) gopter.Gen { + if len(consts) == 0 { + return Fail(reflect.TypeOf(nil)) + } + return func(genParams *gopter.GenParameters) *gopter.GenResult { + idx := genParams.Rng.Intn(len(consts)) + return gopter.NewGenResult(consts[idx], gopter.NoShrinker) + } +} + +// OneGenOf generate one value from a a list of generators +func OneGenOf(gens ...gopter.Gen) gopter.Gen { + if len(gens) == 0 { + return Fail(reflect.TypeOf(nil)) + } + return func(genParams *gopter.GenParameters) *gopter.GenResult { + idx := genParams.Rng.Intn(len(gens)) + return gens[idx](genParams) + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/one_of_test.go b/vendor/github.com/leanovate/gopter/gen/one_of_test.go new file mode 100644 index 000000000..765d7886d --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/one_of_test.go @@ -0,0 +1,53 @@ +package gen_test + +import ( + "reflect" + "testing" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/gen" +) + +func TestOneConstOf(t *testing.T) { + consts := gen.OneConstOf("one", "two", "three", "four") + commonOneOfTest(t, consts) + + fail := gen.OneConstOf() + if _, ok := fail.Sample(); ok { + t.Errorf("Empty OneConstOf generated a value") + } +} + +func TestOneGenOf(t *testing.T) { + consts := gen.OneGenOf(gen.Const("one"), gen.Const("two"), gen.Const("three"), gen.Const("four")) + commonOneOfTest(t, consts) + + fail := gen.OneGenOf() + if _, ok := fail.Sample(); ok { + t.Errorf("Empty OneGenOf generated a value") + } +} + +func commonOneOfTest(t *testing.T, gen gopter.Gen) { + generated := make(map[string]bool, 0) + for i := 0; i < 100; i++ { + value, ok := gen.Sample() + + if !ok || value == nil { + t.Errorf("Invalid consts: %#v", value) + } + v, ok := value.(string) + if !ok { + t.Errorf("Invalid consts: %#v", value) + } + generated[v] = true + } + if !reflect.DeepEqual(generated, map[string]bool{ + "one": true, + "two": true, + "three": true, + "four": true, + }) { + t.Errorf("Not all consts where generated: %#v", generated) + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/ptr_of.go b/vendor/github.com/leanovate/gopter/gen/ptr_of.go new file mode 100644 index 000000000..9c20054ca --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/ptr_of.go @@ -0,0 +1,41 @@ +package gen + +import ( + "reflect" + + "github.com/leanovate/gopter" +) + +// PtrOf generates a pointer to a generated element +func PtrOf(elementGen gopter.Gen) gopter.Gen { + return func(genParams *gopter.GenParameters) *gopter.GenResult { + element := elementGen(genParams) + elementShrinker := element.Shrinker + elementSieve := element.Sieve + value, ok := element.Retrieve() + if !ok || genParams.NextBool() { + result := gopter.NewEmptyResult(reflect.PtrTo(element.ResultType)) + result.Sieve = func(v interface{}) bool { + if elementSieve == nil { + return true + } + r := reflect.ValueOf(v) + return !r.IsValid() || r.IsNil() || elementSieve(r.Elem().Interface()) + } + return result + } + // To get the right pointer type we have to create a slice with one element + slice := reflect.MakeSlice(reflect.SliceOf(element.ResultType), 0, 1) + slice = reflect.Append(slice, reflect.ValueOf(value)) + + result := gopter.NewGenResult(slice.Index(0).Addr().Interface(), PtrShrinker(elementShrinker)) + result.Sieve = func(v interface{}) bool { + if elementSieve == nil { + return true + } + r := reflect.ValueOf(v) + return !r.IsValid() || r.IsNil() || elementSieve(r.Elem().Interface()) + } + return result + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/ptr_of_test.go b/vendor/github.com/leanovate/gopter/gen/ptr_of_test.go new file mode 100644 index 000000000..20da5d3fb --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/ptr_of_test.go @@ -0,0 +1,54 @@ +package gen_test + +import ( + "testing" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/gen" + "github.com/leanovate/gopter/prop" +) + +func TestPtrOf(t *testing.T) { + genParams := gopter.DefaultGenParameters() + elementGen := gen.Const("element") + ptrGen := gen.PtrOf(elementGen) + + for i := 0; i < 100; i++ { + sample, ok := ptrGen(genParams).Retrieve() + + if !ok { + t.Error("Sample was not ok") + } + if sample == nil { + continue + } + stringPtr, ok := sample.(*string) + if !ok { + t.Errorf("Sample not pointer to string: %#v", sample) + } else if *stringPtr != "element" { + t.Errorf("Sample contains invalid value: %#v %#v", sample, *stringPtr) + } + } +} + +type Foo string + +func TestPtrOfFoo(t *testing.T) { + parameters := gopter.DefaultTestParameters() + properties := gopter.NewProperties(parameters) + + properties.Property("PtrOf", prop.ForAll( + func(foo *Foo, + ) bool { + return true + }, + gen.PtrOf(GenFoo()), + )) + properties.TestingRun(t) +} + +func GenFoo() gopter.Gen { + return gen.SliceOfN(16, gen.Rune()).Map(func(v []rune) Foo { + return Foo(v) + }) +} diff --git a/vendor/github.com/leanovate/gopter/gen/ptr_shrink.go b/vendor/github.com/leanovate/gopter/gen/ptr_shrink.go new file mode 100644 index 000000000..0cc1ffd7e --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/ptr_shrink.go @@ -0,0 +1,41 @@ +package gen + +import ( + "reflect" + + "github.com/leanovate/gopter" +) + +type nilShrink struct { + done bool +} + +func (s *nilShrink) Next() (interface{}, bool) { + if !s.done { + s.done = true + return nil, true + } + return nil, false +} + +// PtrShrinker convert a value shrinker to a pointer to value shrinker +func PtrShrinker(elementShrinker gopter.Shrinker) gopter.Shrinker { + return func(v interface{}) gopter.Shrink { + if v == nil { + return gopter.NoShrink + } + rt := reflect.TypeOf(v) + elementShink := elementShrinker(reflect.ValueOf(v).Elem().Interface()) + + nilShrink := &nilShrink{} + return gopter.ConcatShrinks( + nilShrink.Next, + elementShink.Map(func(elem interface{}) interface{} { + slice := reflect.MakeSlice(reflect.SliceOf(rt.Elem()), 0, 1) + slice = reflect.Append(slice, reflect.ValueOf(elem)) + + return slice.Index(0).Addr().Interface() + }), + ) + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/ptr_shrink_test.go b/vendor/github.com/leanovate/gopter/gen/ptr_shrink_test.go new file mode 100644 index 000000000..5711db453 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/ptr_shrink_test.go @@ -0,0 +1,17 @@ +package gen_test + +import ( + "reflect" + "testing" + + "github.com/leanovate/gopter/gen" +) + +func TestPtrShrinker(t *testing.T) { + v := 10 + shinks := []int{0, 5, -5, 8, -8, 9, -9} + intPtrShrink := gen.PtrShrinker(gen.IntShrinker)(&v).All() + if !reflect.DeepEqual(intPtrShrink, []interface{}{nil, &shinks[0], &shinks[1], &shinks[2], &shinks[3], &shinks[4], &shinks[5], &shinks[6]}) { + t.Errorf("Invalid intPtrShrink: %#v", intPtrShrink) + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/regex.go b/vendor/github.com/leanovate/gopter/gen/regex.go new file mode 100644 index 000000000..ec27512c3 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/regex.go @@ -0,0 +1,78 @@ +package gen + +import ( + "reflect" + "regexp" + "regexp/syntax" + "strings" + + "github.com/leanovate/gopter" +) + +// RegexMatch generates matches for a given regular expression +// regexStr is supposed to conform to the perl regular expression syntax +func RegexMatch(regexStr string) gopter.Gen { + regexSyntax, err1 := syntax.Parse(regexStr, syntax.Perl) + regex, err2 := regexp.Compile(regexStr) + if err1 != nil || err2 != nil { + return Fail(reflect.TypeOf("")) + } + return regexMatchGen(regexSyntax.Simplify()).SuchThat(func(v string) bool { + return regex.MatchString(v) + }).WithShrinker(StringShrinker) +} + +func regexMatchGen(regex *syntax.Regexp) gopter.Gen { + switch regex.Op { + case syntax.OpLiteral: + return Const(string(regex.Rune)) + case syntax.OpCharClass: + gens := make([]gopter.Gen, 0, len(regex.Rune)/2) + for i := 0; i+1 < len(regex.Rune); i += 2 { + gens = append(gens, RuneRange(regex.Rune[i], regex.Rune[i+1]).Map(runeToString)) + } + return OneGenOf(gens...) + case syntax.OpAnyChar: + return Rune().Map(runeToString) + case syntax.OpAnyCharNotNL: + return RuneNoControl().Map(runeToString) + case syntax.OpCapture: + return regexMatchGen(regex.Sub[0]) + case syntax.OpStar: + elementGen := regexMatchGen(regex.Sub[0]) + return SliceOf(elementGen).Map(func(v []string) string { + return strings.Join(v, "") + }) + case syntax.OpPlus: + elementGen := regexMatchGen(regex.Sub[0]) + return gopter.CombineGens(elementGen, SliceOf(elementGen)).Map(func(vs []interface{}) string { + return vs[0].(string) + strings.Join(vs[1].([]string), "") + }) + case syntax.OpQuest: + elementGen := regexMatchGen(regex.Sub[0]) + return OneGenOf(Const(""), elementGen) + case syntax.OpConcat: + gens := make([]gopter.Gen, len(regex.Sub)) + for i, sub := range regex.Sub { + gens[i] = regexMatchGen(sub) + } + return gopter.CombineGens(gens...).Map(func(v []interface{}) string { + result := "" + for _, str := range v { + result += str.(string) + } + return result + }) + case syntax.OpAlternate: + gens := make([]gopter.Gen, len(regex.Sub)) + for i, sub := range regex.Sub { + gens[i] = regexMatchGen(sub) + } + return OneGenOf(gens...) + } + return Const("") +} + +func runeToString(v rune) string { + return string(v) +} diff --git a/vendor/github.com/leanovate/gopter/gen/regex_test.go b/vendor/github.com/leanovate/gopter/gen/regex_test.go new file mode 100644 index 000000000..c53386a99 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/regex_test.go @@ -0,0 +1,36 @@ +package gen_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/leanovate/gopter/gen" +) + +func TestRegexMatch(t *testing.T) { + regexs := []string{ + "[a-z][0-9a-zA-Z]*", + "AB[0-9]+", + "1?(zero|one)0", + "ABCD.+1234", + "^[0-9]{3}[A-Z]{5,}[a-z]{10,20}$", + "(?s)[^0-9]*ABCD.*1234", + } + for _, regex := range regexs { + pattern, err := regexp.Compile(regex) + if err != nil { + t.Error("Invalid regex", err) + } + commonGeneratorTest(t, fmt.Sprintf("matches for %s", regex), gen.RegexMatch(regex), func(value interface{}) bool { + str, ok := value.(string) + return ok && pattern.MatchString(str) + }) + } + + gen := gen.RegexMatch("]]}})Invalid{]]]") + value, ok := gen.Sample() + if ok || value != nil { + t.Errorf("Invalid value: %#v", value) + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/retry_until.go b/vendor/github.com/leanovate/gopter/gen/retry_until.go new file mode 100644 index 000000000..de9cf1dce --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/retry_until.go @@ -0,0 +1,21 @@ +package gen + +import "github.com/leanovate/gopter" + +// RetryUntil creates a generator that retries a given generator until a condition in met. +// condition: has to be a function with one parameter (matching the generated value of gen) returning a bool. +// Note: The new generator will only create an empty result once maxRetries is reached. +// Depending on the hit-ratio of the condition is may result in long running tests, use with care. +func RetryUntil(gen gopter.Gen, condition interface{}, maxRetries int) gopter.Gen { + genWithSieve := gen.SuchThat(condition) + return func(genParams *gopter.GenParameters) *gopter.GenResult { + for i := 0; i < maxRetries; i++ { + result := genWithSieve(genParams) + if _, ok := result.Retrieve(); ok { + return result + } + } + resultType := gen(genParams).ResultType + return gopter.NewEmptyResult(resultType) + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/retry_until_test.go b/vendor/github.com/leanovate/gopter/gen/retry_until_test.go new file mode 100644 index 000000000..6894d8a77 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/retry_until_test.go @@ -0,0 +1,33 @@ +package gen_test + +import ( + "testing" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/gen" +) + +func TestRetryUntil(t *testing.T) { + genParams := gopter.DefaultGenParameters() + origGen := gen.IntRange(0, 100) + retryGen := gen.RetryUntil(origGen, func(v int) bool { + return v > 50 + }, 1000) + result := retryGen(genParams) + value, ok := result.Retrieve() + if value == nil || !ok { + t.Errorf("RetryGen generated empty result") + } + if value.(int) <= 50 { + t.Errorf("RetryGen generyte invalid value: %#v", value) + } + + noMatchGen := gen.RetryUntil(origGen, func(v int) bool { + return v > 500 + }, 100) + result = noMatchGen(genParams) + _, ok = result.Retrieve() + if ok { + t.Errorf("RetryGen nomatch generated a value") + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/slice_of.go b/vendor/github.com/leanovate/gopter/gen/slice_of.go new file mode 100644 index 000000000..69ca9380d --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/slice_of.go @@ -0,0 +1,89 @@ +package gen + +import ( + "reflect" + + "github.com/leanovate/gopter" +) + +// SliceOf generates an arbitrary slice of generated elements +// genParams.MaxSize sets an (exclusive) upper limit on the size of the slice +// genParams.MinSize sets an (inclusive) lower limit on the size of the slice +func SliceOf(elementGen gopter.Gen) gopter.Gen { + return func(genParams *gopter.GenParameters) *gopter.GenResult { + len := 0 + if genParams.MaxSize > 0 || genParams.MinSize > 0 { + if genParams.MinSize > genParams.MaxSize { + panic("GenParameters.MinSize must be <= GenParameters.MaxSize") + } + + if genParams.MaxSize == genParams.MinSize { + len = genParams.MaxSize + } else { + len = genParams.Rng.Intn(genParams.MaxSize-genParams.MinSize) + genParams.MinSize + } + } + result, elementSieve, elementShrinker := genSlice(elementGen, genParams, len) + + genResult := gopter.NewGenResult(result.Interface(), SliceShrinker(elementShrinker)) + if elementSieve != nil { + genResult.Sieve = forAllSieve(elementSieve) + } + return genResult + } +} + +// SliceOfN generates a slice of generated elements with definied length +func SliceOfN(len int, elementGen gopter.Gen) gopter.Gen { + return func(genParams *gopter.GenParameters) *gopter.GenResult { + result, elementSieve, elementShrinker := genSlice(elementGen, genParams, len) + + genResult := gopter.NewGenResult(result.Interface(), SliceShrinkerOne(elementShrinker)) + if elementSieve != nil { + genResult.Sieve = func(v interface{}) bool { + rv := reflect.ValueOf(v) + return rv.Len() == len && forAllSieve(elementSieve)(v) + } + } else { + genResult.Sieve = func(v interface{}) bool { + return reflect.ValueOf(v).Len() == len + } + } + return genResult + } +} + +func genSlice(elementGen gopter.Gen, genParams *gopter.GenParameters, len int) (reflect.Value, func(interface{}) bool, gopter.Shrinker) { + element := elementGen(genParams) + elementSieve := element.Sieve + elementShrinker := element.Shrinker + + result := reflect.MakeSlice(reflect.SliceOf(element.ResultType), 0, len) + + for i := 0; i < len; i++ { + value, ok := element.Retrieve() + + if ok { + if value == nil { + result = reflect.Append(result, reflect.Zero(element.ResultType)) + } else { + result = reflect.Append(result, reflect.ValueOf(value)) + } + } + element = elementGen(genParams) + } + + return result, elementSieve, elementShrinker +} + +func forAllSieve(elementSieve func(interface{}) bool) func(interface{}) bool { + return func(v interface{}) bool { + rv := reflect.ValueOf(v) + for i := rv.Len() - 1; i >= 0; i-- { + if !elementSieve(rv.Index(i).Interface()) { + return false + } + } + return true + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/slice_of_test.go b/vendor/github.com/leanovate/gopter/gen/slice_of_test.go new file mode 100644 index 000000000..504b5bea5 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/slice_of_test.go @@ -0,0 +1,172 @@ +package gen_test + +import ( + "testing" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/gen" +) + +func TestSliceOf(t *testing.T) { + genParams := gopter.DefaultGenParameters() + genParams.MaxSize = 50 + elementGen := gen.Const("element") + sliceGen := gen.SliceOf(elementGen) + + for i := 0; i < 100; i++ { + sample, ok := sliceGen(genParams).Retrieve() + + if !ok { + t.Error("Sample was not ok") + } + strings, ok := sample.([]string) + if !ok { + t.Errorf("Sample not slice of string: %#v", sample) + } else { + if len(strings) > 50 { + t.Errorf("Sample has invalid length: %#v", len(strings)) + } + for _, str := range strings { + if str != "element" { + t.Errorf("Sample contains invalid value: %#v", sample) + } + } + } + } + + genParams.MinSize = 10 + + for i := 0; i < 100; i++ { + sample, ok := sliceGen(genParams).Retrieve() + + if !ok { + t.Error("Sample was not ok") + } + strings, ok := sample.([]string) + if !ok { + t.Errorf("Sample not slice of string: %#v", sample) + } else { + if len(strings) > 50 || len(strings) < 10 { + t.Errorf("Sample has invalid length: %#v", len(strings)) + } + for _, str := range strings { + if str != "element" { + t.Errorf("Sample contains invalid value: %#v", sample) + } + } + } + } + + genParams.MaxSize = 10 + + for i := 0; i < 100; i++ { + sample, ok := sliceGen(genParams).Retrieve() + + if !ok { + t.Error("Sample was not ok") + } + strings, ok := sample.([]string) + if !ok { + t.Errorf("Sample not slice of string: %#v", sample) + } else { + if len(strings) != 10 { + t.Errorf("Sample has invalid length: %#v", len(strings)) + } + for _, str := range strings { + if str != "element" { + t.Errorf("Sample contains invalid value: %#v", sample) + } + } + } + } + + genParams.MaxSize = 0 + genParams.MinSize = 0 + + for i := 0; i < 100; i++ { + sample, ok := sliceGen(genParams).Retrieve() + + if !ok { + t.Error("Sample was not ok") + } + strings, ok := sample.([]string) + if !ok { + t.Errorf("Sample not slice of string: %#v", sample) + } else { + if len(strings) != 0 { + t.Errorf("Sample has invalid length: %#v", len(strings)) + } + } + } +} + +func TestSliceOfPanic(t *testing.T) { + genParams := gopter.DefaultGenParameters() + genParams.MaxSize = 0 + genParams.MinSize = 1 + elementGen := gen.Const("element") + sliceGen := gen.SliceOf(elementGen) + + defer func() { + if r := recover(); r == nil { + t.Error("SliceOf did not panic when MinSize was > MaxSize") + } + }() + + sliceGen(genParams).Retrieve() +} + +func TestSliceOfN(t *testing.T) { + elementGen := gen.Const("element") + sliceGen := gen.SliceOfN(10, elementGen) + + for i := 0; i < 100; i++ { + sample, ok := sliceGen.Sample() + + if !ok { + t.Error("Sample was not ok") + } + strings, ok := sample.([]string) + if !ok { + t.Errorf("Sample not slice of string: %#v", sample) + } else { + if len(strings) != 10 { + t.Errorf("Sample has invalid length: %#v", len(strings)) + } + for _, str := range strings { + if str != "element" { + t.Errorf("Sample contains invalid value: %#v", sample) + } + } + } + } +} + +func TestSliceOfNSieve(t *testing.T) { + var called int + elementSieve := func(v interface{}) bool { + called++ + return v == "element" + } + elementGen := gen.Const("element").SuchThat(elementSieve) + sliceGen := gen.SliceOfN(10, elementGen) + result := sliceGen(gopter.DefaultGenParameters()) + value, ok := result.Retrieve() + if !ok || value == nil { + t.Errorf("Invalid value: %#v", value) + } + strs, ok := value.([]string) + if !ok || len(strs) != 10 { + t.Errorf("Invalid value: %#v", value) + } + if called != 20 { + t.Errorf("Invalid called: %d", called) + } + if result.Sieve(strs[0:9]) { + t.Error("Sieve must not allow array len < 10") + } + strs[0] = "bla" + if result.Sieve(strs) { + t.Error("Sieve must not allow array with invalid element") + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/slice_shrink.go b/vendor/github.com/leanovate/gopter/gen/slice_shrink.go new file mode 100644 index 000000000..727a6d9ef --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/slice_shrink.go @@ -0,0 +1,101 @@ +package gen + +import ( + "fmt" + "reflect" + + "github.com/leanovate/gopter" +) + +type sliceShrinkOne struct { + original reflect.Value + index int + elementShrink gopter.Shrink +} + +func (s *sliceShrinkOne) Next() (interface{}, bool) { + value, ok := s.elementShrink() + if !ok { + return nil, false + } + result := reflect.MakeSlice(s.original.Type(), s.original.Len(), s.original.Len()) + reflect.Copy(result, s.original) + result.Index(s.index).Set(reflect.ValueOf(value)) + + return result.Interface(), true +} + +// SliceShrinkerOne creates a slice shrinker from a shrinker for the elements of the slice. +// The length of the slice will remains unchanged, instead each element is shrinked after the +// other. +func SliceShrinkerOne(elementShrinker gopter.Shrinker) gopter.Shrinker { + return func(v interface{}) gopter.Shrink { + rv := reflect.ValueOf(v) + if rv.Kind() != reflect.Slice { + panic(fmt.Sprintf("%#v is not a slice", v)) + } + + shrinks := make([]gopter.Shrink, 0, rv.Len()) + for i := 0; i < rv.Len(); i++ { + sliceShrinkOne := &sliceShrinkOne{ + original: rv, + index: i, + elementShrink: elementShrinker(rv.Index(i).Interface()), + } + shrinks = append(shrinks, sliceShrinkOne.Next) + } + return gopter.ConcatShrinks(shrinks...) + } +} + +type sliceShrink struct { + original reflect.Value + length int + offset int + chunkLength int +} + +func (s *sliceShrink) Next() (interface{}, bool) { + if s.chunkLength == 0 { + return nil, false + } + value := reflect.AppendSlice(reflect.MakeSlice(s.original.Type(), 0, s.length-s.chunkLength), s.original.Slice(0, s.offset)) + s.offset += s.chunkLength + if s.offset < s.length { + value = reflect.AppendSlice(value, s.original.Slice(s.offset, s.length)) + } else { + s.offset = 0 + s.chunkLength >>= 1 + } + + return value.Interface(), true +} + +// SliceShrinker creates a slice shrinker from a shrinker for the elements of the slice. +// The length of the slice will be shrinked as well +func SliceShrinker(elementShrinker gopter.Shrinker) gopter.Shrinker { + return func(v interface{}) gopter.Shrink { + rv := reflect.ValueOf(v) + if rv.Kind() != reflect.Slice { + panic(fmt.Sprintf("%#v is not a slice", v)) + } + sliceShrink := &sliceShrink{ + original: rv, + offset: 0, + length: rv.Len(), + chunkLength: rv.Len() >> 1, + } + + shrinks := make([]gopter.Shrink, 0, rv.Len()+1) + shrinks = append(shrinks, sliceShrink.Next) + for i := 0; i < rv.Len(); i++ { + sliceShrinkOne := &sliceShrinkOne{ + original: rv, + index: i, + elementShrink: elementShrinker(rv.Index(i).Interface()), + } + shrinks = append(shrinks, sliceShrinkOne.Next) + } + return gopter.ConcatShrinks(shrinks...) + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/slice_shrink_test.go b/vendor/github.com/leanovate/gopter/gen/slice_shrink_test.go new file mode 100644 index 000000000..915078ca4 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/slice_shrink_test.go @@ -0,0 +1,86 @@ +package gen_test + +import ( + "reflect" + "testing" + + "github.com/leanovate/gopter/gen" +) + +func TestSliceShrink(t *testing.T) { + oneShrink := gen.SliceShrinker(gen.Int64Shrinker)([]int64{0}).All() + if !reflect.DeepEqual(oneShrink, []interface{}{}) { + t.Errorf("Invalid oneShrink: %#v", oneShrink) + } + + twoShrink := gen.SliceShrinker(gen.Int64Shrinker)([]int64{0, 1}).All() + if !reflect.DeepEqual(twoShrink, []interface{}{ + []int64{1}, + []int64{0}, + []int64{0, 0}, + }) { + t.Errorf("Invalid twoShrink: %#v", twoShrink) + } + + threeShrink := gen.SliceShrinker(gen.Int64Shrinker)([]int64{0, 1, 2}).All() + if !reflect.DeepEqual(threeShrink, []interface{}{ + []int64{1, 2}, + []int64{0, 2}, + []int64{0, 1}, + []int64{0, 0, 2}, + []int64{0, 1, 0}, + []int64{0, 1, 1}, + []int64{0, 1, -1}, + }) { + t.Errorf("Invalid threeShrink: %#v", threeShrink) + } + + fourShrink := gen.SliceShrinker(gen.Int64Shrinker)([]int64{0, 1, 2, 3}).All() + if !reflect.DeepEqual(fourShrink, []interface{}{ + []int64{2, 3}, + []int64{0, 1}, + []int64{1, 2, 3}, + []int64{0, 2, 3}, + []int64{0, 1, 3}, + []int64{0, 1, 2}, + []int64{0, 0, 2, 3}, + []int64{0, 1, 0, 3}, + []int64{0, 1, 1, 3}, + []int64{0, 1, -1, 3}, + []int64{0, 1, 2, 0}, + []int64{0, 1, 2, 2}, + []int64{0, 1, 2, -2}, + }) { + t.Errorf("Invalid fourShrink: %#v", fourShrink) + } +} + +func TestSliceShrinkOne(t *testing.T) { + oneShrink := gen.SliceShrinkerOne(gen.Int64Shrinker)([]int64{0}).All() + if !reflect.DeepEqual(oneShrink, []interface{}{}) { + t.Errorf("Invalid oneShrink: %#v", oneShrink) + } + + threeShrink := gen.SliceShrinkerOne(gen.Int64Shrinker)([]int64{0, 1, 2}).All() + if !reflect.DeepEqual(threeShrink, []interface{}{ + []int64{0, 0, 2}, + []int64{0, 1, 0}, + []int64{0, 1, 1}, + []int64{0, 1, -1}, + }) { + t.Errorf("Invalid threeShrink: %#v", threeShrink) + } + + fourShrink := gen.SliceShrinkerOne(gen.Int64Shrinker)([]int64{0, 1, 2, 3}).All() + if !reflect.DeepEqual(fourShrink, []interface{}{ + []int64{0, 0, 2, 3}, + []int64{0, 1, 0, 3}, + []int64{0, 1, 1, 3}, + []int64{0, 1, -1, 3}, + []int64{0, 1, 2, 0}, + []int64{0, 1, 2, 2}, + []int64{0, 1, 2, -2}, + }) { + t.Errorf("Invalid fourShrink: %#v", fourShrink) + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/string_shrink.go b/vendor/github.com/leanovate/gopter/gen/string_shrink.go new file mode 100644 index 000000000..85cc41056 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/string_shrink.go @@ -0,0 +1,11 @@ +package gen + +import "github.com/leanovate/gopter" + +var runeSliceShrinker = SliceShrinker(gopter.NoShrinker) + +// StringShrinker is a shrinker for strings. +// It is very similiar to a sliace shrinker just that the elements themselves will not be shrinkeed. +func StringShrinker(v interface{}) gopter.Shrink { + return runeSliceShrinker([]rune(v.(string))).Map(runesToString) +} diff --git a/vendor/github.com/leanovate/gopter/gen/strings.go b/vendor/github.com/leanovate/gopter/gen/strings.go new file mode 100644 index 000000000..79a69ef67 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/strings.go @@ -0,0 +1,158 @@ +package gen + +import ( + "reflect" + "unicode" + "unicode/utf8" + + "github.com/leanovate/gopter" +) + +// RuneRange generates runes within a given range +func RuneRange(min, max rune) gopter.Gen { + return genRune(Int64Range(int64(min), int64(max))) +} + +// Rune generates an arbitrary character rune +func Rune() gopter.Gen { + return genRune(Frequency(map[int]gopter.Gen{ + 0xD800: Int64Range(0, 0xD800), + utf8.MaxRune - 0xDFFF: Int64Range(0xDFFF, int64(utf8.MaxRune)), + })) +} + +// RuneNoControl generates an arbitrary character rune that is not a control character +func RuneNoControl() gopter.Gen { + return genRune(Frequency(map[int]gopter.Gen{ + 0xD800: Int64Range(32, 0xD800), + utf8.MaxRune - 0xDFFF: Int64Range(0xDFFF, int64(utf8.MaxRune)), + })) +} + +func genRune(int64Gen gopter.Gen) gopter.Gen { + return int64Gen.Map(func(value int64) rune { + return rune(value) + }).SuchThat(func(v rune) bool { + return utf8.ValidRune(v) + }) +} + +// NumChar generates arbitrary numberic character runes +func NumChar() gopter.Gen { + return RuneRange('0', '9') +} + +// AlphaUpperChar generates arbitrary uppercase alpha character runes +func AlphaUpperChar() gopter.Gen { + return RuneRange('A', 'Z') +} + +// AlphaLowerChar generates arbitrary lowercase alpha character runes +func AlphaLowerChar() gopter.Gen { + return RuneRange('a', 'z') +} + +// AlphaChar generates arbitrary character runes (upper- and lowercase) +func AlphaChar() gopter.Gen { + return Frequency(map[int]gopter.Gen{ + 0: AlphaUpperChar(), + 9: AlphaLowerChar(), + }) +} + +// AlphaNumChar generates arbitrary alpha-numeric character runes +func AlphaNumChar() gopter.Gen { + return Frequency(map[int]gopter.Gen{ + 0: NumChar(), + 9: AlphaChar(), + }) +} + +// UnicodeChar generates arbitrary character runes with a given unicode table +func UnicodeChar(table *unicode.RangeTable) gopter.Gen { + if table == nil || len(table.R16)+len(table.R32) == 0 { + return Fail(reflect.TypeOf(rune('a'))) + } + return func(genParams *gopter.GenParameters) *gopter.GenResult { + tableIdx := genParams.Rng.Intn(len(table.R16) + len(table.R32)) + + var selectedRune rune + if tableIdx < len(table.R16) { + r := table.R16[tableIdx] + runeOffset := uint16(genParams.Rng.Int63n(int64((r.Hi-r.Lo+1)/r.Stride))) * r.Stride + selectedRune = rune(runeOffset + r.Lo) + } else { + r := table.R32[tableIdx-len(table.R16)] + runeOffset := uint32(genParams.Rng.Int63n(int64((r.Hi-r.Lo+1)/r.Stride))) * r.Stride + selectedRune = rune(runeOffset + r.Lo) + } + genResult := gopter.NewGenResult(selectedRune, gopter.NoShrinker) + genResult.Sieve = func(v interface{}) bool { + return unicode.Is(table, v.(rune)) + } + return genResult + } +} + +// AnyString generates an arbitrary string +func AnyString() gopter.Gen { + return genString(Rune(), utf8.ValidRune) +} + +// AlphaString generates an arbitrary string with letters +func AlphaString() gopter.Gen { + return genString(AlphaChar(), unicode.IsLetter) +} + +// NumString generates an arbitrary string with digits +func NumString() gopter.Gen { + return genString(NumChar(), unicode.IsDigit) +} + +// Identifier generates an arbitrary identifier string +// Identitiers are supporsed to start with a lowercase letter and contain only +// letters and digits +func Identifier() gopter.Gen { + return gopter.CombineGens( + AlphaLowerChar(), + SliceOf(AlphaNumChar()), + ).Map(func(values []interface{}) string { + first := values[0].(rune) + tail := values[1].([]rune) + result := make([]rune, 0, len(tail)+1) + return string(append(append(result, first), tail...)) + }).SuchThat(func(str string) bool { + if len(str) < 1 || !unicode.IsLower(([]rune(str))[0]) { + return false + } + for _, ch := range str { + if !unicode.IsLetter(ch) && !unicode.IsDigit(ch) { + return false + } + } + return true + }).WithShrinker(StringShrinker) +} + +// UnicodeString generates an arbitrary string from a given +// unicode table. +func UnicodeString(table *unicode.RangeTable) gopter.Gen { + return genString(UnicodeChar(table), func(ch rune) bool { + return unicode.Is(table, ch) + }) +} + +func genString(runeGen gopter.Gen, runeSieve func(ch rune) bool) gopter.Gen { + return SliceOf(runeGen).Map(runesToString).SuchThat(func(v string) bool { + for _, ch := range v { + if !runeSieve(ch) { + return false + } + } + return true + }).WithShrinker(StringShrinker) +} + +func runesToString(v []rune) string { + return string(v) +} diff --git a/vendor/github.com/leanovate/gopter/gen/strings_test.go b/vendor/github.com/leanovate/gopter/gen/strings_test.go new file mode 100644 index 000000000..44fcf5c84 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/strings_test.go @@ -0,0 +1,161 @@ +package gen_test + +import ( + "testing" + "unicode" + "unicode/utf8" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/gen" +) + +func TestRune(t *testing.T) { + commonGeneratorTest(t, "rune", gen.Rune(), func(value interface{}) bool { + v, ok := value.(rune) + return ok && utf8.ValidRune(v) + }) +} + +func TestNumChar(t *testing.T) { + commonGeneratorTest(t, "num char", gen.NumChar(), func(value interface{}) bool { + v, ok := value.(rune) + return ok && unicode.IsNumber(v) + }) +} + +func TestAlphaUpper(t *testing.T) { + commonGeneratorTest(t, "alpha upper char", gen.AlphaUpperChar(), func(value interface{}) bool { + v, ok := value.(rune) + return ok && unicode.IsUpper(v) && unicode.IsLetter(v) + }) +} + +func TestAlphaLower(t *testing.T) { + commonGeneratorTest(t, "alpha lower char", gen.AlphaLowerChar(), func(value interface{}) bool { + v, ok := value.(rune) + return ok && unicode.IsLower(v) && unicode.IsLetter(v) + }) +} + +func TestAlphaChar(t *testing.T) { + commonGeneratorTest(t, "alpha char", gen.AlphaChar(), func(value interface{}) bool { + v, ok := value.(rune) + return ok && unicode.IsLetter(v) + }) +} + +func TestAnyString(t *testing.T) { + commonGeneratorTest(t, "any string", gen.AnyString(), func(value interface{}) bool { + str, ok := value.(string) + + if !ok { + return false + } + for _, ch := range str { + if !utf8.ValidRune(ch) { + return false + } + } + return true + }) +} + +func TestAlphaString(t *testing.T) { + alphaString := gen.AlphaString() + commonGeneratorTest(t, "alpha string", alphaString, func(value interface{}) bool { + str, ok := value.(string) + + if !ok { + return false + } + for _, ch := range str { + if !utf8.ValidRune(ch) || !unicode.IsLetter(ch) { + return false + } + } + return true + }) + sieve := alphaString(gopter.DefaultGenParameters()).Sieve + if sieve == nil { + t.Error("No sieve") + } + if !sieve("abcdABCD") || sieve("abc12") { + t.Error("Invalid sieve") + } +} + +func TestNumString(t *testing.T) { + numString := gen.NumString() + commonGeneratorTest(t, "num string", numString, func(value interface{}) bool { + str, ok := value.(string) + + if !ok { + return false + } + for _, ch := range str { + if !utf8.ValidRune(ch) || !unicode.IsDigit(ch) { + return false + } + } + return true + }) + sieve := numString(gopter.DefaultGenParameters()).Sieve + if sieve == nil { + t.Error("No sieve") + } + if !sieve("123456789") || sieve("123abcd") { + t.Error("Invalid sieve") + } +} + +func TestIdentifier(t *testing.T) { + identifiers := gen.Identifier() + commonGeneratorTest(t, "identifiers", identifiers, func(value interface{}) bool { + str, ok := value.(string) + + if !ok { + return false + } + if len(str) == 0 || !unicode.IsLetter([]rune(str)[0]) { + return false + } + for _, ch := range str { + if !utf8.ValidRune(ch) || (!unicode.IsDigit(ch) && !unicode.IsLetter(ch)) { + return false + } + } + return true + }) + sieve := identifiers(gopter.DefaultGenParameters()).Sieve + if sieve == nil { + t.Error("No sieve") + } + if !sieve("abc123") || sieve("123abc") || sieve("abcd123-") { + t.Error("Invalid sieve") + } +} + +func TestUnicodeString(t *testing.T) { + fail := gen.UnicodeChar(nil) + value, ok := fail.Sample() + if value != nil || ok { + t.Fail() + } + + for _, table := range unicode.Scripts { + unicodeString := gen.UnicodeString(table) + commonGeneratorTest(t, "unicodeString", unicodeString, func(value interface{}) bool { + str, ok := value.(string) + + if !ok { + return false + } + for _, ch := range str { + if !utf8.ValidRune(ch) || !unicode.Is(table, ch) { + return false + } + } + return true + }) + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/struct.go b/vendor/github.com/leanovate/gopter/gen/struct.go new file mode 100644 index 000000000..d57a829c1 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/struct.go @@ -0,0 +1,69 @@ +package gen + +import ( + "reflect" + + "github.com/leanovate/gopter" +) + +// Struct generates a given struct type. +// rt has to be the reflect type of the struct, gens contains a map of field generators. +// Note that the result types of the generators in gen have to match the type of the correspoinding +// field in the struct. Also note that only public fields of a struct can be generated +func Struct(rt reflect.Type, gens map[string]gopter.Gen) gopter.Gen { + if rt.Kind() == reflect.Ptr { + rt = rt.Elem() + } + if rt.Kind() != reflect.Struct { + return Fail(rt) + } + return func(genParams *gopter.GenParameters) *gopter.GenResult { + result := reflect.New(rt) + + for name, gen := range gens { + field, ok := rt.FieldByName(name) + if !ok { + continue + } + value, ok := gen(genParams).Retrieve() + if !ok { + return gopter.NewEmptyResult(rt) + } + result.Elem().FieldByIndex(field.Index).Set(reflect.ValueOf(value)) + } + + return gopter.NewGenResult(reflect.Indirect(result).Interface(), gopter.NoShrinker) + } +} + +// StructPtr generates pointers to a given struct type. +// Not that SturctPtr does not generate nil, if you want to include nil in your +// testing you should combine gen.PtrOf with gen.Struct. +// rt has to be the reflect type of the struct, gens contains a map of field generators. +// Note that the result types of the generators in gen have to match the type of the correspoinding +// field in the struct. Also note that only public fields of a struct can be generated +func StructPtr(rt reflect.Type, gens map[string]gopter.Gen) gopter.Gen { + if rt.Kind() == reflect.Ptr { + rt = rt.Elem() + } + if rt.Kind() != reflect.Struct { + return Fail(rt) + } + return func(genParams *gopter.GenParameters) *gopter.GenResult { + result := reflect.New(rt) + + for name, gen := range gens { + field, ok := rt.FieldByName(name) + if !ok { + continue + } + value, ok := gen(genParams).Retrieve() + if !ok { + return gopter.NewEmptyResult(rt) + } + result.Elem().FieldByIndex(field.Index).Set(reflect.ValueOf(value)) + } + + return gopter.NewGenResult(result.Interface(), gopter.NoShrinker) + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/struct_test.go b/vendor/github.com/leanovate/gopter/gen/struct_test.go new file mode 100644 index 000000000..62ea5466b --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/struct_test.go @@ -0,0 +1,96 @@ +package gen_test + +import ( + "reflect" + "testing" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/gen" +) + +type testStruct struct { + Value1 string + Value2 int64 + Value3 []int8 + Value4 string +} + +func TestStruct(t *testing.T) { + structGen := gen.Struct(reflect.TypeOf(&testStruct{}), map[string]gopter.Gen{ + "Value1": gen.Identifier(), + "Value2": gen.Int64(), + "Value3": gen.SliceOf(gen.Int8()), + "NotThere": gen.AnyString(), + }) + for i := 0; i < 100; i++ { + value, ok := structGen.Sample() + + if !ok { + t.Errorf("Invalid value: %#v", value) + } + v, ok := value.(testStruct) + if !ok || v.Value1 == "" || v.Value3 == nil || v.Value4 != "" { + t.Errorf("Invalid value: %#v", value) + } + } +} + +func TestStructPropageEmpty(t *testing.T) { + fail := gen.Struct(reflect.TypeOf(&testStruct{}), map[string]gopter.Gen{ + "Value1": gen.Identifier().SuchThat(func(str string) bool { + return false + }), + }) + + if _, ok := fail.Sample(); ok { + t.Errorf("Failing field generator in Struct generated a value") + } +} + +func TestStructNoStruct(t *testing.T) { + fail := gen.Struct(reflect.TypeOf(""), map[string]gopter.Gen{}) + + if _, ok := fail.Sample(); ok { + t.Errorf("Invalid Struct generated a value") + } +} + +func TestStructPtr(t *testing.T) { + structGen := gen.StructPtr(reflect.TypeOf(&testStruct{}), map[string]gopter.Gen{ + "Value1": gen.Identifier(), + "Value2": gen.Int64(), + "Value3": gen.SliceOf(gen.Int8()), + "NotThere": gen.AnyString(), + }) + for i := 0; i < 100; i++ { + value, ok := structGen.Sample() + + if !ok || value == nil { + t.Errorf("Invalid value: %#v", value) + } + v, ok := value.(*testStruct) + if !ok || v.Value1 == "" || v.Value3 == nil || v.Value4 != "" { + t.Errorf("Invalid value: %#v", value) + } + } +} + +func TestStructPtrPropageEmpty(t *testing.T) { + fail := gen.StructPtr(reflect.TypeOf(&testStruct{}), map[string]gopter.Gen{ + "Value1": gen.Identifier().SuchThat(func(str string) bool { + return false + }), + }) + + if _, ok := fail.Sample(); ok { + t.Errorf("Failing field generator in StructPtr generated a value") + } +} + +func TestStructPtrNoStruct(t *testing.T) { + fail := gen.StructPtr(reflect.TypeOf(""), map[string]gopter.Gen{}) + + if _, ok := fail.Sample(); ok { + t.Errorf("Invalid StructPtr generated a value") + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/time.go b/vendor/github.com/leanovate/gopter/gen/time.go new file mode 100644 index 000000000..72e658c5e --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/time.go @@ -0,0 +1,37 @@ +package gen + +import ( + "time" + + "github.com/leanovate/gopter" +) + +// Time generates an arbitrary time.Time within year [0, 9999] +func Time() gopter.Gen { + return func(genParams *gopter.GenParameters) *gopter.GenResult { + sec := genParams.Rng.Int63n(253402214400) // Ensure year in [0, 9999] + usec := genParams.Rng.Int63n(1000000000) + + return gopter.NewGenResult(time.Unix(sec, usec), TimeShrinker) + } +} + +// AnyTime generates an arbitrary time.Time struct (might be way out of bounds of any reason) +func AnyTime() gopter.Gen { + return func(genParams *gopter.GenParameters) *gopter.GenResult { + sec := genParams.NextInt64() + usec := genParams.NextInt64() + + return gopter.NewGenResult(time.Unix(sec, usec), TimeShrinker) + } +} + +// TimeRange generates an arbitrary time.Time with a range +// from defines the start of the time range +// duration defines the overall duration of the time range +func TimeRange(from time.Time, duration time.Duration) gopter.Gen { + return func(genParams *gopter.GenParameters) *gopter.GenResult { + v := from.Add(time.Duration(genParams.Rng.Int63n(int64(duration)))) + return gopter.NewGenResult(v, TimeShrinker) + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/time_shrink.go b/vendor/github.com/leanovate/gopter/gen/time_shrink.go new file mode 100644 index 000000000..f7b76e550 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/time_shrink.go @@ -0,0 +1,27 @@ +package gen + +import ( + "time" + + "github.com/leanovate/gopter" +) + +// TimeShrinker is a shrinker for time.Time structs +func TimeShrinker(v interface{}) gopter.Shrink { + t := v.(time.Time) + sec := t.Unix() + nsec := int64(t.Nanosecond()) + secShrink := uint64Shrink{ + original: uint64(sec), + half: uint64(sec), + } + nsecShrink := uint64Shrink{ + original: uint64(nsec), + half: uint64(nsec), + } + return gopter.Shrink(secShrink.Next).Map(func(v uint64) time.Time { + return time.Unix(int64(v), nsec) + }).Interleave(gopter.Shrink(nsecShrink.Next).Map(func(v uint64) time.Time { + return time.Unix(sec, int64(v)) + })) +} diff --git a/vendor/github.com/leanovate/gopter/gen/time_shrink_test.go b/vendor/github.com/leanovate/gopter/gen/time_shrink_test.go new file mode 100644 index 000000000..86ca224fc --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/time_shrink_test.go @@ -0,0 +1,27 @@ +package gen_test + +import ( + "reflect" + "testing" + "time" + + "github.com/leanovate/gopter/gen" +) + +func TestTimeShrink(t *testing.T) { + timeShrink := gen.TimeShrinker(time.Unix(20, 10)).All() + if !reflect.DeepEqual(timeShrink, []interface{}{ + time.Unix(0, 10), + time.Unix(20, 0), + time.Unix(10, 10), + time.Unix(20, 5), + time.Unix(15, 10), + time.Unix(20, 8), + time.Unix(18, 10), + time.Unix(20, 9), + time.Unix(19, 10), + }) { + t.Errorf("Invalid timeShrink: %#v", timeShrink) + } + +} diff --git a/vendor/github.com/leanovate/gopter/gen/time_test.go b/vendor/github.com/leanovate/gopter/gen/time_test.go new file mode 100644 index 000000000..e517a503e --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/time_test.go @@ -0,0 +1,60 @@ +package gen_test + +import ( + "testing" + "time" + + "github.com/leanovate/gopter/gen" +) + +func TestTime(t *testing.T) { + timeGen := gen.Time() + for i := 0; i < 100; i++ { + value, ok := timeGen.Sample() + + if !ok || value == nil { + t.Errorf("Invalid time: %#v", value) + } + v, ok := value.(time.Time) + if !ok || v.String() == "" { + t.Errorf("Invalid time: %#v", value) + } + if v.Year() < 0 || v.Year() > 9999 { + t.Errorf("Year out of range: %#v", v) + } + } +} + +func TestAnyTime(t *testing.T) { + timeGen := gen.AnyTime() + for i := 0; i < 100; i++ { + value, ok := timeGen.Sample() + + if !ok || value == nil { + t.Errorf("Invalid time: %#v", value) + } + v, ok := value.(time.Time) + if !ok || v.String() == "" { + t.Errorf("Invalid time: %#v", value) + } + } +} + +func TestTimeRegion(t *testing.T) { + duration := time.Duration(10*24*365) * time.Hour + from := time.Unix(1000, 0) + until := from.Add(duration) + timeRange := gen.TimeRange(from, duration) + + for i := 0; i < 100; i++ { + value, ok := timeRange.Sample() + + if !ok || value == nil { + t.Errorf("Invalid time: %#v", value) + } + v, ok := value.(time.Time) + if !ok || v.Before(from) || v.After(until) { + t.Errorf("Invalid time: %#v", value) + } + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/weighted.go b/vendor/github.com/leanovate/gopter/gen/weighted.go new file mode 100644 index 000000000..af97c5051 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/weighted.go @@ -0,0 +1,44 @@ +package gen + +import ( + "fmt" + "sort" + + "github.com/leanovate/gopter" +) + +// WeightedGen adds a weight number to a generator. +// To be used as parameter to gen.Weighted +type WeightedGen struct { + Weight int + Gen gopter.Gen +} + +// Weighted combines multiple generators, where each generator has a weight. +// The weight of a generator is proportional to the probability that the +// generator gets selected. +func Weighted(weightedGens []WeightedGen) gopter.Gen { + if len(weightedGens) == 0 { + panic("weightedGens must be non-empty") + } + weights := make(sort.IntSlice, 0, len(weightedGens)) + + totalWeight := 0 + for _, weightedGen := range weightedGens { + w := weightedGen.Weight + if w <= 0 { + panic(fmt.Sprintf( + "weightedGens must have positive weights; got %d", + w)) + } + totalWeight += weightedGen.Weight + weights = append(weights, totalWeight) + } + return func(genParams *gopter.GenParameters) *gopter.GenResult { + idx := weights.Search(1 + genParams.Rng.Intn(totalWeight)) + gen := weightedGens[idx].Gen + result := gen(genParams) + result.Sieve = nil + return result + } +} diff --git a/vendor/github.com/leanovate/gopter/gen/weighted_test.go b/vendor/github.com/leanovate/gopter/gen/weighted_test.go new file mode 100644 index 000000000..59a81a371 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen/weighted_test.go @@ -0,0 +1,41 @@ +package gen_test + +import ( + "testing" + + "github.com/leanovate/gopter/gen" +) + +func TestWeighted(t *testing.T) { + weighted := gen.Weighted([]gen.WeightedGen{ + {Weight: 1, Gen: gen.Const("A")}, + {Weight: 2, Gen: gen.Const("B")}, + {Weight: 7, Gen: gen.Const("C")}, + }) + results := make(map[string]int) + for i := int64(0); i < int64(1000); i++ { + result, ok := weighted.Sample() + if !ok { + t.FailNow() + } + results[result.(string)]++ + } + expectedResults := map[string]int{ + "A": 100, + "B": 200, + "C": 700, + } + delta := 50 + for _, value := range []string{"A", "B", "C"} { + result := results[value] + expected := expectedResults[value] + if result < expected-delta || result > expected+delta { + t.Errorf( + "Result %d for %v falls outside acceptable range %d, %d", + result, + value, + expected-delta, + expected+delta) + } + } +} diff --git a/vendor/github.com/leanovate/gopter/gen_parameter_test.go b/vendor/github.com/leanovate/gopter/gen_parameter_test.go new file mode 100644 index 000000000..8a28065f6 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen_parameter_test.go @@ -0,0 +1,63 @@ +package gopter_test + +import ( + "math/rand" + "testing" + + "github.com/leanovate/gopter" +) + +type fixedSeed struct { + fixed int64 +} + +func (f *fixedSeed) Int63() int64 { return f.fixed } +func (f *fixedSeed) Seed(seed int64) { f.fixed = seed } + +func TestGenParameters(t *testing.T) { + parameters := &gopter.GenParameters{ + MaxSize: 100, + Rng: rand.New(&fixedSeed{}), + } + + if !parameters.NextBool() { + t.Error("Bool should be true") + } + if parameters.NextInt64() != 0 { + t.Error("int64 should be 0") + } + if parameters.NextUint64() != 0 { + t.Error("uint64 should be 0") + } + + parameters.Rng.Seed(1) + if parameters.NextBool() { + t.Error("Bool should be false") + } + if parameters.NextInt64() != 1 { + t.Error("int64 should be 1") + } + if parameters.NextUint64() != 3 { + t.Error("uint64 should be 3") + } + + parameters.Rng.Seed(2) + if !parameters.NextBool() { + t.Error("Bool should be true") + } + if parameters.NextInt64() != -2 { + t.Error("int64 should be -2") + } + if parameters.NextUint64() != 6 { + t.Error("uint64 should be 6") + } + + param1 := parameters.CloneWithSeed(1024) + param2 := parameters.CloneWithSeed(1024) + + for i := 0; i < 100; i++ { + if param1.NextInt64() != param2.NextInt64() { + t.Error("cloned parameters create different random numbers") + } + } +} diff --git a/vendor/github.com/leanovate/gopter/gen_parameters.go b/vendor/github.com/leanovate/gopter/gen_parameters.go new file mode 100644 index 000000000..a146b106f --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen_parameters.go @@ -0,0 +1,68 @@ +package gopter + +import ( + "math/rand" + "time" +) + +// GenParameters encapsulates the parameters for all generators. +type GenParameters struct { + MinSize int + MaxSize int + MaxShrinkCount int + Rng *rand.Rand +} + +// WithSize modifies the size parameter. The size parameter defines an upper bound for the size of +// generated slices or strings. +func (p *GenParameters) WithSize(size int) *GenParameters { + newParameters := *p + newParameters.MaxSize = size + return &newParameters +} + +// NextBool create a random boolean using the underlying Rng. +func (p *GenParameters) NextBool() bool { + return p.Rng.Int63()&1 == 0 +} + +// NextInt64 create a random int64 using the underlying Rng. +func (p *GenParameters) NextInt64() int64 { + v := p.Rng.Int63() + if p.NextBool() { + return -v + } + return v +} + +// NextUint64 create a random uint64 using the underlying Rng. +func (p *GenParameters) NextUint64() uint64 { + first := uint64(p.Rng.Int63()) + second := uint64(p.Rng.Int63()) + + return (first << 1) ^ second +} + +// CloneWithSeed clone the current parameters with a new seed. +// This is useful to create subsections that can rerun (provided you keep the +// seed) +func (p *GenParameters) CloneWithSeed(seed int64) *GenParameters { + return &GenParameters{ + MinSize: p.MinSize, + MaxSize: p.MaxSize, + MaxShrinkCount: p.MaxShrinkCount, + Rng: rand.New(NewLockedSource(seed)), + } +} + +// DefaultGenParameters creates default GenParameters. +func DefaultGenParameters() *GenParameters { + seed := time.Now().UnixNano() + + return &GenParameters{ + MinSize: 0, + MaxSize: 100, + MaxShrinkCount: 1000, + Rng: rand.New(NewLockedSource(seed)), + } +} diff --git a/vendor/github.com/leanovate/gopter/gen_result.go b/vendor/github.com/leanovate/gopter/gen_result.go new file mode 100644 index 000000000..f692f4e4c --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen_result.go @@ -0,0 +1,55 @@ +package gopter + +import "reflect" + +// GenResult contains the result of a generator. +type GenResult struct { + Labels []string + Shrinker Shrinker + ResultType reflect.Type + Result interface{} + Sieve func(interface{}) bool +} + +// NewGenResult creates a new generator result from for a concrete value and +// shrinker. +// Note: The concrete value "result" not be nil +func NewGenResult(result interface{}, shrinker Shrinker) *GenResult { + return &GenResult{ + Shrinker: shrinker, + ResultType: reflect.TypeOf(result), + Result: result, + } +} + +// NewEmptyResult creates an empty generator result. +// Unless the sieve does not explicitly allow it, empty (i.e. nil-valued) +// results are considered invalid. +func NewEmptyResult(resultType reflect.Type) *GenResult { + return &GenResult{ + ResultType: resultType, + Shrinker: NoShrinker, + } +} + +// Retrieve gets the concrete generator result. +// If the result is invalid or does not pass the sieve there is no concrete +// value and the property using the generator should be undecided. +func (r *GenResult) Retrieve() (interface{}, bool) { + if (r.Sieve == nil && r.Result != nil) || (r.Sieve != nil && r.Sieve(r.Result)) { + return r.Result, true + } + return nil, false +} + +// RetrieveAsValue get the concrete generator result as reflect value. +// If the result is invalid or does not pass the sieve there is no concrete +// value and the property using the generator should be undecided. +func (r *GenResult) RetrieveAsValue() (reflect.Value, bool) { + if r.Result != nil && (r.Sieve == nil || r.Sieve(r.Result)) { + return reflect.ValueOf(r.Result), true + } else if r.Result == nil && r.Sieve != nil && r.Sieve(r.Result) { + return reflect.Zero(r.ResultType), true + } + return reflect.Zero(r.ResultType), false +} diff --git a/vendor/github.com/leanovate/gopter/gen_result_test.go b/vendor/github.com/leanovate/gopter/gen_result_test.go new file mode 100644 index 000000000..d3959c8aa --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen_result_test.go @@ -0,0 +1,26 @@ +package gopter_test + +import ( + "reflect" + "testing" + + "github.com/leanovate/gopter" +) + +func TestNewGenResult(t *testing.T) { + result := gopter.NewGenResult(123, gopter.NoShrinker) + value, ok := result.Retrieve() + + if !ok || value != 123 || result.ResultType.Kind() != reflect.Int { + t.Errorf("Invalid result: %#v", value) + } +} + +func TestNewEmptyResult(t *testing.T) { + result := gopter.NewEmptyResult(reflect.TypeOf(0)) + value, ok := result.Retrieve() + + if ok || value != nil || result.ResultType.Kind() != reflect.Int { + t.Errorf("Invalid result: %#v", value) + } +} diff --git a/vendor/github.com/leanovate/gopter/gen_test.go b/vendor/github.com/leanovate/gopter/gen_test.go new file mode 100644 index 000000000..78665ee7f --- /dev/null +++ b/vendor/github.com/leanovate/gopter/gen_test.go @@ -0,0 +1,387 @@ +package gopter_test + +import ( + "reflect" + "testing" + + "github.com/leanovate/gopter" +) + +func constGen(value interface{}) gopter.Gen { + return func(*gopter.GenParameters) *gopter.GenResult { + return gopter.NewGenResult(value, gopter.NoShrinker) + } +} + +func TestGenSample(t *testing.T) { + gen := constGen("sample") + + value, ok := gen.Sample() + if !ok || value != "sample" { + t.Errorf("Invalid gen sample: %#v", value) + } +} + +func BenchmarkMap(b *testing.B) { + for i := 0; i < b.N; i++ { + gen := constGen("sample") + var mappedWith string + mapper := func(v string) string { + mappedWith = v + return "other" + } + value, ok := gen.Map(mapper).Sample() + if !ok || value != "other" { + b.Errorf("Invalid gen sample: %#v", value) + } + if mappedWith != "sample" { + b.Errorf("Invalid mapped with: %#v", mappedWith) + } + + gen = gen.SuchThat(func(interface{}) bool { + return false + }) + value, ok = gen.Map(mapper).Sample() + if ok { + b.Errorf("Invalid gen sample: %#v", value) + } + } +} + +func TestGenMap(t *testing.T) { + gen := constGen("sample") + var mappedWith string + mapper := func(v string) string { + mappedWith = v + return "other" + } + value, ok := gen.Map(mapper).Sample() + if !ok || value != "other" { + t.Errorf("Invalid gen sample: %#v", value) + } + if mappedWith != "sample" { + t.Errorf("Invalid mapped with: %#v", mappedWith) + } + + gen = gen.SuchThat(func(interface{}) bool { + return false + }) + value, ok = gen.Map(mapper).Sample() + if ok { + t.Errorf("Invalid gen sample: %#v", value) + } +} + +func TestGenMapWithParams(t *testing.T) { + gen := constGen("sample") + var mappedWith string + var mappedWithParams *gopter.GenParameters + mapper := func(v string, params *gopter.GenParameters) string { + mappedWith = v + mappedWithParams = params + return "other" + } + value, ok := gen.Map(mapper).Sample() + if !ok || value != "other" { + t.Errorf("Invalid gen sample: %#v", value) + } + if mappedWith != "sample" { + t.Errorf("Invalid mapped with: %#v", mappedWith) + } + if mappedWithParams == nil || mappedWithParams.MaxSize != 100 { + t.Error("Mapper not called with currect parameters") + } + + gen = gen.SuchThat(func(interface{}) bool { + return false + }) + value, ok = gen.Map(mapper).Sample() + if ok { + t.Errorf("Invalid gen sample: %#v", value) + } +} + +func TestGenMapNoFunc(t *testing.T) { + defer expectPanic(t, "Param of Map has to be a func, but is string") + constGen("sample").Map("not a function") +} + +func TestGenMapTooManyParams(t *testing.T) { + defer expectPanic(t, "Param of Map has to be a func with one or two params, but is 3") + constGen("sample").Map(func(a, b, C string) string { + return "" + }) +} + +func TestGenMapInvalidSecondParam(t *testing.T) { + defer expectPanic(t, "Second parameter of mapper function has to be a *GenParameters") + constGen("sample").Map(func(a, b string) string { + return "" + }) +} + +func TestGenMapToInvalidParamtype(t *testing.T) { + defer expectPanic(t, "Param of Map has to be a func with one param assignable to string, but is int") + constGen("sample").Map(func(a int) string { + return "" + }) +} + +func TestGenMapToManyReturns(t *testing.T) { + defer expectPanic(t, "Param of Map has to be a func with one return value, but is 2") + constGen("sample").Map(func(a string) (string, bool) { + return "", false + }) +} + +func TestGenMapResultIn(t *testing.T) { + gen := constGen("sample") + var mappedWith *gopter.GenResult + mapper := func(result *gopter.GenResult) string { + mappedWith = result + return "other" + } + + value, ok := gen.Map(mapper).Sample() + if !ok || value != "other" { + t.Errorf("Invalid gen sample: %#v", value) + } + if mappedWith == nil { + t.Error("Mapper not called") + } + if mapperValue, ok := mappedWith.Retrieve(); !ok || mapperValue != "sample" { + t.Errorf("Mapper was called with invalid value: %#v", mapperValue) + } +} + +func TestGenMapResultInWithParams(t *testing.T) { + gen := constGen("sample") + var mappedWith *gopter.GenResult + var mappedWithParams *gopter.GenParameters + mapper := func(result *gopter.GenResult, params *gopter.GenParameters) string { + mappedWith = result + mappedWithParams = params + return "other" + } + + value, ok := gen.Map(mapper).Sample() + if !ok || value != "other" { + t.Errorf("Invalid gen sample: %#v", value) + } + if mappedWith == nil { + t.Error("Mapper not called") + } + if mappedWithParams == nil || mappedWithParams.MaxSize != 100 { + t.Error("Mapper not called with currect parameters") + } + if mapperValue, ok := mappedWith.Retrieve(); !ok || mapperValue != "sample" { + t.Errorf("Mapper was called with invalid value: %#v", mapperValue) + } +} + +func TestGenMapResultOut(t *testing.T) { + gen := constGen("sample") + var mappedWith string + mapper := func(v string) *gopter.GenResult { + mappedWith = v + return gopter.NewGenResult("other", gopter.NoShrinker) + } + value, ok := gen.Map(mapper).Sample() + if !ok || value != "other" { + t.Errorf("Invalid gen sample: %#v", value) + } + if mappedWith != "sample" { + t.Errorf("Invalid mapped with: %#v", mappedWith) + } + + gen = gen.SuchThat(func(interface{}) bool { + return false + }) + value, ok = gen.Map(mapper).Sample() + if ok { + t.Errorf("Invalid gen sample: %#v", value) + } +} + +func TestGenMapResultInOut(t *testing.T) { + gen := constGen("sample") + var mappedWith *gopter.GenResult + mapper := func(result *gopter.GenResult) *gopter.GenResult { + mappedWith = result + return gopter.NewGenResult("other", gopter.NoShrinker) + } + + value, ok := gen.Map(mapper).Sample() + if !ok || value != "other" { + t.Errorf("Invalid gen sample: %#v", value) + } + if mappedWith == nil { + t.Error("Mapper not called") + } + if mapperValue, ok := mappedWith.Retrieve(); !ok || mapperValue != "sample" { + t.Errorf("Mapper was called with invalid value: %#v", mapperValue) + } +} + +func TestGenFlatMap(t *testing.T) { + gen := constGen("sample") + var mappedWith interface{} + mapper := func(v interface{}) gopter.Gen { + mappedWith = v + return constGen("other") + } + value, ok := gen.FlatMap(mapper, reflect.TypeOf("")).Sample() + if !ok || value != "other" { + t.Errorf("Invalid gen sample: %#v", value) + } + if mappedWith.(string) != "sample" { + t.Errorf("Invalid mapped with: %#v", mappedWith) + } + + gen = gen.SuchThat(func(interface{}) bool { + return false + }) + value, ok = gen.FlatMap(mapper, reflect.TypeOf("")).Sample() + if ok { + t.Errorf("Invalid gen sample: %#v", value) + } +} + +func TestGenMapResult(t *testing.T) { + gen := constGen("sample") + var mappedWith *gopter.GenResult + mapper := func(result *gopter.GenResult) *gopter.GenResult { + mappedWith = result + return gopter.NewGenResult("other", gopter.NoShrinker) + } + + value, ok := gen.MapResult(mapper).Sample() + if !ok || value != "other" { + t.Errorf("Invalid gen sample: %#v", value) + } + if mappedWith == nil { + t.Error("Mapper not called") + } + if mapperValue, ok := mappedWith.Retrieve(); !ok || mapperValue != "sample" { + t.Errorf("Mapper was called with invalid value: %#v", mapperValue) + } +} + +func TestCombineGens(t *testing.T) { + gens := make([]gopter.Gen, 0, 20) + for i := 0; i < 20; i++ { + gens = append(gens, constGen(i)) + } + gen := gopter.CombineGens(gens...) + raw, ok := gen.Sample() + if !ok { + t.Errorf("Invalid combined gen: %#v", raw) + } + values, ok := raw.([]interface{}) + if !ok || !reflect.DeepEqual(values, []interface{}{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}) { + t.Errorf("Invalid combined gen: %#v", raw) + } + + gens[0] = gens[0].SuchThat(func(interface{}) bool { + return false + }) + gen = gopter.CombineGens(gens...) + raw, ok = gen.Sample() + if ok { + t.Errorf("Invalid combined gen: %#v", raw) + } +} + +func TestSuchThat(t *testing.T) { + var sieveArg string + sieve := func(v string) bool { + sieveArg = v + return true + } + gen := constGen("sample").SuchThat(sieve) + value, ok := gen.Sample() + if !ok || value != "sample" { + t.Errorf("Invalid result: %#v", value) + } + if sieveArg != "sample" { + t.Errorf("Invalid sieveArg: %#v", sieveArg) + } + + sieveArg = "" + var sieve2Arg string + sieve2 := func(v string) bool { + sieve2Arg = v + return false + } + gen = gen.SuchThat(sieve2) + _, ok = gen.Sample() + if ok { + t.Error("Did not expect a result") + } + if sieveArg != "sample" { + t.Errorf("Invalid sieveArg: %#v", sieveArg) + } + if sieve2Arg != "sample" { + t.Errorf("Invalid sieve2Arg: %#v", sieve2Arg) + } +} + +func TestGenSuchThatNoFunc(t *testing.T) { + defer expectPanic(t, "Param of SuchThat has to be a func, but is string") + constGen("sample").SuchThat("not a function") +} + +func TestGenSuchTooManyParams(t *testing.T) { + defer expectPanic(t, "Param of SuchThat has to be a func with one param, but is 2") + constGen("sample").SuchThat(func(a, b string) bool { + return false + }) +} + +func TestGenSuchThatToInvalidParamtype(t *testing.T) { + defer expectPanic(t, "Param of SuchThat has to be a func with one param assignable to string, but is int") + constGen("sample").SuchThat(func(a int) bool { + return false + }) +} + +func TestGenSuchToManyReturns(t *testing.T) { + defer expectPanic(t, "Param of SuchThat has to be a func with one return value, but is 2") + constGen("sample").SuchThat(func(a string) (string, bool) { + return "", false + }) +} + +func TestGenSuchToInvalidReturns(t *testing.T) { + defer expectPanic(t, "Param of SuchThat has to be a func with one return value of bool, but is string") + constGen("sample").SuchThat(func(a string) string { + return "" + }) +} + +func TestWithShrinker(t *testing.T) { + var shrinkerArg interface{} + shrinker := func(v interface{}) gopter.Shrink { + shrinkerArg = v + return gopter.NoShrink + } + gen := constGen("sample").WithShrinker(shrinker) + result := gen(gopter.DefaultGenParameters()) + value, ok := result.Retrieve() + if !ok { + t.Errorf("Invalid combined value: %#v", value) + } + result.Shrinker(value) + if shrinkerArg != "sample" { + t.Errorf("Invalid shrinkerArg: %#v", shrinkerArg) + } +} + +func expectPanic(t *testing.T, expected string) { + r := recover() + if r == nil { + t.Errorf("The code did not panic") + } else if r.(string) != expected { + t.Errorf("Panic does not match: '%#v' != '%#v'", r, expected) + } +} diff --git a/vendor/github.com/leanovate/gopter/locked_source.go b/vendor/github.com/leanovate/gopter/locked_source.go new file mode 100644 index 000000000..34f5241c5 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/locked_source.go @@ -0,0 +1,77 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// Taken from golang lockedSource implementation https://github.com/golang/go/blob/master/src/math/rand/rand.go#L371-L410 + +package gopter + +import ( + "math/rand" + "sync" +) + +type lockedSource struct { + lk sync.Mutex + src rand.Source64 +} + +// NewLockedSource takes a seed and returns a new +// lockedSource for use with rand.New +func NewLockedSource(seed int64) *lockedSource { + return &lockedSource{ + src: rand.NewSource(seed).(rand.Source64), + } +} + +func (r *lockedSource) Int63() (n int64) { + r.lk.Lock() + n = r.src.Int63() + r.lk.Unlock() + return +} + +func (r *lockedSource) Uint64() (n uint64) { + r.lk.Lock() + n = r.src.Uint64() + r.lk.Unlock() + return +} + +func (r *lockedSource) Seed(seed int64) { + r.lk.Lock() + r.src.Seed(seed) + r.lk.Unlock() +} + +// seedPos implements Seed for a lockedSource without a race condition. +func (r *lockedSource) seedPos(seed int64, readPos *int8) { + r.lk.Lock() + r.src.Seed(seed) + *readPos = 0 + r.lk.Unlock() +} + +// read implements Read for a lockedSource without a race condition. +func (r *lockedSource) read(p []byte, readVal *int64, readPos *int8) (n int, err error) { + r.lk.Lock() + n, err = read(p, r.src.Int63, readVal, readPos) + r.lk.Unlock() + return +} + +func read(p []byte, int63 func() int64, readVal *int64, readPos *int8) (n int, err error) { + pos := *readPos + val := *readVal + for n = 0; n < len(p); n++ { + if pos == 0 { + val = int63() + pos = 7 + } + p[n] = byte(val) + val >>= 8 + pos-- + } + *readPos = pos + *readVal = val + return +} diff --git a/vendor/github.com/leanovate/gopter/prop.go b/vendor/github.com/leanovate/gopter/prop.go new file mode 100644 index 000000000..64bf8a092 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/prop.go @@ -0,0 +1,111 @@ +package gopter + +import ( + "fmt" + "math" + "runtime/debug" +) + +// Prop represent some kind of property that (drums please) can and should be checked +type Prop func(*GenParameters) *PropResult + +// SaveProp creates s save property by handling all panics from an inner property +func SaveProp(prop Prop) Prop { + return func(genParams *GenParameters) (result *PropResult) { + defer func() { + if r := recover(); r != nil { + result = &PropResult{ + Status: PropError, + Error: fmt.Errorf("Check paniced: %v %s", r, debug.Stack()), + } + } + }() + + return prop(genParams) + } +} + +// Check the property using specific parameters +func (prop Prop) Check(parameters *TestParameters) *TestResult { + iterations := math.Ceil(float64(parameters.MinSuccessfulTests) / float64(parameters.Workers)) + sizeStep := float64(parameters.MaxSize-parameters.MinSize) / (iterations * float64(parameters.Workers)) + + genParameters := GenParameters{ + MinSize: parameters.MinSize, + MaxSize: parameters.MaxSize, + MaxShrinkCount: parameters.MaxShrinkCount, + Rng: parameters.Rng, + } + runner := &runner{ + parameters: parameters, + worker: func(workerIdx int, shouldStop shouldStop) *TestResult { + var n int + var d int + + isExhaused := func() bool { + return n+d > parameters.MinSuccessfulTests && + 1.0+float64(parameters.Workers*n)*parameters.MaxDiscardRatio < float64(d) + } + + for !shouldStop() && n < int(iterations) { + size := float64(parameters.MinSize) + (sizeStep * float64(workerIdx+(parameters.Workers*(n+d)))) + propResult := prop(genParameters.WithSize(int(size))) + + switch propResult.Status { + case PropUndecided: + d++ + if isExhaused() { + return &TestResult{ + Status: TestExhausted, + Succeeded: n, + Discarded: d, + } + } + case PropTrue: + n++ + case PropProof: + n++ + return &TestResult{ + Status: TestProved, + Succeeded: n, + Discarded: d, + Labels: propResult.Labels, + Args: propResult.Args, + } + case PropFalse: + return &TestResult{ + Status: TestFailed, + Succeeded: n, + Discarded: d, + Labels: propResult.Labels, + Args: propResult.Args, + } + case PropError: + return &TestResult{ + Status: TestError, + Succeeded: n, + Discarded: d, + Labels: propResult.Labels, + Error: propResult.Error, + Args: propResult.Args, + } + } + } + + if isExhaused() { + return &TestResult{ + Status: TestExhausted, + Succeeded: n, + Discarded: d, + } + } + return &TestResult{ + Status: TestPassed, + Succeeded: n, + Discarded: d, + } + }, + } + + return runner.runWorkers() +} diff --git a/vendor/github.com/leanovate/gopter/prop/check_condition_func.go b/vendor/github.com/leanovate/gopter/prop/check_condition_func.go new file mode 100644 index 000000000..6a189a9a8 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/prop/check_condition_func.go @@ -0,0 +1,40 @@ +package prop + +import ( + "errors" + "fmt" + "reflect" + + "github.com/leanovate/gopter" +) + +func checkConditionFunc(check interface{}, numArgs int) (func([]reflect.Value) *gopter.PropResult, error) { + checkVal := reflect.ValueOf(check) + checkType := checkVal.Type() + + if checkType.Kind() != reflect.Func { + return nil, fmt.Errorf("First param of ForrAll has to be a func: %v", checkVal.Kind()) + } + if checkType.NumIn() != numArgs { + return nil, fmt.Errorf("Number of parameters does not match number of generators: %d != %d", checkType.NumIn(), numArgs) + } + if checkType.NumOut() == 0 { + return nil, errors.New("At least one output parameters is required") + } else if checkType.NumOut() > 2 { + return nil, fmt.Errorf("No more than 2 output parameters are allowed: %d", checkType.NumOut()) + } else if checkType.NumOut() == 2 && !checkType.Out(1).Implements(typeOfError) { + return nil, fmt.Errorf("No 2 output has to be error: %v", checkType.Out(1).Kind()) + } else if checkType.NumOut() == 2 { + return func(values []reflect.Value) *gopter.PropResult { + results := checkVal.Call(values) + if results[1].IsNil() { + return convertResult(results[0].Interface(), nil) + } + return convertResult(results[0].Interface(), results[1].Interface().(error)) + }, nil + } + return func(values []reflect.Value) *gopter.PropResult { + results := checkVal.Call(values) + return convertResult(results[0].Interface(), nil) + }, nil +} diff --git a/vendor/github.com/leanovate/gopter/prop/check_condition_func_test.go b/vendor/github.com/leanovate/gopter/prop/check_condition_func_test.go new file mode 100644 index 000000000..c1b9d3c0c --- /dev/null +++ b/vendor/github.com/leanovate/gopter/prop/check_condition_func_test.go @@ -0,0 +1,60 @@ +package prop + +import ( + "reflect" + "testing" +) + +func TestCheckCondition(t *testing.T) { + call, err := checkConditionFunc(0, 0) + if err == nil || call != nil { + t.Error("Should not work for integers") + } + + call, err = checkConditionFunc(func(a, b int) bool { + return false + }, 1) + if err == nil || call != nil { + t.Error("Should not work with wrong number of arguments") + } + + call, err = checkConditionFunc(func(a, b int) { + }, 2) + if err == nil || call != nil { + t.Error("Should not work witout return") + } + + call, err = checkConditionFunc(func(a, b int) (int, int, int) { + return 0, 0, 0 + }, 2) + if err == nil || call != nil { + t.Error("Should not work with too many return") + } + + call, err = checkConditionFunc(func(a, b int) (int, int) { + return 0, 0 + }, 2) + if err == nil || call != nil { + t.Error("Should not work if second return is not an error") + } + + var calledA, calledB int + call, err = checkConditionFunc(func(a, b int) bool { + calledA = a + calledB = b + return true + }, 2) + if err != nil || call == nil { + t.Error("Should work") + } + result := call([]reflect.Value{ + reflect.ValueOf(123), + reflect.ValueOf(456), + }) + if calledA != 123 || calledB != 456 { + t.Errorf("Invalid parameters: %d, %d", calledA, calledB) + } + if !result.Success() { + t.Errorf("Invalid result: %#v", result) + } +} diff --git a/vendor/github.com/leanovate/gopter/prop/convert_result.go b/vendor/github.com/leanovate/gopter/prop/convert_result.go new file mode 100644 index 000000000..12e38cec5 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/prop/convert_result.go @@ -0,0 +1,37 @@ +package prop + +import ( + "fmt" + + "github.com/leanovate/gopter" +) + +func convertResult(result interface{}, err error) *gopter.PropResult { + if err != nil { + return &gopter.PropResult{ + Status: gopter.PropError, + Error: err, + } + } + switch result.(type) { + case bool: + if result.(bool) { + return &gopter.PropResult{Status: gopter.PropTrue} + } + return &gopter.PropResult{Status: gopter.PropFalse} + case string: + if result.(string) == "" { + return &gopter.PropResult{Status: gopter.PropTrue} + } + return &gopter.PropResult{ + Status: gopter.PropFalse, + Labels: []string{result.(string)}, + } + case *gopter.PropResult: + return result.(*gopter.PropResult) + } + return &gopter.PropResult{ + Status: gopter.PropError, + Error: fmt.Errorf("Invalid check result: %#v", result), + } +} diff --git a/vendor/github.com/leanovate/gopter/prop/convert_result_test.go b/vendor/github.com/leanovate/gopter/prop/convert_result_test.go new file mode 100644 index 000000000..acf0573be --- /dev/null +++ b/vendor/github.com/leanovate/gopter/prop/convert_result_test.go @@ -0,0 +1,51 @@ +package prop + +import ( + "errors" + "reflect" + "testing" + + "github.com/leanovate/gopter" +) + +func TestConvertResult(t *testing.T) { + trueResult := convertResult(true, nil) + if trueResult.Status != gopter.PropTrue || trueResult.Error != nil { + t.Errorf("Invalid true result: %#v", trueResult) + } + + falseResult := convertResult(false, nil) + if falseResult.Status != gopter.PropFalse || falseResult.Error != nil { + t.Errorf("Invalid false result: %#v", falseResult) + } + + stringTrueResult := convertResult("", nil) + if stringTrueResult.Status != gopter.PropTrue || + stringTrueResult.Error != nil { + t.Errorf("Invalid string true result: %#v", stringTrueResult) + } + + stringFalseResult := convertResult("Something is wrong", nil) + if stringFalseResult.Status != gopter.PropFalse || + stringFalseResult.Error != nil || + !reflect.DeepEqual(stringFalseResult.Labels, []string{"Something is wrong"}) { + t.Errorf("Invalid string false result: %#v", stringFalseResult) + } + + errorResult := convertResult("Anthing", errors.New("Booom")) + if errorResult.Status != gopter.PropError || errorResult.Error == nil || errorResult.Error.Error() != "Booom" { + t.Errorf("Invalid error result: %#v", errorResult) + } + + propResult := convertResult(&gopter.PropResult{ + Status: gopter.PropProof, + }, nil) + if propResult.Status != gopter.PropProof || propResult.Error != nil { + t.Errorf("Invalid prop result: %#v", propResult) + } + + invalidResult := convertResult(0, nil) + if invalidResult.Status != gopter.PropError || invalidResult.Error == nil { + t.Errorf("Invalid prop result: %#v", invalidResult) + } +} diff --git a/vendor/github.com/leanovate/gopter/prop/doc.go b/vendor/github.com/leanovate/gopter/prop/doc.go new file mode 100644 index 000000000..256ab2e7b --- /dev/null +++ b/vendor/github.com/leanovate/gopter/prop/doc.go @@ -0,0 +1,4 @@ +/* +Package prop contains the most common implementations of a gopter.Prop. +*/ +package prop diff --git a/vendor/github.com/leanovate/gopter/prop/error.go b/vendor/github.com/leanovate/gopter/prop/error.go new file mode 100644 index 000000000..0c91d5354 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/prop/error.go @@ -0,0 +1,14 @@ +package prop + +import "github.com/leanovate/gopter" + +// ErrorProp creates a property that will always fail with an error. +// Mostly used as a fallback when setup/initialization fails +func ErrorProp(err error) gopter.Prop { + return func(genParams *gopter.GenParameters) *gopter.PropResult { + return &gopter.PropResult{ + Status: gopter.PropError, + Error: err, + } + } +} diff --git a/vendor/github.com/leanovate/gopter/prop/error_test.go b/vendor/github.com/leanovate/gopter/prop/error_test.go new file mode 100644 index 000000000..3d8a8b696 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/prop/error_test.go @@ -0,0 +1,18 @@ +package prop_test + +import ( + "errors" + "testing" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/prop" +) + +func TestErrorProp(t *testing.T) { + p := prop.ErrorProp(errors.New("Booom")) + result := p(gopter.DefaultGenParameters()) + + if result.Status != gopter.PropError || result.Error == nil || result.Error.Error() != "Booom" { + t.Errorf("Invalid error prop result: %#v", result) + } +} diff --git a/vendor/github.com/leanovate/gopter/prop/example_invalidconcat_test.go b/vendor/github.com/leanovate/gopter/prop/example_invalidconcat_test.go new file mode 100644 index 000000000..6f6c4d60a --- /dev/null +++ b/vendor/github.com/leanovate/gopter/prop/example_invalidconcat_test.go @@ -0,0 +1,42 @@ +package prop_test + +import ( + "strings" + "unicode" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/gen" + "github.com/leanovate/gopter/prop" +) + +func MisimplementedConcat(a, b string) string { + if strings.IndexFunc(a, unicode.IsDigit) > 5 { + return b + } + return a + b +} + +// Example_invalidconcat demonstrates shrinking of string +// Kudos to @exarkun and @itamarst for finding this issue +func Example_invalidconcat() { + parameters := gopter.DefaultTestParametersWithSeed(1234) // Example should generate reproducable results, otherwise DefaultTestParameters() will suffice + + properties := gopter.NewProperties(parameters) + + properties.Property("length is sum of lengths", prop.ForAll( + func(a, b string) bool { + return MisimplementedConcat(a, b) == a+b + }, + gen.Identifier(), + gen.Identifier(), + )) + + // When using testing.T you might just use: properties.TestingRun(t) + properties.Run(gopter.ConsoleReporter(false)) + // Output: + // ! length is sum of lengths: Falsified after 17 passed tests. + // ARG_0: bahbxh6 + // ARG_0_ORIGINAL (2 shrinks): pkpbahbxh6 + // ARG_1: l + // ARG_1_ORIGINAL (1 shrinks): dl +} diff --git a/vendor/github.com/leanovate/gopter/prop/example_quadratic_test.go b/vendor/github.com/leanovate/gopter/prop/example_quadratic_test.go new file mode 100644 index 000000000..32eec6717 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/prop/example_quadratic_test.go @@ -0,0 +1,66 @@ +package prop_test + +import ( + "errors" + "math" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/gen" + "github.com/leanovate/gopter/prop" +) + +func solveQuadratic(a, b, c float64) (float64, float64, error) { + if a == 0 { + return 0, 0, errors.New("No solution") + } + v := b*b - 4*a*c + if v < 0 { + return 0, 0, errors.New("No solution") + } + v = math.Sqrt(v) + return (-b + v) / 2 / a, (-b - v) / 2 / a, nil +} + +func Example_quadratic() { + parameters := gopter.DefaultTestParametersWithSeed(1234) // Example should generate reproducable results, otherwise DefaultTestParameters() will suffice + + properties := gopter.NewProperties(parameters) + + properties.Property("solve quadratic", prop.ForAll( + func(a, b, c float64) bool { + x1, x2, err := solveQuadratic(a, b, c) + if err != nil { + return true + } + return math.Abs(a*x1*x1+b*x1+c) < 1e-5 && math.Abs(a*x2*x2+b*x2+c) < 1e-5 + }, + gen.Float64(), + gen.Float64(), + gen.Float64(), + )) + + properties.Property("solve quadratic with resonable ranges", prop.ForAll( + func(a, b, c float64) bool { + x1, x2, err := solveQuadratic(a, b, c) + if err != nil { + return true + } + return math.Abs(a*x1*x1+b*x1+c) < 1e-5 && math.Abs(a*x2*x2+b*x2+c) < 1e-5 + }, + gen.Float64Range(-1e8, 1e8), + gen.Float64Range(-1e8, 1e8), + gen.Float64Range(-1e8, 1e8), + )) + + // When using testing.T you might just use: properties.TestingRun(t) + properties.Run(gopter.ConsoleReporter(false)) + // Output: + // ! solve quadratic: Falsified after 0 passed tests. + // ARG_0: -1.4667384313385178e-05 + // ARG_0_ORIGINAL (187 shrinks): -1.0960555181801604e+51 + // ARG_1: 0 + // ARG_1_ORIGINAL (1 shrinks): -1.1203884793568249e+96 + // ARG_2: 6.481285637227244e+10 + // ARG_2_ORIGINAL (905 shrinks): 1.512647219322138e+281 + // + solve quadratic with resonable ranges: OK, passed 100 tests. +} diff --git a/vendor/github.com/leanovate/gopter/prop/example_shrink_test.go b/vendor/github.com/leanovate/gopter/prop/example_shrink_test.go new file mode 100644 index 000000000..9ef9d4a7a --- /dev/null +++ b/vendor/github.com/leanovate/gopter/prop/example_shrink_test.go @@ -0,0 +1,36 @@ +package prop_test + +import ( + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/gen" + "github.com/leanovate/gopter/prop" +) + +func Example_shrink() { + parameters := gopter.DefaultTestParametersWithSeed(1234) // Example should generate reproducable results, otherwise DefaultTestParameters() will suffice + + properties := gopter.NewProperties(parameters) + + properties.Property("fail above 100", prop.ForAll( + func(arg int64) bool { + return arg <= 100 + }, + gen.Int64(), + )) + + properties.Property("fail above 100 no shrink", prop.ForAllNoShrink( + func(arg int64) bool { + return arg <= 100 + }, + gen.Int64(), + )) + + // When using testing.T you might just use: properties.TestingRun(t) + properties.Run(gopter.ConsoleReporter(false)) + // Output: + // ! fail above 100: Falsified after 0 passed tests. + // ARG_0: 101 + // ARG_0_ORIGINAL (56 shrinks): 2041104533947223744 + // ! fail above 100 no shrink: Falsified after 0 passed tests. + // ARG_0: 6006156956070140861 +} diff --git a/vendor/github.com/leanovate/gopter/prop/example_time_test.go b/vendor/github.com/leanovate/gopter/prop/example_time_test.go new file mode 100644 index 000000000..c392f8611 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/prop/example_time_test.go @@ -0,0 +1,55 @@ +package prop_test + +import ( + "time" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/gen" + "github.com/leanovate/gopter/prop" +) + +func Example_timeGen() { + parameters := gopter.DefaultTestParametersWithSeed(1234) // Example should generate reproducable results, otherwise DefaultTestParameters() will suffice + time.Local = time.UTC // Just for this example to generate reproducable results + + properties := gopter.NewProperties(parameters) + + properties.Property("time in range format parsable", prop.ForAll( + func(actual time.Time) (bool, error) { + str := actual.Format(time.RFC3339Nano) + parsed, err := time.Parse(time.RFC3339Nano, str) + return actual.Equal(parsed), err + }, + gen.TimeRange(time.Now(), time.Duration(100*24*365)*time.Hour), + )) + + properties.Property("regular time format parsable", prop.ForAll( + func(actual time.Time) (bool, error) { + str := actual.Format(time.RFC3339Nano) + parsed, err := time.Parse(time.RFC3339Nano, str) + return actual.Equal(parsed), err + }, + gen.Time(), + )) + + properties.Property("any time format parsable", prop.ForAll( + func(actual time.Time) (bool, error) { + str := actual.Format(time.RFC3339Nano) + parsed, err := time.Parse(time.RFC3339Nano, str) + return actual.Equal(parsed), err + }, + gen.AnyTime(), + )) + + properties.Run(gopter.ConsoleReporter(false)) + // Output: + // + time in range format parsable: OK, passed 100 tests. + // + regular time format parsable: OK, passed 100 tests. + // ! any time format parsable: Error on property evaluation after 0 passed + // tests: parsing time "10000-01-01T00:00:00Z" as + // "2006-01-02T15:04:05.999999999Z07:00": cannot parse "0-01-01T00:00:00Z" + // as "-" + // ARG_0: 10000-01-01 00:00:00 +0000 UTC + // ARG_0_ORIGINAL (45 shrinks): 237903042092-02-10 19:15:18.148265469 +0000 + // UTC +} diff --git a/vendor/github.com/leanovate/gopter/prop/forall.go b/vendor/github.com/leanovate/gopter/prop/forall.go new file mode 100644 index 000000000..490228c96 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/prop/forall.go @@ -0,0 +1,123 @@ +package prop + +import ( + "reflect" + + "github.com/leanovate/gopter" +) + +var typeOfError = reflect.TypeOf((*error)(nil)).Elem() + +/* +ForAll creates a property that requires the check condition to be true for all values, if the +condition falsiies the generated values will be shrinked. + +"condition" has to be a function with the same number of parameters as the provided +generators "gens". The function may return a simple bool (true means that the +condition has passed), a string (empty string means that condition has passed), +a *PropResult, or one of former combined with an error. +*/ +func ForAll(condition interface{}, gens ...gopter.Gen) gopter.Prop { + callCheck, err := checkConditionFunc(condition, len(gens)) + if err != nil { + return ErrorProp(err) + } + + return gopter.SaveProp(func(genParams *gopter.GenParameters) *gopter.PropResult { + genResults := make([]*gopter.GenResult, len(gens)) + values := make([]reflect.Value, len(gens)) + var ok bool + for i, gen := range gens { + result := gen(genParams) + genResults[i] = result + values[i], ok = result.RetrieveAsValue() + if !ok { + return &gopter.PropResult{ + Status: gopter.PropUndecided, + } + } + } + result := callCheck(values) + if result.Success() { + for i, genResult := range genResults { + result = result.AddArgs(gopter.NewPropArg(genResult, 0, values[i].Interface(), values[i].Interface())) + } + } else { + for i, genResult := range genResults { + nextResult, nextValue := shrinkValue(genParams.MaxShrinkCount, genResult, values[i].Interface(), result, + func(v interface{}) *gopter.PropResult { + shrinkedOne := make([]reflect.Value, len(values)) + copy(shrinkedOne, values) + if v == nil { + shrinkedOne[i] = reflect.Zero(values[i].Type()) + } else { + shrinkedOne[i] = reflect.ValueOf(v) + } + return callCheck(shrinkedOne) + }) + result = nextResult + if nextValue == nil { + values[i] = reflect.Zero(values[i].Type()) + } else { + values[i] = reflect.ValueOf(nextValue) + } + } + } + return result + }) +} + +// ForAll1 legacy interface to be removed in the future +func ForAll1(gen gopter.Gen, check func(v interface{}) (interface{}, error)) gopter.Prop { + checkFunc := func(v interface{}) *gopter.PropResult { + return convertResult(check(v)) + } + return gopter.SaveProp(func(genParams *gopter.GenParameters) *gopter.PropResult { + genResult := gen(genParams) + value, ok := genResult.Retrieve() + if !ok { + return &gopter.PropResult{ + Status: gopter.PropUndecided, + } + } + result := checkFunc(value) + if result.Success() { + return result.AddArgs(gopter.NewPropArg(genResult, 0, value, value)) + } + + result, _ = shrinkValue(genParams.MaxShrinkCount, genResult, value, result, checkFunc) + return result + }) +} + +func shrinkValue(maxShrinkCount int, genResult *gopter.GenResult, origValue interface{}, + firstFail *gopter.PropResult, check func(interface{}) *gopter.PropResult) (*gopter.PropResult, interface{}) { + lastFail := firstFail + lastValue := origValue + + shrinks := 0 + shrink := genResult.Shrinker(lastValue).Filter(genResult.Sieve) + nextResult, nextValue := firstFailure(shrink, check) + for nextResult != nil && shrinks < maxShrinkCount { + shrinks++ + lastValue = nextValue + lastFail = nextResult + + shrink = genResult.Shrinker(lastValue).Filter(genResult.Sieve) + nextResult, nextValue = firstFailure(shrink, check) + } + + return lastFail.WithArgs(firstFail.Args).AddArgs(gopter.NewPropArg(genResult, shrinks, lastValue, origValue)), lastValue +} + +func firstFailure(shrink gopter.Shrink, check func(interface{}) *gopter.PropResult) (*gopter.PropResult, interface{}) { + value, ok := shrink() + for ok { + result := check(value) + if !result.Success() { + return result, value + } + value, ok = shrink() + } + return nil, nil +} diff --git a/vendor/github.com/leanovate/gopter/prop/forall_no_shrink.go b/vendor/github.com/leanovate/gopter/prop/forall_no_shrink.go new file mode 100644 index 000000000..aa0c5e59c --- /dev/null +++ b/vendor/github.com/leanovate/gopter/prop/forall_no_shrink.go @@ -0,0 +1,59 @@ +package prop + +import ( + "reflect" + + "github.com/leanovate/gopter" +) + +/* +ForAllNoShrink creates a property that requires the check condition to be true for all values. +As the name suggests the generated values will not be shrinked if the condition falsiies. + +"condition" has to be a function with the same number of parameters as the provided +generators "gens". The function may return a simple bool (true means that the +condition has passed), a string (empty string means that condition has passed), +a *PropResult, or one of former combined with an error. +*/ +func ForAllNoShrink(condition interface{}, gens ...gopter.Gen) gopter.Prop { + callCheck, err := checkConditionFunc(condition, len(gens)) + if err != nil { + return ErrorProp(err) + } + + return gopter.SaveProp(func(genParams *gopter.GenParameters) *gopter.PropResult { + genResults := make([]*gopter.GenResult, len(gens)) + values := make([]reflect.Value, len(gens)) + var ok bool + for i, gen := range gens { + result := gen(genParams) + genResults[i] = result + values[i], ok = result.RetrieveAsValue() + if !ok { + return &gopter.PropResult{ + Status: gopter.PropUndecided, + } + } + } + result := callCheck(values) + for i, genResult := range genResults { + result = result.AddArgs(gopter.NewPropArg(genResult, 0, values[i].Interface(), values[i].Interface())) + } + return result + }) +} + +// ForAllNoShrink1 creates a property that requires the check condition to be true for all values +// As the name suggests the generated values will not be shrinked if the condition falsiies +func ForAllNoShrink1(gen gopter.Gen, check func(interface{}) (interface{}, error)) gopter.Prop { + return gopter.SaveProp(func(genParams *gopter.GenParameters) *gopter.PropResult { + genResult := gen(genParams) + value, ok := genResult.Retrieve() + if !ok { + return &gopter.PropResult{ + Status: gopter.PropUndecided, + } + } + return convertResult(check(value)).AddArgs(gopter.NewPropArg(genResult, 0, value, value)) + }) +} diff --git a/vendor/github.com/leanovate/gopter/prop/forall_no_shrink_test.go b/vendor/github.com/leanovate/gopter/prop/forall_no_shrink_test.go new file mode 100644 index 000000000..62dc4731b --- /dev/null +++ b/vendor/github.com/leanovate/gopter/prop/forall_no_shrink_test.go @@ -0,0 +1,54 @@ +package prop_test + +import ( + "testing" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/gen" + "github.com/leanovate/gopter/prop" +) + +func TestForAllNoShrink(t *testing.T) { + parameters := gopter.DefaultTestParameters() + simpleForAll := prop.ForAllNoShrink1( + gen.Const("const value"), + func(value interface{}) (interface{}, error) { + return value.(string) == "const value", nil + }, + ) + + simpleResult := simpleForAll.Check(parameters) + + if simpleResult.Status != gopter.TestPassed || simpleResult.Succeeded != parameters.MinSuccessfulTests { + t.Errorf("Invalid simpleResult: %#v", simpleResult) + } + + simpleForAllFail := prop.ForAllNoShrink1( + gen.Const("const value"), + func(value interface{}) (interface{}, error) { + return value.(string) != "const value", nil + }, + ) + + simpleResultFail := simpleForAllFail.Check(parameters) + + if simpleResultFail.Status != gopter.TestFailed || simpleResultFail.Succeeded != 0 { + t.Errorf("Invalid simpleResultFail: %#v", simpleResultFail) + } + + fail := prop.ForAllNoShrink(0) + result := fail(gopter.DefaultGenParameters()) + if result.Status != gopter.PropError { + t.Errorf("Invalid result: %#v", result) + } + + undecided := prop.ForAllNoShrink(func(a int) bool { + return true + }, gen.Int().SuchThat(func(interface{}) bool { + return false + })) + result = undecided(gopter.DefaultGenParameters()) + if result.Status != gopter.PropUndecided { + t.Errorf("Invalid result: %#v", result) + } +} diff --git a/vendor/github.com/leanovate/gopter/prop/forall_test.go b/vendor/github.com/leanovate/gopter/prop/forall_test.go new file mode 100644 index 000000000..535c96716 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/prop/forall_test.go @@ -0,0 +1,63 @@ +package prop_test + +import ( + "math" + "testing" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/gen" + "github.com/leanovate/gopter/prop" +) + +func TestSqrt(t *testing.T) { + properties := gopter.NewProperties(nil) + + properties.Property("greater one of all greater one", prop.ForAll( + func(v float64) bool { + return math.Sqrt(v) >= 1 + }, + gen.Float64Range(1, math.MaxFloat64), + )) + + properties.Property("greater one of all greater one alternative", prop.ForAll1( + gen.Float64Range(1, math.MaxFloat64), + func(v interface{}) (interface{}, error) { + return math.Sqrt(v.(float64)) >= 1, nil + }, + )) + + properties.Property("squared is equal to value", prop.ForAll( + func(v float64) bool { + r := math.Sqrt(v) + return math.Abs(r*r-v) < 1e-10*v + }, + gen.Float64Range(0, math.MaxFloat64), + )) + + properties.Property("squared is equal to value alternative", prop.ForAll1( + gen.Float64Range(0, math.MaxFloat64), + func(v interface{}) (interface{}, error) { + s := v.(float64) + r := math.Sqrt(s) + return math.Abs(r*r-s) < 1e-10*s, nil + }, + )) + + properties.TestingRun(t) + + fail := prop.ForAll(0) + result := fail(gopter.DefaultGenParameters()) + if result.Status != gopter.PropError { + t.Errorf("Invalid result: %#v", result) + } + + undecided := prop.ForAll(func(a int) bool { + return true + }, gen.Int().SuchThat(func(interface{}) bool { + return false + })) + result = undecided(gopter.DefaultGenParameters()) + if result.Status != gopter.PropUndecided { + t.Errorf("Invalid result: %#v", result) + } +} diff --git a/vendor/github.com/leanovate/gopter/prop_arg.go b/vendor/github.com/leanovate/gopter/prop_arg.go new file mode 100644 index 000000000..4b0e86637 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/prop_arg.go @@ -0,0 +1,32 @@ +package gopter + +import ( + "fmt" + "strings" +) + +// PropArg contains information about the specific values for a certain property check. +// This is mostly used for reporting when a property has falsified. +type PropArg struct { + Arg interface{} + OrigArg interface{} + Label string + Shrinks int +} + +func (p *PropArg) String() string { + return fmt.Sprintf("%v", p.Arg) +} + +// PropArgs is a list of PropArg. +type PropArgs []*PropArg + +// NewPropArg creates a new PropArg. +func NewPropArg(genResult *GenResult, shrinks int, value, origValue interface{}) *PropArg { + return &PropArg{ + Label: strings.Join(genResult.Labels, ", "), + Arg: value, + OrigArg: origValue, + Shrinks: shrinks, + } +} diff --git a/vendor/github.com/leanovate/gopter/prop_arg_test.go b/vendor/github.com/leanovate/gopter/prop_arg_test.go new file mode 100644 index 000000000..887368ea7 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/prop_arg_test.go @@ -0,0 +1,19 @@ +package gopter_test + +import ( + "testing" + + "github.com/leanovate/gopter" +) + +func TestPropArg(t *testing.T) { + gen := constGen("nothing").WithLabel("Label1").WithLabel("Label2") + prop := gopter.NewPropArg(gen(gopter.DefaultGenParameters()), 1, "nothing", "noth") + + if prop.Label != "Label1, Label2" { + t.Errorf("Invalid prop.Label: %#v", prop.Label) + } + if prop.String() != "nothing" { + t.Errorf("Invalid prop.Stirng(): %#v", prop.String()) + } +} diff --git a/vendor/github.com/leanovate/gopter/prop_result.go b/vendor/github.com/leanovate/gopter/prop_result.go new file mode 100644 index 000000000..b230698f7 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/prop_result.go @@ -0,0 +1,108 @@ +package gopter + +type propStatus int + +const ( + // PropProof THe property was proved (i.e. it is known to be correct and will be always true) + PropProof propStatus = iota + // PropTrue The property was true this time + PropTrue + // PropFalse The property was false this time + PropFalse + // PropUndecided The property has no clear outcome this time + PropUndecided + // PropError The property has generated an error + PropError +) + +func (s propStatus) String() string { + switch s { + case PropProof: + return "PROOF" + case PropTrue: + return "TRUE" + case PropFalse: + return "FALSE" + case PropUndecided: + return "UNDECIDED" + case PropError: + return "ERROR" + } + return "" +} + +// PropResult contains the result of a property +type PropResult struct { + Status propStatus + Error error + Args []*PropArg + Labels []string +} + +// NewPropResult create a PropResult with label +func NewPropResult(success bool, label string) *PropResult { + if success { + return &PropResult{ + Status: PropTrue, + Labels: []string{label}, + Args: make([]*PropArg, 0), + } + } + return &PropResult{ + Status: PropFalse, + Labels: []string{label}, + Args: make([]*PropArg, 0), + } +} + +// Success checks if the result was successful +func (r *PropResult) Success() bool { + return r.Status == PropTrue || r.Status == PropProof +} + +// WithArgs sets argument descriptors to the PropResult for reporting +func (r *PropResult) WithArgs(args []*PropArg) *PropResult { + r.Args = args + return r +} + +// AddArgs add argument descriptors to the PropResult for reporting +func (r *PropResult) AddArgs(args ...*PropArg) *PropResult { + r.Args = append(r.Args, args...) + return r +} + +// And combines two PropResult by an and operation. +// The resulting PropResult will be only true if both PropResults are true. +func (r *PropResult) And(other *PropResult) *PropResult { + switch { + case r.Status == PropError: + return r + case other.Status == PropError: + return other + case r.Status == PropFalse: + return r + case other.Status == PropFalse: + return other + case r.Status == PropUndecided: + return r + case other.Status == PropUndecided: + return other + case r.Status == PropProof: + return r.mergeWith(other, other.Status) + case other.Status == PropProof: + return r.mergeWith(other, r.Status) + case r.Status == PropTrue && other.Status == PropTrue: + return r.mergeWith(other, PropTrue) + default: + return r + } +} + +func (r *PropResult) mergeWith(other *PropResult, status propStatus) *PropResult { + return &PropResult{ + Status: status, + Args: append(append(make([]*PropArg, 0, len(r.Args)+len(other.Args)), r.Args...), other.Args...), + Labels: append(append(make([]string, 0, len(r.Labels)+len(other.Labels)), r.Labels...), other.Labels...), + } +} diff --git a/vendor/github.com/leanovate/gopter/prop_result_test.go b/vendor/github.com/leanovate/gopter/prop_result_test.go new file mode 100644 index 000000000..9e3d2af4a --- /dev/null +++ b/vendor/github.com/leanovate/gopter/prop_result_test.go @@ -0,0 +1,76 @@ +package gopter_test + +import ( + "testing" + + "github.com/leanovate/gopter" +) + +func TestPropResult(t *testing.T) { + result := &gopter.PropResult{Status: gopter.PropProof} + if !result.Success() || result.Status.String() != "PROOF" { + t.Errorf("Invalid status: %#v", result) + } + other := &gopter.PropResult{Status: gopter.PropTrue} + if !result.And(other).Success() || result.And(other).Status.String() != "TRUE" { + t.Errorf("Invalid combined state: %#v", result.And(other)) + } + if !other.And(result).Success() || other.And(result).Status.String() != "TRUE" { + t.Errorf("Invalid combined state: %#v", other.And(result)) + } + + result = &gopter.PropResult{Status: gopter.PropTrue} + if !result.Success() || result.Status.String() != "TRUE" { + t.Errorf("Invalid status: %#v", result) + } + if !result.And(other).Success() || result.And(other).Status.String() != "TRUE" { + t.Errorf("Invalid combined state: %#v", result.And(other)) + } + if !other.And(result).Success() || other.And(result).Status.String() != "TRUE" { + t.Errorf("Invalid combined state: %#v", other.And(result)) + } + + result = &gopter.PropResult{Status: gopter.PropFalse} + if result.Success() || result.Status.String() != "FALSE" { + t.Errorf("Invalid status: %#v", result) + } + if result.And(other) != result { + t.Errorf("Invalid combined state: %#v", result.And(other)) + } + if other.And(result) != result { + t.Errorf("Invalid combined state: %#v", other.And(result)) + } + + result = &gopter.PropResult{Status: gopter.PropUndecided} + if result.Success() || result.Status.String() != "UNDECIDED" { + t.Errorf("Invalid status: %#v", result) + } + if result.And(other) != result { + t.Errorf("Invalid combined state: %#v", result.And(other)) + } + if other.And(result) != result { + t.Errorf("Invalid combined state: %#v", other.And(result)) + } + + result = &gopter.PropResult{Status: gopter.PropError} + if result.Success() || result.Status.String() != "ERROR" { + t.Errorf("Invalid status: %#v", result) + } + if result.And(other) != result { + t.Errorf("Invalid combined state: %#v", result.And(other)) + } + if other.And(result) != result { + t.Errorf("Invalid combined state: %#v", other.And(result)) + } +} + +func TestNewPropResult(t *testing.T) { + trueResult := gopter.NewPropResult(true, "label") + if trueResult.Status != gopter.PropTrue || trueResult.Labels[0] != "label" { + t.Errorf("Invalid trueResult: %#v", trueResult) + } + falseResult := gopter.NewPropResult(false, "label") + if falseResult.Status != gopter.PropFalse || falseResult.Labels[0] != "label" { + t.Errorf("Invalid falseResult: %#v", falseResult) + } +} diff --git a/vendor/github.com/leanovate/gopter/prop_test.go b/vendor/github.com/leanovate/gopter/prop_test.go new file mode 100644 index 000000000..1ce151ff5 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/prop_test.go @@ -0,0 +1,175 @@ +package gopter + +import ( + "strings" + "sync/atomic" + "testing" +) + +func TestSaveProp(t *testing.T) { + prop := SaveProp(func(*GenParameters) *PropResult { + panic("Ouchy") + }) + + parameters := DefaultTestParameters() + result := prop.Check(parameters) + + if result.Status != TestError || result.Error == nil || + !strings.HasPrefix(result.Error.Error(), "Check paniced: Ouchy") { + t.Errorf("Invalid result: %#v", result) + } +} + +func TestPropUndecided(t *testing.T) { + var called int64 + prop := Prop(func(genParams *GenParameters) *PropResult { + atomic.AddInt64(&called, 1) + + return &PropResult{ + Status: PropUndecided, + } + }) + + parameters := DefaultTestParameters() + result := prop.Check(parameters) + + if result.Status != TestExhausted || result.Succeeded != 0 { + t.Errorf("Invalid result: %#v", result) + } + if called != int64(parameters.MinSuccessfulTests)+1 { + t.Errorf("Invalid number of calls: %d", called) + } +} + +func TestPropMaxDiscardRatio(t *testing.T) { + var called int64 + prop := Prop(func(genParams *GenParameters) *PropResult { + atomic.AddInt64(&called, 1) + + if genParams.MaxSize > 21 { + return &PropResult{ + Status: PropTrue, + } + } + return &PropResult{ + Status: PropUndecided, + } + }) + + parameters := DefaultTestParameters() + parameters.MaxDiscardRatio = 0.2 + result := prop.Check(parameters) + + if result.Status != TestExhausted || result.Succeeded != 100 { + t.Errorf("Invalid result: %#v", result) + } + if called != int64(parameters.MinSuccessfulTests)+22 { + t.Errorf("Invalid number of calls: %d", called) + } +} + +func TestPropPassed(t *testing.T) { + var called int64 + prop := Prop(func(genParams *GenParameters) *PropResult { + atomic.AddInt64(&called, 1) + + return &PropResult{ + Status: PropTrue, + } + }) + + parameters := DefaultTestParameters() + result := prop.Check(parameters) + + if result.Status != TestPassed || result.Succeeded != parameters.MinSuccessfulTests { + t.Errorf("Invalid result: %#v", result) + } + if called != int64(parameters.MinSuccessfulTests) { + t.Errorf("Invalid number of calls: %d", called) + } +} + +func TestPropProof(t *testing.T) { + var called int64 + prop := Prop(func(genParams *GenParameters) *PropResult { + atomic.AddInt64(&called, 1) + + return &PropResult{ + Status: PropProof, + } + }) + + parameters := DefaultTestParameters() + result := prop.Check(parameters) + + if result.Status != TestProved || result.Succeeded != 1 { + t.Errorf("Invalid result: %#v", result) + } + if called != 1 { + t.Errorf("Invalid number of calls: %d", called) + } +} + +func TestPropFalse(t *testing.T) { + var called int64 + prop := Prop(func(genParams *GenParameters) *PropResult { + atomic.AddInt64(&called, 1) + + return &PropResult{ + Status: PropFalse, + } + }) + + parameters := DefaultTestParameters() + result := prop.Check(parameters) + + if result.Status != TestFailed || result.Succeeded != 0 { + t.Errorf("Invalid result: %#v", result) + } + if called != 1 { + t.Errorf("Invalid number of calls: %d", called) + } +} + +func TestPropError(t *testing.T) { + var called int64 + prop := Prop(func(genParams *GenParameters) *PropResult { + atomic.AddInt64(&called, 1) + + return &PropResult{ + Status: PropError, + } + }) + + parameters := DefaultTestParameters() + result := prop.Check(parameters) + + if result.Status != TestError || result.Succeeded != 0 { + t.Errorf("Invalid result: %#v", result) + } + if called != 1 { + t.Errorf("Invalid number of calls: %d", called) + } +} + +func TestPropPassedMulti(t *testing.T) { + var called int64 + prop := Prop(func(genParams *GenParameters) *PropResult { + atomic.AddInt64(&called, 1) + + return &PropResult{ + Status: PropTrue, + } + }) + + parameters := DefaultTestParameters() + parameters.Workers = 10 + result := prop.Check(parameters) + + if result.Status != TestPassed || result.Succeeded != parameters.MinSuccessfulTests { + t.Errorf("Invalid result: %#v", result) + } + if called != int64(parameters.MinSuccessfulTests) { + t.Errorf("Invalid number of calls: %d", called) + } +} diff --git a/vendor/github.com/leanovate/gopter/properties.go b/vendor/github.com/leanovate/gopter/properties.go new file mode 100644 index 000000000..9d40d5704 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/properties.go @@ -0,0 +1,59 @@ +package gopter + +import "testing" + +// Properties is a collection of properties that should be checked in a test +type Properties struct { + parameters *TestParameters + props map[string]Prop + propNames []string +} + +// NewProperties create new Properties with given test parameters. +// If parameters is nil default test parameters will be used +func NewProperties(parameters *TestParameters) *Properties { + if parameters == nil { + parameters = DefaultTestParameters() + } + return &Properties{ + parameters: parameters, + props: make(map[string]Prop, 0), + propNames: make([]string, 0), + } +} + +// Property add/defines a property in a test. +func (p *Properties) Property(name string, prop Prop) { + p.propNames = append(p.propNames, name) + p.props[name] = prop +} + +// Run checks all definied propertiesand reports the result +func (p *Properties) Run(reporter Reporter) bool { + success := true + for _, propName := range p.propNames { + prop := p.props[propName] + + result := prop.Check(p.parameters) + + reporter.ReportTestResult(propName, result) + if !result.Passed() { + success = false + } + } + return success +} + +// TestingRun checks all definied properties with a testing.T context. +// This the preferred wait to run property tests as part of a go unit test. +func (p *Properties) TestingRun(t *testing.T, opts ...interface{}) { + reporter := ConsoleReporter(true) + for _, opt := range opts { + if r, ok := opt.(Reporter); ok { + reporter = r + } + } + if !p.Run(reporter) { + t.Errorf("failed with initial seed: %d", p.parameters.Seed) + } +} diff --git a/vendor/github.com/leanovate/gopter/properties_test.go b/vendor/github.com/leanovate/gopter/properties_test.go new file mode 100644 index 000000000..6d44d7ce1 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/properties_test.go @@ -0,0 +1,47 @@ +package gopter_test + +import ( + "os" + "testing" + + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/gen" + "github.com/leanovate/gopter/prop" +) + +func TestProperties(t *testing.T) { + parameters := gopter.DefaultTestParameters() + + properties := gopter.NewProperties(parameters) + + properties.Property("always fail", prop.ForAll( + func(v int32) bool { + return false + }, + gen.Int32(), + )) + + fakeT := &testing.T{} + properties.TestingRun(fakeT) + if !fakeT.Failed() { + t.Errorf("fakeT has not failed") + } +} + +func TestPropertiesCustomReporter(t *testing.T) { + parameters := gopter.DefaultTestParameters() + properties := gopter.NewProperties(parameters) + + properties.Property("always fail", prop.ForAll( + func(v int32) bool { + return false + }, + gen.Int32(), + )) + + fakeT := &testing.T{} + properties.TestingRun(fakeT, gopter.NewFormatedReporter(true, 160, os.Stdout)) + if !fakeT.Failed() { + t.Errorf("fakeT has not failed") + } +} diff --git a/vendor/github.com/leanovate/gopter/reporter.go b/vendor/github.com/leanovate/gopter/reporter.go new file mode 100644 index 000000000..40655aa28 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/reporter.go @@ -0,0 +1,7 @@ +package gopter + +// Reporter is a simple interface to report/format the results of a property check. +type Reporter interface { + // ReportTestResult reports a single property result + ReportTestResult(propName string, result *TestResult) +} diff --git a/vendor/github.com/leanovate/gopter/runner.go b/vendor/github.com/leanovate/gopter/runner.go new file mode 100644 index 000000000..2464f89d0 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/runner.go @@ -0,0 +1,77 @@ +package gopter + +import ( + "sync" + "time" +) + +type shouldStop func() bool + +type worker func(int, shouldStop) *TestResult + +type runner struct { + sync.RWMutex + parameters *TestParameters + worker worker +} + +func (r *runner) mergeCheckResults(r1, r2 *TestResult) *TestResult { + var result TestResult + + switch { + case r1 == nil: + return r2 + case r1.Status != TestPassed && r1.Status != TestExhausted: + result = *r1 + case r2.Status != TestPassed && r2.Status != TestExhausted: + result = *r2 + default: + result.Status = TestExhausted + + if r1.Succeeded+r2.Succeeded >= r.parameters.MinSuccessfulTests && + float64(r1.Discarded+r2.Discarded) <= float64(r1.Succeeded+r2.Succeeded)*r.parameters.MaxDiscardRatio { + result.Status = TestPassed + } + } + + result.Succeeded = r1.Succeeded + r2.Succeeded + result.Discarded = r1.Discarded + r2.Discarded + + return &result +} + +func (r *runner) runWorkers() *TestResult { + var stopFlag Flag + defer stopFlag.Set() + + start := time.Now() + if r.parameters.Workers < 2 { + result := r.worker(0, stopFlag.Get) + result.Time = time.Since(start) + return result + } + var waitGroup sync.WaitGroup + waitGroup.Add(r.parameters.Workers) + results := make(chan *TestResult, r.parameters.Workers) + combinedResult := make(chan *TestResult) + + go func() { + var combined *TestResult + for result := range results { + combined = r.mergeCheckResults(combined, result) + } + combinedResult <- combined + }() + for i := 0; i < r.parameters.Workers; i++ { + go func(workerIdx int) { + defer waitGroup.Done() + results <- r.worker(workerIdx, stopFlag.Get) + }(i) + } + waitGroup.Wait() + close(results) + + result := <-combinedResult + result.Time = time.Since(start) + return result +} diff --git a/vendor/github.com/leanovate/gopter/runner_test.go b/vendor/github.com/leanovate/gopter/runner_test.go new file mode 100644 index 000000000..edbb91c53 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/runner_test.go @@ -0,0 +1,189 @@ +package gopter + +import ( + "errors" + "reflect" + "testing" + "time" +) + +func TestRunnerSingleWorker(t *testing.T) { + parameters := DefaultTestParameters() + testRunner := &runner{ + parameters: parameters, + worker: func(num int, shouldStop shouldStop) *TestResult { + return &TestResult{ + Status: TestPassed, + Succeeded: 1, + Discarded: 0, + } + }, + } + + result := testRunner.runWorkers() + + if result.Status != TestPassed || + result.Succeeded != 1 || + result.Discarded != 0 { + t.Errorf("Invalid result: %#v", result) + } +} + +func TestRunnerParallelWorkers(t *testing.T) { + parameters := DefaultTestParameters() + specs := []struct { + workers int + res []TestResult + exp *TestResult + wait []int + }{ + // Test all pass + { + workers: 50, + res: []TestResult{ + { + Status: TestPassed, + Succeeded: 10, + Discarded: 1, + }, + }, + exp: &TestResult{ + Status: TestPassed, + Succeeded: 500, + Discarded: 50, + }, + }, + // Test exhausted + { + workers: 50, + res: []TestResult{ + { + Status: TestExhausted, + Succeeded: 1, + Discarded: 10, + }, + }, + exp: &TestResult{ + Status: TestExhausted, + Succeeded: 50, + Discarded: 500, + }, + }, + // Test all fail + { + workers: 50, + res: []TestResult{ + { + Status: TestFailed, + Succeeded: 0, + Discarded: 0, + Labels: []string{"some label"}, + Error: errors.New("invalid result 0 != 1"), + }, + }, + exp: &TestResult{ + Status: TestFailed, + Succeeded: 0, + Discarded: 0, + Labels: []string{"some label"}, + Error: errors.New("invalid result 0 != 1"), + }, + }, + // a pass and failure + { + workers: 2, + res: []TestResult{ + { + Status: TestPassed, + Succeeded: 94, + Discarded: 1, + }, + { + Status: TestFailed, + Succeeded: 4, + Discarded: 3, + Labels: []string{"some label"}, + Error: errors.New("invalid result 0 != 2"), + }, + }, + exp: &TestResult{ + Status: TestFailed, + Succeeded: 98, + Discarded: 4, + Labels: []string{"some label"}, + Error: errors.New("invalid result 0 != 2"), + }, + wait: []int{1, 0}, + }, + // a pass and multiple failures (first failure returned) + { + workers: 3, + res: []TestResult{ + { + Status: TestPassed, + Succeeded: 94, + Discarded: 1, + }, + { + Status: TestFailed, + Succeeded: 3, + Discarded: 2, + Labels: []string{"worker 1"}, + Error: errors.New("worker 1 error"), + }, + { + Status: TestFailed, + Succeeded: 1, + Discarded: 1, + Labels: []string{"worker 2"}, + Error: errors.New("worker 2 error"), + }, + }, + exp: &TestResult{ + Status: TestFailed, + Succeeded: 98, + Discarded: 4, + Labels: []string{"worker 1"}, + Error: errors.New("worker 1 error"), + }, + wait: []int{0, 1, 2}, + }, + } + + for specIdx, spec := range specs { + parameters.Workers = spec.workers + + testRunner := &runner{ + parameters: parameters, + worker: func(num int, shouldStop shouldStop) *TestResult { + if num < len(spec.wait) { + time.Sleep(time.Duration(spec.wait[num]) * time.Second) + } + + if num < len(spec.res) { + return &spec.res[num] + } + + return &spec.res[0] + }, + } + + result := testRunner.runWorkers() + + if result.Time <= 0 { + t.Errorf("[%d] expected result time to be positive number but got %s", specIdx, result.Time) + } + + // This is not deterministic and + // have validated above the time + result.Time = 0 + + if !reflect.DeepEqual(result, spec.exp) { + t.Errorf("[%d] expected test result %#v but got %#v", + specIdx, + spec.exp, + result, + ) + } + } +} diff --git a/vendor/github.com/leanovate/gopter/shrink.go b/vendor/github.com/leanovate/gopter/shrink.go new file mode 100644 index 000000000..86d07d458 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/shrink.go @@ -0,0 +1,185 @@ +package gopter + +import ( + "fmt" + "reflect" +) + +// Shrink is a stream of shrinked down values. +// Once the result of a shrink is false, it is considered to be exhausted. +// Important notes for implementors: +// * Ensure that the returned stream is finite, even though shrinking will +// eventually be aborted, infinite streams may result in very slow running +// test. +// * Ensure that modifications to the returned value will not affect the +// internal state of your Shrink. If in doubt return by value not by reference +type Shrink func() (interface{}, bool) + +// Filter creates a shrink filtered by a condition +func (s Shrink) Filter(condition func(interface{}) bool) Shrink { + if condition == nil { + return s + } + return func() (interface{}, bool) { + value, ok := s() + for ok && !condition(value) { + value, ok = s() + } + return value, ok + } +} + +// Map creates a shrink by applying a converter to each element of a shrink. +// f: has to be a function with one parameter (matching the generated value) and a single return. +func (s Shrink) Map(f interface{}) Shrink { + mapperVal := reflect.ValueOf(f) + mapperType := mapperVal.Type() + + if mapperVal.Kind() != reflect.Func { + panic(fmt.Sprintf("Param of Map has to be a func, but is %v", mapperType.Kind())) + } + if mapperType.NumIn() != 1 { + panic(fmt.Sprintf("Param of Map has to be a func with one param, but is %v", mapperType.NumIn())) + } + if mapperType.NumOut() != 1 { + panic(fmt.Sprintf("Param of Map has to be a func with one return value, but is %v", mapperType.NumOut())) + } + + return func() (interface{}, bool) { + value, ok := s() + if ok { + return mapperVal.Call([]reflect.Value{reflect.ValueOf(value)})[0].Interface(), ok + } + return nil, false + } +} + +// All collects all shrinks as a slice. Use with care as this might create +// large results depending on the complexity of the shrink +func (s Shrink) All() []interface{} { + result := []interface{}{} + value, ok := s() + for ok { + result = append(result, value) + value, ok = s() + } + return result +} + +type concatedShrink struct { + index int + shrinks []Shrink +} + +func (c *concatedShrink) Next() (interface{}, bool) { + for c.index < len(c.shrinks) { + value, ok := c.shrinks[c.index]() + if ok { + return value, ok + } + c.index++ + } + return nil, false +} + +// ConcatShrinks concats an array of shrinks to a single shrinks +func ConcatShrinks(shrinks ...Shrink) Shrink { + concated := &concatedShrink{ + index: 0, + shrinks: shrinks, + } + return concated.Next +} + +type interleaved struct { + first Shrink + second Shrink + firstExhausted bool + secondExhaused bool + state bool +} + +func (i *interleaved) Next() (interface{}, bool) { + for !i.firstExhausted || !i.secondExhaused { + i.state = !i.state + if i.state && !i.firstExhausted { + value, ok := i.first() + if ok { + return value, true + } + i.firstExhausted = true + } else if !i.state && !i.secondExhaused { + value, ok := i.second() + if ok { + return value, true + } + i.secondExhaused = true + } + } + return nil, false +} + +// Interleave this shrink with another +// Both shrinks are expected to produce the same result +func (s Shrink) Interleave(other Shrink) Shrink { + interleaved := &interleaved{ + first: s, + second: other, + } + return interleaved.Next +} + +// Shrinker creates a shrink for a given value +type Shrinker func(value interface{}) Shrink + +type elementShrink struct { + original []interface{} + index int + elementShrink Shrink +} + +func (e *elementShrink) Next() (interface{}, bool) { + element, ok := e.elementShrink() + if !ok { + return nil, false + } + shrinked := make([]interface{}, len(e.original)) + copy(shrinked, e.original) + shrinked[e.index] = element + + return shrinked, true +} + +// CombineShrinker create a shrinker by combining a list of shrinkers. +// The resulting shrinker will shrink an []interface{} where each element will be shrinked by +// the corresonding shrinker in 'shrinkers'. +// This method is implicitly used by CombineGens. +func CombineShrinker(shrinkers ...Shrinker) Shrinker { + return func(v interface{}) Shrink { + values := v.([]interface{}) + shrinks := make([]Shrink, 0, len(values)) + for i, shrinker := range shrinkers { + if i >= len(values) { + break + } + shrink := &elementShrink{ + original: values, + index: i, + elementShrink: shrinker(values[i]), + } + shrinks = append(shrinks, shrink.Next) + } + return ConcatShrinks(shrinks...) + } +} + +// NoShrink is an empty shrink. +var NoShrink = Shrink(func() (interface{}, bool) { + return nil, false +}) + +// NoShrinker is a shrinker for NoShrink, i.e. a Shrinker that will not shrink any values. +// This is the default Shrinker if none is provided. +var NoShrinker = Shrinker(func(value interface{}) Shrink { + return NoShrink +}) diff --git a/vendor/github.com/leanovate/gopter/shrink_test.go b/vendor/github.com/leanovate/gopter/shrink_test.go new file mode 100644 index 000000000..10af0d7c2 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/shrink_test.go @@ -0,0 +1,155 @@ +package gopter_test + +import ( + "reflect" + "testing" + + "github.com/leanovate/gopter" +) + +type counterShrink struct { + n int +} + +func (c *counterShrink) Next() (interface{}, bool) { + if c.n > 0 { + v := c.n + c.n-- + return v, true + } + return 0, false +} + +func TestShinkAll(t *testing.T) { + counter := &counterShrink{n: 10} + shrink := gopter.Shrink(counter.Next) + + all := shrink.All() + if !reflect.DeepEqual(all, []interface{}{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}) { + t.Errorf("Invalid all: %#v", all) + } +} + +func TestShrinkFilter(t *testing.T) { + counter := &counterShrink{n: 20} + shrink := gopter.Shrink(counter.Next) + + all := shrink.Filter(func(v interface{}) bool { + return v.(int)%2 == 0 + }).All() + if !reflect.DeepEqual(all, []interface{}{20, 18, 16, 14, 12, 10, 8, 6, 4, 2}) { + t.Errorf("Invalid all: %#v", all) + } + + counter = &counterShrink{n: 5} + shrink = gopter.Shrink(counter.Next) + + all = shrink.Filter(nil).All() + if !reflect.DeepEqual(all, []interface{}{5, 4, 3, 2, 1}) { + t.Errorf("Invalid all: %#v", all) + } +} + +func TestShrinkConcat(t *testing.T) { + counterShrink1 := &counterShrink{n: 5} + counterShrink2 := &counterShrink{n: 4} + shrink1 := gopter.Shrink(counterShrink1.Next) + shrink2 := gopter.Shrink(counterShrink2.Next) + + all := gopter.ConcatShrinks(shrink1, shrink2).All() + if !reflect.DeepEqual(all, []interface{}{5, 4, 3, 2, 1, 4, 3, 2, 1}) { + t.Errorf("Invalid all: %#v", all) + } +} + +func TestShrinkInterleave(t *testing.T) { + counterShrink1 := &counterShrink{n: 5} + counterShrink2 := &counterShrink{n: 7} + + shrink1 := gopter.Shrink(counterShrink1.Next) + shrink2 := gopter.Shrink(counterShrink2.Next) + + all := shrink1.Interleave(shrink2).All() + if !reflect.DeepEqual(all, []interface{}{5, 7, 4, 6, 3, 5, 2, 4, 1, 3, 2, 1}) { + t.Errorf("Invalid all: %#v", all) + } +} + +func TestCombineShrinker(t *testing.T) { + var shrinker1Arg, shrinker2Arg interface{} + shrinker1 := func(v interface{}) gopter.Shrink { + shrinker1Arg = v + shrink := &counterShrink{n: 5} + return shrink.Next + } + shrinker2 := func(v interface{}) gopter.Shrink { + shrinker2Arg = v + shrink := &counterShrink{n: 3} + return shrink.Next + } + shrinker := gopter.CombineShrinker(shrinker1, shrinker2) + all := shrinker([]interface{}{123, 456}).All() + if shrinker1Arg != 123 { + t.Errorf("Invalid shrinker1Arg: %#v", shrinker1Arg) + } + if shrinker2Arg != 456 { + t.Errorf("Invalid shrinker1Arg: %#v", shrinker1Arg) + } + if !reflect.DeepEqual(all, []interface{}{ + []interface{}{5, 456}, + []interface{}{4, 456}, + []interface{}{3, 456}, + []interface{}{2, 456}, + []interface{}{1, 456}, + []interface{}{123, 3}, + []interface{}{123, 2}, + []interface{}{123, 1}, + }) { + t.Errorf("Invalid all: %#v", all) + } +} + +func TestShrinkMap(t *testing.T) { + counter := &counterShrink{n: 10} + shrink := gopter.Shrink(counter.Next).Map(func(v int) int { + return 10 - v + }) + + all := shrink.All() + if !reflect.DeepEqual(all, []interface{}{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}) { + t.Errorf("Invalid all: %#v", all) + } +} + +func TestShrinkMapNoFunc(t *testing.T) { + defer expectPanic(t, "Param of Map has to be a func, but is string") + counter := &counterShrink{n: 10} + gopter.Shrink(counter.Next).Map("not a function") +} + +func TestShrinkMapTooManyParams(t *testing.T) { + defer expectPanic(t, "Param of Map has to be a func with one param, but is 2") + counter := &counterShrink{n: 10} + gopter.Shrink(counter.Next).Map(func(a, b string) string { + return "" + }) +} + +func TestShrinkMapToManyReturns(t *testing.T) { + defer expectPanic(t, "Param of Map has to be a func with one return value, but is 2") + counter := &counterShrink{n: 10} + gopter.Shrink(counter.Next).Map(func(a string) (string, bool) { + return "", false + }) +} + +func TestNoShrinker(t *testing.T) { + shrink := gopter.NoShrinker(123) + if shrink == nil { + t.Error("Shrink has to be != nil") + } + value, ok := shrink() + if ok || value != nil { + t.Errorf("Invalid shrink: %#v", value) + } +} diff --git a/vendor/github.com/leanovate/gopter/test_parameters.go b/vendor/github.com/leanovate/gopter/test_parameters.go new file mode 100644 index 000000000..28c295ea3 --- /dev/null +++ b/vendor/github.com/leanovate/gopter/test_parameters.go @@ -0,0 +1,39 @@ +package gopter + +import ( + "math/rand" + "time" +) + +// TestParameters to run property tests +type TestParameters struct { + MinSuccessfulTests int + // MinSize is an (inclusive) lower limit on the size of the parameters + MinSize int + // MaxSize is an (exclusive) upper limit on the size of the parameters + MaxSize int + MaxShrinkCount int + Seed int64 + Rng *rand.Rand + Workers int + MaxDiscardRatio float64 +} + +// DefaultTestParameterWithSeeds creates reasonable default Parameters for most cases based on a fixed RNG-seed +func DefaultTestParametersWithSeed(seed int64) *TestParameters { + return &TestParameters{ + MinSuccessfulTests: 100, + MinSize: 0, + MaxSize: 100, + MaxShrinkCount: 1000, + Seed: seed, + Rng: rand.New(NewLockedSource(seed)), + Workers: 1, + MaxDiscardRatio: 5, + } +} + +// DefaultTestParameterWithSeeds creates reasonable default Parameters for most cases with an undefined RNG-seed +func DefaultTestParameters() *TestParameters { + return DefaultTestParametersWithSeed(time.Now().UnixNano()) +} diff --git a/vendor/github.com/leanovate/gopter/test_result.go b/vendor/github.com/leanovate/gopter/test_result.go new file mode 100644 index 000000000..76cd8f34a --- /dev/null +++ b/vendor/github.com/leanovate/gopter/test_result.go @@ -0,0 +1,51 @@ +package gopter + +import "time" + +type testStatus int + +const ( + // TestPassed indicates that the property check has passed. + TestPassed testStatus = iota + // TestProved indicates that the property has been proved. + TestProved + // TestFailed indicates that the property check has failed. + TestFailed + // TestExhausted indicates that the property check has exhausted, i.e. the generators have + // generated too many empty results. + TestExhausted + // TestError indicates that the property check has finished with an error. + TestError +) + +func (s testStatus) String() string { + switch s { + case TestPassed: + return "PASSED" + case TestProved: + return "PROVED" + case TestFailed: + return "FAILED" + case TestExhausted: + return "EXHAUSTED" + case TestError: + return "ERROR" + } + return "" +} + +// TestResult contains the result of a property property check. +type TestResult struct { + Status testStatus + Succeeded int + Discarded int + Labels []string + Error error + Args PropArgs + Time time.Duration +} + +// Passed checks if the check has passed +func (r *TestResult) Passed() bool { + return r.Status == TestPassed || r.Status == TestProved +} diff --git a/vendor/github.com/leanovate/gopter/test_result_test.go b/vendor/github.com/leanovate/gopter/test_result_test.go new file mode 100644 index 000000000..0633c658e --- /dev/null +++ b/vendor/github.com/leanovate/gopter/test_result_test.go @@ -0,0 +1,49 @@ +package gopter_test + +import ( + "testing" + + "github.com/leanovate/gopter" +) + +func TestTestResult(t *testing.T) { + result := &gopter.TestResult{Status: gopter.TestPassed} + if !result.Passed() { + t.Errorf("Test not passed: %#v", result) + } + if result.Status.String() != "PASSED" { + t.Errorf("Invalid status: %#v", result) + } + + result = &gopter.TestResult{Status: gopter.TestProved} + if !result.Passed() { + t.Errorf("Test not passed: %#v", result) + } + if result.Status.String() != "PROVED" { + t.Errorf("Invalid status: %#v", result) + } + + result = &gopter.TestResult{Status: gopter.TestFailed} + if result.Passed() { + t.Errorf("Test passed: %#v", result) + } + if result.Status.String() != "FAILED" { + t.Errorf("Invalid status: %#v", result) + } + + result = &gopter.TestResult{Status: gopter.TestExhausted} + if result.Passed() { + t.Errorf("Test passed: %#v", result) + } + if result.Status.String() != "EXHAUSTED" { + t.Errorf("Invalid status: %#v", result) + } + + result = &gopter.TestResult{Status: gopter.TestError} + if result.Passed() { + t.Errorf("Test passed: %#v", result) + } + if result.Status.String() != "ERROR" { + t.Errorf("Invalid status: %#v", result) + } +}