// -=-=- StartHere -=-=-
//
// # Chapter I: main.go using the real torsf implementation
//
// In this chapter we will write together a `main.go` file that
// uses the real `torsf` implementation to run the experiment.
//
// (This file is auto-generated from the corresponding source file,
// so make sure you don't edit it manually.)
//
// ## The torsf experiment
//
// This experiment attempts to bootstrap the `tor` binary using
// Snowflake as the pluggable transport.
//
// You can read the [specification](https://github.com/ooni/spec/blob/master/nettests/ts-030-torsf.md)
// of the `torsf` experiment in the [ooni/spec](https://github.com/ooni/spec)
// repository. (The `ooni/spec` repository is the repository
// containing the specification of all OONI nettests, as well
// as of the data formats used by OONI.)
//
// ## The main.go file
//
// We define `main.go` file using `package main`.
//
// ```Go
package main

// ```
//
// ### Imports
//
// Then we add the required imports.
//
// ```Go
import (
	// ```
	//
	// These are standard library imports.
	//
	// ```Go
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"

	// ```
	//
	// The apex/log library is the logging library used by OONI Probe.
	//
	// ```Go
	"github.com/apex/log"

	// ```
	//
	// The torsf package contains the implementation of the torsf experiment.
	//
	// ```Go
	"github.com/ooni/probe-cli/v3/internal/engine/experiment/torsf"

	// ```
	//
	// The mockable package contains widely used mocks.
	//
	// ```Go
	"github.com/ooni/probe-cli/v3/internal/engine/mockable"

	// ```
	//
	// The model package contains the data model used by OONI experiments.
	//
	// ```Go
	"github.com/ooni/probe-cli/v3/internal/model"

	// ```
	//
	// We will need the execabs library to check whether there is
	// a binary called `tor` in the `PATH`.
	//
	// ```Go
	"golang.org/x/sys/execabs"
)

