diff options
-rw-r--r-- | Makefile | 13 | ||||
-rw-r--r-- | README.md | 8 | ||||
-rw-r--r-- | bitmask.pro | 1 | ||||
-rw-r--r-- | gui/backend.go | 11 | ||||
-rw-r--r-- | gui/qml/main.qml | 2 | ||||
-rw-r--r-- | pkg/backend/api.go | 18 | ||||
-rw-r--r-- | pkg/backend/init.go | 12 | ||||
-rw-r--r-- | pkg/backend/mocks.go | 15 | ||||
-rw-r--r-- | pkg/bitmask/init.go | 28 | ||||
-rw-r--r-- | test.pro | 26 | ||||
-rw-r--r-- | tests/.gitignore | 10 | ||||
-rw-r--r-- | tests/test_ui.cpp | 52 | ||||
-rw-r--r-- | tests/tst_smoke.qml | 28 |
13 files changed, 190 insertions, 34 deletions
@@ -14,8 +14,8 @@ VERSION ?= $(shell git describe) # go paths GOPATH = $(shell go env GOPATH) -SYSTRAY = 0xacab.org/leap/bitmask-vpn -GOSYSTRAY = ${GOPATH}/src/${SYSTRAY} +TARGET_GOLIB=lib/libgoshim.a +SOURCE_GOLIB=gui/backend.go # detect OS, we use it for dependencies UNAME = $(shell uname -s) @@ -81,6 +81,15 @@ build_%: test: @go test -tags "integration $(TAGS)" ./... +golib: + CGO_ENABLED=1 go build -buildmode=c-archive -o ${TARGET_GOLIB} ${SOURCE_GOLIB} + +test_ui: golib + @qmake -o tests/Makefile test.pro + @make -C tests clean + @make -C tests + @./tests/build/test_ui + build_win: powershell -Command '$$version=git describe --tags; go build -ldflags "-H windowsgui -X main.version=$$version" ./cmd/*' @@ -41,6 +41,14 @@ Linux ./build.sh +Running tests +------------- + +sudo apt install qml-module-qttest +make test +make test_ui + + i18n ---- diff --git a/bitmask.pro b/bitmask.pro index 115aec7..7a29483 100644 --- a/bitmask.pro +++ b/bitmask.pro @@ -34,7 +34,6 @@ RCC_DIR = release/.rcc UI_DIR = release/.ui Release:DESTDIR = release -Release:DESTDIR = release Release:OBJECTS_DIR = release/.obj Release:MOC_DIR = release/.moc Release:RCC_DIR = release/.rcc diff --git a/gui/backend.go b/gui/backend.go index f7816ee..4a73cc2 100644 --- a/gui/backend.go +++ b/gui/backend.go @@ -44,7 +44,16 @@ func SubscribeToEvent(event string, f unsafe.Pointer) { //export InitializeBitmaskContext func InitializeBitmaskContext() { - backend.InitializeBitmaskContext() + opts := &backend.InitOpts{} + backend.InitializeBitmaskContext(opts) +} + +//export InitializeTestBitmaskContext +func InitializeTestBitmaskContext() { + opts := &backend.InitOpts{} + opts.SkipLaunch = true + backend.InitializeBitmaskContext(opts) + backend.EnableMockBackend() } //export RefreshContext diff --git a/gui/qml/main.qml b/gui/qml/main.qml index efe0111..4aab7f1 100644 --- a/gui/qml/main.qml +++ b/gui/qml/main.qml @@ -9,7 +9,7 @@ ApplicationWindow { id: app visible: false - property var ctx + property var ctx Connections { target: jsonModel diff --git a/pkg/backend/api.go b/pkg/backend/api.go index a19fd40..fea38db 100644 --- a/pkg/backend/api.go +++ b/pkg/backend/api.go @@ -45,10 +45,18 @@ func SubscribeToEvent(event string, f unsafe.Pointer) { subscribe(event, f) } -func InitializeBitmaskContext() { +type InitOpts struct { + Provider string + AppName string + SkipLaunch bool +} + +func InitializeBitmaskContext(opts *InitOpts) { p := bitmask.GetConfiguredProvider() + opts.Provider = p.Provider + opts.AppName = p.AppName - initOnce.Do(func() { initializeContext(p.Provider, p.AppName) }) + initOnce.Do(func() { initializeContext(opts) }) runDonationReminder() go ctx.updateStatus() } @@ -62,7 +70,7 @@ func InstallHelpers() { pickle.InstallHelpers() } -func MockUIInteraction() { - log.Println("mocking ui interaction on port 8080. \nTry 'curl localhost:8080/{on|off|failed}' to toggle status.") - go mockUI() +func EnableMockBackend() { + log.Println("[+] Mocking ui interaction on port 8080. \nTry 'curl localhost:8080/{on|off|failed}' to toggle status.") + go enableMockBackend() } diff --git a/pkg/backend/init.go b/pkg/backend/init.go index 5abb05e..79efdc7 100644 --- a/pkg/backend/init.go +++ b/pkg/backend/init.go @@ -12,11 +12,11 @@ import ( // initializeContext initializes an empty connStatus and assigns it to the // global ctx holder. This is expected to be called only once, so the public // api uses the sync.Once primitive to call this. -func initializeContext(provider, appName string) { +func initializeContext(opts *InitOpts) { var st status = off ctx = &connectionCtx{ - AppName: appName, - Provider: provider, + AppName: opts.AppName, + Provider: opts.Provider, TosURL: config.TosURL, HelpURL: config.HelpURL, DonateURL: config.DonateURL, @@ -28,7 +28,7 @@ func initializeContext(provider, appName string) { errCh := make(chan string) go trigger(OnStatusChanged) go checkErrors(errCh) - initializeBitmask(errCh) + initializeBitmask(errCh, opts) } func checkErrors(errCh chan string) { @@ -39,14 +39,14 @@ func checkErrors(errCh chan string) { } } -func initializeBitmask(errCh chan string) { +func initializeBitmask(errCh chan string, opts *InitOpts) { if ctx == nil { log.Println("bug: cannot initialize bitmask, ctx is nil!") os.Exit(1) } bitmask.InitializeLogger() - b, err := bitmask.InitializeBitmask() + b, err := bitmask.InitializeBitmask(opts.SkipLaunch) if err != nil { log.Println("error: cannot initialize bitmask") errCh <- err.Error() diff --git a/pkg/backend/mocks.go b/pkg/backend/mocks.go index a8ede73..226fa4e 100644 --- a/pkg/backend/mocks.go +++ b/pkg/backend/mocks.go @@ -9,6 +9,14 @@ import ( * should also show a good way of writing functionality tests just for the Qml * layer */ +func enableMockBackend() { + log.Println("[+] You should not use this in production!") + http.HandleFunc("/on", mockUIOn) + http.HandleFunc("/off", mockUIOff) + http.HandleFunc("/failed", mockUIFailed) + http.ListenAndServe(":8080", nil) +} + func mockUIOn(w http.ResponseWriter, r *http.Request) { log.Println("changing status: on") setStatus(on) @@ -23,10 +31,3 @@ func mockUIFailed(w http.ResponseWriter, r *http.Request) { log.Println("changing status: failed") setStatus(failed) } - -func mockUI() { - http.HandleFunc("/on", mockUIOn) - http.HandleFunc("/off", mockUIOff) - http.HandleFunc("/failed", mockUIFailed) - http.ListenAndServe(":8080", nil) -} diff --git a/pkg/bitmask/init.go b/pkg/bitmask/init.go index 33a5911..a96ab87 100644 --- a/pkg/bitmask/init.go +++ b/pkg/bitmask/init.go @@ -56,14 +56,18 @@ func initBitmask(printer *message.Printer) (Bitmask, error) { return b, err } -func InitializeBitmask() (Bitmask, error) { +func InitializeBitmask(skipLaunch bool) (Bitmask, error) { + if skipLaunch { + log.Println("Initializing bitmask, but not launching it...") + } if _, err := os.Stat(config.Path); os.IsNotExist(err) { os.MkdirAll(config.Path, os.ModePerm) } err := pid.AcquirePID() if err != nil { - log.Fatal(err) + log.Println("Error acquiring PID:", err) + return nil, err } defer pid.ReleasePID() @@ -75,13 +79,21 @@ func InitializeBitmask() (Bitmask, error) { return nil, err } - err = checkAndStartBitmask(b, conf) + err = setTransport(b, conf) if err != nil { return nil, err } + if !skipLaunch { + err := maybeStartVPN(b, conf) + if err != nil { + log.Println("Error starting VPN: ", err) + return nil, err + } + } + var as Autostart - if conf.DisableAustostart { + if skipLaunch || conf.DisableAustostart { as = &dummyAutostart{} } else { as = newAutostart(config.ApplicationName, "") @@ -103,7 +115,7 @@ func initPrinter() *message.Printer { return message.NewPrinter(message.MatchLanguage(locale, "en")) } -func checkAndStartBitmask(b Bitmask, conf *config.Config) error { +func setTransport(b Bitmask, conf *config.Config) error { if conf.Obfs4 { err := b.UseTransport("obfs4") if err != nil { @@ -111,12 +123,6 @@ func checkAndStartBitmask(b Bitmask, conf *config.Config) error { return err } } - - err := maybeStartVPN(b, conf) - if err != nil { - log.Println("Error starting VPN: ", err) - return err - } return nil } diff --git a/test.pro b/test.pro new file mode 100644 index 0000000..099e18f --- /dev/null +++ b/test.pro @@ -0,0 +1,26 @@ +TEMPLATE = app +TARGET = test_ui +CONFIG += warn_on qmltestcase + +SOURCES += \ + tests/test_ui.cpp \ + gui/qjsonmodel.cpp \ + gui/handlers.cpp + +HEADERS += \ + lib/libgoshim.h \ + gui/qjsonmodel.h \ + gui/handlers.h + + +LIBS += -L../lib -lgoshim -lpthread + +DESTDIR = build +OBJECTS_DIR = build/.obj +RCC_DIR = build/.rcc +UI_DIR = build/.ui + +Release:DESTDIR = build +Release:OBJECTS_DIR = build/.obj +Release:RCC_DIR = build/.rcc +Release:UI_DIR = build/.ui diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..65af933 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,10 @@ +build/* +*.h +*.sh +*.moc +*.o +*.stash +Makefile +test_ui +moc_handlers.cpp +moc_qjsonmodel.cpp diff --git a/tests/test_ui.cpp b/tests/test_ui.cpp new file mode 100644 index 0000000..f9f960a --- /dev/null +++ b/tests/test_ui.cpp @@ -0,0 +1,52 @@ +// test_ui.cpp +#include <QtQuickTest> +#include <QQmlEngine> +#include <QQmlContext> + +#include "../gui/qjsonmodel.h" +#include "../lib/libgoshim.h" + +class Helper : public QObject +{ + Q_OBJECT + +public: + explicit Helper(QObject *parent = 0); + +public slots: + Q_INVOKABLE QString refreshContext(); +}; + +Helper::Helper(QObject *parent) : QObject(parent) +{ +} + +Q_INVOKABLE QString Helper::refreshContext() +{ + return QString(RefreshContext()); +} + +class Setup : public QObject +{ + Q_OBJECT + +public: + Setup() {} + +public slots: + void qmlEngineAvailable(QQmlEngine *engine) + { + QQmlContext *ctx = engine->rootContext(); + QJsonModel *model = new QJsonModel; + Helper *helper = new Helper(this); + + InitializeTestBitmaskContext(); + + ctx->setContextProperty("jsonModel", model); + ctx->setContextProperty("helper", helper); + } +}; + +QUICK_TEST_MAIN_WITH_SETUP(ui, Setup) + +#include "test_ui.moc" diff --git a/tests/tst_smoke.qml b/tests/tst_smoke.qml new file mode 100644 index 0000000..19904a6 --- /dev/null +++ b/tests/tst_smoke.qml @@ -0,0 +1,28 @@ +import QtQuick 2.3 +import QtTest 1.0 + + +TestCase { + name: "SmokeTests" + + property var ctx + + function refresh() { + ctx = JSON.parse(helper.refreshContext()) + } + + function test_helper() { + compare(Boolean(helper), true, "does helper exist?") + } + + function test_model() { + compare(Boolean(jsonModel), true, "does model exist?") + } + + function test_loadCtx() { + refresh() + compare(ctx.appName, "RiseupVPN", "can read appName?") + compare(ctx.tosURL, "https://riseup.net/tos", "can read tosURL?") + compare(ctx.status, "off", "is initial status off?") + } +} |