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
440
internal/engine/oonimkall/session_integration_test.go
Normal file
440
internal/engine/oonimkall/session_integration_test.go
Normal file
|
|
@ -0,0 +1,440 @@
|
|||
package oonimkall_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
engine "github.com/ooni/probe-cli/v3/internal/engine"
|
||||
"github.com/ooni/probe-cli/v3/internal/engine/geolocate"
|
||||
"github.com/ooni/probe-cli/v3/internal/engine/model"
|
||||
"github.com/ooni/probe-cli/v3/internal/engine/oonimkall"
|
||||
)
|
||||
|
||||
func NewSessionWithAssetsDir(assetsDir string) (*oonimkall.Session, error) {
|
||||
return oonimkall.NewSession(&oonimkall.SessionConfig{
|
||||
AssetsDir: assetsDir,
|
||||
ProbeServicesURL: "https://ams-pg-test.ooni.org/",
|
||||
SoftwareName: "oonimkall-test",
|
||||
SoftwareVersion: "0.1.0",
|
||||
StateDir: "../testdata/oonimkall/state",
|
||||
TempDir: "../testdata/",
|
||||
})
|
||||
}
|
||||
|
||||
func NewSession() (*oonimkall.Session, error) {
|
||||
return NewSessionWithAssetsDir("../testdata/oonimkall/assets")
|
||||
}
|
||||
|
||||
func TestNewSessionWithInvalidStateDir(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skip test in short mode")
|
||||
}
|
||||
sess, err := oonimkall.NewSession(&oonimkall.SessionConfig{
|
||||
StateDir: "",
|
||||
})
|
||||
if err == nil || !strings.HasSuffix(err.Error(), "no such file or directory") {
|
||||
t.Fatal("not the error we expected")
|
||||
}
|
||||
if sess != nil {
|
||||
t.Fatal("expected a nil Session here")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewSessionWithMissingSoftwareName(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skip test in short mode")
|
||||
}
|
||||
sess, err := oonimkall.NewSession(&oonimkall.SessionConfig{
|
||||
StateDir: "../testdata/oonimkall/state",
|
||||
})
|
||||
if err == nil || err.Error() != "AssetsDir is empty" {
|
||||
t.Fatal("not the error we expected")
|
||||
}
|
||||
if sess != nil {
|
||||
t.Fatal("expected a nil Session here")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaybeUpdateResourcesWithCancelledContext(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skip test in short mode")
|
||||
}
|
||||
dir, err := ioutil.TempDir("", "xx")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
sess, err := NewSessionWithAssetsDir(dir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx := sess.NewContext()
|
||||
ctx.Cancel() // cause immediate failure
|
||||
err = sess.MaybeUpdateResources(ctx)
|
||||
if !errors.Is(err, context.Canceled) {
|
||||
t.Fatalf("not the error we expected: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func ReduceErrorForGeolocate(err error) error {
|
||||
if err == nil {
|
||||
return errors.New("we expected an error here")
|
||||
}
|
||||
if errors.Is(err, context.Canceled) {
|
||||
return nil // when we have not downloaded the resources yet
|
||||
}
|
||||
if !errors.Is(err, geolocate.ErrAllIPLookuppersFailed) {
|
||||
return nil // otherwise
|
||||
}
|
||||
return fmt.Errorf("not the error we expected: %w", err)
|
||||
}
|
||||
|
||||
func TestGeolocateWithCancelledContext(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skip test in short mode")
|
||||
}
|
||||
sess, err := NewSession()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx := sess.NewContext()
|
||||
ctx.Cancel() // cause immediate failure
|
||||
location, err := sess.Geolocate(ctx)
|
||||
if err := ReduceErrorForGeolocate(err); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if location != nil {
|
||||
t.Fatal("expected nil location here")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGeolocateGood(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skip test in short mode")
|
||||
}
|
||||
sess, err := NewSession()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx := sess.NewContext()
|
||||
location, err := sess.Geolocate(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if location.ASN == "" {
|
||||
t.Fatal("location.ASN is empty")
|
||||
}
|
||||
if location.Country == "" {
|
||||
t.Fatal("location.Country is empty")
|
||||
}
|
||||
if location.IP == "" {
|
||||
t.Fatal("location.IP is empty")
|
||||
}
|
||||
if location.Org == "" {
|
||||
t.Fatal("location.Org is empty")
|
||||
}
|
||||
}
|
||||
|
||||
func ReduceErrorForSubmitter(err error) error {
|
||||
if err == nil {
|
||||
return errors.New("we expected an error here")
|
||||
}
|
||||
if errors.Is(err, context.Canceled) {
|
||||
return nil // when we have not downloaded the resources yet
|
||||
}
|
||||
if err.Error() == "all available probe services failed" {
|
||||
return nil // otherwise
|
||||
}
|
||||
return fmt.Errorf("not the error we expected: %w", err)
|
||||
}
|
||||
|
||||
func TestSubmitWithCancelledContext(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skip test in short mode")
|
||||
}
|
||||
sess, err := NewSession()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx := sess.NewContext()
|
||||
ctx.Cancel() // cause immediate failure
|
||||
result, err := sess.Submit(ctx, "{}")
|
||||
if err := ReduceErrorForSubmitter(err); err != nil {
|
||||
t.Fatalf("not the error we expected: %+v", err)
|
||||
}
|
||||
if result != nil {
|
||||
t.Fatal("expected nil result here")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubmitWithInvalidJSON(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skip test in short mode")
|
||||
}
|
||||
sess, err := NewSession()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx := sess.NewContext()
|
||||
result, err := sess.Submit(ctx, "{")
|
||||
if err == nil || err.Error() != "unexpected end of JSON input" {
|
||||
t.Fatalf("not the error we expected: %+v", err)
|
||||
}
|
||||
if result != nil {
|
||||
t.Fatal("expected nil result here")
|
||||
}
|
||||
}
|
||||
|
||||
func DoSubmission(ctx *oonimkall.Context, sess *oonimkall.Session) error {
|
||||
inputm := model.Measurement{
|
||||
DataFormatVersion: "0.2.0",
|
||||
MeasurementStartTime: "2019-10-28 12:51:07",
|
||||
MeasurementRuntime: 1.71,
|
||||
ProbeASN: "AS30722",
|
||||
ProbeCC: "IT",
|
||||
ProbeIP: "127.0.0.1",
|
||||
ReportID: "",
|
||||
ResolverIP: "172.217.33.129",
|
||||
SoftwareName: "miniooni",
|
||||
SoftwareVersion: "0.1.0-dev",
|
||||
TestKeys: map[string]bool{"success": true},
|
||||
TestName: "example",
|
||||
TestVersion: "0.1.0",
|
||||
}
|
||||
inputd, err := json.Marshal(inputm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := sess.Submit(ctx, string(inputd))
|
||||
if err != nil {
|
||||
return fmt.Errorf("session_test.go: submit failed: %w", err)
|
||||
}
|
||||
if result.UpdatedMeasurement == "" {
|
||||
return errors.New("expected non empty measurement")
|
||||
}
|
||||
if result.UpdatedReportID == "" {
|
||||
return errors.New("expected non empty report ID")
|
||||
}
|
||||
var outputm model.Measurement
|
||||
return json.Unmarshal([]byte(result.UpdatedMeasurement), &outputm)
|
||||
}
|
||||
|
||||
func TestSubmitMeasurementGood(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skip test in short mode")
|
||||
}
|
||||
sess, err := NewSession()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx := sess.NewContext()
|
||||
if err := DoSubmission(ctx, sess); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubmitCancelContextAfterFirstSubmission(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skip test in short mode")
|
||||
}
|
||||
sess, err := NewSession()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx := sess.NewContext()
|
||||
if err := DoSubmission(ctx, sess); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx.Cancel() // fail second submission
|
||||
err = DoSubmission(ctx, sess)
|
||||
if err == nil || !strings.HasPrefix(err.Error(), "session_test.go: submit failed") {
|
||||
t.Fatalf("not the error we expected: %+v", err)
|
||||
}
|
||||
if !errors.Is(err, context.Canceled) {
|
||||
t.Fatalf("not the error we expected: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckInSuccess(t *testing.T) {
|
||||
sess, err := NewSession()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx := sess.NewContext()
|
||||
config := oonimkall.CheckInConfig{
|
||||
Charging: true,
|
||||
OnWiFi: true,
|
||||
Platform: "android",
|
||||
RunType: "timed",
|
||||
SoftwareName: "ooniprobe-android",
|
||||
SoftwareVersion: "2.7.1",
|
||||
WebConnectivity: &oonimkall.CheckInConfigWebConnectivity{},
|
||||
}
|
||||
config.WebConnectivity.Add("NEWS")
|
||||
config.WebConnectivity.Add("CULTR")
|
||||
result, err := sess.CheckIn(ctx, &config)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %+v", err)
|
||||
}
|
||||
if result == nil || result.WebConnectivity == nil {
|
||||
t.Fatal("got nil result or WebConnectivity")
|
||||
}
|
||||
if len(result.WebConnectivity.URLs) < 1 {
|
||||
t.Fatal("unexpected number of URLs")
|
||||
}
|
||||
if result.WebConnectivity.ReportID == "" {
|
||||
t.Fatal("got empty report ID")
|
||||
}
|
||||
siz := result.WebConnectivity.Size()
|
||||
if siz <= 0 {
|
||||
t.Fatal("unexpected number of URLs")
|
||||
}
|
||||
for idx := int64(0); idx < siz; idx++ {
|
||||
entry := result.WebConnectivity.At(idx)
|
||||
if entry.CategoryCode != "NEWS" && entry.CategoryCode != "CULTR" {
|
||||
t.Fatalf("unexpected category code: %+v", entry)
|
||||
}
|
||||
}
|
||||
if result.WebConnectivity.At(-1) != nil {
|
||||
t.Fatal("expected nil here")
|
||||
}
|
||||
if result.WebConnectivity.At(siz) != nil {
|
||||
t.Fatal("expected nil here")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckInLookupLocationFailure(t *testing.T) {
|
||||
sess, err := NewSession()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx := sess.NewContext()
|
||||
config := oonimkall.CheckInConfig{
|
||||
Charging: true,
|
||||
OnWiFi: true,
|
||||
Platform: "android",
|
||||
RunType: "timed",
|
||||
SoftwareName: "ooniprobe-android",
|
||||
SoftwareVersion: "2.7.1",
|
||||
WebConnectivity: &oonimkall.CheckInConfigWebConnectivity{},
|
||||
}
|
||||
config.WebConnectivity.Add("NEWS")
|
||||
config.WebConnectivity.Add("CULTR")
|
||||
ctx.Cancel() // immediate failure
|
||||
result, err := sess.CheckIn(ctx, &config)
|
||||
if !errors.Is(err, geolocate.ErrAllIPLookuppersFailed) {
|
||||
t.Fatalf("not the error we expected: %+v", err)
|
||||
}
|
||||
if result != nil {
|
||||
t.Fatal("expected nil result here")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckInNewProbeServicesFailure(t *testing.T) {
|
||||
sess, err := NewSession()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sess.TestingCheckInBeforeNewProbeServicesClient = func(ctx *oonimkall.Context) {
|
||||
ctx.Cancel() // cancel execution
|
||||
}
|
||||
ctx := sess.NewContext()
|
||||
config := oonimkall.CheckInConfig{
|
||||
Charging: true,
|
||||
OnWiFi: true,
|
||||
Platform: "android",
|
||||
RunType: "timed",
|
||||
SoftwareName: "ooniprobe-android",
|
||||
SoftwareVersion: "2.7.1",
|
||||
WebConnectivity: &oonimkall.CheckInConfigWebConnectivity{},
|
||||
}
|
||||
config.WebConnectivity.Add("NEWS")
|
||||
config.WebConnectivity.Add("CULTR")
|
||||
result, err := sess.CheckIn(ctx, &config)
|
||||
if !errors.Is(err, engine.ErrAllProbeServicesFailed) {
|
||||
t.Fatalf("not the error we expected: %+v", err)
|
||||
}
|
||||
if result != nil {
|
||||
t.Fatal("expected nil result here")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckInCheckInFailure(t *testing.T) {
|
||||
sess, err := NewSession()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sess.TestingCheckInBeforeCheckIn = func(ctx *oonimkall.Context) {
|
||||
ctx.Cancel() // cancel execution
|
||||
}
|
||||
ctx := sess.NewContext()
|
||||
config := oonimkall.CheckInConfig{
|
||||
Charging: true,
|
||||
OnWiFi: true,
|
||||
Platform: "android",
|
||||
RunType: "timed",
|
||||
SoftwareName: "ooniprobe-android",
|
||||
SoftwareVersion: "2.7.1",
|
||||
WebConnectivity: &oonimkall.CheckInConfigWebConnectivity{},
|
||||
}
|
||||
config.WebConnectivity.Add("NEWS")
|
||||
config.WebConnectivity.Add("CULTR")
|
||||
result, err := sess.CheckIn(ctx, &config)
|
||||
if !errors.Is(err, context.Canceled) {
|
||||
t.Fatalf("not the error we expected: %+v", err)
|
||||
}
|
||||
if result != nil {
|
||||
t.Fatal("expected nil result here")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckInNoParams(t *testing.T) {
|
||||
sess, err := NewSession()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx := sess.NewContext()
|
||||
config := oonimkall.CheckInConfig{
|
||||
Charging: true,
|
||||
OnWiFi: true,
|
||||
Platform: "android",
|
||||
RunType: "timed",
|
||||
SoftwareName: "ooniprobe-android",
|
||||
SoftwareVersion: "2.7.1",
|
||||
}
|
||||
result, err := sess.CheckIn(ctx, &config)
|
||||
if err == nil || err.Error() != "oonimkall: missing webconnectivity config" {
|
||||
t.Fatalf("not the error we expected: %+v", err)
|
||||
}
|
||||
if result != nil {
|
||||
t.Fatal("unexpected not nil result here")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
// Here we're basically testing whether eventually the finalizers
|
||||
// will run and the number of active sessions and cancels will become
|
||||
// balanced. Especially for the number of active cancels, this is an
|
||||
// indication that we've correctly cleaned them up in the session.
|
||||
if exitcode := m.Run(); exitcode != 0 {
|
||||
os.Exit(exitcode)
|
||||
}
|
||||
for {
|
||||
runtime.GC()
|
||||
m, n := oonimkall.ActiveContexts.Load(), oonimkall.ActiveSessions.Load()
|
||||
fmt.Printf("./oonimkall: ActiveContexts: %d; ActiveSessions: %d\n", m, n)
|
||||
if m == 0 && n == 0 {
|
||||
break
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
Loading…
Reference in a new issue