From 7ac5191e8d8601e55492154a6b9f9a995bd111ed Mon Sep 17 00:00:00 2001 From: bbedward Date: Sun, 4 Jan 2026 17:53:47 -0500 Subject: [PATCH] matugen: fix app checking - double nil for flatpak + bin required to skip --- core/.mockery.yml | 6 + core/internal/matugen/matugen.go | 28 ++- core/internal/matugen/matugen_test.go | 35 ++- core/internal/mocks/utils/mock_AppChecker.go | 242 +++++++++++++++++++ core/internal/utils/exec.go | 25 ++ 5 files changed, 322 insertions(+), 14 deletions(-) create mode 100644 core/internal/mocks/utils/mock_AppChecker.go diff --git a/core/.mockery.yml b/core/.mockery.yml index e028d257..d02c9613 100644 --- a/core/.mockery.yml +++ b/core/.mockery.yml @@ -68,3 +68,9 @@ packages: outpkg: mocks_wlclient interfaces: WaylandDisplay: + github.com/AvengeMedia/DankMaterialShell/core/internal/utils: + config: + dir: "internal/mocks/utils" + outpkg: mocks_utils + interfaces: + AppChecker: diff --git a/core/internal/matugen/matugen.go b/core/internal/matugen/matugen.go index 163266be..7c45dcda 100644 --- a/core/internal/matugen/matugen.go +++ b/core/internal/matugen/matugen.go @@ -51,6 +51,7 @@ type Options struct { SyncModeWithPortal bool TerminalsAlwaysDark bool SkipTemplates string + AppChecker utils.AppChecker } type ColorsOutput struct { @@ -101,6 +102,9 @@ func Run(opts Options) error { if opts.IconTheme == "" { opts.IconTheme = "System Default" } + if opts.AppChecker == nil { + opts.AppChecker = utils.DefaultAppChecker{} + } if err := os.MkdirAll(opts.StateDir, 0755); err != nil { return fmt.Errorf("failed to create state dir: %w", err) @@ -353,10 +357,7 @@ func appendConfig( if _, err := os.Stat(configPath); err != nil { return } - cmdExists := checkCmd == nil || utils.AnyCommandExists(checkCmd...) - flatpakExists := checkFlatpaks == nil || utils.AnyFlatpakExists(checkFlatpaks...) - - if !cmdExists && !flatpakExists { + if !appExists(opts.AppChecker, checkCmd, checkFlatpaks) { return } data, err := os.ReadFile(configPath) @@ -372,10 +373,7 @@ func appendTerminalConfig(opts *Options, cfgFile *os.File, tmpDir string, checkC if _, err := os.Stat(configPath); err != nil { return } - cmdExists := checkCmd == nil || utils.AnyCommandExists(checkCmd...) - flatpakExists := checkFlatpaks == nil || utils.AnyFlatpakExists(checkFlatpaks...) - - if !cmdExists && !flatpakExists { + if !appExists(opts.AppChecker, checkCmd, checkFlatpaks) { return } data, err := os.ReadFile(configPath) @@ -428,6 +426,20 @@ func appendTerminalConfig(opts *Options, cfgFile *os.File, tmpDir string, checkC cfgFile.WriteString("\n") } +func appExists(checker utils.AppChecker, checkCmd []string, checkFlatpaks []string) bool { + // Both nil is treated as "skip check" / unconditionally run + if checkCmd == nil && checkFlatpaks == nil { + return true + } + if checkCmd != nil && checker.AnyCommandExists(checkCmd...) { + return true + } + if checkFlatpaks != nil && checker.AnyFlatpakExists(checkFlatpaks...) { + return true + } + return false +} + func appendVSCodeConfig(cfgFile *os.File, name, extBaseDir, shellDir string) { pattern := filepath.Join(extBaseDir, "danklinux.dms-theme-*") matches, err := filepath.Glob(pattern) diff --git a/core/internal/matugen/matugen_test.go b/core/internal/matugen/matugen_test.go index 75862d66..98dfbe92 100644 --- a/core/internal/matugen/matugen_test.go +++ b/core/internal/matugen/matugen_test.go @@ -4,6 +4,8 @@ import ( "os" "path/filepath" "testing" + + mocks_utils "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/utils" ) func TestAppendConfigBinaryExists(t *testing.T) { @@ -28,7 +30,10 @@ func TestAppendConfigBinaryExists(t *testing.T) { } defer cfgFile.Close() - opts := &Options{ShellDir: shellDir} + mockChecker := mocks_utils.NewMockAppChecker(t) + mockChecker.EXPECT().AnyCommandExists("sh").Return(true) + + opts := &Options{ShellDir: shellDir, AppChecker: mockChecker} appendConfig(opts, cfgFile, []string{"sh"}, nil, "test.toml") @@ -68,7 +73,11 @@ func TestAppendConfigBinaryDoesNotExist(t *testing.T) { } defer cfgFile.Close() - opts := &Options{ShellDir: shellDir} + mockChecker := mocks_utils.NewMockAppChecker(t) + mockChecker.EXPECT().AnyCommandExists("nonexistent-binary-12345").Return(false) + mockChecker.EXPECT().AnyFlatpakExists().Return(false) + + opts := &Options{ShellDir: shellDir, AppChecker: mockChecker} appendConfig(opts, cfgFile, []string{"nonexistent-binary-12345"}, []string{}, "test.toml") @@ -105,7 +114,10 @@ func TestAppendConfigFlatpakExists(t *testing.T) { } defer cfgFile.Close() - opts := &Options{ShellDir: shellDir} + mockChecker := mocks_utils.NewMockAppChecker(t) + mockChecker.EXPECT().AnyFlatpakExists("app.zen_browser.zen").Return(true) + + opts := &Options{ShellDir: shellDir, AppChecker: mockChecker} appendConfig(opts, cfgFile, nil, []string{"app.zen_browser.zen"}, "test.toml") @@ -142,7 +154,11 @@ func TestAppendConfigFlatpakDoesNotExist(t *testing.T) { } defer cfgFile.Close() - opts := &Options{ShellDir: shellDir} + mockChecker := mocks_utils.NewMockAppChecker(t) + mockChecker.EXPECT().AnyCommandExists().Return(false) + mockChecker.EXPECT().AnyFlatpakExists("com.nonexistent.flatpak").Return(false) + + opts := &Options{ShellDir: shellDir, AppChecker: mockChecker} appendConfig(opts, cfgFile, []string{}, []string{"com.nonexistent.flatpak"}, "test.toml") @@ -179,7 +195,10 @@ func TestAppendConfigBothExist(t *testing.T) { } defer cfgFile.Close() - opts := &Options{ShellDir: shellDir} + mockChecker := mocks_utils.NewMockAppChecker(t) + mockChecker.EXPECT().AnyCommandExists("sh").Return(true) + + opts := &Options{ShellDir: shellDir, AppChecker: mockChecker} appendConfig(opts, cfgFile, []string{"sh"}, []string{"app.zen_browser.zen"}, "test.toml") @@ -216,7 +235,11 @@ func TestAppendConfigNeitherExists(t *testing.T) { } defer cfgFile.Close() - opts := &Options{ShellDir: shellDir} + mockChecker := mocks_utils.NewMockAppChecker(t) + mockChecker.EXPECT().AnyCommandExists("nonexistent-binary-12345").Return(false) + mockChecker.EXPECT().AnyFlatpakExists("com.nonexistent.flatpak").Return(false) + + opts := &Options{ShellDir: shellDir, AppChecker: mockChecker} appendConfig(opts, cfgFile, []string{"nonexistent-binary-12345"}, []string{"com.nonexistent.flatpak"}, "test.toml") diff --git a/core/internal/mocks/utils/mock_AppChecker.go b/core/internal/mocks/utils/mock_AppChecker.go new file mode 100644 index 00000000..be220c4c --- /dev/null +++ b/core/internal/mocks/utils/mock_AppChecker.go @@ -0,0 +1,242 @@ +// Code generated by mockery v2.53.5. DO NOT EDIT. + +package mocks_utils + +import mock "github.com/stretchr/testify/mock" + +// MockAppChecker is an autogenerated mock type for the AppChecker type +type MockAppChecker struct { + mock.Mock +} + +type MockAppChecker_Expecter struct { + mock *mock.Mock +} + +func (_m *MockAppChecker) EXPECT() *MockAppChecker_Expecter { + return &MockAppChecker_Expecter{mock: &_m.Mock} +} + +// AnyCommandExists provides a mock function with given fields: cmds +func (_m *MockAppChecker) AnyCommandExists(cmds ...string) bool { + _va := make([]interface{}, len(cmds)) + for _i := range cmds { + _va[_i] = cmds[_i] + } + var _ca []interface{} + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for AnyCommandExists") + } + + var r0 bool + if rf, ok := ret.Get(0).(func(...string) bool); ok { + r0 = rf(cmds...) + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// MockAppChecker_AnyCommandExists_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AnyCommandExists' +type MockAppChecker_AnyCommandExists_Call struct { + *mock.Call +} + +// AnyCommandExists is a helper method to define mock.On call +// - cmds ...string +func (_e *MockAppChecker_Expecter) AnyCommandExists(cmds ...interface{}) *MockAppChecker_AnyCommandExists_Call { + return &MockAppChecker_AnyCommandExists_Call{Call: _e.mock.On("AnyCommandExists", + append([]interface{}{}, cmds...)...)} +} + +func (_c *MockAppChecker_AnyCommandExists_Call) Run(run func(cmds ...string)) *MockAppChecker_AnyCommandExists_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]string, len(args)-0) + for i, a := range args[0:] { + if a != nil { + variadicArgs[i] = a.(string) + } + } + run(variadicArgs...) + }) + return _c +} + +func (_c *MockAppChecker_AnyCommandExists_Call) Return(_a0 bool) *MockAppChecker_AnyCommandExists_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockAppChecker_AnyCommandExists_Call) RunAndReturn(run func(...string) bool) *MockAppChecker_AnyCommandExists_Call { + _c.Call.Return(run) + return _c +} + +// AnyFlatpakExists provides a mock function with given fields: flatpaks +func (_m *MockAppChecker) AnyFlatpakExists(flatpaks ...string) bool { + _va := make([]interface{}, len(flatpaks)) + for _i := range flatpaks { + _va[_i] = flatpaks[_i] + } + var _ca []interface{} + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for AnyFlatpakExists") + } + + var r0 bool + if rf, ok := ret.Get(0).(func(...string) bool); ok { + r0 = rf(flatpaks...) + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// MockAppChecker_AnyFlatpakExists_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AnyFlatpakExists' +type MockAppChecker_AnyFlatpakExists_Call struct { + *mock.Call +} + +// AnyFlatpakExists is a helper method to define mock.On call +// - flatpaks ...string +func (_e *MockAppChecker_Expecter) AnyFlatpakExists(flatpaks ...interface{}) *MockAppChecker_AnyFlatpakExists_Call { + return &MockAppChecker_AnyFlatpakExists_Call{Call: _e.mock.On("AnyFlatpakExists", + append([]interface{}{}, flatpaks...)...)} +} + +func (_c *MockAppChecker_AnyFlatpakExists_Call) Run(run func(flatpaks ...string)) *MockAppChecker_AnyFlatpakExists_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]string, len(args)-0) + for i, a := range args[0:] { + if a != nil { + variadicArgs[i] = a.(string) + } + } + run(variadicArgs...) + }) + return _c +} + +func (_c *MockAppChecker_AnyFlatpakExists_Call) Return(_a0 bool) *MockAppChecker_AnyFlatpakExists_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockAppChecker_AnyFlatpakExists_Call) RunAndReturn(run func(...string) bool) *MockAppChecker_AnyFlatpakExists_Call { + _c.Call.Return(run) + return _c +} + +// CommandExists provides a mock function with given fields: cmd +func (_m *MockAppChecker) CommandExists(cmd string) bool { + ret := _m.Called(cmd) + + if len(ret) == 0 { + panic("no return value specified for CommandExists") + } + + var r0 bool + if rf, ok := ret.Get(0).(func(string) bool); ok { + r0 = rf(cmd) + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// MockAppChecker_CommandExists_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CommandExists' +type MockAppChecker_CommandExists_Call struct { + *mock.Call +} + +// CommandExists is a helper method to define mock.On call +// - cmd string +func (_e *MockAppChecker_Expecter) CommandExists(cmd interface{}) *MockAppChecker_CommandExists_Call { + return &MockAppChecker_CommandExists_Call{Call: _e.mock.On("CommandExists", cmd)} +} + +func (_c *MockAppChecker_CommandExists_Call) Run(run func(cmd string)) *MockAppChecker_CommandExists_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *MockAppChecker_CommandExists_Call) Return(_a0 bool) *MockAppChecker_CommandExists_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockAppChecker_CommandExists_Call) RunAndReturn(run func(string) bool) *MockAppChecker_CommandExists_Call { + _c.Call.Return(run) + return _c +} + +// FlatpakExists provides a mock function with given fields: name +func (_m *MockAppChecker) FlatpakExists(name string) bool { + ret := _m.Called(name) + + if len(ret) == 0 { + panic("no return value specified for FlatpakExists") + } + + var r0 bool + if rf, ok := ret.Get(0).(func(string) bool); ok { + r0 = rf(name) + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// MockAppChecker_FlatpakExists_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FlatpakExists' +type MockAppChecker_FlatpakExists_Call struct { + *mock.Call +} + +// FlatpakExists is a helper method to define mock.On call +// - name string +func (_e *MockAppChecker_Expecter) FlatpakExists(name interface{}) *MockAppChecker_FlatpakExists_Call { + return &MockAppChecker_FlatpakExists_Call{Call: _e.mock.On("FlatpakExists", name)} +} + +func (_c *MockAppChecker_FlatpakExists_Call) Run(run func(name string)) *MockAppChecker_FlatpakExists_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *MockAppChecker_FlatpakExists_Call) Return(_a0 bool) *MockAppChecker_FlatpakExists_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockAppChecker_FlatpakExists_Call) RunAndReturn(run func(string) bool) *MockAppChecker_FlatpakExists_Call { + _c.Call.Return(run) + return _c +} + +// NewMockAppChecker creates a new instance of MockAppChecker. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockAppChecker(t interface { + mock.TestingT + Cleanup(func()) +}) *MockAppChecker { + mock := &MockAppChecker{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/internal/utils/exec.go b/core/internal/utils/exec.go index 6d6d7256..c9bfafec 100644 --- a/core/internal/utils/exec.go +++ b/core/internal/utils/exec.go @@ -5,6 +5,31 @@ import ( "strings" ) +type AppChecker interface { + CommandExists(cmd string) bool + AnyCommandExists(cmds ...string) bool + FlatpakExists(name string) bool + AnyFlatpakExists(flatpaks ...string) bool +} + +type DefaultAppChecker struct{} + +func (DefaultAppChecker) CommandExists(cmd string) bool { + return CommandExists(cmd) +} + +func (DefaultAppChecker) AnyCommandExists(cmds ...string) bool { + return AnyCommandExists(cmds...) +} + +func (DefaultAppChecker) FlatpakExists(name string) bool { + return FlatpakExists(name) +} + +func (DefaultAppChecker) AnyFlatpakExists(flatpaks ...string) bool { + return AnyFlatpakExists(flatpaks...) +} + func CommandExists(cmd string) bool { _, err := exec.LookPath(cmd) return err == nil