summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorewy <ewy0@protonmail.com>2026-04-22 18:29:37 +0200
committerewy <ewy0@protonmail.com>2026-04-22 18:29:37 +0200
commit374ed5e1a4dc635c42e33e4133729d40cf3e0e35 (patch)
tree01f12ca3eea0154b61afde3d1ee27df2094644e1
parentee481fa406bb685a16d8b568fca0c9f592c06180 (diff)
work on tests
also replace the annoying search return with a struct
-rw-r--r--.pik/script.override.sh3
-rw-r--r--.pik/site/update.sh1
-rw-r--r--.pik/test.sh2
-rw-r--r--cache/cache.go42
-rw-r--r--cache/cache_test.go30
-rw-r--r--identity/identity.go6
-rw-r--r--main.go52
-rw-r--r--menu/banner.go20
-rw-r--r--paths/paths.go13
-rw-r--r--runner/stub.go56
-rw-r--r--search/search.go66
-rw-r--r--search/search_test.go117
-rw-r--r--testx/create.go29
-rw-r--r--testx/create_test.go7
14 files changed, 329 insertions, 115 deletions
diff --git a/.pik/script.override.sh b/.pik/script.override.sh
new file mode 100644
index 0000000..fb6cf56
--- /dev/null
+++ b/.pik/script.override.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env fish
+# example script
+echo "Overridden!" \ No newline at end of file
diff --git a/.pik/site/update.sh b/.pik/site/update.sh
new file mode 100644
index 0000000..b4745e1
--- /dev/null
+++ b/.pik/site/update.sh
@@ -0,0 +1 @@
+ssh git@ewy.one -- cd /srv/pik/pik "&&" git pull \ No newline at end of file
diff --git a/.pik/test.sh b/.pik/test.sh
new file mode 100644
index 0000000..bb91492
--- /dev/null
+++ b/.pik/test.sh
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+go test -tags test -v ./... \ No newline at end of file
diff --git a/cache/cache.go b/cache/cache.go
index 98bdbe8..8a17592 100644
--- a/cache/cache.go
+++ b/cache/cache.go
@@ -16,6 +16,7 @@ type Cache struct {
Entries []Entry
}
+// Merge combines two caches and filters duplicate keys
func (c Cache) Merge(other Cache) Cache {
mp := make(map[string]string)
for _, e := range append(c.Entries, other.Entries...) {
@@ -33,22 +34,28 @@ type Entry struct {
Label string
}
+// Path is the file path to the "contexts" cache file
var Path = path.Join(paths.Cache, "contexts")
+// FsPath is the Path with the leading slash removed, to be opened from fs.FS
+var FsPath = Path[1:]
+
var UnexpectedEntryError = errors.New("unexpected cache entry")
-func Load() (Cache, error) {
- fd, err := os.Open(Path)
+func LoadFile(root fs.FS, path string) (Cache, error) {
+ fd, err := root.Open(path)
if errors.Is(err, os.ErrNotExist) {
return Cache{}, nil
} else if err != nil {
return Cache{}, err
}
- defer fd.Close()
- return FromReader(fd)
+ if fd != nil {
+ defer fd.Close()
+ }
+ return Load(fd)
}
-func FromReader(r io.Reader) (Cache, error) {
+func Load(r io.Reader) (Cache, error) {
c := Cache{}
scanner := bufio.NewScanner(r)
for scanner.Scan() {
@@ -95,13 +102,22 @@ func New(st *model.State) Cache {
return *c
}
-func Save(s *model.State) error {
- ld, err := Load()
+func SaveFile(path string, s *model.State, loaded Cache) error {
+ fd, err := os.Create(path)
if err != nil {
return err
}
- c := New(s).Merge(ld)
- return os.WriteFile(Path, []byte(c.String()), os.ModePerm)
+ if fd != nil {
+ defer fd.Close()
+ }
+ return Save(s, fd, loaded)
+}
+
+func Save(s *model.State, w io.Writer, loaded Cache) error {
+ result := New(s).Merge(loaded)
+ _, err := w.Write([]byte(result.String()))
+ return err
+
}
func LoadState(f fs.FS, cache Cache, indexers []model.Indexer, runners []model.Runner) (*model.State, []error) {
@@ -127,3 +143,11 @@ outer:
Entries: result,
}
}
+
+func Touch() error {
+ fd, err := os.Create(Path)
+ if fd != nil {
+ defer fd.Close()
+ }
+ return err
+}
diff --git a/cache/cache_test.go b/cache/cache_test.go
index c140154..499c0f5 100644
--- a/cache/cache_test.go
+++ b/cache/cache_test.go
@@ -10,7 +10,7 @@ func TestFromReader_Blank(t *testing.T) {
input := `
`
sr := strings.NewReader(input)
- c, err := FromReader(sr)
+ c, err := Load(sr)
assert.Nil(t, err)
assert.Len(t, c.Entries, 0)
}
@@ -18,7 +18,7 @@ func TestFromReader_Blank(t *testing.T) {
func TestFromReader_OneEntry(t *testing.T) {
input := `/abc/def # deffers`
sr := strings.NewReader(input)
- c, err := FromReader(sr)
+ c, err := Load(sr)
assert.Nil(t, err)
assert.Len(t, c.Entries, 1)
assert.Equal(t, c.Entries[0], Entry{
@@ -33,7 +33,7 @@ func TestFromReader_ManyEntries(t *testing.T) {
/path/src # da source
`
sr := strings.NewReader(input)
- c, err := FromReader(sr)
+ c, err := Load(sr)
assert.Nil(t, err)
assert.Len(t, c.Entries, 3)
assert.Equal(t, c.Entries[0], Entry{
@@ -61,7 +61,7 @@ func TestFromReader_Comments(t *testing.T) {
# // comment
`
sr := strings.NewReader(input)
- c, err := FromReader(sr)
+ c, err := Load(sr)
assert.Nil(t, err)
assert.Len(t, c.Entries, 3)
assert.Equal(t, c.Entries[0], Entry{
@@ -91,3 +91,25 @@ func TestStrip_Nothing(t *testing.T) {
result := c.Strip(old)
assert.Equal(t, c, result)
}
+
+func TestMerge(t *testing.T) {
+ a := Entry{
+ Path: "/usr/share/asdf",
+ }
+ b := Entry{
+ Path: "/test/location",
+ }
+ c := Entry{
+ Path: "/new/mypath",
+ Label: "mypath",
+ }
+ base := Cache{Entries: []Entry{
+ a, b,
+ }}
+ other := Cache{Entries: []Entry{
+ b, c,
+ }}
+ result := base.Merge(other)
+ assert.Len(t, result.Entries, 3)
+ assert.Contains(t, result.Entries, a, b, c)
+}
diff --git a/identity/identity.go b/identity/identity.go
index ee4dfea..1ae13c6 100644
--- a/identity/identity.go
+++ b/identity/identity.go
@@ -8,13 +8,15 @@ type Identity struct {
}
func (i Identity) Is(input string) bool {
- return Reduce(input) == i.Reduced
+ reduced := Reduce(input)
+ return i.Reduced == reduced
}
func New(input string) Identity {
+ reduced := Reduce(input)
return Identity{
Full: input,
- Reduced: Reduce(input),
+ Reduced: reduced,
}
}
diff --git a/main.go b/main.go
index d06c3e7..549d30f 100644
--- a/main.go
+++ b/main.go
@@ -23,29 +23,46 @@ import (
"sync"
)
+// preInitializers are ran before the initializers.
+// useful for initializing stuff like paths, preparing directories, and reading the environment
+var preInitializers = []model.Initializer{
+ paths.Paths,
+}
+
+// initializers are ran before indexing with the indexers,
+// data from the preInitializers can be accessed at this time.
var initializers = []model.Initializer{
+ pikdex.Indexer,
python.Python,
git.Git,
- pikdex.Indexer,
}
+// indexers are methods which scan a directory and return a number of targets.
var indexers = []model.Indexer{
pikdex.Indexer,
just.Indexer,
gnumake.Indexer,
}
+// runners are modules which know how to turn a file into an exec.Cmd
+// all indexers have access to these but only pikdex uses it
var runners = []model.Runner{
shell.Runner,
python.Python,
}
+// hydrators are ran when the menu is required
+// for example adding git info, descriptions, icons...
var hydrators = []model.Modder{
pikdex.Indexer,
git.Git,
}
+// ForceConfirm means we will have to ask for confirmation before running no matter what
var ForceConfirm = false
+
+// SourcesWithoutResults is a failed cache from the previous iteration
+// used for stripping out results to prevent double-index
var SourcesWithoutResults cache.Cache
//go:embed version.txt
@@ -61,6 +78,17 @@ func main() {
}
wg := sync.WaitGroup{}
+ for _, i := range preInitializers {
+ wg.Go(func() {
+ err := i.Init()
+ if err != nil {
+ _, _ = spool.Warn("%v\n", err)
+ }
+ })
+ }
+ wg.Wait()
+
+ wg = sync.WaitGroup{}
for _, i := range initializers {
wg.Go(func() {
err := i.Init()
@@ -95,7 +123,7 @@ func main() {
if !*flags.All {
st, stateErrors = model.NewState(fs, locs, indexers, runners)
} else {
- c, err = cache.Load()
+ c, err = cache.LoadFile(fs, cache.Path[1:])
c.Strip(SourcesWithoutResults)
if err != nil {
_, _ = spool.Warn("%v\n", err)
@@ -106,7 +134,7 @@ func main() {
if stateErrors != nil {
_, _ = spool.Warn("%v\n", stateErrors)
} else {
- err = cache.Save(st)
+ err = cache.SaveFile(cache.Path, st, c)
if err != nil {
_, _ = spool.Warn("%v", err)
}
@@ -142,9 +170,9 @@ func main() {
return
}
- target, src, confirm, _, args := search.Search(st, args...)
- if !*flags.All && target == nil && len(args) > 0 {
- err := pflag.Set("all", "true")
+ result := search.Search(st, args...)
+ // TODO: Move auto-all logic into Search?
+ if !*flags.All && result.Target == nil && len(args) > 0 {
ForceConfirm = true
if err != nil {
_, _ = spool.Warn("%v\n", err)
@@ -155,20 +183,22 @@ func main() {
return
}
- if target == nil {
+ if result.Target == nil {
_, _ = spool.Print("target not found.")
os.Exit(1)
return
}
- if confirm || ForceConfirm {
+ if result.NeedsConfirmation || ForceConfirm {
_, _ = fmt.Fprintf(os.Stderr, "this target is out of tree.\n")
- if !menu.Confirm(os.Stdin, src, target, args...) {
+ if !menu.Confirm(os.Stdin, result.Source, result.Target, args...) {
os.Exit(0)
}
}
-
- err = run.Run(src, target, args...)
+ if result.Overridden {
+ _, _ = fmt.Fprintln(os.Stderr, menu.OverrideWarning(result.Target))
+ }
+ err = run.Run(result.Source, result.Target, args...)
if err != nil {
_, _ = spool.Warn("%v\n", err)
os.Exit(1)
diff --git a/menu/banner.go b/menu/banner.go
index 53c8f16..b65cbbf 100644
--- a/menu/banner.go
+++ b/menu/banner.go
@@ -98,3 +98,23 @@ func InlineCmd(cmd *exec.Cmd) string {
}
return CmdStyle.Render(" # "+CmdDirStyle.Render(paths.ReplaceHome(cmd.Dir)+":"), CmdArgStyle.Render(args...))
}
+
+var (
+ OverrideStyle = style.New(func() lipgloss.Style {
+ return lipgloss.NewStyle()
+ })
+ OverrideCaretColor = lipgloss.Color("1")
+ OverrideCaretStyle = style.New(func() lipgloss.Style {
+ return lipgloss.NewStyle().Foreground(OverrideCaretColor).Bold(true)
+ })
+ OverrideTextStyle = style.New(func() lipgloss.Style {
+ return lipgloss.NewStyle().Faint(true)
+ })
+)
+
+func OverrideWarning(t model.Target) string {
+ return OverrideStyle.Render(lipgloss.JoinHorizontal(lipgloss.Left,
+ OverrideCaretStyle.Render("! "),
+ OverrideTextStyle.Render("overridden by "+t.Label()),
+ ))
+}
diff --git a/paths/paths.go b/paths/paths.go
index 9721fa1..80928df 100644
--- a/paths/paths.go
+++ b/paths/paths.go
@@ -4,7 +4,6 @@ import (
"github.com/adrg/xdg"
"os"
"path/filepath"
- "pik/spool"
"strings"
)
@@ -16,18 +15,24 @@ var (
Ifs = os.Getenv("IFS")
)
-func init() {
+type paths struct {
+}
+
+var Paths = &paths{}
+
+func (p paths) Init() error {
err := os.MkdirAll(Cache, 0700)
if err != nil {
- spool.Warn("%v\n", err)
+ return err
}
err = os.MkdirAll(Config, 0700)
if err != nil {
- spool.Warn("%v\n", err)
+ return err
}
if Ifs == "" {
Ifs = "\n"
}
+ return nil
}
func ReplaceHome(input string) string {
diff --git a/runner/stub.go b/runner/stub.go
new file mode 100644
index 0000000..e1fd8ed
--- /dev/null
+++ b/runner/stub.go
@@ -0,0 +1,56 @@
+//go:build test
+
+package runner
+
+import (
+ "os/exec"
+ "pik/model"
+)
+
+// Stub is the most minimal and useless implementation of the target interface. It only panics. Use if you need a target-compliant struct.
+type Stub struct {
+}
+
+func (s Stub) Matches(input string) bool {
+ //TODO implement me
+ panic("implement me")
+}
+
+func (s Stub) Create(src *model.Source) *exec.Cmd {
+ //TODO implement me
+ panic("implement me")
+}
+
+func (s Stub) Sub() []string {
+ //TODO implement me
+ panic("implement me")
+}
+
+func (s Stub) Label() string {
+ //TODO implement me
+ panic("implement me")
+}
+
+func (s Stub) Hydrate(src *model.Source) (model.HydratedTarget, error) {
+ //TODO implement me
+ panic("implement me")
+}
+
+func (s Stub) Tags() model.Tags {
+ return nil
+}
+
+func (s Stub) ShortestId() string {
+ //TODO implement me
+ panic("implement me")
+}
+
+func (s Stub) Visible() bool {
+ //TODO implement me
+ panic("implement me")
+}
+
+func (s Stub) Invocation(src *model.Source) []string {
+ //TODO implement me
+ panic("implement me")
+}
diff --git a/search/search.go b/search/search.go
index 5b227b2..009dd49 100644
--- a/search/search.go
+++ b/search/search.go
@@ -5,50 +5,70 @@ import (
"slices"
)
-func Search(s *model.State, args ...string) (model.Target, *model.Source, bool, []string, []string) {
+type Result struct {
+ Target model.Target
+ Source *model.Source
+ NeedsConfirmation bool
+ Overridden bool
+ Sub []string
+ Args []string
+}
+
+// Search is the meat of pik
+func Search(s *model.State, args ...string) *Result {
var target model.Target
- var suspect model.Target
- var suspectSource *model.Source
var targetSource *model.Source
- var forward []string
+ var confirm bool
+ var overridden bool
var subdir []string
- confirm := false
+ var forward []string
+ var suspect model.Target
+ var suspectSource *model.Source
args_loop:
- for _, a := range args {
+ for _, arg := range args {
for _, src := range s.Sources {
if targetSource == nil {
- if src.Is(a) {
+ if src.Is(arg) {
targetSource = src
+
+ // try to look for arg target with the same name as the source
+ // "default target" of sorts
for _, t := range targetSource.Targets {
- if t.Matches(a) {
+ if t.Matches(arg) {
target = t
continue args_loop
}
}
+
continue args_loop
}
}
if target == nil && targetSource == nil {
+
+ // uncertain about source, check ours to see if any match
for _, t := range src.Targets {
- if t.Matches(a) {
+ if t.Matches(arg) {
target = t
targetSource = src
continue args_loop
}
}
+
} else if target == nil { // && targetSource == nil (but it is always true)
+
+ // source located,
for _, t := range targetSource.Targets {
- if t.Matches(a) {
+ if t.Matches(arg) {
target = t
continue args_loop
}
}
// if we find the right target
for _, t := range src.Targets {
- if t.Matches(a) {
+ if t.Matches(arg) {
confirm = true
suspect = t
suspectSource = src
@@ -60,10 +80,10 @@ args_loop:
}
if target == nil {
- subdir = append(subdir, a)
+ subdir = append(subdir, arg)
continue args_loop
} else if targetSource != nil {
- forward = append(forward, a)
+ forward = append(forward, arg)
continue args_loop
}
}
@@ -82,5 +102,23 @@ args_loop:
forward = args
}
- return target, targetSource, confirm, subdir, forward
+ if target != nil && targetSource != nil {
+ for _, t := range targetSource.Targets {
+ if slices.Equal(t.Invocation(targetSource), target.Invocation(targetSource)) {
+ if t.Tags().Has(model.Override) {
+ overridden = true
+ target = t
+ }
+ }
+ }
+ }
+
+ return &Result{
+ Target: target,
+ Source: targetSource,
+ NeedsConfirmation: confirm,
+ Overridden: overridden,
+ Sub: subdir,
+ Args: forward,
+ }
}
diff --git a/search/search_test.go b/search/search_test.go
index 59edef1..b5fc835 100644
--- a/search/search_test.go
+++ b/search/search_test.go
@@ -1,3 +1,5 @@
+//go:build test
+
package search
import (
@@ -8,104 +10,99 @@ import (
func TestSearch_TargetOnly(t *testing.T) {
st := testx.TState(testx.TSource("src", "abc", "def"))
- target, source, _, _, _ := Search(st, "def")
- testx.AssertSourceIs(t, "src", source)
- testx.AssertTargetIs(t, "def", target)
+ res := Search(st, "def")
+ testx.AssertSourceIs(t, "src", res.Source)
+ testx.AssertTargetIs(t, "def", res.Target)
}
func TestSearch_TargetAndSource(t *testing.T) {
st := testx.TState(testx.TSource("src", "abc", "def"))
- target, source, _, _, _ := Search(st, "src", "def")
- testx.AssertSourceIs(t, "src", source)
- testx.AssertTargetIs(t, "def", target)
+ res := Search(st, "src", "def")
+ testx.AssertSourceIs(t, "src", res.Source)
+ testx.AssertTargetIs(t, "def", res.Target)
}
func TestSearch_SourceDefaultTarget(t *testing.T) {
st := testx.TState(testx.TSource("src", "abc", "src"))
- target, src, _, _, _ := Search(st, "src")
- testx.AssertSourceIs(t, "src", src)
- assert.NotNil(t, target)
+ res := Search(st, "src")
+ testx.AssertSourceIs(t, "src", res.Source)
+ assert.NotNil(t, res.Target)
}
func TestSearch_SubdirWrong(t *testing.T) {
st := testx.TState(testx.TSource("src", "abc", "src"))
- st.Sources[0].Targets = append(st.Sources[0].Targets, testx.TestTarget{
- Identifier: "script",
- SubValue: []string{"subdir"},
- })
- target, src, confirm, sd, _ := Search(st, "wrong", "script")
- testx.AssertSourceIs(t, "src", src)
- testx.AssertTargetIs(t, "script", target)
- assert.Equal(t, sd, []string{"wrong"})
- assert.NotNil(t, target)
- assert.True(t, confirm)
+ st.Sources[0].Targets = append(st.Sources[0].Targets, testx.TTarget("script", "subdir"))
+ res := Search(st, "wrong", "script")
+ testx.AssertSourceIs(t, "src", res.Source)
+ testx.AssertTargetIs(t, "script", res.Target)
+ assert.Equal(t, []string{"wrong"}, res.Sub)
+ assert.NotNil(t, res.Target)
+ assert.True(t, res.NeedsConfirmation)
}
func TestSearch_SubdirMissing(t *testing.T) {
st := testx.TState(testx.TSource("src", "abc", "src"))
- st.Sources[0].Targets = append(st.Sources[0].Targets, testx.TestTarget{
- Identifier: "script",
- SubValue: []string{"subdir"},
- })
- target, src, confirm, sd, _ := Search(st, "script")
- testx.AssertSourceIs(t, "src", src)
- testx.AssertTargetIs(t, "script", target)
- assert.Nil(t, sd)
- assert.NotNil(t, target)
- assert.False(t, confirm)
+ st.Sources[0].Targets = append(st.Sources[0].Targets, testx.TTarget("script", "subdir"))
+ res := Search(st, "script")
+ testx.AssertSourceIs(t, "src", res.Source)
+ testx.AssertTargetIs(t, "script", res.Target)
+ assert.Nil(t, res.Sub)
+ assert.NotNil(t, res.Target)
+ assert.False(t, res.NeedsConfirmation)
}
func TestSearch_Args(t *testing.T) {
st := testx.TState(testx.TSource("src", "abc", "def"))
- target, source, _, _, args := Search(st, "def", "a1", "a2")
- testx.AssertSourceIs(t, "src", source)
- testx.AssertTargetIs(t, "def", target)
- assert.Equal(t, []string{"a1", "a2"}, args)
+ res := Search(st, "def", "a1", "a2")
+ testx.AssertSourceIs(t, "src", res.Source)
+ testx.AssertTargetIs(t, "def", res.Target)
+ assert.Equal(t, []string{"a1", "a2"}, res.Args)
}
func TestSearch_Args_SubdirMissing(t *testing.T) {
st := testx.TState(testx.TSource("src", "abc", "src"))
- st.Sources[0].Targets = append(st.Sources[0].Targets, testx.TestTarget{
- Identifier: "script",
- SubValue: []string{"subdir"},
- })
- target, src, _, _, args := Search(st, "script", "a1", "a2")
- testx.AssertSourceIs(t, "src", src)
- testx.AssertTargetIs(t, "script", target)
- assert.Equal(t, []string{"a1", "a2"}, args)
+ st.Sources[0].Targets = append(st.Sources[0].Targets, testx.TTarget("script", "subdir"))
+ res := Search(st, "script", "a1", "a2")
+ testx.AssertSourceIs(t, "src", res.Source)
+ testx.AssertTargetIs(t, "script", res.Target)
+ assert.Equal(t, []string{"a1", "a2"}, res.Args)
}
func TestSearch_Args_SubdirPresent(t *testing.T) {
st := testx.TState(testx.TSource("src", "abc", "src"))
- st.Sources[0].Targets = append(st.Sources[0].Targets, testx.TestTarget{
- Identifier: "script",
- SubValue: []string{"subdir"},
- })
- target, src, _, _, args := Search(st, "subdir", "script", "a1", "a2")
- testx.AssertSourceIs(t, "src", src)
- testx.AssertTargetIs(t, "script", target)
- assert.Equal(t, []string{"a1", "a2"}, args)
+ st.Sources[0].Targets = append(st.Sources[0].Targets, testx.TTarget("script", "subdir"))
+ res := Search(st, "subdir", "script", "a1", "a2")
+ testx.AssertSourceIs(t, "src", res.Source)
+ testx.AssertTargetIs(t, "script", res.Target)
+ assert.Equal(t, []string{"a1", "a2"}, res.Args)
}
func TestSearch_SecondarySource(t *testing.T) {
st := testx.TState(testx.TSource("src", "abc", "def"), testx.TSource("aaa", "hjkl"))
- target, source, _, _, _ := Search(st, "aaa", "hjkl")
- testx.AssertSourceIs(t, "aaa", source)
- testx.AssertTargetIs(t, "hjkl", target)
+ res := Search(st, "aaa", "hjkl")
+ testx.AssertSourceIs(t, "aaa", res.Source)
+ testx.AssertTargetIs(t, "hjkl", res.Target)
}
func TestSearch_SecondarySource_DuplicateTargetName(t *testing.T) {
st := testx.TState(testx.TSource("src", "abc", "def"), testx.TSource("aaa", "abc"))
- target, source, confirm, _, _ := Search(st, "aaa", "def")
- testx.AssertSourceIs(t, "src", source)
- testx.AssertTargetIs(t, "def", target)
- assert.True(t, confirm)
+ res := Search(st, "aaa", "def")
+ testx.AssertSourceIs(t, "src", res.Source)
+ testx.AssertTargetIs(t, "def", res.Target)
+ assert.True(t, res.NeedsConfirmation)
}
func TestSearch_SourceTargetMixup(t *testing.T) {
st := testx.TState(testx.TSource("src", "abc"), testx.TSource("aaa", "ccc"))
- target, source, confirm, _, _ := Search(st, "src", "ccc")
- testx.AssertSourceIs(t, "aaa", source)
- testx.AssertTargetIs(t, "ccc", target)
- assert.True(t, confirm)
+ res := Search(st, "src", "ccc")
+ testx.AssertSourceIs(t, "aaa", res.Source)
+ testx.AssertTargetIs(t, "ccc", res.Target)
+ assert.True(t, res.NeedsConfirmation)
+}
+
+func TestSearch_Override(t *testing.T) {
+ st := testx.TState(testx.TSource("src", "abc.override.sh", "abc.sh"))
+ res := Search(st, "src", "abc")
+ assert.Equal(t, "abc.override.sh", res.Target.(*testx.TestTarget).Id.Full)
+ assert.False(t, res.NeedsConfirmation)
}
diff --git a/testx/create.go b/testx/create.go
index 306e50a..62960fd 100644
--- a/testx/create.go
+++ b/testx/create.go
@@ -11,8 +11,9 @@ import (
"testing"
)
-func TTarget(name string) model.Target {
- return TestTarget{Identifier: name}
+func TTarget(name string, sub ...string) model.Target {
+ t := TestTarget{Id: identity.New(name), MyTags: model.TagsFromFilename(name), SubValue: sub}
+ return &t
}
func TSource(name string, targets ...string) *model.Source {
@@ -32,10 +33,18 @@ func TState(sources ...*model.Source) *model.State {
}
type TestTarget struct {
- runner.BaseTarget
- Identifier string
- SubValue []string
- Tags model.Tags
+ runner.Stub
+ Id identity.Identity
+ SubValue []string
+ MyTags model.Tags
+}
+
+func (t TestTarget) Invocation(src *model.Source) []string {
+ return []string{src.Identity.Reduced, t.Id.Reduced}
+}
+
+func (t TestTarget) Matches(input string) bool {
+ return t.Id.Is(input)
}
func (t TestTarget) Visible() bool {
@@ -52,11 +61,7 @@ func (t TestTarget) Sub() []string {
}
func (t TestTarget) Label() string {
- return t.Identifier
-}
-
-func (t TestTarget) Matches(input string) bool {
- return input == t.Identifier
+ return t.Id.Full
}
func (t TestTarget) Create(s *model.Source) *exec.Cmd {
@@ -70,8 +75,10 @@ func AssertTargetIsNot(t *testing.T, input string, target model.Target) {
assert.NotEqual(t, input, target.Label())
}
func AssertSourceIs(t *testing.T, input string, src *model.Source) {
+ assert.NotNil(t, src.Identity)
assert.Equal(t, input, src.Identity.Reduced)
}
func AssertSourceIsNot(t *testing.T, input string, src *model.Source) {
+ assert.NotNil(t, src.Identity)
assert.NotEqual(t, input, src.Identity.Reduced)
}
diff --git a/testx/create_test.go b/testx/create_test.go
index c9f2925..d0e3099 100644
--- a/testx/create_test.go
+++ b/testx/create_test.go
@@ -3,6 +3,7 @@
package testx
import (
+ "github.com/stretchr/testify/assert"
"testing"
)
@@ -25,3 +26,9 @@ func TestAssertTargetIs_Wrong(t *testing.T) {
ta := TTarget("aaaa")
AssertTargetIsNot(t, "bbbbbb", ta)
}
+
+func TestTTargetIdentity(t *testing.T) {
+ ta := TTarget("asdf.hidden.sh")
+ assert.True(t, ta.Matches("asdf"))
+ assert.False(t, ta.Matches("hidden"))
+}