Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tests: reliable workers tests for reordering and acks #60

Merged
merged 81 commits into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
91057af
implementation of reliable transport
ainghazal Jan 24, 2024
aebf87e
rename & reordering
ainghazal Jan 24, 2024
b420eb1
wip
ainghazal Jan 24, 2024
add18bd
remove command, separate pr
ainghazal Jan 24, 2024
2c9761f
add command to get handshake logs
ainghazal Jan 24, 2024
51e2d34
remove unused code
ainghazal Jan 24, 2024
c3cb64d
add sender logging
ainghazal Jan 24, 2024
c85b9f3
add precision time logging in the text handler
ainghazal Jan 24, 2024
ba650b8
clearer logging
ainghazal Jan 24, 2024
0525989
unify packet logging
ainghazal Jan 24, 2024
9e5b744
checkpoint: improve logging, hack client hello ack to get moving
ainghazal Jan 25, 2024
f34af5c
checkpoint
ainghazal Jan 25, 2024
ff1ec51
checkpoint
ainghazal Jan 25, 2024
06d6c37
x
ainghazal Jan 25, 2024
bfd4380
testing
ainghazal Jan 25, 2024
44c6f9b
pass option to do just the handshake and no routes
ainghazal Jan 25, 2024
0e074d1
defend if data before keys
ainghazal Jan 25, 2024
2c6c941
improve comment
ainghazal Jan 29, 2024
b512ad9
comment on newHardResetPacket
ainghazal Jan 29, 2024
690762e
comments
ainghazal Jan 29, 2024
675fd8f
add elapsed time for benchmarking
ainghazal Jan 29, 2024
5b4d2eb
log
ainghazal Jan 29, 2024
8c5816c
add doc.go
ainghazal Jan 29, 2024
0c37284
x
ainghazal Jan 29, 2024
2fb6a3c
remove unused code
ainghazal Jan 29, 2024
80adb95
move comment location
ainghazal Jan 29, 2024
ba94049
x
ainghazal Jan 29, 2024
c941f02
remove commented code
ainghazal Jan 29, 2024
1bc2361
add link in docs
ainghazal Jan 29, 2024
20f89e2
rename in test
ainghazal Jan 29, 2024
9f751fb
remove binary
ainghazal Jan 29, 2024
f30db74
revert Makefile
ainghazal Jan 29, 2024
582ef20
Update internal/packetmuxer/service.go
ainghazal Jan 30, 2024
dcdffec
Update internal/reliabletransport/doc.go
ainghazal Jan 30, 2024
286b136
rename
ainghazal Jan 30, 2024
1fbbee5
apply suggestion
ainghazal Jan 30, 2024
2a15f2d
apply suggestions
ainghazal Jan 30, 2024
09fcc51
terminology
ainghazal Jan 30, 2024
e50e410
fix docs
ainghazal Jan 30, 2024
2095cab
inflight does not need to implement sort
ainghazal Jan 30, 2024
7009ffb
implement set for ack queue
ainghazal Jan 30, 2024
32a83e0
add tests for pending acks + evict after ack
ainghazal Jan 30, 2024
a814e13
test next packet ids to ack
ainghazal Jan 30, 2024
c1747c5
test ack empties
ainghazal Jan 30, 2024
9aaa7f1
remove tlssession logging from this pr, separated in a different one
ainghazal Jan 30, 2024
cc4f35f
note
ainghazal Jan 30, 2024
bddbfe4
x
ainghazal Jan 31, 2024
9171beb
Merge branch 'main' into refactor-reliable-impl
ainghazal Jan 31, 2024
037e6d1
test for inflightSequence
ainghazal Jan 31, 2024
5fcc0c6
fix tests for receiver
ainghazal Jan 31, 2024
60cf8ac
more coverage
ainghazal Jan 31, 2024
ae27c8f
x
ainghazal Jan 31, 2024
b1b0904
tests for next wakeup
ainghazal Jan 31, 2024
a701b83
tests for service initialization
ainghazal Jan 31, 2024
8071942
add more unit tests
ainghazal Jan 31, 2024
8547daf
first naive reordering test
ainghazal Feb 1, 2024
d07895c
going for a walk outside
ainghazal Feb 1, 2024
c5718ad
debug: wtf is going on
ainghazal Feb 1, 2024
b18c3ee
fix bug in sender that breaks loop
ainghazal Feb 1, 2024
a6cece1
remove debug lines
ainghazal Feb 1, 2024
5a961eb
cleanup test a bit
ainghazal Feb 1, 2024
6abd35d
add vpntest module
ainghazal Feb 2, 2024
bf7b83b
cosmetic changes
ainghazal Feb 2, 2024
39bc222
test packetio
ainghazal Feb 2, 2024
0bcb8de
parse acks
ainghazal Feb 2, 2024
d85275c
wip testing acks
ainghazal Feb 2, 2024
31abc06
ack testing utils
ainghazal Feb 2, 2024
7b92e6c
wip: ack duplicates compare set
ainghazal Feb 2, 2024
66a82dc
Merge branch 'main' into reliable-service-tests
ainghazal Feb 2, 2024
6bdc2d5
run the new tests in internal too
ainghazal Feb 2, 2024
80e8e50
x
ainghazal Feb 2, 2024
2a8632b
Merge branch 'main' into reliable-service-tests
ainghazal Feb 2, 2024
c84ad65
return if ack error
ainghazal Feb 2, 2024
cf17b41
add targets for testing internal path
ainghazal Feb 2, 2024
cd00bda
relax coverage threshold for now
ainghazal Feb 2, 2024
893f4e0
coverage for refactor
ainghazal Feb 2, 2024
7dba3b7
igore coverage output
ainghazal Feb 2, 2024
b902a49
remove extra comment
ainghazal Feb 2, 2024
56ab133
Update internal/model/packet.go
ainghazal Feb 7, 2024
20deba9
Update internal/vpntest/packetio.go
ainghazal Feb 7, 2024
8a042ba
address comments from code review
ainghazal Feb 7, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .github/workflows/build-refactor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: build-refactor
# this action is covering internal/ tree with go1.21

