mirror of
https://github.com/safing/portmaster
synced 2025-04-19 10:29:11 +00:00
188 lines
4.1 KiB
Go
188 lines
4.1 KiB
Go
package navigator
|
|
|
|
import (
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/safing/portmaster/spn/hub"
|
|
)
|
|
|
|
var (
|
|
optimizedDefaultMapCreate sync.Once
|
|
optimizedDefaultMap *Map
|
|
)
|
|
|
|
func getOptimizedDefaultTestMap(t *testing.T) *Map {
|
|
t.Helper()
|
|
|
|
optimizedDefaultMapCreate.Do(func() {
|
|
optimizedDefaultMap = createRandomTestMap(2, 100)
|
|
optimizedDefaultMap.optimizeTestMap(t)
|
|
})
|
|
return optimizedDefaultMap
|
|
}
|
|
|
|
func (m *Map) optimizeTestMap(t *testing.T) {
|
|
t.Helper()
|
|
t.Logf("optimizing test map %s with %d pins", m.Name, len(m.all))
|
|
|
|
// Save original Home, as we will be switching around the home for the
|
|
// optimization.
|
|
run := 0
|
|
newLanes := 0
|
|
originalHome := m.home
|
|
mcf := newMeasurementCachedFactory()
|
|
|
|
for {
|
|
run++
|
|
newLanesInRun := 0
|
|
// Let's check if we have a run without any map changes.
|
|
lastRun := true
|
|
|
|
for _, pin := range m.all {
|
|
// Set Home to this Pin for this iteration.
|
|
if !m.SetHome(pin.Hub.ID, nil) {
|
|
panic("failed to set home")
|
|
}
|
|
|
|
// Update measurements for the new home.
|
|
updateMeasurements(m, mcf)
|
|
|
|
optimizeResult, err := m.optimize(nil)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
lanesCreatedWithResult := 0
|
|
for _, connectTo := range optimizeResult.SuggestedConnections {
|
|
// Check if lane to suggested Hub already exists.
|
|
if m.home.Hub.GetLaneTo(connectTo.Hub.ID) != nil {
|
|
continue
|
|
}
|
|
|
|
// Add lanes to the Hub status.
|
|
_ = m.home.Hub.AddLane(createLane(connectTo.Hub.ID))
|
|
_ = connectTo.Hub.AddLane(createLane(m.home.Hub.ID))
|
|
|
|
// Update Hubs in map.
|
|
m.UpdateHub(m.home.Hub)
|
|
m.UpdateHub(connectTo.Hub)
|
|
newLanes++
|
|
newLanesInRun++
|
|
|
|
// We are changing the map in this run, so this is not the last.
|
|
lastRun = false
|
|
|
|
// Only create as many lanes as suggested by the result.
|
|
lanesCreatedWithResult++
|
|
if lanesCreatedWithResult >= optimizeResult.MaxConnect {
|
|
break
|
|
}
|
|
}
|
|
if optimizeResult.Purpose != OptimizePurposeTargetStructure {
|
|
// If we aren't yet building the target structure, we need to keep building.
|
|
lastRun = false
|
|
}
|
|
}
|
|
|
|
// Log progress.
|
|
if t != nil {
|
|
t.Logf(
|
|
"optimizing: added %d lanes in run #%d (%d Hubs) - %d new lanes in total",
|
|
newLanesInRun,
|
|
run,
|
|
len(m.all),
|
|
newLanes,
|
|
)
|
|
}
|
|
|
|
// End optimization after last run.
|
|
if lastRun {
|
|
break
|
|
}
|
|
}
|
|
|
|
// Log what was done and set home back to the original value.
|
|
if t != nil {
|
|
t.Logf("finished optimizing test map %s: added %d lanes in %d runs", m.Name, newLanes, run)
|
|
}
|
|
m.home = originalHome
|
|
}
|
|
|
|
func TestOptimize(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
m := getOptimizedDefaultTestMap(t)
|
|
matcher := m.defaultOptions().Destination.Matcher(m.intel)
|
|
originalHome := m.home
|
|
|
|
for _, pin := range m.all {
|
|
// Set Home to this Pin for this iteration.
|
|
m.home = pin
|
|
err := m.recalculateReachableHubs()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
for _, peer := range m.all {
|
|
// Check if the Pin matches the criteria.
|
|
if !matcher(peer) {
|
|
continue
|
|
}
|
|
|
|
// TODO: Adapt test to new regions.
|
|
if peer.HopDistance > 5 {
|
|
t.Errorf("Optimization error: %s is %d hops away from %s", peer, peer.HopDistance, pin)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Print stats
|
|
t.Logf("optimized map:\n%s\n", m.Stats())
|
|
|
|
m.home = originalHome
|
|
}
|
|
|
|
func updateMeasurements(m *Map, mcf *measurementCachedFactory) {
|
|
for _, pin := range m.all {
|
|
pin.measurements = mcf.getOrCreate(m.home.Hub.ID, pin.Hub.ID)
|
|
}
|
|
}
|
|
|
|
type measurementCachedFactory struct {
|
|
cache map[string]*hub.Measurements
|
|
}
|
|
|
|
func newMeasurementCachedFactory() *measurementCachedFactory {
|
|
return &measurementCachedFactory{
|
|
cache: make(map[string]*hub.Measurements),
|
|
}
|
|
}
|
|
|
|
func (mcf *measurementCachedFactory) getOrCreate(from, to string) *hub.Measurements {
|
|
var id string
|
|
comparison := strings.Compare(from, to)
|
|
switch {
|
|
case comparison == 0:
|
|
return nil
|
|
case comparison > 0:
|
|
id = from + "-" + to
|
|
case comparison < 0:
|
|
id = to + "-" + from
|
|
}
|
|
|
|
m, ok := mcf.cache[id]
|
|
if ok {
|
|
return m
|
|
}
|
|
|
|
m = hub.NewMeasurements()
|
|
m.Latency = createLatency()
|
|
m.Capacity = createCapacity()
|
|
m.CalculatedCost = CalculateLaneCost(
|
|
m.Latency,
|
|
m.Capacity,
|
|
)
|
|
mcf.cache[id] = m
|
|
return m
|
|
}
|