1
0
mirror of https://github.com/mum4k/termdash.git synced 2025-04-25 13:48:50 +08:00

Test coverage for closed focus groups.

This commit is contained in:
Jakub Sobon 2020-12-27 01:00:42 -05:00
parent 69bc767fb5
commit 5c68b72bbe
No known key found for this signature in database
GPG Key ID: F2451A77FB05D3B7
5 changed files with 438 additions and 19 deletions

View File

@ -289,16 +289,17 @@ func (c *Container) inFocusGroup(fg FocusGroup) bool {
// changes the focused container.
// Caller must hold c.mu.
func (c *Container) updateFocusFromKeyboard(k *terminalapi.Keyboard) {
nextGroupsForKey, isGroupKeyForNext := c.opts.global.keyFocusGroupsNext[k.Key]
prevGroupsForKey, isGroupKeyForPrev := c.opts.global.keyFocusGroupsPrevious[k.Key]
active := c.focusTracker.active()
nextGroupsForKey, isGroupKeyForNext := active.opts.global.keyFocusGroupsNext[k.Key]
prevGroupsForKey, isGroupKeyForPrev := active.opts.global.keyFocusGroupsPrevious[k.Key]
nextMatchesContGroup, nextG := nextGroupsForKey.firstMatching(c.opts.keyFocusGroups)
prevMatchesContGroup, prevG := prevGroupsForKey.firstMatching(c.opts.keyFocusGroups)
nextMatchesContGroup, nextG := nextGroupsForKey.firstMatching(active.opts.keyFocusGroups)
prevMatchesContGroup, prevG := prevGroupsForKey.firstMatching(active.opts.keyFocusGroups)
switch {
case c.opts.global.keyFocusNext != nil && *c.opts.global.keyFocusNext == k.Key:
case active.opts.global.keyFocusNext != nil && *active.opts.global.keyFocusNext == k.Key:
c.focusTracker.next( /* group = */ nil)
case c.opts.global.keyFocusPrevious != nil && *c.opts.global.keyFocusPrevious == k.Key:
case active.opts.global.keyFocusPrevious != nil && *active.opts.global.keyFocusPrevious == k.Key:
c.focusTracker.previous( /* group = */ nil)
case isGroupKeyForNext && nextMatchesContGroup:
c.focusTracker.next(&nextG)

View File

@ -529,6 +529,63 @@ func TestNew(t *testing.T) {
},
wantContainerErr: true,
},
{
desc: "fails on KeyFocusGroups with a negative group",
termSize: image.Point{10, 20},
container: func(ft *faketerm.Terminal) (*Container, error) {
return New(
ft,
KeyFocusGroups(0, -1),
)
},
wantContainerErr: true,
},
{
desc: "fails on KeyFocusGroupsNext with a negative group",
termSize: image.Point{10, 20},
container: func(ft *faketerm.Terminal) (*Container, error) {
return New(
ft,
KeyFocusGroupsNext('n', 0, -1),
)
},
wantContainerErr: true,
},
{
desc: "fails on KeyFocusGroupsPrevious with a negative group",
termSize: image.Point{10, 20},
container: func(ft *faketerm.Terminal) (*Container, error) {
return New(
ft,
KeyFocusGroupsPrevious('p', 0, -1),
)
},
wantContainerErr: true,
},
{
desc: "fails on KeyFocusGroupsNext with a key already assigned as KeyFocusGroupsPrevious",
termSize: image.Point{10, 20},
container: func(ft *faketerm.Terminal) (*Container, error) {
return New(
ft,
KeyFocusGroupsPrevious('n', 0),
KeyFocusGroupsNext('n', 0),
)
},
wantContainerErr: true,
},
{
desc: "fails on KeyFocusGroupsPrevious with a key already assigned as KeyFocusGroupsNext",
termSize: image.Point{10, 20},
container: func(ft *faketerm.Terminal) (*Container, error) {
return New(
ft,
KeyFocusGroupsNext('n', 0),
KeyFocusGroupsPrevious('n', 0),
)
},
wantContainerErr: true,
},
{
desc: "empty container",
termSize: image.Point{10, 10},

View File

@ -68,6 +68,11 @@ func newFocusTracker(c *Container) *focusTracker {
}
}
// active returns container that is currently active.
func (ft *focusTracker) active() *Container {
return ft.container
}
// isActive determines if the provided container is the currently active container.
func (ft *focusTracker) isActive(c *Container) bool {
return ft.container == c

View File

@ -261,9 +261,22 @@ func TestPointCont(t *testing.T) {
}
}
// contLocIntro prints out an introduction explaining the used container
// contLocIntro3 prints out an introduction explaining the used container
// locations on test failures.
func contLocIntro() string {
func contLocIntro3() string {
var s strings.Builder
s.WriteString("Container locations refer to containers in the following tree, i.e. contLocA is the root container:\n")
s.WriteString(`
A
/ \
B C
`)
return s.String()
}
// contLocIntro5 prints out an introduction explaining the used container
// locations on test failures.
func contLocIntro5() string {
var s strings.Builder
s.WriteString("Container locations refer to containers in the following tree, i.e. contLocA is the root container:\n")
s.WriteString(`
@ -292,6 +305,8 @@ var contLocNames = map[contLoc]string{
contLocA: "contLocA",
contLocB: "contLocB",
contLocC: "contLocC",
contLocD: "contLocD",
contLocE: "contLocE",
}
const (
@ -299,10 +314,12 @@ const (
contLocA
contLocB
contLocC
contLocD
contLocE
)
func TestFocusTrackerMouse(t *testing.T) {
t.Log(contLocIntro())
t.Log(contLocIntro3())
ft, err := faketerm.New(image.Point{10, 10})
if err != nil {
@ -496,9 +513,15 @@ const (
contDirPrevious
)
func TestFocusTrackerNextAndPrevious(t *testing.T) {
t.Log(contLocIntro())
// contSize determines the size of the container used in the test.
type contSize int
const (
contSize3 contSize = iota
contSize5
)
func TestFocusTrackerNextAndPrevious(t *testing.T) {
ft, err := faketerm.New(image.Point{10, 10})
if err != nil {
t.Fatalf("faketerm.New => unexpected error: %v", err)
@ -511,6 +534,7 @@ func TestFocusTrackerNextAndPrevious(t *testing.T) {
tests := []struct {
desc string
contSize contSize
container func(ft *faketerm.Terminal) (*Container, error)
events []*terminalapi.Keyboard
wantFocused contLoc
@ -1071,6 +1095,314 @@ func TestFocusTrackerNextAndPrevious(t *testing.T) {
wantFocused: contLocB,
wantProcessed: 1,
},
{
desc: "a focus group can have multiple keys configured for next",
container: func(ft *faketerm.Terminal) (*Container, error) {
return New(
ft,
KeyFocusGroups(1),
SplitVertical(
Left(
KeyFocusSkip(),
KeyFocusGroups(1),
),
Right(
KeyFocusSkip(),
KeyFocusGroups(1),
),
),
KeyFocusGroupsNext('n', 1),
KeyFocusGroupsNext(keyboard.KeyArrowRight, 1),
)
},
events: []*terminalapi.Keyboard{
{Key: 'n'},
{Key: keyboard.KeyArrowRight},
},
wantFocused: contLocC,
wantProcessed: 2,
},
{
desc: "a focus group can have multiple keys configured for previous",
container: func(ft *faketerm.Terminal) (*Container, error) {
return New(
ft,
KeyFocusGroups(1),
SplitVertical(
Left(
KeyFocusGroups(1),
),
Right(
KeyFocusGroups(1),
),
),
KeyFocusGroupsPrevious('n', 1),
KeyFocusGroupsPrevious(keyboard.KeyArrowRight, 1),
)
},
events: []*terminalapi.Keyboard{
{Key: 'n'},
{Key: keyboard.KeyArrowRight},
},
wantFocused: contLocB,
wantProcessed: 2,
},
{
desc: "a container can be in multiple focus groups, rotates within group while on next",
contSize: contSize5,
container: func(ft *faketerm.Terminal) (*Container, error) {
return New( // contLocA
ft,
KeyFocusGroups(1),
SplitVertical(
Left( // contLocB
KeyFocusGroups(1),
SplitVertical(
Left( // contLocD
KeyFocusGroups(1),
),
Right( // contLocE
KeyFocusGroups(1, 2),
),
),
),
Right( // contLocC
KeyFocusGroups(1, 2),
),
),
KeyFocusGroupsNext('n', 1),
KeyFocusGroupsNext(keyboard.KeyArrowRight, 2),
)
},
events: []*terminalapi.Keyboard{
{Key: 'n'}, // focuses contLocD
{Key: 'n'}, // focuses contLocE
{Key: keyboard.KeyArrowRight}, // focuses contLocC
{Key: keyboard.KeyArrowRight}, // rotates focus to contLocE
},
wantFocused: contLocE,
wantProcessed: 4,
},
{
desc: "a container can be in multiple focus groups, rotates within group while on previous",
contSize: contSize5,
container: func(ft *faketerm.Terminal) (*Container, error) {
return New( // contLocA
ft,
KeyFocusGroups(1),
SplitVertical(
Left( // contLocB
KeyFocusGroups(1),
SplitVertical(
Left( // contLocD
KeyFocusGroups(1),
),
Right( // contLocE
KeyFocusGroups(1, 2),
),
),
),
Right( // contLocC
KeyFocusGroups(1, 2),
),
),
KeyFocusGroupsPrevious('n', 1),
KeyFocusGroupsPrevious(keyboard.KeyArrowLeft, 2),
)
},
events: []*terminalapi.Keyboard{
{Key: 'n'}, // focuses contLocC
{Key: keyboard.KeyArrowLeft}, // focuses contLocE
{Key: keyboard.KeyArrowLeft}, // rotates focus back to contLocC
},
wantFocused: contLocC,
wantProcessed: 3,
},
{
desc: "same key and group, first group takes priority, group 1 is first",
contSize: contSize5,
container: func(ft *faketerm.Terminal) (*Container, error) {
return New( // contLocA
ft,
KeyFocusGroups(1),
SplitVertical(
Left( // contLocB
KeyFocusGroups(1),
SplitVertical(
Left( // contLocD
KeyFocusGroups(1, 2),
),
Right( // contLocE
KeyFocusGroups(1),
),
),
),
Right( // contLocC
KeyFocusGroups(1, 2),
),
),
KeyFocusGroupsNext('n', 1),
KeyFocusGroupsNext('n', 2),
)
},
events: []*terminalapi.Keyboard{
{Key: 'n'}, // focuses contLocD
{Key: 'n'}, // focuses contLocE
},
wantFocused: contLocE,
wantProcessed: 2,
},
{
desc: "same key and group, first group takes priority, group 2 is first",
contSize: contSize5,
container: func(ft *faketerm.Terminal) (*Container, error) {
return New( // contLocA
ft,
KeyFocusGroups(1),
SplitVertical(
Left( // contLocB
KeyFocusGroups(1),
SplitVertical(
Left( // contLocD
KeyFocusGroups(2, 1),
),
Right( // contLocE
KeyFocusGroups(1),
),
),
),
Right( // contLocC
KeyFocusGroups(1, 2),
),
),
KeyFocusGroupsNext('n', 1),
KeyFocusGroupsNext('n', 2),
)
},
events: []*terminalapi.Keyboard{
{Key: 'n'}, // focuses contLocD
{Key: 'n'}, // focuses contLocC
},
wantFocused: contLocC,
wantProcessed: 2,
},
{
desc: "KeyFocusGroups called multiple times, same key and group, first group takes priority, group 2 is first",
contSize: contSize5,
container: func(ft *faketerm.Terminal) (*Container, error) {
return New( // contLocA
ft,
KeyFocusGroups(1),
SplitVertical(
Left( // contLocB
KeyFocusGroups(1),
SplitVertical(
Left( // contLocD
KeyFocusGroups(2),
KeyFocusGroups(1),
),
Right( // contLocE
KeyFocusGroups(1),
),
),
),
Right( // contLocC
KeyFocusGroups(1, 2),
),
),
KeyFocusGroupsNext('n', 1),
KeyFocusGroupsNext('n', 2),
)
},
events: []*terminalapi.Keyboard{
{Key: 'n'}, // focuses contLocD
{Key: 'n'}, // focuses contLocC
},
wantFocused: contLocC,
wantProcessed: 2,
},
{
desc: "global KeyFocusNext moves focus out of a focus group",
contSize: contSize3,
container: func(ft *faketerm.Terminal) (*Container, error) {
return New( // contLocA
ft,
SplitVertical(
Left( // contLocB
KeyFocusGroups(1),
),
Right( // contLocC
),
),
KeyFocusNext('n'),
KeyFocusGroupsNext(keyboard.KeyArrowRight, 1),
)
},
events: []*terminalapi.Keyboard{
{Key: 'n'}, // focuses contLocB in focus group 1
{Key: 'n'}, // focuses contLocC
},
wantFocused: contLocC,
wantProcessed: 2,
},
{
desc: "global KeyFocusPrevious moves focus out of a focus group",
contSize: contSize3,
container: func(ft *faketerm.Terminal) (*Container, error) {
return New( // contLocA
ft,
SplitVertical(
Left( // contLocB
),
Right( // contLocC
KeyFocusGroups(1),
),
),
KeyFocusPrevious('p'),
KeyFocusGroupsPrevious(keyboard.KeyArrowLeft, 1),
)
},
events: []*terminalapi.Keyboard{
{Key: 'p'}, // focuses contLocC in focus group 1
{Key: 'p'}, // focuses contLocB
},
wantFocused: contLocB,
wantProcessed: 2,
},
{
desc: "KeyFocusGroups with no arguments removes all groups",
contSize: contSize5,
container: func(ft *faketerm.Terminal) (*Container, error) {
return New( // contLocA
ft,
KeyFocusGroups(1),
SplitVertical(
Left( // contLocB
KeyFocusGroups(1),
SplitVertical(
Left( // contLocD
KeyFocusGroups(1),
),
Right( // contLocE
KeyFocusGroups(1),
KeyFocusGroups(),
),
),
),
Right( // contLocC
KeyFocusGroups(1),
),
),
KeyFocusGroupsNext('n', 1),
)
},
events: []*terminalapi.Keyboard{
{Key: 'n'}, // focuses contLocD
{Key: 'n'}, // focuses contLocC
},
wantFocused: contLocC,
wantProcessed: 2,
},
}
for _, tc := range tests {
@ -1102,18 +1434,42 @@ func TestFocusTrackerNextAndPrevious(t *testing.T) {
wantFocused = root.first
case contLocC:
wantFocused = root.second
case contLocD:
wantFocused = root.first.first
case contLocE:
wantFocused = root.first.second
default:
t.Fatalf("unsupported wantFocused value => %v", wf)
}
if !root.focusTracker.isActive(wantFocused) {
t.Errorf("isActive(%v) => false, want true, status: contLocA(%v):%v, contLocB(%v):%v, contLocC(%v):%v",
tc.wantFocused,
contLocA, root.focusTracker.isActive(root),
contLocB, root.focusTracker.isActive(root.first),
contLocC, root.focusTracker.isActive(root.second),
)
switch tc.contSize {
case contSize3:
t.Log(contLocIntro3())
if !root.focusTracker.isActive(wantFocused) {
t.Errorf("isActive(%v) => false, want true, status: %v:%v, %v:%v, %v:%v",
tc.wantFocused,
contLocA, root.focusTracker.isActive(root),
contLocB, root.focusTracker.isActive(root.first),
contLocC, root.focusTracker.isActive(root.second),
)
}
case contSize5:
t.Log(contLocIntro5())
if !root.focusTracker.isActive(wantFocused) {
t.Errorf("isActive(%v) => false, want true, status: %v:%v, %v:%v, %v:%v, %v:%v, %v:%v",
tc.wantFocused,
contLocA, root.focusTracker.isActive(root),
contLocB, root.focusTracker.isActive(root.first),
contLocC, root.focusTracker.isActive(root.second),
contLocD, root.focusTracker.isActive(root.first.first),
contLocE, root.focusTracker.isActive(root.first.second),
)
}
default:
t.Errorf("unknown contSize: %v", tc.contSize)
}
})
}
}

View File

@ -924,7 +924,7 @@ func KeyFocusSkip() Option {
// moved between them sharing the same keyboard key.
type FocusGroup int
// KeyFocusGroups assigns this container to focus groups of with the specified
// KeyFocusGroups assigns this container to focus groups with the specified
// numbers.
//
// See either of (KeysFocusGroupNext, KeysFocusGroupPrevious) for a description