on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
short-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: setup go
uses: actions/setup-go@v2
with:
go-version: '1.21'
- name: Run short tests
run: go test --short -cover ./internal/...

gosec:
runs-on: ubuntu-latest
env:
GO111MODULE: on
steps:
- name: Checkout Source
uses: actions/checkout@v2
- name: Run Gosec security scanner
uses: securego/gosec@master
with:
args: '-no-fail ./...'

coverage-threshold:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: setup go
uses: actions/setup-go@v2
with:
go-version: '1.21'
- name: Ensure coverage threshold
run: make test-coverage-threshold-refactor

integration:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: setup go
uses: actions/setup-go@v2
with:
go-version: '1.21'
- name: run integration tests
run: go test -v ./tests/integration

4 changes: 1 addition & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
*.swo
*.pem
*.ovpn
/*.out
data/*
measurements/*
coverage.out
coverage-ping.out
cov-threshold.out
9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ TARGET ?= "1.1.1.1"
COUNT ?= 5
TIMEOUT ?= 10
LOCAL_TARGET := $(shell ip -4 addr show docker0 | grep 'inet ' | awk '{print $$2}' | cut -f 1 -d /)
COVERAGE_THRESHOLD := 88
COVERAGE_THRESHOLD := 80
FLAGS=-ldflags="-w -s -buildid=none -linkmode=external" -buildmode=pie -buildvcs=false

build:
Expand Down Expand Up @@ -33,10 +33,17 @@ test:
test-coverage:
go test -coverprofile=coverage.out ./vpn

test-coverage-refactor:
go test -coverprofile=coverage.out ./internal/...

test-coverage-threshold:
go test --short -coverprofile=cov-threshold.out ./vpn
./scripts/go-coverage-check.sh cov-threshold.out ${COVERAGE_THRESHOLD}

test-coverage-threshold-refactor:
go test --short -coverprofile=cov-threshold-refactor.out ./internal/...
./scripts/go-coverage-check.sh cov-threshold-refactor.out ${COVERAGE_THRESHOLD}

test-short:
go test -race -short -v ./...

Expand Down
27 changes: 27 additions & 0 deletions internal/model/packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,33 @@ const (
P_DATA_V2 // 9
)

// NewOpcodeFromString returns an opcode from a string representation, and an error if it cannot parse the opcode
// representation. The zero return value is invalid and always coupled with a non-nil error.
func NewOpcodeFromString(s string) (Opcode, error) {
switch s {
case "CONTROL_HARD_RESET_CLIENT_V1":
return P_CONTROL_HARD_RESET_CLIENT_V1, nil
case "CONTROL_HARD_RESET_SERVER_V1":
return P_CONTROL_HARD_RESET_SERVER_V1, nil
case "CONTROL_SOFT_RESET_V1":
return P_CONTROL_SOFT_RESET_V1, nil
case "CONTROL_V1":
return P_CONTROL_V1, nil
case "ACK_V1":
return P_ACK_V1, nil
case "DATA_V1":
return P_DATA_V1, nil
case "CONTROL_HARD_RESET_CLIENT_V2":
return P_CONTROL_HARD_RESET_CLIENT_V2, nil
case "CONTROL_HARD_RESET_SERVER_V2":
return P_CONTROL_HARD_RESET_SERVER_V2, nil
case "DATA_V2":
return P_DATA_V2, nil
default:
return 0, errors.New("unknown opcode")
}
}

// String returns the opcode string representation
func (op Opcode) String() string {
switch op {
Expand Down
47 changes: 47 additions & 0 deletions internal/reliabletransport/common_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package reliabletransport

import (
"github.com/apex/log"
"github.com/ooni/minivpn/internal/bytesx"
"github.com/ooni/minivpn/internal/model"
"github.com/ooni/minivpn/internal/runtimex"
"github.com/ooni/minivpn/internal/session"
"github.com/ooni/minivpn/internal/workers"
)

//
// Common utilities for tests in this package.
//

// initManagers initializes a workers manager and a session manager.
func initManagers() (*workers.Manager, *session.Manager) {
w := workers.NewManager(log.Log)
s, err := session.NewManager(log.Log)
runtimex.PanicOnError(err, "cannot create session manager")
return w, s
}

// newRandomSessionID returns a random session ID to initialize mock sessions.
func newRandomSessionID() model.SessionID {
b, err := bytesx.GenRandomBytes(8)
if err != nil {
panic(err)
}
return model.SessionID(b)
}

func ackSetFromInts(s []int) *ackSet {
acks := make([]model.PacketID, 0)
for _, i := range s {
acks = append(acks, model.PacketID(i))
}
return newACKSet(acks...)
}

func ackSetFromRange(start, total int) *ackSet {
acks := make([]model.PacketID, 0)
for i := 0; i < total; i++ {
acks = append(acks, model.PacketID(start+i))
}
return newACKSet(acks...)
}
4 changes: 2 additions & 2 deletions internal/reliabletransport/receiver.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package reliabletransport
import (
"bytes"
"fmt"
"log"
ainghazal marked this conversation as resolved.
Show resolved Hide resolved
"sort"

"github.com/ooni/minivpn/internal/model"
Expand Down Expand Up @@ -42,7 +41,7 @@ func (ws *workersState) moveUpWorker() {
// We should be able to deterministically test how this affects the state machine.

// drop a packet that is not for our session
if !bytes.Equal(packet.LocalSessionID[:], ws.sessionManager.RemoteSessionID()) {
if !bytes.Equal(packet.RemoteSessionID[:], ws.sessionManager.LocalSessionID()) {
ws.logger.Warnf(
"%s: packet with invalid RemoteSessionID: expected %x; got %x",
workerName,
Expand All @@ -64,6 +63,7 @@ func (ws *workersState) moveUpWorker() {

if inserted := receiver.MaybeInsertIncoming(packet); !inserted {
// this packet was not inserted in the queue: we drop it
ws.logger.Debugf("Dropping packet: %v", packet.ID)
continue
}

Expand Down
164 changes: 164 additions & 0 deletions internal/reliabletransport/reliable_ack_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
package reliabletransport

import (
"slices"
"testing"
"time"

"github.com/apex/log"
"github.com/ooni/minivpn/internal/model"
"github.com/ooni/minivpn/internal/vpntest"
)

// test that everything that is received from below is eventually ACKed to the sender.
func TestReliable_ACK(t *testing.T) {

log.SetLevel(log.DebugLevel)

type args struct {
inputSequence []string
start int
wantacks int
}

tests := []struct {
name string
args args
}{
{
name: "ten ordered packets in",
args: args{
inputSequence: []string{
"[1] CONTROL_V1 +1ms",
"[2] CONTROL_V1 +1ms",
"[3] CONTROL_V1 +1ms",
"[4] CONTROL_V1 +1ms",
"[5] CONTROL_V1 +1ms",
"[6] CONTROL_V1 +1ms",
"[7] CONTROL_V1 +1ms",
"[8] CONTROL_V1 +1ms",
"[9] CONTROL_V1 +1ms",
"[10] CONTROL_V1 +1ms",
},
start: 1,
wantacks: 10,
},
},
{
name: "five ordered packets with offset",
args: args{
inputSequence: []string{
"[100] CONTROL_V1 +1ms",
"[101] CONTROL_V1 +1ms",
"[102] CONTROL_V1 +1ms",
"[103] CONTROL_V1 +1ms",
"[104] CONTROL_V1 +1ms",
},
start: 100,
wantacks: 5,
},
},
{
name: "five reversed packets",
args: args{
inputSequence: []string{
"[5] CONTROL_V1 +1ms",
"[4] CONTROL_V1 +1ms",
"[3] CONTROL_V1 +1ms",
"[2] CONTROL_V1 +1ms",
"[1] CONTROL_V1 +1ms",
},
start: 1,
wantacks: 5,
},
},
{
name: "ten unordered packets with duplicates",
args: args{
inputSequence: []string{
"[5] CONTROL_V1 +1ms",
"[1] CONTROL_V1 +1ms",
"[5] CONTROL_V1 +1ms",
"[2] CONTROL_V1 +1ms",
"[1] CONTROL_V1 +1ms",
"[4] CONTROL_V1 +1ms",
"[2] CONTROL_V1 +1ms",
"[3] CONTROL_V1 +1ms",
"[3] CONTROL_V1 +1ms",
"[4] CONTROL_V1 +1ms",
},
start: 1,
wantacks: 5,
},
},
/*
{
name: "a burst of packets",
args: args{
inputSequence: []string{
"[5] CONTROL_V1 +1ms",
"[1] CONTROL_V1 +1ms",
"[3] CONTROL_V1 +1ms",
"[2] CONTROL_V1 +1ms",
"[4] CONTROL_V1 +1ms",
},
start: 1,
wantacks: 5,
},
},
*/
Comment on lines +94 to +109
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should delete commented-out code or explain why there's commented out code

}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := &Service{}

