// Copyright 2019 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package fspath

import (
	"reflect"
	"strings"
	"testing"

	"gvisor.dev/gvisor/pkg/syserror"
)

func TestParseIteratorPartialPathnames(t *testing.T) {
	path, err := Parse("/foo//bar///baz////")
	if err != nil {
		t.Fatalf("Parse failed: %v", err)
	}
	// Parse strips leading slashes, and records their presence as
	// Path.Absolute.
	if !path.Absolute {
		t.Errorf("Path.Absolute: got false, wanted true")
	}
	// Parse strips trailing slashes, and records their presence as Path.Dir.
	if !path.Dir {
		t.Errorf("Path.Dir: got false, wanted true")
	}
	// The first Iterator.partialPathname is the input pathname, with leading
	// and trailing slashes stripped.
	it := path.Begin
	if want := "foo//bar///baz"; it.partialPathname != want {
		t.Errorf("first Iterator.partialPathname: got %q, wanted %q", it.partialPathname, want)
	}
	// Successive Iterator.partialPathnames remove the leading path component
	// and following slashes, until we run out of path components and get a
	// terminal Iterator.
	it = it.Next()
	if want := "bar///baz"; it.partialPathname != want {
		t.Errorf("second Iterator.partialPathname: got %q, wanted %q", it.partialPathname, want)
	}
	it = it.Next()
	if want := "baz"; it.partialPathname != want {
		t.Errorf("third Iterator.partialPathname: got %q, wanted %q", it.partialPathname, want)
	}
	it = it.Next()
	if want := ""; it.partialPathname != want {
		t.Errorf("fourth Iterator.partialPathname: got %q, wanted %q", it.partialPathname, want)
	}
	if it.Ok() {
		t.Errorf("fourth Iterator.Ok(): got true, wanted false")
	}
}

func TestParse(t *testing.T) {
	type testCase struct {
		pathname string
		relpath  []string
		abs      bool
		dir      bool
	}
	tests := []testCase{
		{
			pathname: "/",
			relpath:  []string{},
			abs:      true,
			dir:      true,
		},
		{
			pathname: "//",
			relpath:  []string{},
			abs:      true,
			dir:      true,
		},
	}
	for _, sep := range []string{"/", "//"} {
		for _, abs := range []bool{false, true} {
			for _, dir := range []bool{false, true} {
				for _, pcs := range [][]string{
					// single path component
					{"foo"},
					// multiple path components, including non-UTF-8
					{".", "foo", "..", "\xe6", "bar"},
				} {
					prefix := ""
					if abs {
						prefix = sep
					}
					suffix := ""
					if dir {
						suffix = sep
					}
					tests = append(tests, testCase{
						pathname: prefix + strings.Join(pcs, sep) + suffix,
						relpath:  pcs,
						abs:      abs,
						dir:      dir,
					})
				}
			}
		}
	}

	for _, test := range tests {
		t.Run(test.pathname, func(t *testing.T) {
			p, err := Parse(test.pathname)
			if err != nil {
				t.Fatalf("failed to parse pathname %q: %v", test.pathname, err)
			}
			t.Logf("pathname %q => path %q", test.pathname, p)
			if p.Absolute != test.abs {
				t.Errorf("path absoluteness: got %v, wanted %v", p.Absolute, test.abs)
			}
			if p.Dir != test.dir {
				t.Errorf("path must resolve to a directory: got %v, wanted %v", p.Dir, test.dir)
			}
			pcs := []string{}
			for pit := p.Begin; pit.Ok(); pit = pit.Next() {
				pcs = append(pcs, pit.String())
			}
			if !reflect.DeepEqual(pcs, test.relpath) {
				t.Errorf("relative path: got %v, wanted %v", pcs, test.relpath)
			}
		})
	}
}

func TestParseEmptyPathname(t *testing.T) {
	p, err := Parse("")
	if err != syserror.ENOENT {
		t.Errorf("parsing empty pathname: got (%v, %v), wanted (<unspecified>, ENOENT)", p, err)
	}
}