Restructure modules (#1572)
* Move portbase into monorepo * Add new simple module mgr * [WIP] Switch to new simple module mgr * Add StateMgr and more worker variants * [WIP] Switch more modules * [WIP] Switch more modules * [WIP] swtich more modules * [WIP] switch all SPN modules * [WIP] switch all service modules * [WIP] Convert all workers to the new module system * [WIP] add new task system to module manager * [WIP] Add second take for scheduling workers * [WIP] Add FIXME for bugs in new scheduler * [WIP] Add minor improvements to scheduler * [WIP] Add new worker scheduler * [WIP] Fix more bug related to new module system * [WIP] Fix start handing of the new module system * [WIP] Improve startup process * [WIP] Fix minor issues * [WIP] Fix missing subsystem in settings * [WIP] Initialize managers in constructor * [WIP] Move module event initialization to constrictors * [WIP] Fix setting for enabling and disabling the SPN module * [WIP] Move API registeration into module construction * [WIP] Update states mgr for all modules * [WIP] Add CmdLine operation support * Add state helper methods to module group and instance * Add notification and module status handling to status package * Fix starting issues * Remove pilot widget and update security lock to new status data * Remove debug logs * Improve http server shutdown * Add workaround for cleanly shutting down firewall+netquery * Improve logging * Add syncing states with notifications for new module system * Improve starting, stopping, shutdown; resolve FIXMEs/TODOs * [WIP] Fix most unit tests * Review new module system and fix minor issues * Push shutdown and restart events again via API * Set sleep mode via interface * Update example/template module * [WIP] Fix spn/cabin unit test * Remove deprecated UI elements * Make log output more similar for the logging transition phase * Switch spn hub and observer cmds to new module system * Fix log sources * Make worker mgr less error prone * Fix tests and minor issues * Fix observation hub * Improve shutdown and restart handling * Split up big connection.go source file * Move varint and dsd packages to structures repo * Improve expansion test * Fix linter warnings * Fix interception module on windows * Fix linter errors --------- Co-authored-by: Vladimir Stoilov <vladimir@safing.io>
This commit is contained in:
368
base/config/get_test.go
Normal file
368
base/config/get_test.go
Normal file
@@ -0,0 +1,368 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/safing/portmaster/base/log"
|
||||
)
|
||||
|
||||
func parseAndReplaceConfig(jsonData string) error {
|
||||
m, err := JSONToMap([]byte(jsonData))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
validationErrors, _ := ReplaceConfig(m)
|
||||
if len(validationErrors) > 0 {
|
||||
return fmt.Errorf("%d errors, first: %w", len(validationErrors), validationErrors[0])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseAndReplaceDefaultConfig(jsonData string) error {
|
||||
m, err := JSONToMap([]byte(jsonData))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
validationErrors, _ := ReplaceDefaultConfig(m)
|
||||
if len(validationErrors) > 0 {
|
||||
return fmt.Errorf("%d errors, first: %w", len(validationErrors), validationErrors[0])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func quickRegister(t *testing.T, key string, optType OptionType, defaultValue interface{}) {
|
||||
t.Helper()
|
||||
|
||||
err := Register(&Option{
|
||||
Name: key,
|
||||
Key: key,
|
||||
Description: "test config",
|
||||
ReleaseLevel: ReleaseLevelStable,
|
||||
ExpertiseLevel: ExpertiseLevelUser,
|
||||
OptType: optType,
|
||||
DefaultValue: defaultValue,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) { //nolint:paralleltest
|
||||
// reset
|
||||
options = make(map[string]*Option)
|
||||
|
||||
err := log.Start()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
quickRegister(t, "monkey", OptTypeString, "c")
|
||||
quickRegister(t, "zebras/zebra", OptTypeStringArray, []string{"a", "b"})
|
||||
quickRegister(t, "elephant", OptTypeInt, -1)
|
||||
quickRegister(t, "hot", OptTypeBool, false)
|
||||
quickRegister(t, "cold", OptTypeBool, true)
|
||||
|
||||
err = parseAndReplaceConfig(`
|
||||
{
|
||||
"monkey": "a",
|
||||
"zebras": {
|
||||
"zebra": ["black", "white"]
|
||||
},
|
||||
"elephant": 2,
|
||||
"hot": true,
|
||||
"cold": false
|
||||
}
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = parseAndReplaceDefaultConfig(`
|
||||
{
|
||||
"monkey": "b",
|
||||
"snake": "0",
|
||||
"elephant": 0
|
||||
}
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
monkey := GetAsString("monkey", "none")
|
||||
if monkey() != "a" {
|
||||
t.Errorf("monkey should be a, is %s", monkey())
|
||||
}
|
||||
|
||||
zebra := GetAsStringArray("zebras/zebra", []string{})
|
||||
if len(zebra()) != 2 || zebra()[0] != "black" || zebra()[1] != "white" {
|
||||
t.Errorf("zebra should be [\"black\", \"white\"], is %v", zebra())
|
||||
}
|
||||
|
||||
elephant := GetAsInt("elephant", -1)
|
||||
if elephant() != 2 {
|
||||
t.Errorf("elephant should be 2, is %d", elephant())
|
||||
}
|
||||
|
||||
hot := GetAsBool("hot", false)
|
||||
if !hot() {
|
||||
t.Errorf("hot should be true, is %v", hot())
|
||||
}
|
||||
|
||||
cold := GetAsBool("cold", true)
|
||||
if cold() {
|
||||
t.Errorf("cold should be false, is %v", cold())
|
||||
}
|
||||
|
||||
err = parseAndReplaceConfig(`
|
||||
{
|
||||
"monkey": "3"
|
||||
}
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if monkey() != "3" {
|
||||
t.Errorf("monkey should be 0, is %s", monkey())
|
||||
}
|
||||
|
||||
if elephant() != 0 {
|
||||
t.Errorf("elephant should be 0, is %d", elephant())
|
||||
}
|
||||
|
||||
zebra()
|
||||
hot()
|
||||
|
||||
// concurrent
|
||||
GetAsString("monkey", "none")()
|
||||
GetAsStringArray("zebras/zebra", []string{})()
|
||||
GetAsInt("elephant", -1)()
|
||||
GetAsBool("hot", false)()
|
||||
|
||||
// perspective
|
||||
|
||||
// load data
|
||||
pLoaded := make(map[string]interface{})
|
||||
err = json.Unmarshal([]byte(`{
|
||||
"monkey": "a",
|
||||
"zebras": {
|
||||
"zebra": ["black", "white"]
|
||||
},
|
||||
"elephant": 2,
|
||||
"hot": true,
|
||||
"cold": false
|
||||
}`), &pLoaded)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// create
|
||||
p, err := NewPerspective(pLoaded)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
monkeyVal, ok := p.GetAsString("monkey")
|
||||
if !ok || monkeyVal != "a" {
|
||||
t.Errorf("[perspective] monkey should be a, is %+v", monkeyVal)
|
||||
}
|
||||
|
||||
zebraVal, ok := p.GetAsStringArray("zebras/zebra")
|
||||
if !ok || len(zebraVal) != 2 || zebraVal[0] != "black" || zebraVal[1] != "white" {
|
||||
t.Errorf("[perspective] zebra should be [\"black\", \"white\"], is %+v", zebraVal)
|
||||
}
|
||||
|
||||
elephantVal, ok := p.GetAsInt("elephant")
|
||||
if !ok || elephantVal != 2 {
|
||||
t.Errorf("[perspective] elephant should be 2, is %+v", elephantVal)
|
||||
}
|
||||
|
||||
hotVal, ok := p.GetAsBool("hot")
|
||||
if !ok || !hotVal {
|
||||
t.Errorf("[perspective] hot should be true, is %+v", hotVal)
|
||||
}
|
||||
|
||||
coldVal, ok := p.GetAsBool("cold")
|
||||
if !ok || coldVal {
|
||||
t.Errorf("[perspective] cold should be false, is %+v", coldVal)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReleaseLevel(t *testing.T) { //nolint:paralleltest
|
||||
// reset
|
||||
options = make(map[string]*Option)
|
||||
registerReleaseLevelOption()
|
||||
|
||||
// setup
|
||||
subsystemOption := &Option{
|
||||
Name: "test subsystem",
|
||||
Key: "subsystem/test",
|
||||
Description: "test config",
|
||||
ReleaseLevel: ReleaseLevelStable,
|
||||
ExpertiseLevel: ExpertiseLevelUser,
|
||||
OptType: OptTypeBool,
|
||||
DefaultValue: false,
|
||||
}
|
||||
err := Register(subsystemOption)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = SetConfigOption("subsystem/test", true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
testSubsystem := GetAsBool("subsystem/test", false)
|
||||
|
||||
// test option level stable
|
||||
subsystemOption.ReleaseLevel = ReleaseLevelStable
|
||||
err = SetConfigOption(releaseLevelKey, ReleaseLevelNameStable)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !testSubsystem() {
|
||||
t.Error("should be active")
|
||||
}
|
||||
err = SetConfigOption(releaseLevelKey, ReleaseLevelNameBeta)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !testSubsystem() {
|
||||
t.Error("should be active")
|
||||
}
|
||||
err = SetConfigOption(releaseLevelKey, ReleaseLevelNameExperimental)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !testSubsystem() {
|
||||
t.Error("should be active")
|
||||
}
|
||||
|
||||
// test option level beta
|
||||
subsystemOption.ReleaseLevel = ReleaseLevelBeta
|
||||
err = SetConfigOption(releaseLevelKey, ReleaseLevelNameStable)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if testSubsystem() {
|
||||
t.Errorf("should be inactive: opt=%d system=%d", subsystemOption.ReleaseLevel, getReleaseLevel())
|
||||
}
|
||||
err = SetConfigOption(releaseLevelKey, ReleaseLevelNameBeta)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !testSubsystem() {
|
||||
t.Error("should be active")
|
||||
}
|
||||
err = SetConfigOption(releaseLevelKey, ReleaseLevelNameExperimental)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !testSubsystem() {
|
||||
t.Error("should be active")
|
||||
}
|
||||
|
||||
// test option level experimental
|
||||
subsystemOption.ReleaseLevel = ReleaseLevelExperimental
|
||||
err = SetConfigOption(releaseLevelKey, ReleaseLevelNameStable)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if testSubsystem() {
|
||||
t.Error("should be inactive")
|
||||
}
|
||||
err = SetConfigOption(releaseLevelKey, ReleaseLevelNameBeta)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if testSubsystem() {
|
||||
t.Error("should be inactive")
|
||||
}
|
||||
err = SetConfigOption(releaseLevelKey, ReleaseLevelNameExperimental)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !testSubsystem() {
|
||||
t.Error("should be active")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGetAsStringCached(b *testing.B) {
|
||||
// reset
|
||||
options = make(map[string]*Option)
|
||||
|
||||
// Setup
|
||||
err := parseAndReplaceConfig(`{
|
||||
"monkey": "banana"
|
||||
}`)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
monkey := GetAsString("monkey", "no banana")
|
||||
|
||||
// Reset timer for precise results
|
||||
b.ResetTimer()
|
||||
|
||||
// Start benchmark
|
||||
for range b.N {
|
||||
monkey()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGetAsStringRefetch(b *testing.B) {
|
||||
// Setup
|
||||
err := parseAndReplaceConfig(`{
|
||||
"monkey": "banana"
|
||||
}`)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
// Reset timer for precise results
|
||||
b.ResetTimer()
|
||||
|
||||
// Start benchmark
|
||||
for range b.N {
|
||||
getValueCache("monkey", nil, OptTypeString)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGetAsIntCached(b *testing.B) {
|
||||
// Setup
|
||||
err := parseAndReplaceConfig(`{
|
||||
"elephant": 1
|
||||
}`)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
elephant := GetAsInt("elephant", -1)
|
||||
|
||||
// Reset timer for precise results
|
||||
b.ResetTimer()
|
||||
|
||||
// Start benchmark
|
||||
for range b.N {
|
||||
elephant()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGetAsIntRefetch(b *testing.B) {
|
||||
// Setup
|
||||
err := parseAndReplaceConfig(`{
|
||||
"elephant": 1
|
||||
}`)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
// Reset timer for precise results
|
||||
b.ResetTimer()
|
||||
|
||||
// Start benchmark
|
||||
for range b.N {
|
||||
getValueCache("elephant", nil, OptTypeInt)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user