// just to properly initialize it, we don't care about these
s.ControlToReliable = make(chan *model.Packet)
// this one up to control/tls also needs to be buffered because otherwise
// we'll block on the receiver when delivering up.
reliableToControl := make(chan *model.Packet, 1024)
s.ReliableToControl = &reliableToControl

// the only two channels we're going to be testing on this test
// we want to buffer enough to be safe writing to them.
dataIn := make(chan *model.Packet, 1024)
dataOut := make(chan *model.Packet, 1024)

s.MuxerToReliable = dataIn // up
s.DataOrControlToMuxer = &dataOut // down

workers, session := initManagers()

t0 := time.Now()

// let the workers pump up the jam!
s.StartWorkers(log.Log, workers, session)

writer := vpntest.NewPacketWriter(dataIn)

// initialize a mock session ID for our peer
peerSessionID := newRandomSessionID()

writer.RemoteSessionID = model.SessionID(session.LocalSessionID())
writer.LocalSessionID = peerSessionID
session.SetRemoteSessionID(peerSessionID)

go writer.WriteSequence(tt.args.inputSequence)

reader := vpntest.NewPacketReader(dataOut)
witness := vpntest.NewWitness(reader)

if ok := witness.VerifyNumberOfACKs(tt.args.start, tt.args.wantacks, t0); !ok {
got := len(witness.Log().ACKs())
t.Errorf("TestACK: got = %v, want %v", got, tt.args.wantacks)
}
gotAckSet := ackSetFromInts(witness.Log().ACKs()).sorted()
wantAckSet := ackSetFromRange(tt.args.start, tt.args.wantacks).sorted()

if !slices.Equal(gotAckSet, wantAckSet) {
t.Errorf("TestACK: got = %v, want %v", gotAckSet, wantAckSet)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

}
})
}
}
Loading
Loading