diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index 27e6e7d..3c943ae 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -4,6 +4,7 @@
 name: Go
 
 on:
+  workflow_dispatch:
   push:
     branches: ["main"]
   pull_request:
@@ -11,12 +12,15 @@ on:
 
 jobs:
   build:
-    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        os: [ubuntu-latest, windows-latest, macos-latest]
+    runs-on: ${{ matrix.os }}
     steps:
       - uses: actions/checkout@v4
 
       - name: setup
-        uses: actions/setup-go@v4
+        uses: actions/setup-go@v5
         with:
           go-version: "1.20"
 
@@ -27,7 +31,9 @@ jobs:
         run: go test -v ./...
 
       - name: gofmt
+        if: matrix.os == 'ubuntu-latest'
         run: exit $(gofmt -l . | wc -l)
 
       - name: vet
+        if: matrix.os == 'ubuntu-latest'
         run: go vet -all=true -v=true .
diff --git a/assert_test.go b/assert_test.go
index cfb7487..b546c31 100644
--- a/assert_test.go
+++ b/assert_test.go
@@ -2,13 +2,11 @@ package flags
 
 import (
 	"fmt"
-	"io"
-	"io/ioutil"
-	"os"
-	"os/exec"
 	"path"
 	"runtime"
 	"testing"
+
+	"github.com/sergi/go-diff/diffmatchpatch"
 )
 
 func assertCallerInfo() (string, int) {
@@ -128,50 +126,18 @@ func assertParseFail(t *testing.T, typ ErrorType, msg string, data interface{},
 	return ret
 }
 
-func diff(a, b string) (string, error) {
-	atmp, err := ioutil.TempFile("", "help-diff")
-
-	if err != nil {
-		return "", err
-	}
-
-	btmp, err := ioutil.TempFile("", "help-diff")
-
-	if err != nil {
-		return "", err
-	}
-
-	if _, err := io.WriteString(atmp, a); err != nil {
-		return "", err
-	}
-
-	if _, err := io.WriteString(btmp, b); err != nil {
-		return "", err
-	}
-
-	ret, err := exec.Command("diff", "-u", "-d", "--label", "got", atmp.Name(), "--label", "expected", btmp.Name()).Output()
-
-	os.Remove(atmp.Name())
-	os.Remove(btmp.Name())
-
-	if err.Error() == "exit status 1" {
-		return string(ret), nil
-	}
-
-	return string(ret), err
-}
-
 func assertDiff(t *testing.T, actual, expected, msg string) {
 	if actual == expected {
 		return
 	}
 
-	ret, err := diff(actual, expected)
+	dmp := diffmatchpatch.New()
+	diffs := dmp.DiffMain(actual, expected, false)
 
-	if err != nil {
-		assertErrorf(t, "Unexpected diff error: %s", err)
-		assertErrorf(t, "Unexpected %s, expected:\n\n%s\n\nbut got\n\n%s", msg, expected, actual)
-	} else {
-		assertErrorf(t, "Unexpected %s:\n\n%s", msg, ret)
+	if len(diffs) == 0 {
+		return
 	}
+
+	pretty := dmp.DiffPrettyText(diffs)
+	assertErrorf(t, "Unexpected %s:\n\n%s", msg, pretty)
 }
diff --git a/completion.go b/completion.go
index 8ed61f1..d11a6fa 100644
--- a/completion.go
+++ b/completion.go
@@ -91,10 +91,21 @@ func (c *completion) completeOptionNames(s *parseState, prefix string, match str
 	var results []Completion
 	repeats := map[string]bool{}
 
+	var longprefix string
+	var shortprefix string
+
+	if prefix == "/" {
+		longprefix = "/"
+		shortprefix = "/"
+	} else {
+		longprefix = "--"
+		shortprefix = "-"
+	}
+
 	for name, opt := range s.lookup.longNames {
 		if strings.HasPrefix(name, match) && !opt.Hidden {
 			results = append(results, Completion{
-				Item:        defaultLongOptDelimiter + name,
+				Item:        longprefix + name,
 				Description: opt.Description,
 			})
 
@@ -108,7 +119,7 @@ func (c *completion) completeOptionNames(s *parseState, prefix string, match str
 		for name, opt := range s.lookup.shortNames {
 			if _, exist := repeats[name]; !exist && strings.HasPrefix(name, match) && !opt.Hidden {
 				results = append(results, Completion{
-					Item:        string(defaultShortOptDelimiter) + name,
+					Item:        shortprefix + name,
 					Description: opt.Description,
 				})
 			}
diff --git a/completion_test.go b/completion_test.go
index aa3af90..00414cc 100644
--- a/completion_test.go
+++ b/completion_test.go
@@ -8,6 +8,7 @@ import (
 	"path/filepath"
 	"reflect"
 	"runtime"
+	"sort"
 	"strings"
 	"testing"
 )
@@ -81,6 +82,14 @@ type completionTest struct {
 
 var completionTests []completionTest
 
+func makeLongName(option string) string {
+	return defaultLongOptDelimiter + option
+}
+
+func makeShortName(option string) string {
+	return string(defaultShortOptDelimiter) + option
+}
+
 func init() {
 	_, sourcefile, _, _ := runtime.Caller(0)
 	completionTestSourcedir := filepath.Join(filepath.SplitList(path.Dir(sourcefile))...)
@@ -97,15 +106,15 @@ func init() {
 	completionTests = []completionTest{
 		{
 			// Short names
-			[]string{"-"},
-			[]string{"--debug", "--required", "--verbose", "--version", "-i"},
+			[]string{makeShortName("")},
+			[]string{makeLongName("debug"), makeLongName("required"), makeLongName("verbose"), makeLongName("version"), makeShortName("i")},
 			false,
 		},
 
 		{
 			// Short names full
-			[]string{"-i"},
-			[]string{"-i"},
+			[]string{makeShortName("i")},
+			[]string{makeShortName("i")},
 			false,
 		},
 
@@ -137,8 +146,8 @@ func init() {
 
 		{
 			// Long names partial
-			[]string{"--ver"},
-			[]string{"--verbose", "--version"},
+			[]string{makeLongName("ver")},
+			[]string{makeLongName("verbose"), makeLongName("version")},
 			false,
 		},
 
@@ -197,69 +206,69 @@ func init() {
 
 		{
 			// Flag filename
-			[]string{"rm", "-f", path.Join(completionTestSourcedir, "completion")},
+			[]string{"rm", makeShortName("f"), filepath.Join(completionTestSourcedir, "completion")},
 			completionTestFilename,
 			false,
 		},
 
 		{
 			// Flag short concat last filename
-			[]string{"rm", "-of", path.Join(completionTestSourcedir, "completion")},
+			[]string{"rm", "-of", filepath.Join(completionTestSourcedir, "completion")},
 			completionTestFilename,
 			false,
 		},
 
 		{
 			// Flag concat filename
-			[]string{"rm", "-f" + path.Join(completionTestSourcedir, "completion")},
+			[]string{"rm", "-f" + filepath.Join(completionTestSourcedir, "completion")},
 			[]string{"-f" + completionTestFilename[0], "-f" + completionTestFilename[1]},
 			false,
 		},
 
 		{
 			// Flag equal concat filename
-			[]string{"rm", "-f=" + path.Join(completionTestSourcedir, "completion")},
+			[]string{"rm", "-f=" + filepath.Join(completionTestSourcedir, "completion")},
 			[]string{"-f=" + completionTestFilename[0], "-f=" + completionTestFilename[1]},
 			false,
 		},
 
 		{
 			// Flag concat long filename
-			[]string{"rm", "--filename=" + path.Join(completionTestSourcedir, "completion")},
+			[]string{"rm", "--filename=" + filepath.Join(completionTestSourcedir, "completion")},
 			[]string{"--filename=" + completionTestFilename[0], "--filename=" + completionTestFilename[1]},
 			false,
 		},
 
 		{
 			// Flag long filename
-			[]string{"rm", "--filename", path.Join(completionTestSourcedir, "completion")},
+			[]string{"rm", "--filename", filepath.Join(completionTestSourcedir, "completion")},
 			completionTestFilename,
 			false,
 		},
 
 		{
 			// To subdir
-			[]string{"rm", "--filename", path.Join(completionTestSourcedir, "examples/bash-")},
-			[]string{path.Join(completionTestSourcedir, "examples/bash-completion/")},
+			[]string{"rm", "--filename", filepath.Join(completionTestSourcedir, "examples/bash-")},
+			[]string{filepath.Join(completionTestSourcedir, "examples/bash-completion/")},
 			false,
 		},
 
 		{
 			// Subdirectory
-			[]string{"rm", "--filename", path.Join(completionTestSourcedir, "examples") + "/"},
+			[]string{"rm", "--filename", filepath.Join(completionTestSourcedir, "examples") + "/"},
 			completionTestSubdir,
 			false,
 		},
 
 		{
 			// Custom completed
-			[]string{"rename", "-c", "hello un"},
+			[]string{"rename", makeShortName("c"), "hello un"},
 			[]string{"hello universe"},
 			false,
 		},
 		{
 			// Multiple flag filename
-			[]string{"add-multi-flag", "-f", filepath.Join(completionTestSourcedir, "completion")},
+			[]string{"add-multi-flag", makeShortName("f"), filepath.Join(completionTestSourcedir, "completion")},
 			completionTestFilename,
 			false,
 		},
@@ -282,6 +291,9 @@ func TestCompletion(t *testing.T) {
 			items[i] = v.Item
 		}
 
+		sort.Strings(items)
+		sort.Strings(test.Completed)
+
 		if !reflect.DeepEqual(items, test.Completed) {
 			t.Errorf("Args: %#v, %#v\n  Expected: %#v\n  Got:     %#v", test.Args, test.ShowDescriptions, test.Completed, items)
 		}
diff --git a/go.mod b/go.mod
index 934cfbe..d0daa25 100644
--- a/go.mod
+++ b/go.mod
@@ -2,4 +2,7 @@ module github.com/jessevdk/go-flags
 
 go 1.20
 
-require golang.org/x/sys v0.21.0
+require (
+	github.com/sergi/go-diff v1.3.1
+	golang.org/x/sys v0.21.0
+)
diff --git a/go.sum b/go.sum
index ac7fb31..bac7088 100644
--- a/go.sum
+++ b/go.sum
@@ -1,2 +1,20 @@
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
+github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
 golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
diff --git a/help_test.go b/help_test.go
index dee8533..7f8b463 100644
--- a/help_test.go
+++ b/help_test.go
@@ -153,7 +153,7 @@ Arguments:
 Available commands:
   bommand  A command with only hidden options
   command  A command (aliases: cm, cmd)
-  parent   A command with a sub command
+  parent   A parent command
 `
 		} else {
 			expected = `Usage:
diff --git a/parser_test.go b/parser_test.go
index df37044..e675cfd 100644
--- a/parser_test.go
+++ b/parser_test.go
@@ -101,7 +101,7 @@ func TestDefaults(t *testing.T) {
 		{
 			msg:         "non-zero value arguments, expecting overwritten arguments",
 			args:        []string{"-3=true"},
-			expectedErr: "bool flag `-3' cannot have an argument",
+			expectedErr: "bool flag `" + makeShortName("3") + "' cannot have an argument",
 		},
 		{
 			msg:  "zero value arguments, expecting overwritten arguments",
diff --git a/termsize.go b/termsize.go
index 7bcf66f..eb08532 100644
--- a/termsize.go
+++ b/termsize.go
@@ -4,13 +4,19 @@
 package flags
 
 import (
+	"flag"
+
 	"golang.org/x/sys/unix"
 )
 
 func getTerminalColumns() int {
+	if flag.Lookup("test.v") != nil {
+		return defaultTermSize
+	}
+
 	ws, err := unix.IoctlGetWinsize(0, unix.TIOCGWINSZ)
 	if err != nil {
-		return 80
+		return defaultTermSize
 	}
 	return int(ws.Col)
 }
diff --git a/termsize_defaults.go b/termsize_defaults.go
new file mode 100644
index 0000000..df2ec29
--- /dev/null
+++ b/termsize_defaults.go
@@ -0,0 +1,3 @@
+package flags
+
+const defaultTermSize = 80
diff --git a/termsize_nosysioctl.go b/termsize_nosysioctl.go
index d839220..6d557a9 100644
--- a/termsize_nosysioctl.go
+++ b/termsize_nosysioctl.go
@@ -4,5 +4,5 @@
 package flags
 
 func getTerminalColumns() int {
-	return 80
+	return defaultTermSize
 }
diff --git a/termsize_windows.go b/termsize_windows.go
index 189a1b3..763f2dd 100644
--- a/termsize_windows.go
+++ b/termsize_windows.go
@@ -4,6 +4,7 @@
 package flags
 
 import (
+	"flag"
 	"syscall"
 	"unsafe"
 )
@@ -66,21 +67,23 @@ func GetConsoleScreenBufferInfo(handle uintptr) (*CONSOLE_SCREEN_BUFFER_INFO, er
 }
 
 func getTerminalColumns() int {
-	defaultWidth := 80
+	if flag.Lookup("test.v") != nil {
+		return defaultTermSize
+	}
 
 	stdoutHandle, err := getStdHandle(syscall.STD_OUTPUT_HANDLE)
 	if err != nil {
-		return defaultWidth
+		return defaultTermSize
 	}
 
 	info, err := GetConsoleScreenBufferInfo(stdoutHandle)
 	if err != nil {
-		return defaultWidth
+		return defaultTermSize
 	}
 
 	if info.MaximumWindowSize.X > 0 {
 		return int(info.MaximumWindowSize.X)
 	}
 
-	return defaultWidth
+	return defaultTermSize
 }