Repository: JanDeDobbeleer/oh-my-posh
Pull request #2042 (2022-04-06 07:05:45)

fix: sync DirMatchesOneOf
### Prerequisitesx
- [x] I have read and understood the `CONTRIBUTING` guide
- [x] The commit message follows the [conventional commits][cc] guidelines
- [x] Tests for the changes have been added (for bug fixes/features)
- [x] Docs have been added / updated (for bug fixes/features)

### Description

relates to #2038


<!---

Tips:

If you're not comfortable with working with Git, we're working a guide (https://ohmyposh.dev/docs/contributing_git) to help you out.
Oh My Posh advises GitKraken (https://www.gitkraken.com/invite/nQmDPR9D) as your preferred cross platform Git GUI power tool.

-->

[cc]: https://www.conventionalcommits.org/en/v1.0.0/#summary



Patch:
diff --git a/src/engine/segment.go b/src/engine/segment.go
index 269deef6..5272cbce 100644
--- a/src/engine/segment.go
+++ b/src/engine/segment.go
@@ -197,7 +197,7 @@ func (segment *Segment) cwdIncluded() bool {
 		return true
 	}
 
-	return environment.DirMatchesOneOf(segment.env, segment.env.Pwd(), list)
+	return segment.env.DirMatchesOneOf(segment.env.Pwd(), list)
 }
 
 func (segment *Segment) cwdExcluded() bool {
@@ -206,7 +206,7 @@ func (segment *Segment) cwdExcluded() bool {
 		value = segment.Properties[properties.IgnoreFolders]
 	}
 	list := properties.ParseStringArray(value)
-	return environment.DirMatchesOneOf(segment.env, segment.env.Pwd(), list)
+	return segment.env.DirMatchesOneOf(segment.env.Pwd(), list)
 }
 
 func (segment *Segment) shouldInvokeWithTip(tip string) bool {
diff --git a/src/environment/shell.go b/src/environment/shell.go
index 618fdcec..6e895421 100644
--- a/src/environment/shell.go
+++ b/src/environment/shell.go
@@ -16,6 +16,7 @@ import (
 	"runtime"
 	"strconv"
 	"strings"
+	"sync"
 	"time"
 
 	"github.com/distatus/battery"
@@ -134,6 +135,7 @@ type Environment interface {
 	HasFolder(folder string) bool
 	HasParentFilePath(path string) (fileInfo *FileInfo, err error)
 	HasFileInParentDirs(pattern string, depth uint) bool
+	DirMatchesOneOf(dir string, regexes []string) bool
 	HasCommand(command string) bool
 	FileContent(file string) string
 	FolderList(path string) []string
@@ -185,14 +187,16 @@ const (
 )
 
 type ShellEnvironment struct {
-	CmdFlags   *Flags
-	Version    string
+	CmdFlags *Flags
+	Version  string
+
 	cwd        string
 	cmdCache   *commandCache
 	fileCache  *fileCache
 	tmplCache  *TemplateCache
 	logBuilder strings.Builder
 	debug      bool
+	lock       sync.Mutex
 }
 
 func (env *ShellEnvironment) Init(debug bool) {
@@ -720,9 +724,15 @@ func (env *ShellEnvironment) TemplateCache() *TemplateCache {
 	return tmplCache
 }
 
-func DirMatchesOneOf(env Environment, dir string, regexes []string) bool {
+func (env *ShellEnvironment) DirMatchesOneOf(dir string, regexes []string) bool {
+	env.lock.Lock()
+	defer env.lock.Unlock()
+	return dirMatchesOneOf(dir, env.Home(), env.GOOS(), regexes)
+}
+
+func dirMatchesOneOf(dir, home, goos string, regexes []string) bool {
 	normalizedCwd := strings.ReplaceAll(dir, "\\", "/")
-	normalizedHomeDir := strings.ReplaceAll(env.Home(), "\\", "/")
+	normalizedHomeDir := strings.ReplaceAll(home, "\\", "/")
 
 	for _, element := range regexes {
 		normalizedElement := strings.ReplaceAll(element, "\\\\", "/")
@@ -730,7 +740,6 @@ func DirMatchesOneOf(env Environment, dir string, regexes []string) bool {
 			normalizedElement = strings.Replace(normalizedElement, "~", normalizedHomeDir, 1)
 		}
 		pattern := fmt.Sprintf("^%s$", normalizedElement)
-		goos := env.GOOS()
 		if goos == WindowsPlatform || goos == DarwinPlatform {
 			pattern = "(?i)" + pattern
 		}
diff --git a/src/mock/environment.go b/src/mock/environment.go
index 0d188736..822a90fa 100644
--- a/src/mock/environment.go
+++ b/src/mock/environment.go
@@ -214,3 +214,8 @@ func (env *MockedEnvironment) HasFileInParentDirs(pattern string, depth uint) bo
 	args := env.Called(pattern, depth)
 	return args.Bool(0)
 }
+
+func (env *MockedEnvironment) DirMatchesOneOf(dir string, regexes []string) bool {
+	args := env.Called(dir, regexes)
+	return args.Bool(0)
+}
diff --git a/src/segments/scm.go b/src/segments/scm.go
index 4ab2b2b3..13c8debd 100644
--- a/src/segments/scm.go
+++ b/src/segments/scm.go
@@ -74,7 +74,7 @@ func (s *scm) shouldIgnoreRootRepository(rootDir string) bool {
 	if len(excludedFolders) == 0 {
 		return false
 	}
-	return environment.DirMatchesOneOf(s.env, rootDir, excludedFolders)
+	return s.env.DirMatchesOneOf(rootDir, excludedFolders)
 }
 
 func (s *scm) FileContents(folder, file string) string {


Test patch:
diff --git a/src/engine/segment_test.go b/src/engine/segment_test.go
index 52b16740..1880714a 100644
--- a/src/engine/segment_test.go
+++ b/src/engine/segment_test.go
@@ -59,38 +59,27 @@ func TestParseTestConfig(t *testing.T) {
 
 func TestShouldIncludeFolder(t *testing.T) {
 	cases := []struct {
-		Case           string
-		IncludeFolders []string
-		ExcludeFolders []string
-		Expected       bool
+		Case     string
+		Included bool
+		Excluded bool
+		Expected bool
 	}{
-		{Case: "Base Case", IncludeFolders: nil, ExcludeFolders: nil, Expected: true},
-		{Case: "Base Case Empty Arrays", IncludeFolders: []string{}, ExcludeFolders: []string{}, Expected: true},
-
-		{Case: "Include", IncludeFolders: []string{"Projects/oh-my-posh"}, ExcludeFolders: nil, Expected: true},
-		{Case: "Include Regex", IncludeFolders: []string{"Projects.*"}, ExcludeFolders: nil, Expected: true},
-		{Case: "Include Mismatch", IncludeFolders: []string{"Projects/nope"}, ExcludeFolders: nil, Expected: false},
-		{Case: "Include Regex Mismatch", IncludeFolders: []string{"zProjects.*"}, ExcludeFolders: nil, Expected: false},
-
-		{Case: "Exclude", IncludeFolders: nil, ExcludeFolders: []string{"Projects/oh-my-posh"}, Expected: false},
-		{Case: "Exclude Regex", IncludeFolders: nil, ExcludeFolders: []string{"Projects.*"}, Expected: false},
-		{Case: "Exclude Mismatch", IncludeFolders: nil, ExcludeFolders: []string{"Projects/nope"}, Expected: true},
-		{Case: "Exclude Regex Mismatch", IncludeFolders: nil, ExcludeFolders: []string{"zProjects.*"}, Expected: true},
-
-		{Case: "Include Match / Exclude Match", IncludeFolders: []string{"Projects.*"}, ExcludeFolders: []string{"Projects/oh-my-posh"}, Expected: false},
-		{Case: "Include Match / Exclude Mismatch", IncludeFolders: []string{"Projects.*"}, ExcludeFolders: []string{"Projects/nope"}, Expected: true},
-		{Case: "Include Mismatch / Exclude Match", IncludeFolders: []string{"zProjects.*"}, ExcludeFolders: []string{"Projects/oh-my-posh"}, Expected: false},
-		{Case: "Include Mismatch / Exclude Mismatch", IncludeFolders: []string{"zProjects.*"}, ExcludeFolders: []string{"Projects/nope"}, Expected: false},
+		{Case: "Include", Included: true, Excluded: false, Expected: true},
+		{Case: "Exclude", Included: false, Excluded: true, Expected: false},
+		{Case: "Include & Exclude", Included: true, Excluded: true, Expected: false},
+		{Case: "!Include & !Exclude", Included: false, Excluded: false, Expected: false},
 	}
 	for _, tc := range cases {
 		env := new(mock.MockedEnvironment)
 		env.On("GOOS").Return(environment.LinuxPlatform)
 		env.On("Home").Return("")
 		env.On("Pwd").Return(cwd)
+		env.On("DirMatchesOneOf", cwd, []string{"Projects/oh-my-posh"}).Return(tc.Included)
+		env.On("DirMatchesOneOf", cwd, []string{"Projects/nope"}).Return(tc.Excluded)
 		segment := &Segment{
 			Properties: properties.Map{
-				properties.IncludeFolders: tc.IncludeFolders,
-				properties.ExcludeFolders: tc.ExcludeFolders,
+				properties.IncludeFolders: []string{"Projects/oh-my-posh"},
+				properties.ExcludeFolders: []string{"Projects/nope"},
 			},
 			env: env,
 		}
@@ -99,48 +88,6 @@ func TestShouldIncludeFolder(t *testing.T) {
 	}
 }
 
-func TestShouldIncludeFolderRegexInverted(t *testing.T) {
-	env := new(mock.MockedEnvironment)
-	env.On("GOOS").Return(environment.LinuxPlatform)
-	env.On("Home").Return("")
-	env.On("Pwd").Return(cwd)
-	segment := &Segment{
-		Properties: properties.Map{
-			properties.ExcludeFolders: []string{"(?!Projects[\\/]).*"},
-		},
-		env: env,
-	}
-	// detect panic(thrown by MustCompile)
-	defer func() {
-		if err := recover(); err != nil {
-			// display a message explaining omp failed(with the err)
-			assert.Equal(t, "regexp: Compile(`^(?!Projects[\\/]).*$`): error parsing regexp: invalid or unsupported Perl syntax: `(?!`", err)
-		}
-	}()
-	segment.shouldIncludeFolder()
-}
-
-func TestShouldIncludeFolderRegexInvertedNonEscaped(t *testing.T) {
-	env := new(mock.MockedEnvironment)
-	env.On("GOOS").Return(environment.LinuxPlatform)
-	env.On("Home").Return("")
-	env.On("Pwd").Return(cwd)
-	segment := &Segment{
-		Properties: properties.Map{
-			properties.ExcludeFolders: []string{"(?!Projects/).*"},
-		},
-		env: env,
-	}
-	// detect panic(thrown by MustCompile)
-	defer func() {
-		if err := recover(); err != nil {
-			// display a message explaining omp failed(with the err)
-			assert.Equal(t, "regexp: Compile(`^(?!Projects/).*$`): error parsing regexp: invalid or unsupported Perl syntax: `(?!`", err)
-		}
-	}()
-	segment.shouldIncludeFolder()
-}
-
 func TestGetColors(t *testing.T) {
 	cases := []struct {
 		Case          string
diff --git a/src/environment/shell_test.go b/src/environment/shell_test.go
index e2e019cd..2c8b61ae 100644
--- a/src/environment/shell_test.go
+++ b/src/environment/shell_test.go
@@ -44,3 +44,56 @@ func TestWindowsPathWithDriveLetter(t *testing.T) {
 		assert.Equal(t, env.Pwd(), tc.Expected)
 	}
 }
+
+func TestDirMatchesOneOf(t *testing.T) {
+	cases := []struct {
+		GOOS     string
+		HomeDir  string
+		Dir      string
+		Pattern  string
+		Expected bool
+	}{
+		{GOOS: LinuxPlatform, HomeDir: "/home/bill", Dir: "/home/bill", Pattern: "/home/bill", Expected: true},
+		{GOOS: LinuxPlatform, HomeDir: "/home/bill", Dir: "/home/bill/foo", Pattern: "~/foo", Expected: true},
+		{GOOS: LinuxPlatform, HomeDir: "/home/bill", Dir: "/home/bill/foo", Pattern: "~/Foo", Expected: false},
+		{GOOS: LinuxPlatform, HomeDir: "/home/bill", Dir: "/home/bill/foo", Pattern: "~\\\\foo", Expected: true},
+		{GOOS: LinuxPlatform, HomeDir: "/home/bill", Dir: "/home/bill/foo/bar", Pattern: "~/fo.*", Expected: true},
+		{GOOS: LinuxPlatform, HomeDir: "/home/bill", Dir: "/home/bill/foo", Pattern: "~/fo\\w", Expected: true},
+
+		{GOOS: WindowsPlatform, HomeDir: "C:\\Users\\Bill", Dir: "C:\\Users\\Bill", Pattern: "C:\\\\Users\\\\Bill", Expected: true},
+		{GOOS: WindowsPlatform, HomeDir: "C:\\Users\\Bill", Dir: "C:\\Users\\Bill", Pattern: "C:/Users/Bill", Expected: true},
+		{GOOS: WindowsPlatform, HomeDir: "C:\\Users\\Bill", Dir: "C:\\Users\\Bill", Pattern: "c:/users/bill", Expected: true},
+		{GOOS: WindowsPlatform, HomeDir: "C:\\Users\\Bill", Dir: "C:\\Users\\Bill", Pattern: "~", Expected: true},
+		{GOOS: WindowsPlatform, HomeDir: "C:\\Users\\Bill", Dir: "C:\\Users\\Bill\\Foo", Pattern: "~/Foo", Expected: true},
+		{GOOS: WindowsPlatform, HomeDir: "C:\\Users\\Bill", Dir: "C:\\Users\\Bill\\Foo", Pattern: "~/foo", Expected: true},
+		{GOOS: WindowsPlatform, HomeDir: "C:\\Users\\Bill", Dir: "C:\\Users\\Bill\\Foo\\Bar", Pattern: "~/fo.*", Expected: true},
+		{GOOS: WindowsPlatform, HomeDir: "C:\\Users\\Bill", Dir: "C:\\Users\\Bill\\Foo", Pattern: "~/fo\\w", Expected: true},
+	}
+
+	for _, tc := range cases {
+		got := dirMatchesOneOf(tc.Dir, tc.HomeDir, tc.GOOS, []string{tc.Pattern})
+		assert.Equal(t, tc.Expected, got)
+	}
+}
+
+func TestDirMatchesOneOfRegexInverted(t *testing.T) {
+	// detect panic(thrown by MustCompile)
+	defer func() {
+		if err := recover(); err != nil {
+			// display a message explaining omp failed(with the err)
+			assert.Equal(t, "regexp: Compile(`^(?!Projects[\\/]).*$`): error parsing regexp: invalid or unsupported Perl syntax: `(?!`", err)
+		}
+	}()
+	_ = dirMatchesOneOf("Projects/oh-my-posh", "", LinuxPlatform, []string{"(?!Projects[\\/]).*"})
+}
+
+func TestDirMatchesOneOfRegexInvertedNonEscaped(t *testing.T) {
+	// detect panic(thrown by MustCompile)
+	defer func() {
+		if err := recover(); err != nil {
+			// display a message explaining omp failed(with the err)
+			assert.Equal(t, "regexp: Compile(`^(?!Projects/).*$`): error parsing regexp: invalid or unsupported Perl syntax: `(?!`", err)
+		}
+	}()
+	_ = dirMatchesOneOf("Projects/oh-my-posh", "", LinuxPlatform, []string{"(?!Projects/).*"})
+}
diff --git a/src/main_test.go b/src/main_test.go
deleted file mode 100644
index cfc10b83..00000000
--- a/src/main_test.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package main
-
-import (
-	"oh-my-posh/environment"
-	"oh-my-posh/mock"
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-)
-
-// This can only be tested here due to circular dependencies
-// Which might be an indaction of a fault architecture but
-// I honestly could not figure out how to do this better
-// without making the solution worse.
-func TestDirMatchesOneOf(t *testing.T) {
-	cases := []struct {
-		GOOS     string
-		HomeDir  string
-		Dir      string
-		Pattern  string
-		Expected bool
-	}{
-		{GOOS: environment.LinuxPlatform, HomeDir: "/home/bill", Dir: "/home/bill", Pattern: "/home/bill", Expected: true},
-		{GOOS: environment.LinuxPlatform, HomeDir: "/home/bill", Dir: "/home/bill/foo", Pattern: "~/foo", Expected: true},
-		{GOOS: environment.LinuxPlatform, HomeDir: "/home/bill", Dir: "/home/bill/foo", Pattern: "~/Foo", Expected: false},
-		{GOOS: environment.LinuxPlatform, HomeDir: "/home/bill", Dir: "/home/bill/foo", Pattern: "~\\\\foo", Expected: true},
-		{GOOS: environment.LinuxPlatform, HomeDir: "/home/bill", Dir: "/home/bill/foo/bar", Pattern: "~/fo.*", Expected: true},
-		{GOOS: environment.LinuxPlatform, HomeDir: "/home/bill", Dir: "/home/bill/foo", Pattern: "~/fo\\w", Expected: true},
-		{GOOS: environment.WindowsPlatform, HomeDir: "C:\\Users\\Bill", Dir: "C:\\Users\\Bill", Pattern: "C:\\\\Users\\\\Bill", Expected: true},
-		{GOOS: environment.WindowsPlatform, HomeDir: "C:\\Users\\Bill", Dir: "C:\\Users\\Bill", Pattern: "C:/Users/Bill", Expected: true},
-		{GOOS: environment.WindowsPlatform, HomeDir: "C:\\Users\\Bill", Dir: "C:\\Users\\Bill", Pattern: "c:/users/bill", Expected: true},
-		{GOOS: environment.WindowsPlatform, HomeDir: "C:\\Users\\Bill", Dir: "C:\\Users\\Bill", Pattern: "~", Expected: true},
-		{GOOS: environment.WindowsPlatform, HomeDir: "C:\\Users\\Bill", Dir: "C:\\Users\\Bill\\Foo", Pattern: "~/Foo", Expected: true},
-		{GOOS: environment.WindowsPlatform, HomeDir: "C:\\Users\\Bill", Dir: "C:\\Users\\Bill\\Foo", Pattern: "~/foo", Expected: true},
-		{GOOS: environment.WindowsPlatform, HomeDir: "C:\\Users\\Bill", Dir: "C:\\Users\\Bill\\Foo\\Bar", Pattern: "~/fo.*", Expected: true},
-		{GOOS: environment.WindowsPlatform, HomeDir: "C:\\Users\\Bill", Dir: "C:\\Users\\Bill\\Foo", Pattern: "~/fo\\w", Expected: true},
-	}
-
-	for _, tc := range cases {
-		env := new(mock.MockedEnvironment)
-		env.On("GOOS").Return(tc.GOOS)
-		env.On("Home").Return(tc.HomeDir)
-		got := environment.DirMatchesOneOf(env, tc.Dir, []string{tc.Pattern})
-		assert.Equal(t, tc.Expected, got)
-	}
-}
diff --git a/src/segments/scm_test.go b/src/segments/scm_test.go
index d9d6f4d7..9b6f711d 100644
--- a/src/segments/scm_test.go
+++ b/src/segments/scm_test.go
@@ -1,8 +1,6 @@
 package segments
 
 import (
-	"oh-my-posh/environment"
-	"oh-my-posh/mock"
 	"oh-my-posh/properties"
 	"testing"
 
@@ -156,35 +154,3 @@ func TestTruncateBranchWithSymbol(t *testing.T) {
 		assert.Equal(t, tc.Expected, p.truncateBranch(tc.Branch), tc.Case)
 	}
 }
-
-func TestScmShouldIgnoreRootRepository(t *testing.T) {
-	cases := []struct {
-		Case     string
-		Dir      string
-		Expected bool
-	}{
-		{Case: "inside excluded", Dir: "/home/bill/repo"},
-		{Case: "oustide excluded", Dir: "/home/melinda"},
-		{Case: "excluded exact match", Dir: "/home/gates", Expected: true},
-		{Case: "excluded inside match", Dir: "/home/gates/bill", Expected: true},
-	}
-
-	for _, tc := range cases {
-		excludeFolders := []string{
-			"/home/bill",
-			"/home/gates.*",
-		}
-		props := properties.Map{
-			properties.ExcludeFolders: excludeFolders,
-		}
-		env := new(mock.MockedEnvironment)
-		env.On("Home").Return("/home/bill")
-		env.On("GOOS").Return(environment.WindowsPlatform)
-		s := &scm{
-			props: props,
-			env:   env,
-		}
-		got := s.shouldIgnoreRootRepository(tc.Dir)
-		assert.Equal(t, tc.Expected, got, tc.Case)
-	}
-}