refactor(engine): allow scripts to register experiments (#860)

See https://github.com/ooni/probe/issues/2216
This commit is contained in:
Simone Basso
2022-08-17 10:57:03 +02:00
committed by GitHub
parent 69602abe8a
commit 6a0ae5c70b
53 changed files with 1438 additions and 1200 deletions
+12
View File
@@ -0,0 +1,12 @@
package registry
// Where we register all the available experiments.
var allexperiments = map[string]*Factory{}
// ExperimentNames returns the name of all experiments
func ExperimentNames() (names []string) {
for key := range allexperiments {
names = append(names, key)
}
return
}
+23
View File
@@ -0,0 +1,23 @@
package registry
//
// Registers the `dash' experiment.
//
import (
"github.com/ooni/probe-cli/v3/internal/engine/experiment/dash"
"github.com/ooni/probe-cli/v3/internal/model"
)
func init() {
allexperiments["dash"] = &Factory{
build: func(config interface{}) model.ExperimentMeasurer {
return dash.NewExperimentMeasurer(
*config.(*dash.Config),
)
},
config: &dash.Config{},
interruptible: true,
inputPolicy: model.InputNone,
}
}
+22
View File
@@ -0,0 +1,22 @@
package registry
//
// Registers the `dnscheck' experiment.
//
import (
"github.com/ooni/probe-cli/v3/internal/engine/experiment/dnscheck"
"github.com/ooni/probe-cli/v3/internal/model"
)
func init() {
allexperiments["dnscheck"] = &Factory{
build: func(config interface{}) model.ExperimentMeasurer {
return dnscheck.NewExperimentMeasurer(
*config.(*dnscheck.Config),
)
},
config: &dnscheck.Config{},
inputPolicy: model.InputOrStaticDefault,
}
}
+22
View File
@@ -0,0 +1,22 @@
package registry
//
// Registers the `dnsping' experiment.
//
import (
"github.com/ooni/probe-cli/v3/internal/engine/experiment/dnsping"
"github.com/ooni/probe-cli/v3/internal/model"
)
func init() {
allexperiments["dnsping"] = &Factory{
build: func(config interface{}) model.ExperimentMeasurer {
return dnsping.NewExperimentMeasurer(
*config.(*dnsping.Config),
)
},
config: &dnsping.Config{},
inputPolicy: model.InputOrStaticDefault,
}
}
+2
View File
@@ -0,0 +1,2 @@
// Package registry contains a registry of all the available experiments.
package registry
+28
View File
@@ -0,0 +1,28 @@
package registry
//
// Registers the `example' experiment.
//
import (
"time"
"github.com/ooni/probe-cli/v3/internal/engine/experiment/example"
"github.com/ooni/probe-cli/v3/internal/model"
)
func init() {
allexperiments["example"] = &Factory{
build: func(config interface{}) model.ExperimentMeasurer {
return example.NewExperimentMeasurer(
*config.(*example.Config), "example",
)
},
config: &example.Config{
Message: "Good day from the example experiment!",
SleepTime: int64(time.Second),
},
interruptible: true,
inputPolicy: model.InputNone,
}
}
+223
View File
@@ -0,0 +1,223 @@
package registry
//
// Factory for constructing experiments.
//
import (
"errors"
"fmt"
"reflect"
"strconv"
"github.com/iancoleman/strcase"
"github.com/ooni/probe-cli/v3/internal/model"
)
// Factory allows to construct an experiment measurer.
type Factory struct {
// build is the constructor that build an experiment with the given config.
build func(config interface{}) model.ExperimentMeasurer
// config contains the experiment's config.
config any
// inputPolicy contains the experiment's InputPolicy.
inputPolicy model.InputPolicy
// interruptible indicates whether the experiment is interruptible.
interruptible bool
}
// Interruptible returns whether the experiment is interruptible.
func (b *Factory) Interruptible() bool {
return b.interruptible
}
// InputPolicy returns the experiment's InputPolicy.
func (b *Factory) InputPolicy() model.InputPolicy {
return b.inputPolicy
}
var (
// ErrConfigIsNotAStructPointer indicates we expected a pointer to struct.
ErrConfigIsNotAStructPointer = errors.New("config is not a struct pointer")
// ErrNoSuchField indicates there's no field with the given name.
ErrNoSuchField = errors.New("no such field")
// ErrCannotSetIntegerOption means SetOptionAny couldn't set an integer option.
ErrCannotSetIntegerOption = errors.New("cannot set integer option")
// ErrInvalidStringRepresentationOfBool indicates the string you passed
// to SetOptionaAny is not a valid string representation of a bool.
ErrInvalidStringRepresentationOfBool = errors.New("invalid string representation of bool")
// ErrCannotSetBoolOption means SetOptionAny couldn't set a bool option.
ErrCannotSetBoolOption = errors.New("cannot set bool option")
// ErrCannotSetStringOption means SetOptionAny couldn't set a string option.
ErrCannotSetStringOption = errors.New("cannot set string option")
// ErrUnsupportedOptionType means we don't support the type passed to
// the SetOptionAny method as an opaque any type.
ErrUnsupportedOptionType = errors.New("unsupported option type")
)
// Options returns the options exposed by this experiment.
func (b *Factory) Options() (map[string]model.ExperimentOptionInfo, error) {
result := make(map[string]model.ExperimentOptionInfo)
ptrinfo := reflect.ValueOf(b.config)
if ptrinfo.Kind() != reflect.Ptr {
return nil, ErrConfigIsNotAStructPointer
}
structinfo := ptrinfo.Elem().Type()
if structinfo.Kind() != reflect.Struct {
return nil, ErrConfigIsNotAStructPointer
}
for i := 0; i < structinfo.NumField(); i++ {
field := structinfo.Field(i)
result[field.Name] = model.ExperimentOptionInfo{
Doc: field.Tag.Get("ooni"),
Type: field.Type.String(),
}
}
return result, nil
}
// setOptionBool sets a bool option.
func (b *Factory) setOptionBool(field reflect.Value, value any) error {
switch v := value.(type) {
case bool:
field.SetBool(v)
return nil
case string:
if v != "true" && v != "false" {
return fmt.Errorf("%w: %s", ErrInvalidStringRepresentationOfBool, v)
}
field.SetBool(v == "true")
return nil
default:
return fmt.Errorf("%w from a value of type %T", ErrCannotSetBoolOption, value)
}
}
// setOptionInt sets an int option
func (b *Factory) setOptionInt(field reflect.Value, value any) error {
switch v := value.(type) {
case int64:
field.SetInt(v)
return nil
case int32:
field.SetInt(int64(v))
return nil
case int16:
field.SetInt(int64(v))
return nil
case int8:
field.SetInt(int64(v))
return nil
case int:
field.SetInt(int64(v))
return nil
case string:
number, err := strconv.ParseInt(v, 10, 64)
if err != nil {
return fmt.Errorf("%w: %s", ErrCannotSetIntegerOption, err.Error())
}
field.SetInt(number)
return nil
default:
return fmt.Errorf("%w from a value of type %T", ErrCannotSetIntegerOption, value)
}
}
// setOptionString sets a string option
func (b *Factory) setOptionString(field reflect.Value, value any) error {
switch v := value.(type) {
case string:
field.SetString(v)
return nil
default:
return fmt.Errorf("%w from a value of type %T", ErrCannotSetStringOption, value)
}
}
// SetOptionAny sets an option given any value.
func (b *Factory) SetOptionAny(key string, value any) error {
field, err := b.fieldbyname(b.config, key)
if err != nil {
return err
}
switch field.Kind() {
case reflect.Int64:
return b.setOptionInt(field, value)
case reflect.Bool:
return b.setOptionBool(field, value)
case reflect.String:
return b.setOptionString(field, value)
default:
return fmt.Errorf("%w: %T", ErrUnsupportedOptionType, value)
}
}
// SetOptionsAny calls SetOptionAny for each entry inside [options].
func (b *Factory) SetOptionsAny(options map[string]any) error {
for key, value := range options {
if err := b.SetOptionAny(key, value); err != nil {
return err
}
}
return nil
}
// fieldbyname return v's field whose name is equal to the given key.
func (b *Factory) fieldbyname(v interface{}, key string) (reflect.Value, error) {
// See https://stackoverflow.com/a/6396678/4354461
ptrinfo := reflect.ValueOf(v)
if ptrinfo.Kind() != reflect.Ptr {
return reflect.Value{}, fmt.Errorf("%w but a %T", ErrConfigIsNotAStructPointer, v)
}
structinfo := ptrinfo.Elem()
if structinfo.Kind() != reflect.Struct {
return reflect.Value{}, fmt.Errorf("%w but a %T", ErrConfigIsNotAStructPointer, v)
}
field := structinfo.FieldByName(key)
if !field.IsValid() || !field.CanSet() {
return reflect.Value{}, fmt.Errorf("%w: %s", ErrNoSuchField, key)
}
return field, nil
}
// NewExperimentMeasurer creates the experiment
func (b *Factory) NewExperimentMeasurer() model.ExperimentMeasurer {
return b.build(b.config)
}
// CanonicalizeExperimentName allows code to provide experiment names
// in a more flexible way, where we have aliases.
//
// Because we allow for uppercase experiment names for backwards
// compatibility with MK, we need to add some exceptions here when
// mapping (e.g., DNSCheck => dnscheck).
func CanonicalizeExperimentName(name string) string {
switch name = strcase.ToSnake(name); name {
case "ndt_7":
name = "ndt" // since 2020-03-18, we use ndt7 to implement ndt by default
case "dns_check":
name = "dnscheck"
case "stun_reachability":
name = "stunreachability"
default:
}
return name
}
// NewFactory creates a new Factory instance.
func NewFactory(name string) (*Factory, error) {
factory := allexperiments[CanonicalizeExperimentName(name)]
if factory == nil {
return nil, fmt.Errorf("no such experiment: %s", name)
}
return factory, nil
}
+347
View File
@@ -0,0 +1,347 @@
package registry
import (
"errors"
"testing"
"github.com/google/go-cmp/cmp"
)
type fakeExperimentConfig struct {
Chan chan any `ooni:"we cannot set this"`
String string `ooni:"a string"`
Truth bool `ooni:"something that no-one knows"`
Value int64 `ooni:"a number"`
}
func TestExperimentBuilderOptions(t *testing.T) {
t.Run("when config is not a pointer", func(t *testing.T) {
b := &Factory{
config: 17,
}
options, err := b.Options()
if !errors.Is(err, ErrConfigIsNotAStructPointer) {
t.Fatal("expected an error here")
}
if options != nil {
t.Fatal("expected nil here")
}
})
t.Run("when config is not a struct", func(t *testing.T) {
number := 17
b := &Factory{
config: &number,
}
options, err := b.Options()
if !errors.Is(err, ErrConfigIsNotAStructPointer) {
t.Fatal("expected an error here")
}
if options != nil {
t.Fatal("expected nil here")
}
})
t.Run("when config is a pointer to struct", func(t *testing.T) {
config := &fakeExperimentConfig{}
b := &Factory{
config: config,
}
options, err := b.Options()
if err != nil {
t.Fatal(err)
}
for name, value := range options {
switch name {
case "Chan":
if value.Doc != "we cannot set this" {
t.Fatal("invalid doc")
}
if value.Type != "chan interface {}" {
t.Fatal("invalid type", value.Type)
}
case "String":
if value.Doc != "a string" {
t.Fatal("invalid doc")
}
if value.Type != "string" {
t.Fatal("invalid type", value.Type)
}
case "Truth":
if value.Doc != "something that no-one knows" {
t.Fatal("invalid doc")
}
if value.Type != "bool" {
t.Fatal("invalid type", value.Type)
}
case "Value":
if value.Doc != "a number" {
t.Fatal("invalid doc")
}
if value.Type != "int64" {
t.Fatal("invalid type", value.Type)
}
default:
t.Fatal("unknown name", name)
}
}
})
}
func TestExperimentBuilderSetOptionAny(t *testing.T) {
var inputs = []struct {
TestCaseName string
InitialConfig any
FieldName string
FieldValue any
ExpectErr error
ExpectConfig any
}{{
TestCaseName: "config is not a pointer",
InitialConfig: fakeExperimentConfig{},
FieldName: "Antani",
FieldValue: true,
ExpectErr: ErrConfigIsNotAStructPointer,
ExpectConfig: fakeExperimentConfig{},
}, {
TestCaseName: "config is not a pointer to struct",
InitialConfig: func() *int {
v := 17
return &v
}(),
FieldName: "Antani",
FieldValue: true,
ExpectErr: ErrConfigIsNotAStructPointer,
ExpectConfig: func() *int {
v := 17
return &v
}(),
}, {
TestCaseName: "for missing field",
InitialConfig: &fakeExperimentConfig{},
FieldName: "Antani",
FieldValue: true,
ExpectErr: ErrNoSuchField,
ExpectConfig: &fakeExperimentConfig{},
}, {
TestCaseName: "[bool] for true value represented as string",
InitialConfig: &fakeExperimentConfig{},
FieldName: "Truth",
FieldValue: "true",
ExpectErr: nil,
ExpectConfig: &fakeExperimentConfig{
Truth: true,
},
}, {
TestCaseName: "[bool] for false value represented as string",
InitialConfig: &fakeExperimentConfig{
Truth: true,
},
FieldName: "Truth",
FieldValue: "false",
ExpectErr: nil,
ExpectConfig: &fakeExperimentConfig{
Truth: false, // must have been flipped
},
}, {
TestCaseName: "[bool] for true value",
InitialConfig: &fakeExperimentConfig{},
FieldName: "Truth",
FieldValue: true,
ExpectErr: nil,
ExpectConfig: &fakeExperimentConfig{
Truth: true,
},
}, {
TestCaseName: "[bool] for false value",
InitialConfig: &fakeExperimentConfig{
Truth: true,
},
FieldName: "Truth",
FieldValue: false,
ExpectErr: nil,
ExpectConfig: &fakeExperimentConfig{
Truth: false, // must have been flipped
},
}, {
TestCaseName: "[bool] for invalid string representation of bool",
InitialConfig: &fakeExperimentConfig{},
FieldName: "Truth",
FieldValue: "xxx",
ExpectErr: ErrInvalidStringRepresentationOfBool,
ExpectConfig: &fakeExperimentConfig{},
}, {
TestCaseName: "[bool] for value we don't know how to convert to bool",
InitialConfig: &fakeExperimentConfig{},
FieldName: "Truth",
FieldValue: make(chan any),
ExpectErr: ErrCannotSetBoolOption,
ExpectConfig: &fakeExperimentConfig{},
}, {
TestCaseName: "[int] for int",
InitialConfig: &fakeExperimentConfig{},
FieldName: "Value",
FieldValue: 17,
ExpectErr: nil,
ExpectConfig: &fakeExperimentConfig{
Value: 17,
},
}, {
TestCaseName: "[int] for int64",
InitialConfig: &fakeExperimentConfig{},
FieldName: "Value",
FieldValue: int64(17),
ExpectErr: nil,
ExpectConfig: &fakeExperimentConfig{
Value: 17,
},
}, {
TestCaseName: "[int] for int32",
InitialConfig: &fakeExperimentConfig{},
FieldName: "Value",
FieldValue: int32(17),
ExpectErr: nil,
ExpectConfig: &fakeExperimentConfig{
Value: 17,
},
}, {
TestCaseName: "[int] for int16",
InitialConfig: &fakeExperimentConfig{},
FieldName: "Value",
FieldValue: int16(17),
ExpectErr: nil,
ExpectConfig: &fakeExperimentConfig{
Value: 17,
},
}, {
TestCaseName: "[int] for int8",
InitialConfig: &fakeExperimentConfig{},
FieldName: "Value",
FieldValue: int8(17),
ExpectErr: nil,
ExpectConfig: &fakeExperimentConfig{
Value: 17,
},
}, {
TestCaseName: "[int] for string representation of int",
InitialConfig: &fakeExperimentConfig{},
FieldName: "Value",
FieldValue: "17",
ExpectErr: nil,
ExpectConfig: &fakeExperimentConfig{
Value: 17,
},
}, {
TestCaseName: "[int] for invalid string representation of int",
InitialConfig: &fakeExperimentConfig{},
FieldName: "Value",
FieldValue: "xx",
ExpectErr: ErrCannotSetIntegerOption,
ExpectConfig: &fakeExperimentConfig{},
}, {
TestCaseName: "[int] for type we don't know how to convert to int",
InitialConfig: &fakeExperimentConfig{},
FieldName: "Value",
FieldValue: make(chan any),
ExpectErr: ErrCannotSetIntegerOption,
ExpectConfig: &fakeExperimentConfig{},
}, {
TestCaseName: "[string] for serialized bool value while setting a string value",
InitialConfig: &fakeExperimentConfig{},
FieldName: "String",
FieldValue: "true",
ExpectErr: nil,
ExpectConfig: &fakeExperimentConfig{
String: "true",
},
}, {
TestCaseName: "[string] for serialized int value while setting a string value",
InitialConfig: &fakeExperimentConfig{},
FieldName: "String",
FieldValue: "155",
ExpectErr: nil,
ExpectConfig: &fakeExperimentConfig{
String: "155",
},
}, {
TestCaseName: "[string] for any other string",
InitialConfig: &fakeExperimentConfig{},
FieldName: "String",
FieldValue: "xxx",
ExpectErr: nil,
ExpectConfig: &fakeExperimentConfig{
String: "xxx",
},
}, {
TestCaseName: "[string] for type we don't know how to convert to string",
InitialConfig: &fakeExperimentConfig{},
FieldName: "String",
FieldValue: make(chan any),
ExpectErr: ErrCannotSetStringOption,
ExpectConfig: &fakeExperimentConfig{},
}, {
TestCaseName: "for a field that we don't know how to set",
InitialConfig: &fakeExperimentConfig{},
FieldName: "Chan",
FieldValue: make(chan any),
ExpectErr: ErrUnsupportedOptionType,
ExpectConfig: &fakeExperimentConfig{},
}}
for _, input := range inputs {
t.Run(input.TestCaseName, func(t *testing.T) {
ec := input.InitialConfig
b := &Factory{config: ec}
err := b.SetOptionAny(input.FieldName, input.FieldValue)
if !errors.Is(err, input.ExpectErr) {
t.Fatal(err)
}
if diff := cmp.Diff(input.ExpectConfig, ec); diff != "" {
t.Fatal(diff)
}
})
}
}
func TestExperimentBuilderSetOptionsAny(t *testing.T) {
b := &Factory{config: &fakeExperimentConfig{}}
t.Run("we correctly handle an empty map", func(t *testing.T) {
if err := b.SetOptionsAny(nil); err != nil {
t.Fatal(err)
}
})
t.Run("we correctly handle a map containing options", func(t *testing.T) {
f := &fakeExperimentConfig{}
privateb := &Factory{config: f}
opts := map[string]any{
"String": "yoloyolo",
"Value": "174",
"Truth": "true",
}
if err := privateb.SetOptionsAny(opts); err != nil {
t.Fatal(err)
}
if f.String != "yoloyolo" {
t.Fatal("cannot set string value")
}
if f.Value != 174 {
t.Fatal("cannot set integer value")
}
if f.Truth != true {
t.Fatal("cannot set bool value")
}
})
t.Run("we handle mistakes in a map containing string options", func(t *testing.T) {
opts := map[string]any{
"String": "yoloyolo",
"Value": "xx",
"Truth": "true",
}
if err := b.SetOptionsAny(opts); !errors.Is(err, ErrCannotSetIntegerOption) {
t.Fatal("unexpected err", err)
}
})
}
+22
View File
@@ -0,0 +1,22 @@
package registry
//
// Registers the `fbmessenger' experiment.
//
import (
"github.com/ooni/probe-cli/v3/internal/engine/experiment/fbmessenger"
"github.com/ooni/probe-cli/v3/internal/model"
)
func init() {
allexperiments["facebook_messenger"] = &Factory{
build: func(config interface{}) model.ExperimentMeasurer {
return fbmessenger.NewExperimentMeasurer(
*config.(*fbmessenger.Config),
)
},
config: &fbmessenger.Config{},
inputPolicy: model.InputNone,
}
}
+22
View File
@@ -0,0 +1,22 @@
package registry
//
// Registers the `hhfm' experiment.
//
import (
"github.com/ooni/probe-cli/v3/internal/engine/experiment/hhfm"
"github.com/ooni/probe-cli/v3/internal/model"
)
func init() {
allexperiments["http_header_field_manipulation"] = &Factory{
build: func(config interface{}) model.ExperimentMeasurer {
return hhfm.NewExperimentMeasurer(
*config.(*hhfm.Config),
)
},
config: &hhfm.Config{},
inputPolicy: model.InputNone,
}
}
+22
View File
@@ -0,0 +1,22 @@
package registry
//
// Registers the `hirl' experiment.
//
import (
"github.com/ooni/probe-cli/v3/internal/engine/experiment/hirl"
"github.com/ooni/probe-cli/v3/internal/model"
)
func init() {
allexperiments["http_invalid_request_line"] = &Factory{
build: func(config interface{}) model.ExperimentMeasurer {
return hirl.NewExperimentMeasurer(
*config.(*hirl.Config),
)
},
config: &hirl.Config{},
inputPolicy: model.InputNone,
}
}
+22
View File
@@ -0,0 +1,22 @@
package registry
//
// Registers the `httphostheader' experiment.
//
import (
"github.com/ooni/probe-cli/v3/internal/engine/experiment/httphostheader"
"github.com/ooni/probe-cli/v3/internal/model"
)
func init() {
allexperiments["http_host_header"] = &Factory{
build: func(config interface{}) model.ExperimentMeasurer {
return httphostheader.NewExperimentMeasurer(
*config.(*httphostheader.Config),
)
},
config: &httphostheader.Config{},
inputPolicy: model.InputOrQueryBackend,
}
}
+23
View File
@@ -0,0 +1,23 @@
package registry
//
// Registers the `ndt' experiment.
//
import (
"github.com/ooni/probe-cli/v3/internal/engine/experiment/ndt7"
"github.com/ooni/probe-cli/v3/internal/model"
)
func init() {
allexperiments["ndt"] = &Factory{
build: func(config interface{}) model.ExperimentMeasurer {
return ndt7.NewExperimentMeasurer(
*config.(*ndt7.Config),
)
},
config: &ndt7.Config{},
interruptible: true,
inputPolicy: model.InputNone,
}
}
+22
View File
@@ -0,0 +1,22 @@
package registry
//
// Registers the `psiphon' experiment.
//
import (
"github.com/ooni/probe-cli/v3/internal/engine/experiment/psiphon"
"github.com/ooni/probe-cli/v3/internal/model"
)
func init() {
allexperiments["psiphon"] = &Factory{
build: func(config interface{}) model.ExperimentMeasurer {
return psiphon.NewExperimentMeasurer(
*config.(*psiphon.Config),
)
},
config: &psiphon.Config{},
inputPolicy: model.InputOptional,
}
}
+22
View File
@@ -0,0 +1,22 @@
package registry
//
// Registers the `quicping' experiment.
//
import (
"github.com/ooni/probe-cli/v3/internal/engine/experiment/quicping"
"github.com/ooni/probe-cli/v3/internal/model"
)
func init() {
allexperiments["quicping"] = &Factory{
build: func(config interface{}) model.ExperimentMeasurer {
return quicping.NewExperimentMeasurer(
*config.(*quicping.Config),
)
},
config: &quicping.Config{},
inputPolicy: model.InputStrictlyRequired,
}
}
+22
View File
@@ -0,0 +1,22 @@
package registry
//
// Registers the `riseupvpn' experiment.
//
import (
"github.com/ooni/probe-cli/v3/internal/engine/experiment/riseupvpn"
"github.com/ooni/probe-cli/v3/internal/model"
)
func init() {
allexperiments["riseupvpn"] = &Factory{
build: func(config interface{}) model.ExperimentMeasurer {
return riseupvpn.NewExperimentMeasurer(
*config.(*riseupvpn.Config),
)
},
config: &riseupvpn.Config{},
inputPolicy: model.InputNone,
}
}
+22
View File
@@ -0,0 +1,22 @@
package registry
//
// Registers the `run' experiment.
//
import (
"github.com/ooni/probe-cli/v3/internal/engine/experiment/run"
"github.com/ooni/probe-cli/v3/internal/model"
)
func init() {
allexperiments["run"] = &Factory{
build: func(config interface{}) model.ExperimentMeasurer {
return run.NewExperimentMeasurer(
*config.(*run.Config),
)
},
config: &run.Config{},
inputPolicy: model.InputStrictlyRequired,
}
}
+22
View File
@@ -0,0 +1,22 @@
package registry
//
// Registers the `signal' experiment.
//
import (
"github.com/ooni/probe-cli/v3/internal/engine/experiment/signal"
"github.com/ooni/probe-cli/v3/internal/model"
)
func init() {
allexperiments["signal"] = &Factory{
build: func(config interface{}) model.ExperimentMeasurer {
return signal.NewExperimentMeasurer(
*config.(*signal.Config),
)
},
config: &signal.Config{},
inputPolicy: model.InputNone,
}
}
+22
View File
@@ -0,0 +1,22 @@
package registry
//
// Registers the `simplequicping' experiment.
//
import (
"github.com/ooni/probe-cli/v3/internal/engine/experiment/simplequicping"
"github.com/ooni/probe-cli/v3/internal/model"
)
func init() {
allexperiments["simplequicping"] = &Factory{
build: func(config interface{}) model.ExperimentMeasurer {
return simplequicping.NewExperimentMeasurer(
*config.(*simplequicping.Config),
)
},
config: &simplequicping.Config{},
inputPolicy: model.InputStrictlyRequired,
}
}
+22
View File
@@ -0,0 +1,22 @@
package registry
//
// Registers the `sniblocking' experiment.
//
import (
"github.com/ooni/probe-cli/v3/internal/engine/experiment/sniblocking"
"github.com/ooni/probe-cli/v3/internal/model"
)
func init() {
allexperiments["sni_blocking"] = &Factory{
build: func(config interface{}) model.ExperimentMeasurer {
return sniblocking.NewExperimentMeasurer(
*config.(*sniblocking.Config),
)
},
config: &sniblocking.Config{},
inputPolicy: model.InputOrQueryBackend,
}
}
+22
View File
@@ -0,0 +1,22 @@
package registry
//
// Registers the `stunreachability' experiment.
//
import (
"github.com/ooni/probe-cli/v3/internal/engine/experiment/stunreachability"
"github.com/ooni/probe-cli/v3/internal/model"
)
func init() {
allexperiments["stunreachability"] = &Factory{
build: func(config interface{}) model.ExperimentMeasurer {
return stunreachability.NewExperimentMeasurer(
*config.(*stunreachability.Config),
)
},
config: &stunreachability.Config{},
inputPolicy: model.InputOrStaticDefault,
}
}
+22
View File
@@ -0,0 +1,22 @@
package registry
//
// Registers the `tcpping' experiment.
//
import (
"github.com/ooni/probe-cli/v3/internal/engine/experiment/tcpping"
"github.com/ooni/probe-cli/v3/internal/model"
)
func init() {
allexperiments["tcpping"] = &Factory{
build: func(config interface{}) model.ExperimentMeasurer {
return tcpping.NewExperimentMeasurer(
*config.(*tcpping.Config),
)
},
config: &tcpping.Config{},
inputPolicy: model.InputStrictlyRequired,
}
}
+23
View File
@@ -0,0 +1,23 @@
package registry
//
// Registers the `telegram' experiment.
//
import (
"github.com/ooni/probe-cli/v3/internal/engine/experiment/telegram"
"github.com/ooni/probe-cli/v3/internal/model"
)
func init() {
allexperiments["telegram"] = &Factory{
build: func(config any) model.ExperimentMeasurer {
return telegram.NewExperimentMeasurer(
config.(telegram.Config),
)
},
config: &telegram.Config{},
interruptible: false,
inputPolicy: model.InputNone,
}
}
+22
View File
@@ -0,0 +1,22 @@
package registry
//
// Registers the `tlsping' experiment.
//
import (
"github.com/ooni/probe-cli/v3/internal/engine/experiment/tlsping"
"github.com/ooni/probe-cli/v3/internal/model"
)
func init() {
allexperiments["tlsping"] = &Factory{
build: func(config interface{}) model.ExperimentMeasurer {
return tlsping.NewExperimentMeasurer(
*config.(*tlsping.Config),
)
},
config: &tlsping.Config{},
inputPolicy: model.InputStrictlyRequired,
}
}
+22
View File
@@ -0,0 +1,22 @@
package registry
//
// Registers the `tlstool' experiment.
//
import (
"github.com/ooni/probe-cli/v3/internal/engine/experiment/tlstool"
"github.com/ooni/probe-cli/v3/internal/model"
)
func init() {
allexperiments["tlstool"] = &Factory{
build: func(config interface{}) model.ExperimentMeasurer {
return tlstool.NewExperimentMeasurer(
*config.(*tlstool.Config),
)
},
config: &tlstool.Config{},
inputPolicy: model.InputOrQueryBackend,
}
}
+22
View File
@@ -0,0 +1,22 @@
package registry
//
// Registers the `tor' experiment.
//
import (
"github.com/ooni/probe-cli/v3/internal/engine/experiment/tor"
"github.com/ooni/probe-cli/v3/internal/model"
)
func init() {
allexperiments["tor"] = &Factory{
build: func(config interface{}) model.ExperimentMeasurer {
return tor.NewExperimentMeasurer(
*config.(*tor.Config),
)
},
config: &tor.Config{},
inputPolicy: model.InputNone,
}
}
+22
View File
@@ -0,0 +1,22 @@
package registry
//
// Registers the `torsf' experiment.
//
import (
"github.com/ooni/probe-cli/v3/internal/engine/experiment/torsf"
"github.com/ooni/probe-cli/v3/internal/model"
)
func init() {
allexperiments["torsf"] = &Factory{
build: func(config interface{}) model.ExperimentMeasurer {
return torsf.NewExperimentMeasurer(
*config.(*torsf.Config),
)
},
config: &torsf.Config{},
inputPolicy: model.InputNone,
}
}
+22
View File
@@ -0,0 +1,22 @@
package registry
//
// Registers the `urlgetter' experiment.
//
import (
"github.com/ooni/probe-cli/v3/internal/engine/experiment/urlgetter"
"github.com/ooni/probe-cli/v3/internal/model"
)
func init() {
allexperiments["urlgetter"] = &Factory{
build: func(config interface{}) model.ExperimentMeasurer {
return urlgetter.NewExperimentMeasurer(
*config.(*urlgetter.Config),
)
},
config: &urlgetter.Config{},
inputPolicy: model.InputStrictlyRequired,
}
}
+22
View File
@@ -0,0 +1,22 @@
package registry
//
// Registers the `vanilla_tor' experiment.
//
import (
"github.com/ooni/probe-cli/v3/internal/engine/experiment/vanillator"
"github.com/ooni/probe-cli/v3/internal/model"
)
func init() {
allexperiments["vanilla_tor"] = &Factory{
build: func(config interface{}) model.ExperimentMeasurer {
return vanillator.NewExperimentMeasurer(
*config.(*vanillator.Config),
)
},
config: &vanillator.Config{},
inputPolicy: model.InputNone,
}
}
+23
View File
@@ -0,0 +1,23 @@
package registry
//
// Registers the `web_connectivity' experiment.
//
import (
"github.com/ooni/probe-cli/v3/internal/engine/experiment/webconnectivity"
"github.com/ooni/probe-cli/v3/internal/model"
)
func init() {
allexperiments["web_connectivity"] = &Factory{
build: func(config any) model.ExperimentMeasurer {
return webconnectivity.NewExperimentMeasurer(
config.(webconnectivity.Config),
)
},
config: &webconnectivity.Config{},
interruptible: false,
inputPolicy: model.InputOrQueryBackend,
}
}
+22
View File
@@ -0,0 +1,22 @@
package registry
//
// Registers the `whatsapp' experiment.
//
import (
"github.com/ooni/probe-cli/v3/internal/engine/experiment/whatsapp"
"github.com/ooni/probe-cli/v3/internal/model"
)
func init() {
allexperiments["whatsapp"] = &Factory{
build: func(config interface{}) model.ExperimentMeasurer {
return whatsapp.NewExperimentMeasurer(
*config.(*whatsapp.Config),
)
},
config: &whatsapp.Config{},
inputPolicy: model.InputNone,
}
}