Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,28 @@ on:
jobs:
test:
name: Tests
runs-on: ubuntu-latest
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
# The TTCN-3 conformance test suite under
# testdata/ttcn3-conformance-tests/ has paths > 260 chars,
# which trips Windows' MAX_PATH limit during checkout. This
# config must run BEFORE actions/checkout so the clone itself
# can write the long paths.
- name: Enable Git long paths (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: git config --system core.longpaths true
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: stable
- name: Test
run: go test -race -v ./...
run: go test -race ./...

lint:
name: Linting
Expand Down
8 changes: 6 additions & 2 deletions internal/cache/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cache_test

import (
"os"
"path/filepath"
"testing"

"github.com/nokia/ntt/internal/cache"
Expand All @@ -14,7 +15,10 @@ func init() {
}

func TestLookup(t *testing.T) {
os.Setenv("NTT_CACHE", "testdata/cache")
// The cache directory is joined with filepath.Join in the
// implementation, so on Windows the expected separator is "\".
cacheDir := filepath.FromSlash("testdata/cache")
os.Setenv("NTT_CACHE", cacheDir)
assert.Equal(t, "./file", cache.Lookup("./file"))
assert.Equal(t, "./cache.go", cache.Lookup("./cache.go"))

Expand All @@ -23,5 +27,5 @@ func TestLookup(t *testing.T) {
assert.Equal(t, ".", cache.Lookup("."))
assert.Equal(t, "..", cache.Lookup(".."))
assert.Equal(t, "cache.go", cache.Lookup("cache.go"))
assert.Equal(t, "testdata/cache/other.go", cache.Lookup("other.go"))
assert.Equal(t, filepath.Join(cacheDir, "other.go"), cache.Lookup("other.go"))
}
85 changes: 55 additions & 30 deletions internal/fs/fs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"testing"

"github.com/nokia/ntt/internal/fs"
"github.com/nokia/ntt/internal/lsp/span"
"github.com/stretchr/testify/assert"
)

Expand All @@ -23,7 +24,8 @@ func TestBytesFromURL(t *testing.T) {
panic(err)
}

f := fs.Open("file://" + path)
// Constructing the URL
f := fs.Open(string(span.URIFromPath(path)))
b, err := f.Bytes()
assert.Nil(t, err)
assert.Equal(t, expected, b)
Expand All @@ -32,83 +34,106 @@ func TestBytesFromURL(t *testing.T) {
func TestCaching(t *testing.T) {
assert.Equal(t, "package.yml", fs.Open("package.yml").Path())

os.Setenv("NTT_CACHE", "testdata/cache")
assert.Equal(t, "testdata/cache/package.yml", fs.Open("package.yml").Path())
cacheDir := filepath.FromSlash("testdata/cache")
os.Setenv("NTT_CACHE", cacheDir)
assert.Equal(t, filepath.Join(cacheDir, "package.yml"), fs.Open("package.yml").Path())
}

func TestJoinPath(t *testing.T) {
tests := []struct {
// JoinPath returns OS-native file paths but keeps URLs untouched.
// We mark URL expectations explicitly so we don't accidentally
// run them through filepath.FromSlash.
type joinCase struct {
first, second string
want string
}{
{"", "", ""},
{".", "", "."},
{".", "a", "a"},
{"/", "b", "/b"},
{"//", "c", "/c"},
{"/", "/d", "/d"},
{"e", "f", "e/f"},
{"/g", "h", "/g/h"},
{"/i", "../j", "/j"},
{"file://k", "l", "file://k/l"},
{"file:///m", "n", "file:///m/n"},
{"file:///o", "../p", "file:///p"},
isURL bool
}
tests := []joinCase{
{"", "", "", false},
{".", "", ".", false},
{".", "a", "a", false},
{"/", "b", "/b", false},
{"//", "c", "/c", false},
{"/", "/d", "/d", false},
{"e", "f", "e/f", false},
{"/g", "h", "/g/h", false},
{"/i", "../j", "/j", false},
{"file://k", "l", "file://k/l", true},
{"file:///m", "n", "file:///m/n", true},
{"file:///o", "../p", "file:///p", true},
}

for _, test := range tests {
want := test.want
if !test.isURL {
want = filepath.FromSlash(want)
}
got := fs.JoinPath(test.first, test.second)
assert.Equal(t, test.want, got)
assert.Equal(t, want, got)
}

}

func TestTTCN3Files(t *testing.T) {
// fromSlash converts the slash-style literals we keep in this
// test to whatever path separator the host OS uses, so the
// suite runs on Windows as well as Unix.
fromSlash := func(paths []string) []string {
out := make([]string, len(paths))
for i, p := range paths {
out[i] = filepath.FromSlash(p)
}
return out
}

t.Run("empty", func(t *testing.T) {
got, err := fs.TTCN3Files()
assert.Nil(t, err)
assert.Nil(t, got)
})
t.Run("dir", func(t *testing.T) {
got, err := fs.TTCN3Files("testdata/TestTTCN3Files")
got, err := fs.TTCN3Files(filepath.FromSlash("testdata/TestTTCN3Files"))
assert.Nil(t, err)
assert.Nil(t, got)
})
t.Run("dir", func(t *testing.T) {
got, err := fs.TTCN3Files("testdata/TestTTCN3Files/some-dir")
got, err := fs.TTCN3Files(filepath.FromSlash("testdata/TestTTCN3Files/some-dir"))
assert.Nil(t, err)
assert.Nil(t, got)
})
t.Run("dir", func(t *testing.T) {
want := []string{
want := fromSlash([]string{
"testdata/TestTTCN3Files/ttcn3-dir/a.ttcn3",
"testdata/TestTTCN3Files/ttcn3-dir/b.ttcn",
"testdata/TestTTCN3Files/ttcn3-dir/c.ttcnpp",
}
got, err := fs.TTCN3Files("testdata/TestTTCN3Files/ttcn3-dir")
})
got, err := fs.TTCN3Files(filepath.FromSlash("testdata/TestTTCN3Files/ttcn3-dir"))
assert.Nil(t, err)
assert.Equal(t, want, got)
})
t.Run("errors", func(t *testing.T) {
want := []string{
want := fromSlash([]string{
"testdata/TestTTCN3Files/xxx-dir/a.ttcn3",
}
got, err := fs.TTCN3Files("testdata/TestTTCN3Files/xxx-dir/a.ttcn3")
})
got, err := fs.TTCN3Files(filepath.FromSlash("testdata/TestTTCN3Files/xxx-dir/a.ttcn3"))
assert.True(t, errors.Is(err, os.ErrNotExist))
assert.Equal(t, want, got)
})
t.Run("file", func(t *testing.T) {
want := []string{
want := fromSlash([]string{
"testdata/TestTTCN3Files/ttcn3-dir/a.ttcn3",
"testdata/TestTTCN3Files/ttcn3-dir/a.ttcn3",
}
})
got, err := fs.TTCN3Files(
"testdata/TestTTCN3Files/ttcn3-dir/a.ttcn3",
"testdata/TestTTCN3Files/ttcn3-dir/a.ttcn3",
filepath.FromSlash("testdata/TestTTCN3Files/ttcn3-dir/a.ttcn3"),
filepath.FromSlash("testdata/TestTTCN3Files/ttcn3-dir/a.ttcn3"),
)
assert.Nil(t, err)
assert.Equal(t, want, got)
})
t.Run("URI", func(t *testing.T) {
// URIs always use forward slashes regardless of host OS,
// so no conversion here.
want := []string{"foo://a.ttcn3"}
got, err := fs.TTCN3Files("foo://a.ttcn3")
assert.Nil(t, err)
Expand Down
5 changes: 3 additions & 2 deletions internal/lsp/hover_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package lsp_test

import (
"fmt"
"path/filepath"
"testing"

"github.com/nokia/ntt/internal/fs"
Expand Down Expand Up @@ -80,7 +81,7 @@ func TestPlainTextHoverForPortDefFromDecl(t *testing.T) {
"port P p1\n" +
"possible map / connect statements\n" +
"_________________________________\n" +
"/TestPlainTextHoverForPortDefFromDecl.ttcn3:9\n"
filepath.FromSlash("/TestPlainTextHoverForPortDefFromDecl.ttcn3") + ":9\n"

assert.Equal(t, expected, actual.Contents.Value)
}
Expand All @@ -105,7 +106,7 @@ func TestPlainTextHoverForPortDefFromUsage(t *testing.T) {
"port P p1\n" +
"possible map / connect statements\n" +
"_________________________________\n" +
"/TestPlainTextHoverForPortDefFromUsage.ttcn3:9\n"
filepath.FromSlash("/TestPlainTextHoverForPortDefFromUsage.ttcn3") + ":9\n"

assert.Equal(t, expected, actual.Contents.Value)
}
Expand Down
28 changes: 24 additions & 4 deletions internal/lsp/span/uri_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,36 @@
package span_test

import (
"os"
"strings"
"testing"

"github.com/nokia/ntt/internal/lsp/span"
)

// currentDrive returns the upper-case drive letter of the current
// working directory (e.g. "C"). We use this to build expectations in
// TestURIFromPath dynamically, because GitHub Actions Windows runners
// expose D: as the default drive whereas developers typically run on
// C: - either way the test should still pass.
func currentDrive(t *testing.T) string {
t.Helper()
cwd, err := os.Getwd()
if err != nil {
t.Fatalf("getwd: %v", err)
}
if len(cwd) < 2 || cwd[1] != ':' {
t.Fatalf("cwd %q has no drive letter", cwd)
}
return strings.ToUpper(string(cwd[0]))
}

// TestURI tests the conversion between URIs and filenames. The test cases
// include Windows-style URIs and filepaths, but we avoid having OS-specific
// tests by using only forward slashes, assuming that the standard library
// functions filepath.ToSlash and filepath.FromSlash do not need testing.
func TestURIFromPath(t *testing.T) {
drive := currentDrive(t)
for _, test := range []struct {
path, wantFile string
wantURI span.URI
Expand All @@ -43,13 +63,13 @@ func TestURIFromPath(t *testing.T) {
},
{
path: `\path\to\dir`,
wantFile: `C:\path\to\dir`,
wantURI: span.URI("file:///C:/path/to/dir"),
wantFile: drive + `:\path\to\dir`,
wantURI: span.URI("file:///" + drive + ":/path/to/dir"),
},
{
path: `\a\b\c\src\bob.go`,
wantFile: `C:\a\b\c\src\bob.go`,
wantURI: span.URI("file:///C:/a/b/c/src/bob.go"),
wantFile: drive + `:\a\b\c\src\bob.go`,
wantURI: span.URI("file:///" + drive + ":/a/b/c/src/bob.go"),
},
{
path: `c:\Go\src\bob george\george\george.go`,
Expand Down
16 changes: 13 additions & 3 deletions project/project_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -229,7 +230,9 @@ func TestWithManifest(t *testing.T) {
t.Run("paths", func(t *testing.T) {
c, err := manifest("foo/bar/package.yml", "hooks_file: file")
assert.Nil(t, err)
assert.Equal(t, "foo/bar/file", c.HooksFile)
// hooks_file is resolved relative to the manifest, so the
// expectation must use the host's native separator.
assert.Equal(t, filepath.FromSlash("foo/bar/file"), c.HooksFile)
})
t.Run("paths", func(t *testing.T) {
c, err := manifest("foo/bar/package.yml", "hooks_file: $VAR")
Expand All @@ -239,7 +242,14 @@ func TestWithManifest(t *testing.T) {
t.Run("paths", func(t *testing.T) {
c, err := manifest("foo/bar/package.yml", "hooks_file: /file")
assert.Nil(t, err)
assert.Equal(t, "/file", c.HooksFile)
// On POSIX "/file" is absolute and stays as-is; on Windows
// it lacks a drive letter so it's treated as relative and
// joined with the manifest directory.
want := "/file"
if runtime.GOOS == "windows" {
want = filepath.FromSlash("foo/bar/file")
}
assert.Equal(t, want, c.HooksFile)
})
t.Run("paths", func(t *testing.T) {
c, err := manifest("foo/bar/package.yml", "hooks_file: https://file.txt")
Expand All @@ -260,7 +270,7 @@ func TestWithManifest(t *testing.T) {
VAR: file
hooks_file: $VAR`)
assert.Nil(t, err)
assert.Equal(t, "foo/bar/file", c.HooksFile)
assert.Equal(t, filepath.FromSlash("foo/bar/file"), c.HooksFile)
})
}

Expand Down