1// Package example contains a simple example experiment.
2//
3// You could use this code to boostrap the implementation of
4// a new experiment that you are working on.
5package example
6
7import (
8	"context"
9	"errors"
10	"time"
11
12	"github.com/ooni/probe-cli/v3/internal/engine/model"
13)
14
15const testVersion = "0.1.0"
16
17// Config contains the experiment config.
18//
19// This contains all the settings that user can set to modify the behaviour
20// of this experiment. By tagging these variables with `ooni:"..."`, we allow
21// miniooni's -O flag to find them and set them.
22type Config struct {
23	Message     string `ooni:"Message to emit at test completion"`
24	ReturnError bool   `ooni:"Toogle to return a mocked error"`
25	SleepTime   int64  `ooni:"Amount of time to sleep for"`
26}
27
28// TestKeys contains the experiment's result.
29//
30// This is what will end up into the Measurement.TestKeys field
31// when you run this experiment.
32//
33// In other words, the variables in this struct will be
34// the specific results of this experiment.
35type TestKeys struct {
36	Success bool `json:"success"`
37}
38
39// Measurer performs the measurement.
40type Measurer struct {
41	config   Config
42	testName string
43}
44
45// ExperimentName implements model.ExperimentMeasurer.ExperimentName.
46func (m Measurer) ExperimentName() string {
47	return m.testName
48}
49
50// ExperimentVersion implements model.ExperimentMeasurer.ExperimentVersion.
51func (m Measurer) ExperimentVersion() string {
52	return testVersion
53}
54
55// ErrFailure is the error returned when you set the
56// config.ReturnError field to true.
57var ErrFailure = errors.New("mocked error")
58
59// Run implements model.ExperimentMeasurer.Run.
60func (m Measurer) Run(
61	ctx context.Context, sess model.ExperimentSession,
62	measurement *model.Measurement, callbacks model.ExperimentCallbacks,
63) error {
64	var err error
65	if m.config.ReturnError {
66		err = ErrFailure
67	}
68	testkeys := &TestKeys{Success: err == nil}
69	measurement.TestKeys = testkeys
70	sess.Logger().Warnf("%s", "Follow the white rabbit.")
71	ctx, cancel := context.WithTimeout(ctx, time.Duration(m.config.SleepTime))
72	defer cancel()
73	<-ctx.Done()
74	sess.Logger().Infof("%s", "Knock, knock, Neo.")
75	callbacks.OnProgress(1.0, m.config.Message)
76	return err
77}
78
79// NewExperimentMeasurer creates a new ExperimentMeasurer.
80func NewExperimentMeasurer(config Config, testName string) model.ExperimentMeasurer {
81	return Measurer{config: config, testName: testName}
82}
83
84// SummaryKeys contains summary keys for this experiment.
85//
86// Note that this structure is part of the ABI contract with probe-cli
87// therefore we should be careful when changing it.
88type SummaryKeys struct {
89	IsAnomaly bool `json:"-"`
90}
91
92// GetSummaryKeys implements model.ExperimentMeasurer.GetSummaryKeys.
93func (m Measurer) GetSummaryKeys(measurement *model.Measurement) (interface{}, error) {
94	return SummaryKeys{IsAnomaly: false}, nil
95}
96