Merge pull request #1 from alexwlchan/add-github-actions
- ID
1eb9612- date
2025-10-12 12:09:01+00:00- author
Alex Chan <alexc@tailscale.com>- parents
84465bb,fb8d812- message
Merge pull request #1 from alexwlchan/add-github-actions Bring in changes from how I'm using it; add GitHub Actions and tests- changed files
3 files, 75 additions, 17 deletions
Changed files
.github/workflows/test.yml (0) → .github/workflows/test.yml (368)
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..fe422d9
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,23 @@
+name: Go
+
+on: [push]
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ go-version:
+ - "1.25"
+
+ steps:
+ - uses: actions/checkout@v5
+
+ - name: Setup Go ${{ matrix.go-version }}
+ uses: actions/setup-go@v5
+ with:
+ go-version: ${{ matrix.go-version }}
+
+ - name: Run tests
+ run: go test
q.go (2236) → q.go (2451)
diff --git a/q.go b/q.go
index 3300804..cf9a179 100644
--- a/q.go
+++ b/q.go
@@ -4,10 +4,30 @@ import (
"bufio"
"fmt"
"os"
+ "regexp"
"runtime"
"strings"
)
+// Choose how to write the function name in the file.
+//
+// e.g. test functions can be long and have multiple parts, so we just
+// pull out the most meaningful part.
+func chooseDisplayName(functionName string) string {
+ parts := strings.Split(functionName, ".")
+
+ // If this is an anonymous function, the name will be something like
+ // "func1", which is unhelpful.
+ //
+ // Throw away that part and get the next part.
+ if m, _ := regexp.MatchString("^func[0-9]+$", parts[len(parts)-1]); len(parts) > 1 && m {
+ fmt.Println(m)
+ return parts[len(parts)-2]
+ }
+
+ return parts[len(parts)-1]
+}
+
func getFunctionName() string {
pc, _, _, ok := runtime.Caller(2)
if !ok {
@@ -19,14 +39,7 @@ func getFunctionName() string {
return "<unknown>"
}
- // The name of test functions can be long and have multiple parts,
- // e.g. tailscale.com/wgengine/magicsock.TestNetworkDownSendErrors
- //
- // For brevity, just get the last part.
- parts := strings.Split(fn.Name(), ".")
- lastPart := parts[len(parts)-1]
-
- return lastPart
+ return chooseDisplayName(fn.Name())
}
func getExpression() string {
@@ -79,7 +92,7 @@ func toString(value any, a ...any) string {
case fmt.Stringer:
return v.String()
default:
- return fmt.Sprintf("%v", v) // fallback
+ return fmt.Sprintf("%+v", v) // fallback
}
}
@@ -93,14 +106,7 @@ func Q(value any, a ...any) {
functionName := getFunctionName()
expression := getExpression()
- var line string
-
- switch value.(type) {
- case string:
- line = "\x1b[32m" + functionName + "\x1b[39m: " + toString(value, a...) + "\n\n"
- default:
- line = "\x1b[32m" + functionName + "\x1b[39m: " + expression + " = \x1b[36m" + toString(value, a...) + "\x1b[39m\n\n"
- }
+ line := "\x1b[32m" + functionName + "\x1b[39m: " + expression + " = \x1b[36m" + toString(value, a...) + "\x1b[39m\n\n"
if _, err = f.WriteString(line); err != nil {
panic(err)
q_test.go (0) → q_test.go (705)
diff --git a/q_test.go b/q_test.go
new file mode 100644
index 0000000..ead6941
--- /dev/null
+++ b/q_test.go
@@ -0,0 +1,29 @@
+package q
+
+import "testing"
+
+func TestChooseDisplayName(t *testing.T) {
+ for _, tt := range []struct {
+ label string
+ functionName string
+ displayName string
+ }{
+ {
+ label: "test-function",
+ functionName: "tailscale.com/wgengine/magicsock.TestNetworkDownSendErrors",
+ displayName: "TestNetworkDownSendErrors",
+ },
+ {
+ label: "test-function-with-parameters",
+ functionName: "tailscale.com/tstest/integration.TestOneNodeUpAuth.func1",
+ displayName: "TestOneNodeUpAuth",
+ },
+ } {
+ t.Run(tt.label, func(t *testing.T) {
+ if got := chooseDisplayName(tt.functionName); got != tt.displayName {
+ t.Fatalf("wanted: %s, got: %s", tt.displayName, got)
+ }
+ })
+
+ }
+}