cmd/gorelease: move path functions to new file and add tests

Change-Id: Id148d04c86a7b835a917de5e6eca9ab7b1c52f49
Reviewed-on: https://go-review.googlesource.com/c/exp/+/238837
Run-TryBot: Jay Conrod <jayconrod@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Bryan C. Mills <bcmills@google.com>
Reviewed-by: Michael Matloob <matloob@golang.org>
diff --git a/cmd/gorelease/gorelease.go b/cmd/gorelease/gorelease.go
index 4a2dbed..2ec5d04 100644
--- a/cmd/gorelease/gorelease.go
+++ b/cmd/gorelease/gorelease.go
@@ -634,49 +634,6 @@
 	return path[i-1:]
 }
 
-// hasPathPrefix reports whether the slash-separated path s
-// begins with the elements in prefix.
-// Copied from cmd/go/internal/str.HasPathPrefix.
-func hasPathPrefix(s, prefix string) bool {
-	if len(s) == len(prefix) {
-		return s == prefix
-	}
-	if prefix == "" {
-		return true
-	}
-	if len(s) > len(prefix) {
-		if prefix[len(prefix)-1] == '/' || s[len(prefix)] == '/' {
-			return s[:len(prefix)] == prefix
-		}
-	}
-	return false
-}
-
-// hasFilePathPrefix reports whether the filesystem path s
-// begins with the elements in prefix.
-// Copied from cmd/go/internal/str.HasFilePathPrefix.
-func hasFilePathPrefix(s, prefix string) bool {
-	sv := strings.ToUpper(filepath.VolumeName(s))
-	pv := strings.ToUpper(filepath.VolumeName(prefix))
-	s = s[len(sv):]
-	prefix = prefix[len(pv):]
-	switch {
-	default:
-		return false
-	case sv != pv:
-		return false
-	case len(s) == len(prefix):
-		return s == prefix
-	case prefix == "":
-		return true
-	case len(s) > len(prefix):
-		if prefix[len(prefix)-1] == filepath.Separator {
-			return strings.HasPrefix(s, prefix)
-		}
-		return s[len(prefix)] == filepath.Separator && s[:len(prefix)] == prefix
-	}
-}
-
 // copyModuleToTempDir copies module files from modRoot to a subdirectory of
 // scratchDir. Submodules, vendor directories, and irregular files are excluded.
 // An error is returned if the module contains any files or directories that
diff --git a/cmd/gorelease/path.go b/cmd/gorelease/path.go
new file mode 100644
index 0000000..6249342
--- /dev/null
+++ b/cmd/gorelease/path.go
@@ -0,0 +1,53 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+	"path/filepath"
+	"strings"
+)
+
+// hasPathPrefix reports whether the slash-separated path s
+// begins with the elements in prefix.
+// Copied from cmd/go/internal/str.HasPathPrefix.
+func hasPathPrefix(s, prefix string) bool {
+	if len(s) == len(prefix) {
+		return s == prefix
+	}
+	if prefix == "" {
+		return true
+	}
+	if len(s) > len(prefix) {
+		if prefix[len(prefix)-1] == '/' || s[len(prefix)] == '/' {
+			return s[:len(prefix)] == prefix
+		}
+	}
+	return false
+}
+
+// hasFilePathPrefix reports whether the filesystem path s
+// begins with the elements in prefix.
+// Copied from cmd/go/internal/str.HasFilePathPrefix.
+func hasFilePathPrefix(s, prefix string) bool {
+	sv := strings.ToUpper(filepath.VolumeName(s))
+	pv := strings.ToUpper(filepath.VolumeName(prefix))
+	s = s[len(sv):]
+	prefix = prefix[len(pv):]
+	switch {
+	default:
+		return false
+	case pv != "" && sv != pv:
+		return false
+	case len(s) == len(prefix):
+		return s == prefix
+	case prefix == "":
+		return true
+	case len(s) > len(prefix):
+		if prefix[len(prefix)-1] == filepath.Separator {
+			return strings.HasPrefix(s, prefix)
+		}
+		return s[len(prefix)] == filepath.Separator && s[:len(prefix)] == prefix
+	}
+}
diff --git a/cmd/gorelease/path_test.go b/cmd/gorelease/path_test.go
new file mode 100644
index 0000000..b7c12e1
--- /dev/null
+++ b/cmd/gorelease/path_test.go
@@ -0,0 +1,114 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+	"runtime"
+	"testing"
+)
+
+func TestHasPathPrefix(t *testing.T) {
+	for _, test := range []struct {
+		desc, path, prefix string
+		want               bool
+	}{
+		{
+			desc:   "empty_prefix",
+			path:   "a/b",
+			prefix: "",
+			want:   true,
+		}, {
+			desc:   "partial_prefix",
+			path:   "a/b",
+			prefix: "a",
+			want:   true,
+		}, {
+			desc:   "full_prefix",
+			path:   "a/b",
+			prefix: "a/b",
+			want:   true,
+		}, {
+			desc:   "partial_component",
+			path:   "aa/b",
+			prefix: "a",
+			want:   false,
+		},
+	} {
+		t.Run(test.desc, func(t *testing.T) {
+			if got := hasPathPrefix(test.path, test.prefix); got != test.want {
+				t.Errorf("hasPathPrefix(%q, %q): got %v, want %v", test.path, test.prefix, got, test.want)
+			}
+		})
+	}
+}
+
+func TestHasFilePathPrefix(t *testing.T) {
+	type test struct {
+		desc, path, prefix string
+		want               bool
+	}
+	var tests []test
+	if runtime.GOOS == "windows" {
+		tests = []test{
+			{
+				desc:   "empty_prefix",
+				path:   `c:\a\b`,
+				prefix: "",
+				want:   true,
+			}, {
+				desc:   "drive_prefix",
+				path:   `c:\a\b`,
+				prefix: `c:\`,
+				want:   true,
+			}, {
+				desc:   "partial_prefix",
+				path:   `c:\a\b`,
+				prefix: `c:\a`,
+				want:   true,
+			}, {
+				desc:   "full_prefix",
+				path:   `c:\a\b`,
+				prefix: `c:\a\b`,
+				want:   true,
+			}, {
+				desc:   "partial_component",
+				path:   `c:\aa\b`,
+				prefix: `c:\a`,
+				want:   false,
+			},
+		}
+	} else {
+		tests = []test{
+			{
+				desc:   "empty_prefix",
+				path:   "/a/b",
+				prefix: "",
+				want:   true,
+			}, {
+				desc:   "partial_prefix",
+				path:   "/a/b",
+				prefix: "/a",
+				want:   true,
+			}, {
+				desc:   "full_prefix",
+				path:   "/a/b",
+				prefix: "/a/b",
+				want:   true,
+			}, {
+				desc:   "partial_component",
+				path:   "/aa/b",
+				prefix: "/a",
+				want:   false,
+			},
+		}
+	}
+	for _, test := range tests {
+		t.Run(test.desc, func(t *testing.T) {
+			if got := hasFilePathPrefix(test.path, test.prefix); got != test.want {
+				t.Errorf("hasFilePathPrefix(%q, %q): got %v, want %v", test.path, test.prefix, got, test.want)
+			}
+		})
+	}
+}