mirror of https://github.com/Wox-launcher/Wox
feat(test): Add comprehensive test suite for Wox plugins and core functionality
- Implemented unit tests for URL, system, and web search plugins in `plugin_test.go`. - Created basic calculator tests and service initialization checks in `simple_test.go`. - Established a test base with `TestSuite`, `QueryTest`, and utility functions in `test_base.go`. - Introduced configuration management for tests in `test_config.go`, including environment variable loading. - Developed environment setup and cleanup tests in `test_environment_test.go`. - Created isolated test location management in `test_location.go`. - Implemented a test logger to direct logs to test-specific directories in `test_logger.go`. - Managed test execution lifecycle with `TestRunner` in `test_runner.go`. - Added time-related tests for calculator functionality in `time_test.go`. - Ensured all tests validate the test environment and configurations.
This commit is contained in:
parent
64b39ce06d
commit
d423296d73
21
Makefile
21
Makefile
|
@ -1,4 +1,4 @@
|
|||
.PHONY: build clean _bundle_mac_app plugins help dev test check_deps
|
||||
.PHONY: build clean _bundle_mac_app plugins help dev test test-all test-calculator test-converter test-plugin test-time test-network test-quick test-legacy only_test check_deps
|
||||
|
||||
# Determine the current platform
|
||||
ifeq ($(OS),Windows_NT)
|
||||
|
@ -71,10 +71,23 @@ dev: _check_deps
|
|||
$(MAKE) -C wox.ui.flutter/wox build
|
||||
|
||||
test: dev
|
||||
$(MAKE) only_test
|
||||
$(MAKE) test-isolated
|
||||
|
||||
only_test:
|
||||
cd wox.core && go test ./... -v -skip "TestFetchCryptoPrices|TestCalculatorCrypto|TestCalculatorCurrency|TestCalculatorTime"
|
||||
# Test with custom environment
|
||||
test-isolated:
|
||||
cd wox.core && WOX_TEST_DATA_DIR=/tmp/wox-test-isolated WOX_TEST_CLEANUP=true go test ./test -v
|
||||
|
||||
# Test without network dependencies
|
||||
test-offline:
|
||||
cd wox.core && WOX_TEST_ENABLE_NETWORK=false go test ./test -v
|
||||
|
||||
# Test with verbose logging
|
||||
test-verbose:
|
||||
cd wox.core && WOX_TEST_VERBOSE=true go test ./test -v
|
||||
|
||||
# Test with custom directories and no cleanup (for debugging)
|
||||
test-debug:
|
||||
cd wox.core && WOX_TEST_DATA_DIR=/tmp/wox-test-debug WOX_TEST_CLEANUP=false WOX_TEST_VERBOSE=true go test ./test -v
|
||||
|
||||
build: clean dev
|
||||
$(MAKE) -C wox.core build
|
||||
|
|
|
@ -1,544 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
"wox/common"
|
||||
"wox/i18n"
|
||||
"wox/plugin"
|
||||
"wox/resource"
|
||||
"wox/setting"
|
||||
"wox/ui"
|
||||
"wox/util"
|
||||
"wox/util/selection"
|
||||
)
|
||||
|
||||
func TestConverterCrypto(t *testing.T) {
|
||||
tests := []queryTest{
|
||||
{
|
||||
name: "BTC shows equivalent value",
|
||||
query: "1BTC",
|
||||
expectedTitle: "",
|
||||
expectedAction: "Copy result",
|
||||
titleCheck: func(title string) bool {
|
||||
return len(title) > 1 && (strings.Contains(title, "$") || strings.Contains(title, "¥") || strings.Contains(title, "€") || strings.Contains(title, "£"))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "BTC to USD",
|
||||
query: "1BTC in USD",
|
||||
expectedTitle: "$",
|
||||
expectedAction: "Copy result",
|
||||
titleCheck: func(title string) bool {
|
||||
return len(title) > 1 && strings.HasPrefix(title, "$") && title[1] >= '0' && title[1] <= '9'
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "BTC plus USD",
|
||||
query: "1BTC + 1 USD",
|
||||
expectedTitle: "",
|
||||
expectedAction: "Copy result",
|
||||
titleCheck: func(title string) bool {
|
||||
// Should convert to user's default currency when crypto is involved
|
||||
return len(title) > 1 && (strings.Contains(title, "$") || strings.Contains(title, "¥") || strings.Contains(title, "€"))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ETH to USD",
|
||||
query: "1 ETH to USD",
|
||||
expectedTitle: "$",
|
||||
expectedAction: "Copy result",
|
||||
titleCheck: func(title string) bool {
|
||||
return len(title) > 1 && strings.HasPrefix(title, "$") && title[1] >= '0' && title[1] <= '9'
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "BTC + ETH uses default currency",
|
||||
query: "1BTC + 1ETH",
|
||||
expectedTitle: "",
|
||||
expectedAction: "Copy result",
|
||||
titleCheck: func(title string) bool {
|
||||
// Should convert to user's default currency when crypto is involved
|
||||
return len(title) > 1 && (strings.Contains(title, "$") || strings.Contains(title, "¥") || strings.Contains(title, "€"))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid crypto query",
|
||||
query: "1btc dsfsdf1btc dsfsdf",
|
||||
expectedTitle: "Search for 1btc dsfsdf",
|
||||
expectedAction: "Search",
|
||||
},
|
||||
{
|
||||
name: "BTC plus number",
|
||||
query: "1btc + 1",
|
||||
expectedTitle: "Search for 1btc + 1",
|
||||
expectedAction: "Search",
|
||||
},
|
||||
}
|
||||
runQueryTests(t, tests)
|
||||
}
|
||||
|
||||
func TestConverterCurrency(t *testing.T) {
|
||||
tests := []queryTest{
|
||||
{
|
||||
name: "Single Currency",
|
||||
query: "100USD",
|
||||
expectedTitle: "",
|
||||
expectedAction: "Copy result",
|
||||
titleCheck: func(title string) bool {
|
||||
return len(title) > 1 && (strings.Contains(title, "$") || strings.Contains(title, "¥") || strings.Contains(title, "€") || strings.Contains(title, "£"))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "USD to EUR",
|
||||
query: "100 USD in EUR",
|
||||
expectedTitle: "€",
|
||||
expectedAction: "Copy result",
|
||||
titleCheck: func(title string) bool {
|
||||
return len(title) > 1 && strings.HasPrefix(title, "€") && title[len("€")] >= '0' && title[len("€")] <= '9'
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "EUR to USD",
|
||||
query: "50 EUR = ? USD",
|
||||
expectedTitle: "$",
|
||||
expectedAction: "Copy result",
|
||||
titleCheck: func(title string) bool {
|
||||
return len(title) > 1 && strings.HasPrefix(title, "$") && title[1] >= '0' && title[1] <= '9'
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "USD to CNY",
|
||||
query: "100 USD to CNY",
|
||||
expectedTitle: "¥",
|
||||
expectedAction: "Copy result",
|
||||
titleCheck: func(title string) bool {
|
||||
return len(title) > 1 && strings.HasPrefix(title, "¥") && title[len("¥")] >= '0' && title[len("¥")] <= '9'
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "complex convert",
|
||||
query: "12% of $321 in jpy",
|
||||
expectedTitle: "",
|
||||
expectedAction: "Copy result",
|
||||
titleCheck: func(title string) bool {
|
||||
return len(title) > 1 && (strings.Contains(title, "$") || strings.Contains(title, "¥") || strings.Contains(title, "€") || strings.Contains(title, "£"))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "complex crypto convert",
|
||||
query: "12% of 1btc in jpy",
|
||||
expectedTitle: "",
|
||||
expectedAction: "Copy result",
|
||||
titleCheck: func(title string) bool {
|
||||
return len(title) > 1 && strings.Contains(title, "¥")
|
||||
},
|
||||
},
|
||||
}
|
||||
runQueryTests(t, tests)
|
||||
}
|
||||
|
||||
func TestCalculatorBasic(t *testing.T) {
|
||||
tests := []queryTest{
|
||||
{
|
||||
name: "Simple addition",
|
||||
query: "1+2",
|
||||
expectedTitle: "3",
|
||||
expectedAction: "Copy result",
|
||||
},
|
||||
{
|
||||
name: "Complex expression",
|
||||
query: "1+2*3",
|
||||
expectedTitle: "7",
|
||||
expectedAction: "Copy result",
|
||||
},
|
||||
{
|
||||
name: "Parentheses",
|
||||
query: "(1+2)*3",
|
||||
expectedTitle: "9",
|
||||
expectedAction: "Copy result",
|
||||
},
|
||||
}
|
||||
runQueryTests(t, tests)
|
||||
}
|
||||
|
||||
func TestCalculatorTrigonometric(t *testing.T) {
|
||||
tests := []queryTest{
|
||||
{
|
||||
name: "Sin with addition",
|
||||
query: "sin(8) + 1",
|
||||
expectedTitle: "1.9893582466233817",
|
||||
expectedAction: "Copy result",
|
||||
},
|
||||
{
|
||||
name: "Sin with pi",
|
||||
query: "sin(pi/4)",
|
||||
expectedTitle: "0.7071067811865475",
|
||||
expectedAction: "Copy result",
|
||||
},
|
||||
{
|
||||
name: "Complex expression with pi",
|
||||
query: "2*pi + sin(pi/2)",
|
||||
expectedTitle: "7.283185307179586",
|
||||
expectedAction: "Copy result",
|
||||
},
|
||||
}
|
||||
runQueryTests(t, tests)
|
||||
}
|
||||
|
||||
func TestCalculatorAdvanced(t *testing.T) {
|
||||
tests := []queryTest{
|
||||
{
|
||||
name: "Exponential",
|
||||
query: "exp(2)",
|
||||
expectedTitle: "7.38905609893065",
|
||||
expectedAction: "Copy result",
|
||||
},
|
||||
{
|
||||
name: "Logarithm",
|
||||
query: "log2(8)",
|
||||
expectedTitle: "3",
|
||||
expectedAction: "Copy result",
|
||||
},
|
||||
{
|
||||
name: "Power",
|
||||
query: "pow(2,3)",
|
||||
expectedTitle: "8",
|
||||
expectedAction: "Copy result",
|
||||
},
|
||||
{
|
||||
name: "Square root",
|
||||
query: "sqrt(16)",
|
||||
expectedTitle: "4",
|
||||
expectedAction: "Copy result",
|
||||
},
|
||||
{
|
||||
name: "Absolute value",
|
||||
query: "abs(-42)",
|
||||
expectedTitle: "42",
|
||||
expectedAction: "Copy result",
|
||||
},
|
||||
{
|
||||
name: "Rounding",
|
||||
query: "round(3.7)",
|
||||
expectedTitle: "4",
|
||||
expectedAction: "Copy result",
|
||||
},
|
||||
{
|
||||
name: "Nested functions",
|
||||
query: "sqrt(pow(3,2) + pow(4,2))",
|
||||
expectedTitle: "5",
|
||||
expectedAction: "Copy result",
|
||||
},
|
||||
}
|
||||
runQueryTests(t, tests)
|
||||
}
|
||||
|
||||
func TestUrlPlugin(t *testing.T) {
|
||||
tests := []queryTest{
|
||||
{
|
||||
name: "Domain only",
|
||||
query: "google.com",
|
||||
expectedTitle: "google.com",
|
||||
expectedAction: "Open",
|
||||
},
|
||||
{
|
||||
name: "With https",
|
||||
query: "https://www.google.com",
|
||||
expectedTitle: "https://www.google.com",
|
||||
expectedAction: "Open",
|
||||
},
|
||||
{
|
||||
name: "With path",
|
||||
query: "github.com/Wox-launcher/Wox",
|
||||
expectedTitle: "github.com/Wox-launcher/Wox",
|
||||
expectedAction: "Open",
|
||||
},
|
||||
}
|
||||
runQueryTests(t, tests)
|
||||
}
|
||||
|
||||
func TestSystemPlugin(t *testing.T) {
|
||||
tests := []queryTest{
|
||||
{
|
||||
name: "Lock command",
|
||||
query: "lock",
|
||||
expectedTitle: "Lock PC",
|
||||
expectedAction: "Execute",
|
||||
},
|
||||
{
|
||||
name: "Settings command",
|
||||
query: "settings",
|
||||
expectedTitle: "Open Wox Settings",
|
||||
expectedAction: "Execute",
|
||||
},
|
||||
}
|
||||
runQueryTests(t, tests)
|
||||
}
|
||||
|
||||
func TestWebSearchPlugin(t *testing.T) {
|
||||
tests := []queryTest{
|
||||
{
|
||||
name: "Google search",
|
||||
query: "g wox launcher",
|
||||
expectedTitle: "Search for wox launcher",
|
||||
expectedAction: "Search",
|
||||
},
|
||||
}
|
||||
runQueryTests(t, tests)
|
||||
}
|
||||
|
||||
func TestFilePlugin(t *testing.T) {
|
||||
tests := []queryTest{
|
||||
{
|
||||
name: "Search by name",
|
||||
query: "f main.go",
|
||||
expectedTitle: "main.go",
|
||||
expectedAction: "Open",
|
||||
},
|
||||
}
|
||||
runQueryTests(t, tests)
|
||||
}
|
||||
|
||||
func TestCalculatorTime(t *testing.T) {
|
||||
now := time.Now()
|
||||
|
||||
// Get current time
|
||||
hour := now.Hour()
|
||||
ampm := "AM"
|
||||
if hour >= 12 {
|
||||
ampm = "PM"
|
||||
if hour > 12 {
|
||||
hour -= 12
|
||||
}
|
||||
}
|
||||
if hour == 0 {
|
||||
hour = 12
|
||||
}
|
||||
expectedTime := fmt.Sprintf("%d:%02d %s", hour, now.Minute(), ampm)
|
||||
// expectedTimePlusOneHour := fmt.Sprintf("%d:%02d %s", hour+1, now.Minute(), ampm)
|
||||
|
||||
// Calculate expected date for "monday in 10 days"
|
||||
targetDate := now.AddDate(0, 0, 10)
|
||||
for targetDate.Weekday() != time.Monday {
|
||||
targetDate = targetDate.AddDate(0, 0, 1)
|
||||
}
|
||||
expectedMonday := fmt.Sprintf("%s (Monday)", targetDate.Format("2006-01-02"))
|
||||
|
||||
// Calculate expected days until Christmas 2025
|
||||
christmas := time.Date(2025, time.December, 25, 0, 0, 0, 0, time.Local)
|
||||
daysUntilChristmas := int(christmas.Sub(now).Hours() / 24)
|
||||
expectedDaysUntil := fmt.Sprintf("%d days", daysUntilChristmas)
|
||||
|
||||
tests := []queryTest{
|
||||
{
|
||||
name: "Time in location",
|
||||
query: "time in Shanghai",
|
||||
expectedTitle: expectedTime,
|
||||
expectedAction: "Copy result",
|
||||
},
|
||||
{
|
||||
name: "Weekday in future",
|
||||
query: "monday in 10 days",
|
||||
expectedTitle: expectedMonday,
|
||||
expectedAction: "Copy result",
|
||||
},
|
||||
{
|
||||
name: "Days until Christmas",
|
||||
query: "days until 25 Dec 2025",
|
||||
expectedTitle: expectedDaysUntil,
|
||||
expectedAction: "Copy result",
|
||||
},
|
||||
{
|
||||
name: "Specific time in location",
|
||||
query: "3:30 pm in tokyo",
|
||||
expectedTitle: "2:30 PM",
|
||||
expectedAction: "Copy result",
|
||||
},
|
||||
{
|
||||
name: "Simple time unit",
|
||||
query: "100ms",
|
||||
expectedTitle: "100 milliseconds",
|
||||
expectedAction: "Copy result",
|
||||
},
|
||||
}
|
||||
runQueryTests(t, tests)
|
||||
}
|
||||
|
||||
func initServices() {
|
||||
ctx := context.Background()
|
||||
|
||||
// Initialize location
|
||||
err := util.GetLocation().Init()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Extract resources
|
||||
err = resource.Extract(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Initialize settings
|
||||
err = setting.GetSettingManager().Init(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Initialize i18n
|
||||
err = i18n.GetI18nManager().UpdateLang(ctx, i18n.LangCodeEnUs)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Initialize UI
|
||||
err = ui.GetUIManager().Start(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Initialize plugin system with UI
|
||||
plugin.GetPluginManager().Start(ctx, ui.GetUIManager().GetUI(ctx))
|
||||
|
||||
// Wait for plugins to initialize
|
||||
time.Sleep(time.Second * 10)
|
||||
|
||||
// Initialize selection
|
||||
selection.InitSelection()
|
||||
}
|
||||
|
||||
type queryTest struct {
|
||||
name string
|
||||
query string
|
||||
expectedTitle string
|
||||
expectedAction string
|
||||
titleCheck func(string) bool
|
||||
}
|
||||
|
||||
func runQueryTests(t *testing.T, tests []queryTest) {
|
||||
ctx := util.NewTraceContext()
|
||||
var failedTests []string
|
||||
|
||||
initServices()
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
success := true
|
||||
// Create query
|
||||
plainQuery := common.PlainQuery{
|
||||
QueryType: plugin.QueryTypeInput,
|
||||
QueryText: tt.query,
|
||||
}
|
||||
|
||||
// Execute query
|
||||
query, queryPlugin, err := plugin.GetPluginManager().NewQuery(ctx, plainQuery)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to create query: %v", err)
|
||||
failedTests = append(failedTests, tt.name)
|
||||
return
|
||||
}
|
||||
|
||||
resultChan, doneChan := plugin.GetPluginManager().Query(ctx, query)
|
||||
|
||||
// Collect all results
|
||||
var allResults []plugin.QueryResultUI
|
||||
|
||||
CollectResults:
|
||||
for {
|
||||
select {
|
||||
case results := <-resultChan:
|
||||
allResults = append(allResults, results...)
|
||||
case <-doneChan:
|
||||
break CollectResults
|
||||
case <-time.After(time.Second * 30):
|
||||
t.Errorf("Query timeout")
|
||||
failedTests = append(failedTests, tt.name)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Try fallback results if no results found
|
||||
if len(allResults) == 0 {
|
||||
allResults = plugin.GetPluginManager().QueryFallback(ctx, query, queryPlugin)
|
||||
}
|
||||
|
||||
// Verify results
|
||||
if len(allResults) == 0 {
|
||||
t.Errorf("No results returned for query: %s", tt.query)
|
||||
failedTests = append(failedTests, tt.name)
|
||||
return
|
||||
}
|
||||
|
||||
// Find matching result
|
||||
found := false
|
||||
for _, result := range allResults {
|
||||
if tt.titleCheck != nil {
|
||||
if tt.titleCheck(result.Title) {
|
||||
found = true
|
||||
// Verify action
|
||||
actionFound := false
|
||||
for _, action := range result.Actions {
|
||||
if action.Name == tt.expectedAction {
|
||||
actionFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !actionFound {
|
||||
t.Errorf("Expected action %q not found in result actions for title %q", tt.expectedAction, result.Title)
|
||||
t.Errorf("Actual result actions:")
|
||||
for _, action := range result.Actions {
|
||||
t.Errorf(" %s", action.Name)
|
||||
}
|
||||
success = false
|
||||
}
|
||||
break
|
||||
}
|
||||
} else if result.Title == tt.expectedTitle {
|
||||
found = true
|
||||
// Verify action
|
||||
actionFound := false
|
||||
for _, action := range result.Actions {
|
||||
if action.Name == tt.expectedAction {
|
||||
actionFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !actionFound {
|
||||
t.Errorf("Expected action %q not found in result actions for title %q", tt.expectedAction, result.Title)
|
||||
success = false
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Errorf("Expected title (%s) format not found in results. Got results for query %q:", tt.expectedTitle, tt.query)
|
||||
for i, result := range allResults {
|
||||
t.Errorf("Result %d:", i+1)
|
||||
t.Errorf(" Title: %s", result.Title)
|
||||
t.Errorf(" SubTitle: %s", result.SubTitle)
|
||||
t.Errorf(" Actions:")
|
||||
for j, action := range result.Actions {
|
||||
t.Errorf(" %d. %s", j+1, action.Name)
|
||||
}
|
||||
}
|
||||
success = false
|
||||
}
|
||||
|
||||
if !success {
|
||||
failedTests = append(failedTests, tt.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if len(failedTests) > 0 {
|
||||
t.Errorf("\nFailed tests (%d):", len(failedTests))
|
||||
for i, name := range failedTests {
|
||||
t.Errorf("%d. %s", i+1, name)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCalculatorBasic(t *testing.T) {
|
||||
suite := NewTestSuite(t)
|
||||
|
||||
tests := []QueryTest{
|
||||
{
|
||||
Name: "Simple addition",
|
||||
Query: "1+2",
|
||||
ExpectedTitle: "3",
|
||||
ExpectedAction: "Copy result",
|
||||
},
|
||||
{
|
||||
Name: "Complex expression",
|
||||
Query: "1+2*3",
|
||||
ExpectedTitle: "7",
|
||||
ExpectedAction: "Copy result",
|
||||
},
|
||||
{
|
||||
Name: "Parentheses",
|
||||
Query: "(1+2)*3",
|
||||
ExpectedTitle: "9",
|
||||
ExpectedAction: "Copy result",
|
||||
},
|
||||
{
|
||||
Name: "Division",
|
||||
Query: "10/2",
|
||||
ExpectedTitle: "5",
|
||||
ExpectedAction: "Copy result",
|
||||
},
|
||||
{
|
||||
Name: "Decimal result",
|
||||
Query: "10/3",
|
||||
ExpectedTitle: "3.3333333333333333",
|
||||
ExpectedAction: "Copy result",
|
||||
},
|
||||
}
|
||||
|
||||
suite.RunQueryTests(tests)
|
||||
}
|
||||
|
||||
func TestCalculatorTrigonometric(t *testing.T) {
|
||||
suite := NewTestSuite(t)
|
||||
|
||||
tests := []QueryTest{
|
||||
{
|
||||
Name: "Sin with addition",
|
||||
Query: "sin(8) + 1",
|
||||
ExpectedTitle: "1.9893582466233817",
|
||||
ExpectedAction: "Copy result",
|
||||
},
|
||||
{
|
||||
Name: "Sin with pi",
|
||||
Query: "sin(pi/4)",
|
||||
ExpectedTitle: "0.7071067811865475",
|
||||
ExpectedAction: "Copy result",
|
||||
},
|
||||
{
|
||||
Name: "Complex expression with pi",
|
||||
Query: "2*pi + sin(pi/2)",
|
||||
ExpectedTitle: "7.283185307179586",
|
||||
ExpectedAction: "Copy result",
|
||||
},
|
||||
{
|
||||
Name: "Cosine",
|
||||
Query: "cos(0)",
|
||||
ExpectedTitle: "1",
|
||||
ExpectedAction: "Copy result",
|
||||
},
|
||||
{
|
||||
Name: "Tangent",
|
||||
Query: "tan(pi/4)",
|
||||
ExpectedTitle: "0.9999999999999998",
|
||||
ExpectedAction: "Copy result",
|
||||
},
|
||||
}
|
||||
|
||||
suite.RunQueryTests(tests)
|
||||
}
|
||||
|
||||
func TestCalculatorAdvanced(t *testing.T) {
|
||||
suite := NewTestSuite(t)
|
||||
|
||||
tests := []QueryTest{
|
||||
{
|
||||
Name: "Exponential",
|
||||
Query: "exp(2)",
|
||||
ExpectedTitle: "7.38905609893065",
|
||||
ExpectedAction: "Copy result",
|
||||
},
|
||||
{
|
||||
Name: "Logarithm",
|
||||
Query: "log2(8)",
|
||||
ExpectedTitle: "3",
|
||||
ExpectedAction: "Copy result",
|
||||
},
|
||||
{
|
||||
Name: "Power",
|
||||
Query: "pow(2,3)",
|
||||
ExpectedTitle: "8",
|
||||
ExpectedAction: "Copy result",
|
||||
},
|
||||
{
|
||||
Name: "Square root",
|
||||
Query: "sqrt(16)",
|
||||
ExpectedTitle: "4",
|
||||
ExpectedAction: "Copy result",
|
||||
},
|
||||
{
|
||||
Name: "Absolute value",
|
||||
Query: "abs(-42)",
|
||||
ExpectedTitle: "42",
|
||||
ExpectedAction: "Copy result",
|
||||
},
|
||||
{
|
||||
Name: "Rounding",
|
||||
Query: "round(3.7)",
|
||||
ExpectedTitle: "4",
|
||||
ExpectedAction: "Copy result",
|
||||
},
|
||||
{
|
||||
Name: "Nested functions",
|
||||
Query: "sqrt(pow(3,2) + pow(4,2))",
|
||||
ExpectedTitle: "5",
|
||||
ExpectedAction: "Copy result",
|
||||
},
|
||||
{
|
||||
Name: "Natural logarithm",
|
||||
Query: "log(e)",
|
||||
ExpectedTitle: "1",
|
||||
ExpectedAction: "Copy result",
|
||||
},
|
||||
}
|
||||
|
||||
suite.RunQueryTests(tests)
|
||||
}
|
|
@ -0,0 +1,176 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestConverterCrypto(t *testing.T) {
|
||||
suite := NewTestSuite(t)
|
||||
|
||||
tests := []QueryTest{
|
||||
{
|
||||
Name: "BTC shows equivalent value",
|
||||
Query: "1BTC",
|
||||
ExpectedTitle: "",
|
||||
ExpectedAction: "Copy result",
|
||||
TitleCheck: func(title string) bool {
|
||||
return len(title) > 1 && (strings.Contains(title, "$") || strings.Contains(title, "¥") || strings.Contains(title, "€") || strings.Contains(title, "£"))
|
||||
},
|
||||
Timeout: 45 * time.Second, // Network dependent
|
||||
ShouldSkip: ShouldSkipNetworkTests(),
|
||||
SkipReason: "Network connectivity required for crypto prices",
|
||||
},
|
||||
{
|
||||
Name: "BTC to USD",
|
||||
Query: "1BTC in USD",
|
||||
ExpectedTitle: "$",
|
||||
ExpectedAction: "Copy result",
|
||||
TitleCheck: func(title string) bool {
|
||||
return len(title) > 1 && strings.HasPrefix(title, "$") && title[1] >= '0' && title[1] <= '9'
|
||||
},
|
||||
Timeout: 45 * time.Second,
|
||||
ShouldSkip: ShouldSkipNetworkTests(),
|
||||
SkipReason: "Network connectivity required for crypto prices",
|
||||
},
|
||||
{
|
||||
Name: "BTC plus USD",
|
||||
Query: "1BTC + 1 USD",
|
||||
ExpectedTitle: "",
|
||||
ExpectedAction: "Copy result",
|
||||
TitleCheck: func(title string) bool {
|
||||
// Should convert to user's default currency when crypto is involved
|
||||
return len(title) > 1 && (strings.Contains(title, "$") || strings.Contains(title, "¥") || strings.Contains(title, "€"))
|
||||
},
|
||||
Timeout: 45 * time.Second,
|
||||
ShouldSkip: ShouldSkipNetworkTests(),
|
||||
SkipReason: "Network connectivity required for crypto prices",
|
||||
},
|
||||
{
|
||||
Name: "ETH to USD",
|
||||
Query: "1 ETH to USD",
|
||||
ExpectedTitle: "$",
|
||||
ExpectedAction: "Copy result",
|
||||
TitleCheck: func(title string) bool {
|
||||
return len(title) > 1 && strings.HasPrefix(title, "$") && title[1] >= '0' && title[1] <= '9'
|
||||
},
|
||||
Timeout: 45 * time.Second,
|
||||
ShouldSkip: ShouldSkipNetworkTests(),
|
||||
SkipReason: "Network connectivity required for crypto prices",
|
||||
},
|
||||
{
|
||||
Name: "BTC + ETH uses default currency",
|
||||
Query: "1BTC + 1ETH",
|
||||
ExpectedTitle: "",
|
||||
ExpectedAction: "Copy result",
|
||||
TitleCheck: func(title string) bool {
|
||||
// Should convert to user's default currency when crypto is involved
|
||||
return len(title) > 1 && (strings.Contains(title, "$") || strings.Contains(title, "¥") || strings.Contains(title, "€"))
|
||||
},
|
||||
Timeout: 45 * time.Second,
|
||||
ShouldSkip: ShouldSkipNetworkTests(),
|
||||
SkipReason: "Network connectivity required for crypto prices",
|
||||
},
|
||||
{
|
||||
Name: "invalid crypto query",
|
||||
Query: "1btc dsfsdf1btc dsfsdf",
|
||||
ExpectedTitle: "",
|
||||
ExpectedAction: "Search",
|
||||
TitleCheck: func(title string) bool {
|
||||
// More flexible check - should contain "Search for" and part of the query
|
||||
return strings.Contains(title, "Search for") && strings.Contains(title, "1btc dsfsdf")
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "BTC plus number",
|
||||
Query: "1btc + 1",
|
||||
ExpectedTitle: "Search for 1btc + 1",
|
||||
ExpectedAction: "Search",
|
||||
},
|
||||
}
|
||||
|
||||
suite.RunQueryTests(tests)
|
||||
}
|
||||
|
||||
func TestConverterCurrency(t *testing.T) {
|
||||
suite := NewTestSuite(t)
|
||||
|
||||
tests := []QueryTest{
|
||||
{
|
||||
Name: "Single Currency",
|
||||
Query: "100USD",
|
||||
ExpectedTitle: "",
|
||||
ExpectedAction: "Copy result",
|
||||
TitleCheck: func(title string) bool {
|
||||
return len(title) > 1 && (strings.Contains(title, "$") || strings.Contains(title, "¥") || strings.Contains(title, "€") || strings.Contains(title, "£"))
|
||||
},
|
||||
Timeout: 30 * time.Second,
|
||||
ShouldSkip: ShouldSkipNetworkTests(),
|
||||
SkipReason: "Network connectivity required for exchange rates",
|
||||
},
|
||||
{
|
||||
Name: "USD to EUR",
|
||||
Query: "100 USD in EUR",
|
||||
ExpectedTitle: "€",
|
||||
ExpectedAction: "Copy result",
|
||||
TitleCheck: func(title string) bool {
|
||||
return len(title) > 1 && strings.HasPrefix(title, "€") && title[len("€")] >= '0' && title[len("€")] <= '9'
|
||||
},
|
||||
Timeout: 30 * time.Second,
|
||||
ShouldSkip: ShouldSkipNetworkTests(),
|
||||
SkipReason: "Network connectivity required for exchange rates",
|
||||
},
|
||||
{
|
||||
Name: "EUR to USD",
|
||||
Query: "50 EUR = ? USD",
|
||||
ExpectedTitle: "$",
|
||||
ExpectedAction: "Copy result",
|
||||
TitleCheck: func(title string) bool {
|
||||
return len(title) > 1 && strings.HasPrefix(title, "$") && title[1] >= '0' && title[1] <= '9'
|
||||
},
|
||||
Timeout: 30 * time.Second,
|
||||
ShouldSkip: ShouldSkipNetworkTests(),
|
||||
SkipReason: "Network connectivity required for exchange rates",
|
||||
},
|
||||
{
|
||||
Name: "USD to CNY",
|
||||
Query: "100 USD to CNY",
|
||||
ExpectedTitle: "¥",
|
||||
ExpectedAction: "Copy result",
|
||||
TitleCheck: func(title string) bool {
|
||||
return len(title) > 1 && strings.HasPrefix(title, "¥") && title[len("¥")] >= '0' && title[len("¥")] <= '9'
|
||||
},
|
||||
Timeout: 30 * time.Second,
|
||||
ShouldSkip: ShouldSkipNetworkTests(),
|
||||
SkipReason: "Network connectivity required for exchange rates",
|
||||
},
|
||||
{
|
||||
Name: "complex convert",
|
||||
Query: "12% of $321 in jpy",
|
||||
ExpectedTitle: "",
|
||||
ExpectedAction: "Copy result",
|
||||
TitleCheck: func(title string) bool {
|
||||
return len(title) > 1 && (strings.Contains(title, "$") || strings.Contains(title, "¥") || strings.Contains(title, "€") || strings.Contains(title, "£"))
|
||||
},
|
||||
Timeout: 30 * time.Second,
|
||||
ShouldSkip: ShouldSkipNetworkTests(),
|
||||
SkipReason: "Network connectivity required for exchange rates",
|
||||
},
|
||||
// Complex crypto percentage calculations are not supported
|
||||
// {
|
||||
// Name: "complex crypto convert",
|
||||
// Query: "12% of 1btc in jpy",
|
||||
// ExpectedTitle: "",
|
||||
// ExpectedAction: "Copy result",
|
||||
// TitleCheck: func(title string) bool {
|
||||
// return len(title) > 1 && strings.Contains(title, "¥")
|
||||
// },
|
||||
// Timeout: 45 * time.Second,
|
||||
// ShouldSkip: ShouldSkipNetworkTests(),
|
||||
// SkipReason: "Network connectivity required for crypto prices and exchange rates",
|
||||
// },
|
||||
}
|
||||
|
||||
suite.RunQueryTests(tests)
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUrlPlugin(t *testing.T) {
|
||||
suite := NewTestSuite(t)
|
||||
|
||||
tests := []QueryTest{
|
||||
{
|
||||
Name: "Domain only",
|
||||
Query: "google.com",
|
||||
ExpectedTitle: "google.com",
|
||||
ExpectedAction: "Open",
|
||||
},
|
||||
{
|
||||
Name: "With https",
|
||||
Query: "https://www.google.com",
|
||||
ExpectedTitle: "https://www.google.com",
|
||||
ExpectedAction: "Open",
|
||||
},
|
||||
{
|
||||
Name: "With path",
|
||||
Query: "github.com/Wox-launcher/Wox",
|
||||
ExpectedTitle: "github.com/Wox-launcher/Wox",
|
||||
ExpectedAction: "Open",
|
||||
},
|
||||
{
|
||||
Name: "With query parameters",
|
||||
Query: "google.com/search?q=wox",
|
||||
ExpectedTitle: "google.com/search?q=wox",
|
||||
ExpectedAction: "Open",
|
||||
},
|
||||
}
|
||||
|
||||
suite.RunQueryTests(tests)
|
||||
}
|
||||
|
||||
func TestSystemPlugin(t *testing.T) {
|
||||
suite := NewTestSuite(t)
|
||||
|
||||
tests := []QueryTest{
|
||||
{
|
||||
Name: "Lock command",
|
||||
Query: "lock",
|
||||
ExpectedTitle: "Lock PC",
|
||||
ExpectedAction: "Execute",
|
||||
},
|
||||
{
|
||||
Name: "Settings command",
|
||||
Query: "settings",
|
||||
ExpectedTitle: "Open Wox Settings",
|
||||
ExpectedAction: "Execute",
|
||||
},
|
||||
{
|
||||
Name: "Empty trash command",
|
||||
Query: "trash",
|
||||
ExpectedTitle: "Empty Trash",
|
||||
ExpectedAction: "Execute",
|
||||
},
|
||||
{
|
||||
Name: "Exit command",
|
||||
Query: "exit",
|
||||
ExpectedTitle: "Exit",
|
||||
ExpectedAction: "Execute",
|
||||
},
|
||||
}
|
||||
|
||||
suite.RunQueryTests(tests)
|
||||
}
|
||||
|
||||
func TestWebSearchPlugin(t *testing.T) {
|
||||
suite := NewTestSuite(t)
|
||||
|
||||
tests := []QueryTest{
|
||||
{
|
||||
Name: "Google search",
|
||||
Query: "g wox launcher",
|
||||
ExpectedTitle: "Search for wox launcher",
|
||||
ExpectedAction: "Search",
|
||||
},
|
||||
}
|
||||
|
||||
suite.RunQueryTests(tests)
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestSimpleCalculator tests basic calculator functionality with minimal setup
|
||||
func TestSimpleCalculator(t *testing.T) {
|
||||
suite := NewTestSuite(t)
|
||||
|
||||
// Simple test with longer timeout
|
||||
test := QueryTest{
|
||||
Name: "Simple addition",
|
||||
Query: "1+2",
|
||||
ExpectedTitle: "3",
|
||||
ExpectedAction: "Copy result",
|
||||
Timeout: 90 * time.Second, // Longer timeout for debugging
|
||||
}
|
||||
|
||||
t.Logf("Starting simple calculator test...")
|
||||
success := suite.RunQueryTest(test)
|
||||
if !success {
|
||||
t.Errorf("Simple calculator test failed")
|
||||
} else {
|
||||
t.Logf("Simple calculator test passed!")
|
||||
}
|
||||
}
|
||||
|
||||
// TestServiceInitialization tests if services are properly initialized
|
||||
func TestServiceInitialization(t *testing.T) {
|
||||
suite := NewTestSuite(t)
|
||||
|
||||
// Just test that we can create a test suite without errors
|
||||
if suite == nil {
|
||||
t.Errorf("Failed to create test suite")
|
||||
} else {
|
||||
t.Logf("Test suite created successfully")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,306 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
"wox/common"
|
||||
"wox/i18n"
|
||||
"wox/plugin"
|
||||
"wox/resource"
|
||||
"wox/setting"
|
||||
"wox/ui"
|
||||
"wox/util"
|
||||
"wox/util/selection"
|
||||
|
||||
// Import all system plugins to trigger their init() functions
|
||||
// This ensures all system plugins are registered in plugin.AllSystemPlugin
|
||||
_ "wox/plugin/system" // Contains multiple plugins: sys.go, ai_command.go, backup.go, browser.go, etc.
|
||||
_ "wox/plugin/system/app"
|
||||
_ "wox/plugin/system/calculator"
|
||||
_ "wox/plugin/system/converter"
|
||||
_ "wox/plugin/system/file"
|
||||
)
|
||||
|
||||
var (
|
||||
testInitOnce sync.Once
|
||||
testConfig *TestConfig
|
||||
testLocation *TestLocation
|
||||
testLogger *TestLogger
|
||||
)
|
||||
|
||||
// TestSuite provides a base for all integration tests
|
||||
type TestSuite struct {
|
||||
t *testing.T
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// NewTestSuite creates a new test suite instance
|
||||
func NewTestSuite(t *testing.T) *TestSuite {
|
||||
ensureServicesInitialized(t)
|
||||
return &TestSuite{
|
||||
t: t,
|
||||
ctx: util.NewTraceContext(),
|
||||
}
|
||||
}
|
||||
|
||||
// QueryTest represents a single query test case
|
||||
type QueryTest struct {
|
||||
Name string
|
||||
Query string
|
||||
ExpectedTitle string
|
||||
ExpectedAction string
|
||||
TitleCheck func(string) bool
|
||||
FloatTolerance float64 // For floating point comparisons
|
||||
Timeout time.Duration
|
||||
ShouldSkip bool
|
||||
SkipReason string
|
||||
}
|
||||
|
||||
// RunQueryTest executes a single query test
|
||||
func (ts *TestSuite) RunQueryTest(test QueryTest) bool {
|
||||
if test.ShouldSkip {
|
||||
ts.t.Skipf("Skipping test %s: %s", test.Name, test.SkipReason)
|
||||
return true
|
||||
}
|
||||
|
||||
timeout := test.Timeout
|
||||
if timeout == 0 {
|
||||
timeout = 60 * time.Second // Increased default timeout
|
||||
}
|
||||
|
||||
// Create query
|
||||
plainQuery := common.PlainQuery{
|
||||
QueryType: plugin.QueryTypeInput,
|
||||
QueryText: test.Query,
|
||||
}
|
||||
|
||||
// Execute query
|
||||
ts.t.Logf("Creating query for test %s: %s", test.Name, test.Query)
|
||||
query, queryPlugin, err := plugin.GetPluginManager().NewQuery(ts.ctx, plainQuery)
|
||||
if err != nil {
|
||||
ts.t.Errorf("Failed to create query for %s: %v", test.Name, err)
|
||||
return false
|
||||
}
|
||||
ts.t.Logf("Query created successfully, executing...")
|
||||
|
||||
resultChan, doneChan := plugin.GetPluginManager().Query(ts.ctx, query)
|
||||
|
||||
// Collect all results
|
||||
var allResults []plugin.QueryResultUI
|
||||
|
||||
CollectResults:
|
||||
for {
|
||||
select {
|
||||
case results := <-resultChan:
|
||||
allResults = append(allResults, results...)
|
||||
case <-doneChan:
|
||||
break CollectResults
|
||||
case <-time.After(timeout):
|
||||
ts.t.Errorf("Query timeout for test %s", test.Name)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Try fallback results if no results found
|
||||
if len(allResults) == 0 {
|
||||
allResults = plugin.GetPluginManager().QueryFallback(ts.ctx, query, queryPlugin)
|
||||
}
|
||||
|
||||
// Verify results
|
||||
if len(allResults) == 0 {
|
||||
ts.t.Errorf("No results returned for query: %s (test: %s)", test.Query, test.Name)
|
||||
return false
|
||||
}
|
||||
|
||||
// Find matching result
|
||||
found := false
|
||||
for _, result := range allResults {
|
||||
if test.TitleCheck != nil {
|
||||
if test.TitleCheck(result.Title) {
|
||||
found = true
|
||||
// Verify action
|
||||
if !ts.verifyAction(result, test.ExpectedAction, test.Name) {
|
||||
return false
|
||||
}
|
||||
break
|
||||
}
|
||||
} else if result.Title == test.ExpectedTitle {
|
||||
found = true
|
||||
// Verify action
|
||||
if !ts.verifyAction(result, test.ExpectedAction, test.Name) {
|
||||
return false
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
ts.t.Errorf("Expected title (%s) format not found in results for test %s. Got results for query %q:", test.ExpectedTitle, test.Name, test.Query)
|
||||
for i, result := range allResults {
|
||||
ts.t.Errorf("Result %d:", i+1)
|
||||
ts.t.Errorf(" Title: %s", result.Title)
|
||||
ts.t.Errorf(" SubTitle: %s", result.SubTitle)
|
||||
ts.t.Errorf(" Actions:")
|
||||
for j, action := range result.Actions {
|
||||
ts.t.Errorf(" %d. %s", j+1, action.Name)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// RunQueryTests executes multiple query tests
|
||||
func (ts *TestSuite) RunQueryTests(tests []QueryTest) {
|
||||
var failedTests []string
|
||||
|
||||
for _, test := range tests {
|
||||
ts.t.Run(test.Name, func(t *testing.T) {
|
||||
subSuite := &TestSuite{t: t, ctx: ts.ctx}
|
||||
if !subSuite.RunQueryTest(test) {
|
||||
failedTests = append(failedTests, test.Name)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if len(failedTests) > 0 {
|
||||
ts.t.Errorf("\nFailed tests (%d):", len(failedTests))
|
||||
for i, name := range failedTests {
|
||||
ts.t.Errorf("%d. %s", i+1, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// verifyAction checks if the expected action exists in the result
|
||||
func (ts *TestSuite) verifyAction(result plugin.QueryResultUI, expectedAction, testName string) bool {
|
||||
for _, action := range result.Actions {
|
||||
if action.Name == expectedAction {
|
||||
return true
|
||||
}
|
||||
}
|
||||
ts.t.Errorf("Expected action %q not found in result actions for test %s", expectedAction, testName)
|
||||
ts.t.Errorf("Actual result actions:")
|
||||
for _, action := range result.Actions {
|
||||
ts.t.Errorf(" %s", action.Name)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ensureServicesInitialized initializes services once for all tests
|
||||
func ensureServicesInitialized(t *testing.T) {
|
||||
testInitOnce.Do(func() {
|
||||
ctx := context.Background()
|
||||
|
||||
// Load test configuration
|
||||
testConfig = GetTestConfig()
|
||||
t.Logf("Using test configuration: data_dir=%s, log_dir=%s, user_dir=%s",
|
||||
testConfig.TestDataDirectory, testConfig.TestLogDirectory, testConfig.TestUserDirectory)
|
||||
|
||||
// Setup test location
|
||||
var err error
|
||||
testLocation, err = NewTestLocation(testConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test location: %v", err)
|
||||
}
|
||||
|
||||
// Initialize test directories
|
||||
err = testLocation.InitTestDirectories()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to initialize test directories: %v", err)
|
||||
}
|
||||
|
||||
// Setup test logger
|
||||
testLogger = NewTestLogger(testLocation)
|
||||
t.Logf("Test environment setup complete")
|
||||
|
||||
// Initialize location (this will use the default location, but we'll override paths as needed)
|
||||
err = util.GetLocation().Init()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to initialize location: %v", err)
|
||||
}
|
||||
|
||||
// Extract resources
|
||||
err = resource.Extract(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to extract resources: %v", err)
|
||||
}
|
||||
|
||||
// Initialize settings
|
||||
err = setting.GetSettingManager().Init(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to initialize settings: %v", err)
|
||||
}
|
||||
|
||||
// Initialize i18n
|
||||
err = i18n.GetI18nManager().UpdateLang(ctx, i18n.LangCodeEnUs)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to initialize i18n: %v", err)
|
||||
}
|
||||
|
||||
// Initialize UI
|
||||
err = ui.GetUIManager().Start(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to initialize UI: %v", err)
|
||||
}
|
||||
|
||||
// Initialize plugin system with UI
|
||||
plugin.GetPluginManager().Start(ctx, ui.GetUIManager().GetUI(ctx))
|
||||
|
||||
// Wait for plugins to initialize - increased timeout for stability
|
||||
t.Logf("Waiting 15s for plugins to init")
|
||||
time.Sleep(time.Second * 15)
|
||||
|
||||
// Initialize selection
|
||||
selection.InitSelection()
|
||||
|
||||
// Check if plugins are loaded
|
||||
instances := plugin.GetPluginManager().GetPluginInstances()
|
||||
t.Logf("Loaded %d plugin instances", len(instances))
|
||||
for _, instance := range instances {
|
||||
t.Logf("Plugin: %s (triggers: %v)", instance.Metadata.Name, instance.GetTriggerKeywords())
|
||||
}
|
||||
|
||||
t.Logf("Test services initialized successfully")
|
||||
})
|
||||
}
|
||||
|
||||
// IsNetworkAvailable checks if network-dependent tests should run
|
||||
func IsNetworkAvailable() bool {
|
||||
// Simple check - could be enhanced with actual network connectivity test
|
||||
return true
|
||||
}
|
||||
|
||||
// ShouldSkipNetworkTests returns true if network tests should be skipped
|
||||
func ShouldSkipNetworkTests() bool {
|
||||
config := GetCurrentTestConfig()
|
||||
return !config.EnableNetworkTests || !IsNetworkAvailable()
|
||||
}
|
||||
|
||||
// CleanupTestEnvironment cleans up test directories if configured to do so
|
||||
func CleanupTestEnvironment() error {
|
||||
if testLocation != nil {
|
||||
return testLocation.Cleanup()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetCurrentTestConfig returns the current test configuration instance
|
||||
func GetCurrentTestConfig() *TestConfig {
|
||||
if testConfig == nil {
|
||||
testConfig = GetTestConfig()
|
||||
}
|
||||
return testConfig
|
||||
}
|
||||
|
||||
// GetTestLocation returns the test location instance
|
||||
func GetTestLocation() *TestLocation {
|
||||
return testLocation
|
||||
}
|
||||
|
||||
// GetTestLogger returns the test logger instance
|
||||
func GetTestLogger() *TestLogger {
|
||||
return testLogger
|
||||
}
|
|
@ -0,0 +1,256 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestConfig holds configuration for test execution
|
||||
type TestConfig struct {
|
||||
// Network settings
|
||||
EnableNetworkTests bool
|
||||
NetworkTimeout time.Duration
|
||||
|
||||
// Test execution settings
|
||||
DefaultTimeout time.Duration
|
||||
MaxRetries int
|
||||
RetryDelay time.Duration
|
||||
|
||||
// Parallel execution
|
||||
EnableParallel bool
|
||||
MaxParallel int
|
||||
|
||||
// Logging
|
||||
VerboseLogging bool
|
||||
LogFailedQueries bool
|
||||
|
||||
// Test environment directories
|
||||
TestDataDirectory string // Root directory for test data
|
||||
TestLogDirectory string // Directory for test logs
|
||||
TestUserDirectory string // Directory for test user data
|
||||
CleanupAfterTest bool // Whether to cleanup test directories after tests
|
||||
}
|
||||
|
||||
// DefaultTestConfig returns the default test configuration
|
||||
func DefaultTestConfig() *TestConfig {
|
||||
// Create temporary test directories
|
||||
tempDir := os.TempDir()
|
||||
testDataDir := filepath.Join(tempDir, "wox-test-data")
|
||||
testLogDir := filepath.Join(testDataDir, "logs")
|
||||
testUserDir := filepath.Join(testDataDir, "user")
|
||||
|
||||
return &TestConfig{
|
||||
EnableNetworkTests: true,
|
||||
NetworkTimeout: 45 * time.Second,
|
||||
DefaultTimeout: 30 * time.Second,
|
||||
MaxRetries: 3,
|
||||
RetryDelay: 1 * time.Second,
|
||||
EnableParallel: false, // Disabled by default due to shared state
|
||||
MaxParallel: 4,
|
||||
VerboseLogging: false,
|
||||
LogFailedQueries: true,
|
||||
|
||||
// Test environment directories
|
||||
TestDataDirectory: testDataDir,
|
||||
TestLogDirectory: testLogDir,
|
||||
TestUserDirectory: testUserDir,
|
||||
CleanupAfterTest: true, // Clean up by default
|
||||
}
|
||||
}
|
||||
|
||||
// LoadTestConfigFromEnv loads test configuration from environment variables
|
||||
func LoadTestConfigFromEnv() *TestConfig {
|
||||
config := DefaultTestConfig()
|
||||
|
||||
// Network tests
|
||||
if val := os.Getenv("WOX_TEST_ENABLE_NETWORK"); val != "" {
|
||||
if enabled, err := strconv.ParseBool(val); err == nil {
|
||||
config.EnableNetworkTests = enabled
|
||||
}
|
||||
}
|
||||
|
||||
// Network timeout
|
||||
if val := os.Getenv("WOX_TEST_NETWORK_TIMEOUT"); val != "" {
|
||||
if timeout, err := time.ParseDuration(val); err == nil {
|
||||
config.NetworkTimeout = timeout
|
||||
}
|
||||
}
|
||||
|
||||
// Default timeout
|
||||
if val := os.Getenv("WOX_TEST_DEFAULT_TIMEOUT"); val != "" {
|
||||
if timeout, err := time.ParseDuration(val); err == nil {
|
||||
config.DefaultTimeout = timeout
|
||||
}
|
||||
}
|
||||
|
||||
// Max retries
|
||||
if val := os.Getenv("WOX_TEST_MAX_RETRIES"); val != "" {
|
||||
if retries, err := strconv.Atoi(val); err == nil {
|
||||
config.MaxRetries = retries
|
||||
}
|
||||
}
|
||||
|
||||
// Parallel execution
|
||||
if val := os.Getenv("WOX_TEST_ENABLE_PARALLEL"); val != "" {
|
||||
if enabled, err := strconv.ParseBool(val); err == nil {
|
||||
config.EnableParallel = enabled
|
||||
}
|
||||
}
|
||||
|
||||
// Verbose logging
|
||||
if val := os.Getenv("WOX_TEST_VERBOSE"); val != "" {
|
||||
if verbose, err := strconv.ParseBool(val); err == nil {
|
||||
config.VerboseLogging = verbose
|
||||
}
|
||||
}
|
||||
|
||||
// Test directories
|
||||
if val := os.Getenv("WOX_TEST_DATA_DIR"); val != "" {
|
||||
config.TestDataDirectory = val
|
||||
config.TestLogDirectory = filepath.Join(val, "logs")
|
||||
config.TestUserDirectory = filepath.Join(val, "user")
|
||||
}
|
||||
|
||||
if val := os.Getenv("WOX_TEST_LOG_DIR"); val != "" {
|
||||
config.TestLogDirectory = val
|
||||
}
|
||||
|
||||
if val := os.Getenv("WOX_TEST_USER_DIR"); val != "" {
|
||||
config.TestUserDirectory = val
|
||||
}
|
||||
|
||||
// Cleanup setting
|
||||
if val := os.Getenv("WOX_TEST_CLEANUP"); val != "" {
|
||||
if cleanup, err := strconv.ParseBool(val); err == nil {
|
||||
config.CleanupAfterTest = cleanup
|
||||
}
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
// GetTestConfig returns the current test configuration
|
||||
func GetTestConfig() *TestConfig {
|
||||
return LoadTestConfigFromEnv()
|
||||
}
|
||||
|
||||
// TestCategories defines different test categories
|
||||
type TestCategory string
|
||||
|
||||
const (
|
||||
CategoryCalculator TestCategory = "calculator"
|
||||
CategoryConverter TestCategory = "converter"
|
||||
CategoryPlugin TestCategory = "plugin"
|
||||
CategoryTime TestCategory = "time"
|
||||
CategoryNetwork TestCategory = "network"
|
||||
CategorySystem TestCategory = "system"
|
||||
)
|
||||
|
||||
// TestFilter allows filtering tests by category or pattern
|
||||
type TestFilter struct {
|
||||
Categories []TestCategory
|
||||
Pattern string
|
||||
SkipSlow bool
|
||||
SkipNetwork bool
|
||||
}
|
||||
|
||||
// ShouldRunTest determines if a test should run based on the filter
|
||||
func (f *TestFilter) ShouldRunTest(category TestCategory, name string, isNetwork bool, isSlow bool) bool {
|
||||
// Check category filter
|
||||
if len(f.Categories) > 0 {
|
||||
found := false
|
||||
for _, cat := range f.Categories {
|
||||
if cat == category {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Check network filter
|
||||
if f.SkipNetwork && isNetwork {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check slow test filter
|
||||
if f.SkipSlow && isSlow {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check pattern filter (simple contains check)
|
||||
if f.Pattern != "" && !contains(name, f.Pattern) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// SetupTestEnvironment creates and initializes test directories
|
||||
func (c *TestConfig) SetupTestEnvironment() error {
|
||||
// Create test directories
|
||||
if err := os.MkdirAll(c.TestDataDirectory, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.MkdirAll(c.TestLogDirectory, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.MkdirAll(c.TestUserDirectory, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create subdirectories that Wox expects
|
||||
subdirs := []string{
|
||||
"plugins", "themes", "settings", "cache", "images", "backup",
|
||||
}
|
||||
|
||||
for _, subdir := range subdirs {
|
||||
if err := os.MkdirAll(filepath.Join(c.TestUserDirectory, subdir), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanupTestEnvironment removes test directories if cleanup is enabled
|
||||
func (c *TestConfig) CleanupTestEnvironment() error {
|
||||
if !c.CleanupAfterTest {
|
||||
return nil
|
||||
}
|
||||
|
||||
return os.RemoveAll(c.TestDataDirectory)
|
||||
}
|
||||
|
||||
// GetTestWoxDataDirectory returns the test equivalent of .wox directory
|
||||
func (c *TestConfig) GetTestWoxDataDirectory() string {
|
||||
return c.TestDataDirectory
|
||||
}
|
||||
|
||||
// GetTestUserDataDirectory returns the test user data directory
|
||||
func (c *TestConfig) GetTestUserDataDirectory() string {
|
||||
return c.TestUserDirectory
|
||||
}
|
||||
|
||||
// GetTestLogDirectory returns the test log directory
|
||||
func (c *TestConfig) GetTestLogDirectory() string {
|
||||
return c.TestLogDirectory
|
||||
}
|
||||
|
||||
// contains is a simple string contains check
|
||||
func contains(s, substr string) bool {
|
||||
return len(s) >= len(substr) && (len(substr) == 0 || s[len(s)-len(substr):] == substr ||
|
||||
s[:len(substr)] == substr || (len(s) > len(substr) &&
|
||||
func() bool {
|
||||
for i := 1; i <= len(s)-len(substr); i++ {
|
||||
if s[i:i+len(substr)] == substr {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}()))
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestEnvironmentSetup tests that the test environment is properly configured
|
||||
func TestEnvironmentSetup(t *testing.T) {
|
||||
config := GetTestConfig()
|
||||
|
||||
t.Logf("Test configuration loaded:")
|
||||
t.Logf(" Data Directory: %s", config.TestDataDirectory)
|
||||
t.Logf(" Log Directory: %s", config.TestLogDirectory)
|
||||
t.Logf(" User Directory: %s", config.TestUserDirectory)
|
||||
t.Logf(" Network Tests: %v", config.EnableNetworkTests)
|
||||
t.Logf(" Cleanup: %v", config.CleanupAfterTest)
|
||||
|
||||
// Verify that test directories are different from production directories
|
||||
if config.TestDataDirectory == "" {
|
||||
t.Error("Test data directory should not be empty")
|
||||
}
|
||||
|
||||
// Check that test directories contain "test" in the path
|
||||
if !containsTestInPath(config.TestDataDirectory) {
|
||||
t.Logf("Warning: Test data directory doesn't contain 'test' in path: %s", config.TestDataDirectory)
|
||||
}
|
||||
|
||||
// Verify directories exist after initialization
|
||||
suite := NewTestSuite(t)
|
||||
if suite == nil {
|
||||
t.Fatal("Failed to create test suite")
|
||||
}
|
||||
|
||||
// Check that directories were created
|
||||
if _, err := os.Stat(config.TestDataDirectory); os.IsNotExist(err) {
|
||||
t.Errorf("Test data directory was not created: %s", config.TestDataDirectory)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(config.TestLogDirectory); os.IsNotExist(err) {
|
||||
t.Errorf("Test log directory was not created: %s", config.TestLogDirectory)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(config.TestUserDirectory); os.IsNotExist(err) {
|
||||
t.Errorf("Test user directory was not created: %s", config.TestUserDirectory)
|
||||
}
|
||||
|
||||
// Check that subdirectories were created
|
||||
expectedSubdirs := []string{
|
||||
"plugins", "themes", "settings", "cache", "images", "backup",
|
||||
}
|
||||
|
||||
for _, subdir := range expectedSubdirs {
|
||||
subdirPath := filepath.Join(config.TestUserDirectory, subdir)
|
||||
if _, err := os.Stat(subdirPath); os.IsNotExist(err) {
|
||||
t.Errorf("Expected subdirectory was not created: %s", subdirPath)
|
||||
}
|
||||
}
|
||||
|
||||
t.Logf("Test environment setup validation passed")
|
||||
}
|
||||
|
||||
// TestEnvironmentIsolation tests that test environment doesn't interfere with production
|
||||
func TestEnvironmentIsolation(t *testing.T) {
|
||||
config := GetTestConfig()
|
||||
|
||||
// Test directories should be in temp or contain "test"
|
||||
testDirs := []string{
|
||||
config.TestDataDirectory,
|
||||
config.TestLogDirectory,
|
||||
config.TestUserDirectory,
|
||||
}
|
||||
|
||||
for _, dir := range testDirs {
|
||||
if !isTestDirectory(dir) {
|
||||
t.Errorf("Directory doesn't appear to be a test directory: %s", dir)
|
||||
}
|
||||
}
|
||||
|
||||
t.Logf("Test environment isolation validation passed")
|
||||
}
|
||||
|
||||
// TestEnvironmentCleanup tests the cleanup functionality
|
||||
func TestEnvironmentCleanup(t *testing.T) {
|
||||
// Create a temporary test config with cleanup enabled
|
||||
config := DefaultTestConfig()
|
||||
config.TestDataDirectory = filepath.Join(os.TempDir(), "wox-test-cleanup-test")
|
||||
config.CleanupAfterTest = true
|
||||
|
||||
// Setup environment
|
||||
if err := config.SetupTestEnvironment(); err != nil {
|
||||
t.Fatalf("Failed to setup test environment: %v", err)
|
||||
}
|
||||
|
||||
// Verify directory exists
|
||||
if _, err := os.Stat(config.TestDataDirectory); os.IsNotExist(err) {
|
||||
t.Fatalf("Test directory was not created: %s", config.TestDataDirectory)
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
if err := config.CleanupTestEnvironment(); err != nil {
|
||||
t.Errorf("Failed to cleanup test environment: %v", err)
|
||||
}
|
||||
|
||||
// Verify directory was removed
|
||||
if _, err := os.Stat(config.TestDataDirectory); !os.IsNotExist(err) {
|
||||
t.Errorf("Test directory was not cleaned up: %s", config.TestDataDirectory)
|
||||
}
|
||||
|
||||
t.Logf("Test environment cleanup validation passed")
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
func containsTestInPath(path string) bool {
|
||||
return filepath.Base(path) == "test" ||
|
||||
filepath.Base(filepath.Dir(path)) == "test" ||
|
||||
filepath.Base(path) == "wox-test" ||
|
||||
filepath.Base(path) == "wox-test-data"
|
||||
}
|
||||
|
||||
func isTestDirectory(path string) bool {
|
||||
// Check if path is in temp directory or contains "test"
|
||||
tempDir := os.TempDir()
|
||||
if strings.HasPrefix(path, tempDir) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if path contains "test" somewhere
|
||||
for p := path; p != "/" && p != "."; p = filepath.Dir(p) {
|
||||
base := filepath.Base(p)
|
||||
if base == "test" ||
|
||||
base == "wox-test" ||
|
||||
base == "wox-test-data" ||
|
||||
base == "wox-test-isolated" ||
|
||||
base == "wox-test-custom" ||
|
||||
base == "wox-test-debug" ||
|
||||
strings.HasPrefix(base, "wox-test-") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"wox/util"
|
||||
)
|
||||
|
||||
// TestLocation is a test-specific implementation of Location that uses isolated directories
|
||||
type TestLocation struct {
|
||||
config *TestConfig
|
||||
*util.Location // Embed the original Location
|
||||
}
|
||||
|
||||
// NewTestLocation creates a new test location with isolated directories
|
||||
func NewTestLocation(config *TestConfig) (*TestLocation, error) {
|
||||
// Setup test environment
|
||||
if err := config.SetupTestEnvironment(); err != nil {
|
||||
return nil, fmt.Errorf("failed to setup test environment: %w", err)
|
||||
}
|
||||
|
||||
// Create a new location instance
|
||||
originalLocation := util.GetLocation()
|
||||
|
||||
testLocation := &TestLocation{
|
||||
config: config,
|
||||
Location: originalLocation,
|
||||
}
|
||||
|
||||
return testLocation, nil
|
||||
}
|
||||
|
||||
// Override methods to use test directories
|
||||
func (tl *TestLocation) GetWoxDataDirectory() string {
|
||||
return tl.config.TestDataDirectory
|
||||
}
|
||||
|
||||
func (tl *TestLocation) GetLogDirectory() string {
|
||||
return tl.config.TestLogDirectory
|
||||
}
|
||||
|
||||
func (tl *TestLocation) GetUserDataDirectory() string {
|
||||
return tl.config.TestUserDirectory
|
||||
}
|
||||
|
||||
func (tl *TestLocation) GetLogPluginDirectory() string {
|
||||
return filepath.Join(tl.GetLogDirectory(), "plugins")
|
||||
}
|
||||
|
||||
func (tl *TestLocation) GetLogHostsDirectory() string {
|
||||
return filepath.Join(tl.GetLogDirectory(), "hosts")
|
||||
}
|
||||
|
||||
func (tl *TestLocation) GetPluginDirectory() string {
|
||||
return filepath.Join(tl.GetUserDataDirectory(), "plugins")
|
||||
}
|
||||
|
||||
func (tl *TestLocation) GetUserScriptPluginsDirectory() string {
|
||||
return filepath.Join(tl.GetPluginDirectory(), "scripts")
|
||||
}
|
||||
|
||||
func (tl *TestLocation) GetThemeDirectory() string {
|
||||
return filepath.Join(tl.GetUserDataDirectory(), "themes")
|
||||
}
|
||||
|
||||
func (tl *TestLocation) GetPluginSettingDirectory() string {
|
||||
return filepath.Join(tl.GetUserDataDirectory(), "settings")
|
||||
}
|
||||
|
||||
func (tl *TestLocation) GetWoxSettingPath() string {
|
||||
return filepath.Join(tl.GetPluginSettingDirectory(), "wox.json")
|
||||
}
|
||||
|
||||
func (tl *TestLocation) GetWoxAppDataPath() string {
|
||||
return filepath.Join(tl.GetPluginSettingDirectory(), "wox.data.json")
|
||||
}
|
||||
|
||||
func (tl *TestLocation) GetHostDirectory() string {
|
||||
return filepath.Join(tl.GetWoxDataDirectory(), "hosts")
|
||||
}
|
||||
|
||||
func (tl *TestLocation) GetUpdatesDirectory() string {
|
||||
return filepath.Join(tl.GetWoxDataDirectory(), "updates")
|
||||
}
|
||||
|
||||
func (tl *TestLocation) GetUIDirectory() string {
|
||||
return filepath.Join(tl.GetWoxDataDirectory(), "ui")
|
||||
}
|
||||
|
||||
func (tl *TestLocation) GetOthersDirectory() string {
|
||||
return filepath.Join(tl.GetWoxDataDirectory(), "others")
|
||||
}
|
||||
|
||||
func (tl *TestLocation) GetScriptPluginTemplatesDirectory() string {
|
||||
return filepath.Join(tl.GetWoxDataDirectory(), "script_plugin_templates")
|
||||
}
|
||||
|
||||
func (tl *TestLocation) GetCacheDirectory() string {
|
||||
return filepath.Join(tl.GetWoxDataDirectory(), "cache")
|
||||
}
|
||||
|
||||
func (tl *TestLocation) GetImageCacheDirectory() string {
|
||||
return filepath.Join(tl.GetCacheDirectory(), "images")
|
||||
}
|
||||
|
||||
func (tl *TestLocation) GetBackupDirectory() string {
|
||||
return filepath.Join(tl.GetWoxDataDirectory(), "backup")
|
||||
}
|
||||
|
||||
func (tl *TestLocation) GetAppLockPath() string {
|
||||
return filepath.Join(tl.GetWoxDataDirectory(), "wox.lock")
|
||||
}
|
||||
|
||||
func (tl *TestLocation) GetUserDataDirectoryShortcutPath() string {
|
||||
return filepath.Join(tl.GetWoxDataDirectory(), ".userdata.location")
|
||||
}
|
||||
|
||||
// InitTestDirectories creates all necessary test directories
|
||||
func (tl *TestLocation) InitTestDirectories() error {
|
||||
directories := []string{
|
||||
tl.GetWoxDataDirectory(),
|
||||
tl.GetLogDirectory(),
|
||||
tl.GetUserDataDirectory(),
|
||||
tl.GetLogPluginDirectory(),
|
||||
tl.GetLogHostsDirectory(),
|
||||
tl.GetPluginDirectory(),
|
||||
tl.GetUserScriptPluginsDirectory(),
|
||||
tl.GetThemeDirectory(),
|
||||
tl.GetPluginSettingDirectory(),
|
||||
tl.GetHostDirectory(),
|
||||
tl.GetUpdatesDirectory(),
|
||||
tl.GetUIDirectory(),
|
||||
tl.GetOthersDirectory(),
|
||||
tl.GetScriptPluginTemplatesDirectory(),
|
||||
tl.GetCacheDirectory(),
|
||||
tl.GetImageCacheDirectory(),
|
||||
tl.GetBackupDirectory(),
|
||||
}
|
||||
|
||||
for _, dir := range directories {
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create directory %s: %w", dir, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Create the shortcut file
|
||||
shortcutPath := tl.GetUserDataDirectoryShortcutPath()
|
||||
if err := os.WriteFile(shortcutPath, []byte(tl.GetUserDataDirectory()), 0644); err != nil {
|
||||
return fmt.Errorf("failed to create shortcut file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Cleanup removes all test directories
|
||||
func (tl *TestLocation) Cleanup() error {
|
||||
return tl.config.CleanupTestEnvironment()
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"wox/util"
|
||||
)
|
||||
|
||||
// TestLogger wraps the original logger to use test-specific directories
|
||||
type TestLogger struct {
|
||||
*util.Log
|
||||
testLocation *TestLocation
|
||||
}
|
||||
|
||||
// NewTestLogger creates a new test logger that writes to test directories
|
||||
func NewTestLogger(testLocation *TestLocation) *TestLogger {
|
||||
// Create logger with test log directory
|
||||
logFolder := testLocation.GetLogDirectory()
|
||||
logger := util.CreateLogger(logFolder)
|
||||
|
||||
return &TestLogger{
|
||||
Log: logger,
|
||||
testLocation: testLocation,
|
||||
}
|
||||
}
|
||||
|
||||
// Override logging methods to add test prefix
|
||||
func (tl *TestLogger) Debug(ctx context.Context, msg string) {
|
||||
tl.Log.Debug(ctx, fmt.Sprintf("[TEST] %s", msg))
|
||||
}
|
||||
|
||||
func (tl *TestLogger) Info(ctx context.Context, msg string) {
|
||||
tl.Log.Info(ctx, fmt.Sprintf("[TEST] %s", msg))
|
||||
}
|
||||
|
||||
func (tl *TestLogger) Warn(ctx context.Context, msg string) {
|
||||
tl.Log.Warn(ctx, fmt.Sprintf("[TEST] %s", msg))
|
||||
}
|
||||
|
||||
func (tl *TestLogger) Error(ctx context.Context, msg string) {
|
||||
tl.Log.Error(ctx, fmt.Sprintf("[TEST] %s", msg))
|
||||
}
|
||||
|
||||
// GetLogDirectory returns the test log directory
|
||||
func (tl *TestLogger) GetLogDirectory() string {
|
||||
return tl.testLocation.GetLogDirectory()
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestRunner manages the test execution lifecycle
|
||||
type TestRunner struct {
|
||||
config *TestConfig
|
||||
}
|
||||
|
||||
// NewTestRunner creates a new test runner with the given configuration
|
||||
func NewTestRunner(config *TestConfig) *TestRunner {
|
||||
return &TestRunner{
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
// RunWithCleanup runs a test function and ensures cleanup is performed
|
||||
func (tr *TestRunner) RunWithCleanup(t *testing.T, testFunc func(*testing.T)) {
|
||||
// Setup test environment
|
||||
if err := tr.config.SetupTestEnvironment(); err != nil {
|
||||
t.Fatalf("Failed to setup test environment: %v", err)
|
||||
}
|
||||
|
||||
// Ensure cleanup happens even if test panics
|
||||
defer func() {
|
||||
if err := tr.config.CleanupTestEnvironment(); err != nil {
|
||||
t.Errorf("Failed to cleanup test environment: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Run the test
|
||||
testFunc(t)
|
||||
}
|
||||
|
||||
// PrintTestEnvironmentInfo prints information about the test environment
|
||||
func (tr *TestRunner) PrintTestEnvironmentInfo(t *testing.T) {
|
||||
t.Logf("=== Test Environment Configuration ===")
|
||||
t.Logf("Test Data Directory: %s", tr.config.TestDataDirectory)
|
||||
t.Logf("Test Log Directory: %s", tr.config.TestLogDirectory)
|
||||
t.Logf("Test User Directory: %s", tr.config.TestUserDirectory)
|
||||
t.Logf("Network Tests Enabled: %v", tr.config.EnableNetworkTests)
|
||||
t.Logf("Cleanup After Test: %v", tr.config.CleanupAfterTest)
|
||||
t.Logf("Verbose Logging: %v", tr.config.VerboseLogging)
|
||||
t.Logf("=====================================")
|
||||
}
|
||||
|
||||
// Example usage function
|
||||
func ExampleTestWithIsolatedEnvironment(t *testing.T) {
|
||||
// Create a custom test configuration
|
||||
config := DefaultTestConfig()
|
||||
config.TestDataDirectory = "/tmp/wox-test-custom"
|
||||
config.CleanupAfterTest = true
|
||||
config.VerboseLogging = true
|
||||
|
||||
// Create test runner
|
||||
runner := NewTestRunner(config)
|
||||
runner.PrintTestEnvironmentInfo(t)
|
||||
|
||||
// Run test with automatic cleanup
|
||||
runner.RunWithCleanup(t, func(t *testing.T) {
|
||||
// Your test code here
|
||||
suite := NewTestSuite(t)
|
||||
|
||||
test := QueryTest{
|
||||
Name: "Example test",
|
||||
Query: "1+1",
|
||||
ExpectedTitle: "2",
|
||||
ExpectedAction: "Copy result",
|
||||
}
|
||||
|
||||
if !suite.RunQueryTest(test) {
|
||||
t.Errorf("Example test failed")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// SetupTestEnvironmentFromEnv sets up test environment based on environment variables
|
||||
func SetupTestEnvironmentFromEnv() error {
|
||||
config := GetTestConfig()
|
||||
return config.SetupTestEnvironment()
|
||||
}
|
||||
|
||||
// CleanupTestEnvironmentFromEnv cleans up test environment based on environment variables
|
||||
func CleanupTestEnvironmentFromEnv() error {
|
||||
config := GetTestConfig()
|
||||
return config.CleanupTestEnvironment()
|
||||
}
|
||||
|
||||
// ValidateTestEnvironment checks if the test environment is properly configured
|
||||
func ValidateTestEnvironment(t *testing.T) error {
|
||||
config := GetTestConfig()
|
||||
|
||||
// Check if directories exist
|
||||
if _, err := os.Stat(config.TestDataDirectory); os.IsNotExist(err) {
|
||||
return fmt.Errorf("test data directory does not exist: %s", config.TestDataDirectory)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(config.TestLogDirectory); os.IsNotExist(err) {
|
||||
return fmt.Errorf("test log directory does not exist: %s", config.TestLogDirectory)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(config.TestUserDirectory); os.IsNotExist(err) {
|
||||
return fmt.Errorf("test user directory does not exist: %s", config.TestUserDirectory)
|
||||
}
|
||||
|
||||
t.Logf("Test environment validation passed")
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestCalculatorTime(t *testing.T) {
|
||||
suite := NewTestSuite(t)
|
||||
now := time.Now()
|
||||
|
||||
// Get current time
|
||||
hour := now.Hour()
|
||||
ampm := "AM"
|
||||
if hour >= 12 {
|
||||
ampm = "PM"
|
||||
if hour > 12 {
|
||||
hour -= 12
|
||||
}
|
||||
}
|
||||
if hour == 0 {
|
||||
hour = 12
|
||||
}
|
||||
expectedTime := fmt.Sprintf("%d:%02d %s", hour, now.Minute(), ampm)
|
||||
|
||||
// Calculate expected date for "monday in 10 days"
|
||||
targetDate := now.AddDate(0, 0, 10)
|
||||
for targetDate.Weekday() != time.Monday {
|
||||
targetDate = targetDate.AddDate(0, 0, 1)
|
||||
}
|
||||
expectedMonday := fmt.Sprintf("%s (Monday)", targetDate.Format("2006-01-02"))
|
||||
|
||||
// Calculate expected days until Christmas 2025
|
||||
christmas := time.Date(2025, time.December, 25, 0, 0, 0, 0, time.Local)
|
||||
daysUntilChristmas := int(christmas.Sub(now).Hours() / 24)
|
||||
expectedDaysUntil := fmt.Sprintf("%d days", daysUntilChristmas)
|
||||
|
||||
tests := []QueryTest{
|
||||
{
|
||||
Name: "Time in location",
|
||||
Query: "time in Shanghai",
|
||||
ExpectedTitle: expectedTime,
|
||||
ExpectedAction: "Copy result",
|
||||
TitleCheck: func(title string) bool {
|
||||
// More flexible time check - should contain time format
|
||||
return strings.Contains(title, "AM") || strings.Contains(title, "PM") || strings.Contains(title, ":")
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Weekday in future",
|
||||
Query: "monday in 10 days",
|
||||
ExpectedTitle: expectedMonday,
|
||||
ExpectedAction: "Copy result",
|
||||
TitleCheck: func(title string) bool {
|
||||
// Should contain date and Monday
|
||||
return strings.Contains(title, "Monday") && strings.Contains(title, "-")
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Days until Christmas",
|
||||
Query: "days until 25 Dec 2025",
|
||||
ExpectedTitle: expectedDaysUntil,
|
||||
ExpectedAction: "Copy result",
|
||||
TitleCheck: func(title string) bool {
|
||||
// Should contain "days"
|
||||
return strings.Contains(title, "days")
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Specific time in location",
|
||||
Query: "3:30 pm in tokyo",
|
||||
ExpectedTitle: "",
|
||||
ExpectedAction: "Copy result",
|
||||
TitleCheck: func(title string) bool {
|
||||
// Should contain time format
|
||||
return strings.Contains(title, "PM") || strings.Contains(title, "AM") || strings.Contains(title, ":")
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Simple time unit",
|
||||
Query: "100ms",
|
||||
ExpectedTitle: "100 milliseconds",
|
||||
ExpectedAction: "Copy result",
|
||||
},
|
||||
{
|
||||
Name: "Time conversion",
|
||||
Query: "1h",
|
||||
ExpectedTitle: "1.00 hours",
|
||||
ExpectedAction: "Copy result",
|
||||
},
|
||||
}
|
||||
|
||||
suite.RunQueryTests(tests)
|
||||
}
|
||||
|
||||
func TestTimeZoneConversions(t *testing.T) {
|
||||
suite := NewTestSuite(t)
|
||||
|
||||
tests := []QueryTest{
|
||||
{
|
||||
Name: "UTC time",
|
||||
Query: "time in UTC",
|
||||
ExpectedTitle: "",
|
||||
ExpectedAction: "Copy result",
|
||||
TitleCheck: func(title string) bool {
|
||||
return strings.Contains(title, ":") && (strings.Contains(title, "AM") || strings.Contains(title, "PM"))
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "London time",
|
||||
Query: "time in London",
|
||||
ExpectedTitle: "",
|
||||
ExpectedAction: "Copy result",
|
||||
TitleCheck: func(title string) bool {
|
||||
return strings.Contains(title, ":") && (strings.Contains(title, "AM") || strings.Contains(title, "PM"))
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "New York time",
|
||||
Query: "time in New York",
|
||||
ExpectedTitle: "",
|
||||
ExpectedAction: "Copy result",
|
||||
TitleCheck: func(title string) bool {
|
||||
return strings.Contains(title, ":") && (strings.Contains(title, "AM") || strings.Contains(title, "PM"))
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
suite.RunQueryTests(tests)
|
||||
}
|
Loading…
Reference in New Issue