gioui.org@v0.6.1-0.20240506124620-7a9ce51988ce/io/input/semantic_test.go (about) 1 // SPDX-License-Identifier: Unlicense OR MIT 2 3 package input 4 5 import ( 6 "fmt" 7 "image" 8 "reflect" 9 "testing" 10 11 "gioui.org/f32" 12 "gioui.org/io/event" 13 "gioui.org/io/pointer" 14 "gioui.org/io/semantic" 15 "gioui.org/op" 16 "gioui.org/op/clip" 17 ) 18 19 func TestEmptySemantics(t *testing.T) { 20 var r Router 21 tree := r.AppendSemantics(nil) 22 if len(tree) != 1 { 23 t.Errorf("expected 1 semantic node for empty tree, got %d", len(tree)) 24 } 25 } 26 27 func TestSemanticTree(t *testing.T) { 28 var ( 29 ops op.Ops 30 r Router 31 ) 32 t1 := clip.Rect(image.Rect(0, 0, 75, 75)).Push(&ops) 33 semantic.DescriptionOp("child1").Add(&ops) 34 t1.Pop() 35 t2 := clip.Rect(image.Rect(25, 25, 100, 100)).Push(&ops) 36 semantic.DescriptionOp("child2").Add(&ops) 37 t2.Pop() 38 r.Frame(&ops) 39 tests := []struct { 40 x, y float32 41 desc string 42 }{ 43 {24, 24, "child1"}, 44 {50, 50, "child2"}, 45 {100, 100, ""}, 46 } 47 tree := r.AppendSemantics(nil) 48 verifyTree(t, 0, tree[0]) 49 for _, test := range tests { 50 p := f32.Pt(test.x, test.y) 51 id, found := r.SemanticAt(p) 52 if !found { 53 t.Errorf("no semantic node at %v", p) 54 } 55 n, found := lookupNode(tree, id) 56 if !found { 57 t.Errorf("no id %d in semantic tree", id) 58 } 59 if got := n.Desc.Description; got != test.desc { 60 t.Errorf("got semantic description %s at %v, expected %s", got, p, test.desc) 61 } 62 } 63 64 // Verify stable IDs. 65 r.Frame(&ops) 66 tree2 := r.AppendSemantics(nil) 67 if !reflect.DeepEqual(tree, tree2) { 68 fmt.Println("First tree:") 69 printTree(0, tree[0]) 70 fmt.Println("Second tree:") 71 printTree(0, tree2[0]) 72 t.Error("same semantic description lead to differing trees") 73 } 74 } 75 76 func TestSemanticDescription(t *testing.T) { 77 var ops op.Ops 78 79 h := new(int) 80 event.Op(&ops, h) 81 semantic.DescriptionOp("description").Add(&ops) 82 semantic.LabelOp("label").Add(&ops) 83 semantic.Button.Add(&ops) 84 semantic.EnabledOp(false).Add(&ops) 85 semantic.SelectedOp(true).Add(&ops) 86 var r Router 87 events(&r, -1, pointer.Filter{ 88 Target: h, 89 Kinds: pointer.Press | pointer.Release, 90 }) 91 r.Frame(&ops) 92 tree := r.AppendSemantics(nil) 93 got := tree[0].Desc 94 exp := SemanticDesc{ 95 Class: 1, 96 Description: "description", 97 Label: "label", 98 Selected: true, 99 Disabled: true, 100 Gestures: ClickGesture, 101 Bounds: image.Rectangle{Min: image.Point{X: -1e+06, Y: -1e+06}, Max: image.Point{X: 1e+06, Y: 1e+06}}, 102 } 103 if got != exp { 104 t.Errorf("semantic description mismatch:\nGot: %+v\nWant: %+v", got, exp) 105 } 106 } 107 108 func lookupNode(tree []SemanticNode, id SemanticID) (SemanticNode, bool) { 109 for _, n := range tree { 110 if id == n.ID { 111 return n, true 112 } 113 } 114 return SemanticNode{}, false 115 } 116 117 func verifyTree(t *testing.T, parent SemanticID, n SemanticNode) { 118 t.Helper() 119 if n.ParentID != parent { 120 t.Errorf("node %d: got parent %d, want %d", n.ID, n.ParentID, parent) 121 } 122 for _, c := range n.Children { 123 verifyTree(t, n.ID, c) 124 } 125 } 126 127 func printTree(indent int, n SemanticNode) { 128 for i := 0; i < indent; i++ { 129 fmt.Print("\t") 130 } 131 fmt.Printf("%d: %+v\n", n.ID, n.Desc) 132 for _, c := range n.Children { 133 printTree(indent+1, c) 134 } 135 }