// ```
//
// ### Main function
//
// Finally, here's the code of the `main function`.
//
// ```Go
func main() {
	// ```
	//
	// We start by checking whether there is an executable named `"tor"` in
	// the `PATH`. If there is no such executable, we fail with an error.
	//
	// ```Go
	if _, err := execabs.LookPath("tor"); err != nil {
		log.Fatal("cannot find the tor executable in path")
	}
	// ```
	//
	// Then, we create a temporary directory to hold any state that may be
	// required either by the `tor` or by the Snowflake pluggable transport.
	//
	// ```Go
	tempdir, err := ioutil.TempDir("", "")
	if err != nil {
		log.WithError(err).Fatal("cannot create temporary directory")
	}
	// ```
	//
	// ### Creating the experiment measurer
	//
	// All OONI experiments implement a function called
	// `NewExprimentMeasurer` that allows you to make
	// an `ExperimentMeasurer` instance. The `ExperimentMeasurer`
	// is an `interface` defined by the `model` package we
	// imported above. Because we don't want to configure
	// any setting (and the experiment does not support any
	// setting anyway), here we're passing to the
	// `NewExperimentMeasurer` factory an empty `Config`.
	//
	// ```Go
	m := torsf.NewExperimentMeasurer(torsf.Config{})
	// ```
	//
	// ### Creating the measurement
	//
	// Next, we create an empty `Measurement`. OONI measurements
	// are JSON data structures that contain generic fields common
	// to all OONI experiments and experiment-specific data. The
	// experiment-specific data is contained by a the `test_keys`
	// field of the `Measurement`.
	//
	// In the *real* OONI implementation, there is common code
	// that fills the several fields of a `Measurement`. For
	// example, it will fill the country code and the autonomous
	// system number of the network in which the OONI Probe is
	// running. Because this is just an example to illustrate
	// how to write experiments, we will not bother with doing
	// that. Instead, we will pass to the experiment just an
	// emtpy measurement where no field has been set.
	//
	// ```Go
	measurement := &model.Measurement{}
	// ```
	//
	// ### Creating the callbacks
	//
	// Then, we create an instance of the experiment callbacks. The
	// experiment callbacks historically groups a set of callbacks
	// called when the measurer is running. At the moment of writing
	// this note, the `model.ExperimentCallbacks` contains just a
	// single method called `OnDataUsage`, which is used to tell the
	// caller which is the amount of data used by the experiment.
	//
	// Because this is an example for illustrative purposes, here
	// we construct an implementation of `ExperimentCallbacks` that
	// just prints the data usage using the `log.Log` logger.
	//
	// ```Go
	callbacks := model.NewPrinterCallbacks(log.Log)
	// ```
	//
	// ### Creating a session
	//
	// The `ExperimentMeasurer` also wants a `Session`. In normal
	// OONI code, the `Session` is a data structure containing
	// information regarding the current measurement session. Since
	// this is just an illustrative example, rather than creating
	// a real `Session` instance, we use much-simpler mock.
	//
	// The interface required by a `Session` is called
	// `ExperimentSession` and is part of the `model` package.
	//
	// Here we configure this mockable session to use `log.Log`
	// as a logger and the previously computed temp dir.
	//
	// ```Go
	sess := &mockable.Session{
		MockableLogger:  log.Log,
		MockableTempDir: tempdir,
	}
	// ```
	//
	// # Running the experiment
	//
	// At last, it's time to run the experiment using all the
	// previously constructed data structures. The `Run` function
	// is the main function you need to implement when you are
	// defining a new OONI experiment.
	//
	// By convention, the `Run` function only returns an error
	// when some precondition required by the experiment is
	// not met. Say that, for example, the experiment needs a
	// port listening on the local host. If we cannot create
	// such a port, we will return an error to the caller.
	//
	// For network errors, instead, we return nil. Consider the
	// case where we connect to a remote host and the connection
	// fails. This is not really an error, rather it's a result
	// that we will include into the measurement.
	//
	// Apart from the other arguments that we discussed previously,
	// the `Run` function also wants a `context.Context` as its
	// first argument. The context is used to interrupt long running
	// functions early, and our code (mostly) honours contexts.
	//
	// Since here we are just writing a simple example, we don't
	// need any fancy context and we pass a `context.Background` to `Run`.
	//
	// ```Go
	ctx := context.Background()
	if err = m.Run(ctx, sess, measurement, callbacks); err != nil {
		log.WithError(err).Fatal("torsf experiment failed")
	}
	// ```
	//
	// ### Printing the measurement result
	//
	// The `Run` function modifies the `TestKeys` (`test_keys` in JSON)
	// field of the measurement. The real OONI implementation would
	// now submit this measurement. Because this is an illustrative example,
	// we will just pretty-print the measurement on the `stdout`.
	//
	// ```Go
	data, err := json.Marshal(measurement)
	if err != nil {
		log.WithError(err).Fatal("json.Marshal failed")
	}
	fmt.Printf("%s\n", data)
}

// ```
//
// ## Running the code
//
// You can now run this code as follows:
//
// ```
// $ go run ./experiment/torsf/chapter01 | jq
// [snip]
// {
//   "data_format_version": "",
//   "input": null,
//   "measurement_start_time": "",
//   "probe_asn": "",
//   "probe_cc": "",
//   "probe_network_name": "",
//   "report_id": "",
//   "resolver_asn": "",
//   "resolver_ip": "",
//   "resolver_network_name": "",
//   "software_name": "",
//   "software_version": "",
//   "test_keys": {
//     "bootstrap_time": 68.909067459,
//     "failure": null
//   },
//   "test_name": "",
//   "test_runtime": 0,
//   "test_start_time": "",
//   "test_version": ""
//}
// ```
//
// We have snipped through logs and we have used `jq` to
// pretty print the measurement. You see that all the fields
// except the `test_keys` are empty.
//
// Let us now analyze the content of the `test_keys`:
//
// - the `bootstrap_time` field contains the time (in seconds) to
// bootstrap `tor` using the Snowflake transport;
//
// - the `failure` field contains the error that occurred, if
// any, or `null` if no error occurred.
//
// ## Concluding remarks
//
// This is all you need to know in terms of minimal code for
// running an OONI experiment. In the remainder of this tutorial,
// we will show how to reimplement the `torsf` experiment.
//
// Apart from minor changes, the `main.go` file would basically
// not change for the remainder of this tutorial.
