chore: merge probe-engine into probe-cli (#201)
This is how I did it: 1. `git clone https://github.com/ooni/probe-engine internal/engine` 2. ``` (cd internal/engine && git describe --tags) v0.23.0 ``` 3. `nvim go.mod` (merging `go.mod` with `internal/engine/go.mod` 4. `rm -rf internal/.git internal/engine/go.{mod,sum}` 5. `git add internal/engine` 6. `find . -type f -name \*.go -exec sed -i 's@/ooni/probe-engine@/ooni/probe-cli/v3/internal/engine@g' {} \;` 7. `go build ./...` (passes) 8. `go test -race ./...` (temporary failure on RiseupVPN) 9. `go mod tidy` 10. this commit message Once this piece of work is done, we can build a new version of `ooniprobe` that is using `internal/engine` directly. We need to do more work to ensure all the other functionality in `probe-engine` (e.g. making mobile packages) are still WAI. Part of https://github.com/ooni/probe/issues/1335
This commit is contained in:
parent
b1ce300c8d
commit
d57c78bc71
535 changed files with 66182 additions and 23 deletions
|
|
@ -0,0 +1,417 @@
|
|||
package webconnectivity_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/apex/log"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
engine "github.com/ooni/probe-cli/v3/internal/engine"
|
||||
"github.com/ooni/probe-cli/v3/internal/engine/experiment/webconnectivity"
|
||||
"github.com/ooni/probe-cli/v3/internal/engine/model"
|
||||
"github.com/ooni/probe-cli/v3/internal/engine/netx/archival"
|
||||
"github.com/ooni/probe-cli/v3/internal/engine/netx/errorx"
|
||||
)
|
||||
|
||||
func TestNewExperimentMeasurer(t *testing.T) {
|
||||
measurer := webconnectivity.NewExperimentMeasurer(webconnectivity.Config{})
|
||||
if measurer.ExperimentName() != "web_connectivity" {
|
||||
t.Fatal("unexpected name")
|
||||
}
|
||||
if measurer.ExperimentVersion() != "0.2.0" {
|
||||
t.Fatal("unexpected version")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSuccess(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skip test in short mode")
|
||||
}
|
||||
measurer := webconnectivity.NewExperimentMeasurer(webconnectivity.Config{})
|
||||
ctx := context.Background()
|
||||
// we need a real session because we need the web-connectivity helper
|
||||
// as well as the ASN database
|
||||
sess := newsession(t, true)
|
||||
measurement := &model.Measurement{Input: "http://www.example.com"}
|
||||
callbacks := model.NewPrinterCallbacks(log.Log)
|
||||
err := measurer.Run(ctx, sess, measurement, callbacks)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tk := measurement.TestKeys.(*webconnectivity.TestKeys)
|
||||
if tk.ControlFailure != nil {
|
||||
t.Fatal("unexpected control_failure")
|
||||
}
|
||||
if tk.DNSExperimentFailure != nil {
|
||||
t.Fatal("unexpected dns_experiment_failure")
|
||||
}
|
||||
if tk.HTTPExperimentFailure != nil {
|
||||
t.Fatal("unexpected http_experiment_failure")
|
||||
}
|
||||
// TODO(bassosimone): write further checks here?
|
||||
}
|
||||
|
||||
func TestMeasureWithCancelledContext(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skip test in short mode")
|
||||
}
|
||||
measurer := webconnectivity.NewExperimentMeasurer(webconnectivity.Config{})
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel() // immediately fail
|
||||
// we need a real session because we need the web-connectivity helper
|
||||
sess := newsession(t, true)
|
||||
measurement := &model.Measurement{Input: "http://www.example.com"}
|
||||
callbacks := model.NewPrinterCallbacks(log.Log)
|
||||
if err := measurer.Run(ctx, sess, measurement, callbacks); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tk := measurement.TestKeys.(*webconnectivity.TestKeys)
|
||||
if *tk.ControlFailure != errorx.FailureInterrupted {
|
||||
t.Fatal("unexpected control_failure")
|
||||
}
|
||||
if *tk.DNSExperimentFailure != errorx.FailureInterrupted {
|
||||
t.Fatal("unexpected dns_experiment_failure")
|
||||
}
|
||||
if tk.HTTPExperimentFailure != nil {
|
||||
t.Fatal("unexpected http_experiment_failure")
|
||||
}
|
||||
// TODO(bassosimone): write further checks here?
|
||||
sk, err := measurer.GetSummaryKeys(measurement)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, ok := sk.(webconnectivity.SummaryKeys); !ok {
|
||||
t.Fatal("invalid type for summary keys")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMeasureWithNoInput(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skip test in short mode")
|
||||
}
|
||||
measurer := webconnectivity.NewExperimentMeasurer(webconnectivity.Config{})
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// we need a real session because we need the web-connectivity helper
|
||||
sess := newsession(t, true)
|
||||
measurement := &model.Measurement{Input: ""}
|
||||
callbacks := model.NewPrinterCallbacks(log.Log)
|
||||
err := measurer.Run(ctx, sess, measurement, callbacks)
|
||||
if !errors.Is(err, webconnectivity.ErrNoInput) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tk := measurement.TestKeys.(*webconnectivity.TestKeys)
|
||||
if tk.ControlFailure != nil {
|
||||
t.Fatal("unexpected control_failure")
|
||||
}
|
||||
if tk.DNSExperimentFailure != nil {
|
||||
t.Fatal("unexpected dns_experiment_failure")
|
||||
}
|
||||
if tk.HTTPExperimentFailure != nil {
|
||||
t.Fatal("unexpected http_experiment_failure")
|
||||
}
|
||||
// TODO(bassosimone): write further checks here?
|
||||
}
|
||||
|
||||
func TestMeasureWithInputNotBeingAnURL(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skip test in short mode")
|
||||
}
|
||||
measurer := webconnectivity.NewExperimentMeasurer(webconnectivity.Config{})
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// we need a real session because we need the web-connectivity helper
|
||||
sess := newsession(t, true)
|
||||
measurement := &model.Measurement{Input: "\t\t\t\t\t\t"}
|
||||
callbacks := model.NewPrinterCallbacks(log.Log)
|
||||
err := measurer.Run(ctx, sess, measurement, callbacks)
|
||||
if !errors.Is(err, webconnectivity.ErrInputIsNotAnURL) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tk := measurement.TestKeys.(*webconnectivity.TestKeys)
|
||||
if tk.ControlFailure != nil {
|
||||
t.Fatal("unexpected control_failure")
|
||||
}
|
||||
if tk.DNSExperimentFailure != nil {
|
||||
t.Fatal("unexpected dns_experiment_failure")
|
||||
}
|
||||
if tk.HTTPExperimentFailure != nil {
|
||||
t.Fatal("unexpected http_experiment_failure")
|
||||
}
|
||||
// TODO(bassosimone): write further checks here?
|
||||
}
|
||||
|
||||
func TestMeasureWithUnsupportedInput(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skip test in short mode")
|
||||
}
|
||||
measurer := webconnectivity.NewExperimentMeasurer(webconnectivity.Config{})
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// we need a real session because we need the web-connectivity helper
|
||||
sess := newsession(t, true)
|
||||
measurement := &model.Measurement{Input: "dnslookup://example.com"}
|
||||
callbacks := model.NewPrinterCallbacks(log.Log)
|
||||
err := measurer.Run(ctx, sess, measurement, callbacks)
|
||||
if !errors.Is(err, webconnectivity.ErrUnsupportedInput) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tk := measurement.TestKeys.(*webconnectivity.TestKeys)
|
||||
if tk.ControlFailure != nil {
|
||||
t.Fatal("unexpected control_failure")
|
||||
}
|
||||
if tk.DNSExperimentFailure != nil {
|
||||
t.Fatal("unexpected dns_experiment_failure")
|
||||
}
|
||||
if tk.HTTPExperimentFailure != nil {
|
||||
t.Fatal("unexpected http_experiment_failure")
|
||||
}
|
||||
// TODO(bassosimone): write further checks here?
|
||||
}
|
||||
|
||||
func TestMeasureWithNoAvailableTestHelpers(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skip test in short mode")
|
||||
}
|
||||
measurer := webconnectivity.NewExperimentMeasurer(webconnectivity.Config{})
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// we need a real session because we need the web-connectivity helper
|
||||
sess := newsession(t, false)
|
||||
measurement := &model.Measurement{Input: "https://www.example.com"}
|
||||
callbacks := model.NewPrinterCallbacks(log.Log)
|
||||
err := measurer.Run(ctx, sess, measurement, callbacks)
|
||||
if !errors.Is(err, webconnectivity.ErrNoAvailableTestHelpers) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tk := measurement.TestKeys.(*webconnectivity.TestKeys)
|
||||
if tk.ControlFailure != nil {
|
||||
t.Fatal("unexpected control_failure")
|
||||
}
|
||||
if tk.DNSExperimentFailure != nil {
|
||||
t.Fatal("unexpected dns_experiment_failure")
|
||||
}
|
||||
if tk.HTTPExperimentFailure != nil {
|
||||
t.Fatal("unexpected http_experiment_failure")
|
||||
}
|
||||
// TODO(bassosimone): write further checks here?
|
||||
}
|
||||
|
||||
func newsession(t *testing.T, lookupBackends bool) model.ExperimentSession {
|
||||
sess, err := engine.NewSession(engine.SessionConfig{
|
||||
AssetsDir: "../../testdata",
|
||||
AvailableProbeServices: []model.Service{{
|
||||
Address: "https://ams-pg-test.ooni.org",
|
||||
Type: "https",
|
||||
}},
|
||||
Logger: log.Log,
|
||||
SoftwareName: "ooniprobe-engine",
|
||||
SoftwareVersion: "0.0.1",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if lookupBackends {
|
||||
if err := sess.MaybeLookupBackends(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if err := sess.MaybeLookupLocation(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return sess
|
||||
}
|
||||
|
||||
func TestComputeTCPBlocking(t *testing.T) {
|
||||
var (
|
||||
falseValue = false
|
||||
trueValue = true
|
||||
)
|
||||
failure := io.EOF.Error()
|
||||
anotherFailure := "unknown_error"
|
||||
type args struct {
|
||||
measurement []archival.TCPConnectEntry
|
||||
control map[string]webconnectivity.ControlTCPConnectResult
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []archival.TCPConnectEntry
|
||||
}{{
|
||||
name: "with all empty",
|
||||
args: args{},
|
||||
want: []archival.TCPConnectEntry{},
|
||||
}, {
|
||||
name: "with control failure",
|
||||
args: args{
|
||||
measurement: []archival.TCPConnectEntry{{
|
||||
IP: "1.1.1.1",
|
||||
Port: 853,
|
||||
Status: archival.TCPConnectStatus{
|
||||
Failure: &failure,
|
||||
Success: false,
|
||||
},
|
||||
}},
|
||||
},
|
||||
want: []archival.TCPConnectEntry{{
|
||||
IP: "1.1.1.1",
|
||||
Port: 853,
|
||||
Status: archival.TCPConnectStatus{
|
||||
Failure: &failure,
|
||||
Success: false,
|
||||
},
|
||||
}},
|
||||
}, {
|
||||
name: "with failures on both ends",
|
||||
args: args{
|
||||
measurement: []archival.TCPConnectEntry{{
|
||||
IP: "1.1.1.1",
|
||||
Port: 853,
|
||||
Status: archival.TCPConnectStatus{
|
||||
Failure: &failure,
|
||||
Success: false,
|
||||
},
|
||||
}},
|
||||
control: map[string]webconnectivity.ControlTCPConnectResult{
|
||||
"1.1.1.1:853": {
|
||||
Failure: &anotherFailure,
|
||||
Status: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []archival.TCPConnectEntry{{
|
||||
IP: "1.1.1.1",
|
||||
Port: 853,
|
||||
Status: archival.TCPConnectStatus{
|
||||
Blocked: &falseValue,
|
||||
Failure: &failure,
|
||||
Success: false,
|
||||
},
|
||||
}},
|
||||
}, {
|
||||
name: "with failure on the probe side",
|
||||
args: args{
|
||||
measurement: []archival.TCPConnectEntry{{
|
||||
IP: "1.1.1.1",
|
||||
Port: 853,
|
||||
Status: archival.TCPConnectStatus{
|
||||
Failure: &failure,
|
||||
Success: false,
|
||||
},
|
||||
}},
|
||||
control: map[string]webconnectivity.ControlTCPConnectResult{
|
||||
"1.1.1.1:853": {
|
||||
Failure: nil,
|
||||
Status: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []archival.TCPConnectEntry{{
|
||||
IP: "1.1.1.1",
|
||||
Port: 853,
|
||||
Status: archival.TCPConnectStatus{
|
||||
Blocked: &trueValue,
|
||||
Failure: &failure,
|
||||
Success: false,
|
||||
},
|
||||
}},
|
||||
}, {
|
||||
name: "with failure on the control side",
|
||||
args: args{
|
||||
measurement: []archival.TCPConnectEntry{{
|
||||
IP: "1.1.1.1",
|
||||
Port: 853,
|
||||
Status: archival.TCPConnectStatus{
|
||||
Failure: nil,
|
||||
Success: true,
|
||||
},
|
||||
}},
|
||||
control: map[string]webconnectivity.ControlTCPConnectResult{
|
||||
"1.1.1.1:853": {
|
||||
Failure: &failure,
|
||||
Status: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []archival.TCPConnectEntry{{
|
||||
IP: "1.1.1.1",
|
||||
Port: 853,
|
||||
Status: archival.TCPConnectStatus{
|
||||
Blocked: &falseValue,
|
||||
Failure: nil,
|
||||
Success: true,
|
||||
},
|
||||
}},
|
||||
}}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := webconnectivity.ComputeTCPBlocking(tt.args.measurement, tt.args.control)
|
||||
if diff := cmp.Diff(tt.want, got); diff != "" {
|
||||
t.Fatal(diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSummaryKeysInvalidType(t *testing.T) {
|
||||
measurement := new(model.Measurement)
|
||||
m := &webconnectivity.Measurer{}
|
||||
_, err := m.GetSummaryKeys(measurement)
|
||||
if err.Error() != "invalid test keys type" {
|
||||
t.Fatal("not the error we expected")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSummaryKeysWorksAsIntended(t *testing.T) {
|
||||
failure := io.EOF.Error()
|
||||
truy := true
|
||||
tests := []struct {
|
||||
tk webconnectivity.TestKeys
|
||||
Accessible bool
|
||||
Blocking string
|
||||
isAnomaly bool
|
||||
}{{
|
||||
tk: webconnectivity.TestKeys{},
|
||||
Accessible: false,
|
||||
Blocking: "",
|
||||
isAnomaly: false,
|
||||
}, {
|
||||
tk: webconnectivity.TestKeys{Summary: webconnectivity.Summary{
|
||||
BlockingReason: &failure,
|
||||
}},
|
||||
Accessible: false,
|
||||
Blocking: failure,
|
||||
isAnomaly: true,
|
||||
}, {
|
||||
tk: webconnectivity.TestKeys{Summary: webconnectivity.Summary{
|
||||
Accessible: &truy,
|
||||
}},
|
||||
Accessible: true,
|
||||
Blocking: "",
|
||||
isAnomaly: false,
|
||||
}}
|
||||
for idx, tt := range tests {
|
||||
t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) {
|
||||
m := &webconnectivity.Measurer{}
|
||||
measurement := &model.Measurement{TestKeys: &tt.tk}
|
||||
got, err := m.GetSummaryKeys(measurement)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
sk := got.(webconnectivity.SummaryKeys)
|
||||
if sk.IsAnomaly != tt.isAnomaly {
|
||||
t.Fatal("unexpected isAnomaly value")
|
||||
}
|
||||
if sk.Accessible != tt.Accessible {
|
||||
t.Fatal("unexpected Accessible value")
|
||||
}
|
||||
if sk.Blocking != tt.Blocking {
|
||||
t.Fatal("unexpected Accessible value")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue