https://github.com/akkartik/mu/blob/main/504test-screen.mu
  1 # Some primitives for checking the state of fake screen objects.
  2 
  3 # validate data on screen regardless of attributes (color, bold, etc.)
  4 # Mu doesn't have multi-line strings, so we provide functions for rows or portions of rows.
  5 # Tab characters (that translate into multiple screen cells) not supported.
  6 
  7 fn check-screen-row screen: (addr screen), y: int, expected: (addr array byte), msg: (addr array byte) {
  8   check-screen-row-from screen, 0/x, y, expected, msg
  9 }
 10 
 11 fn check-screen-row-from screen-on-stack: (addr screen), x: int, y: int, expected: (addr array byte), msg: (addr array byte) {
 12   var screen/esi: (addr screen) <- copy screen-on-stack
 13   var idx/ecx: int <- screen-cell-index screen, x, y
 14   # compare 'expected' with the screen contents starting at 'idx', grapheme by grapheme
 15   var e: (stream byte 0x100)
 16   var e-addr/edx: (addr stream byte) <- address e
 17   write e-addr, expected
 18   {
 19     var done?/eax: boolean <- stream-empty? e-addr
 20     compare done?, 0
 21     break-if-!=
 22     var _g/eax: grapheme <- screen-grapheme-at-idx screen, idx
 23     var g/ebx: grapheme <- copy _g
 24     var expected-grapheme/eax: grapheme <- read-grapheme e-addr
 25     # compare graphemes
 26     $check-screen-row-from:compare-graphemes: {
 27       # if expected-grapheme is space, null grapheme is also ok
 28       {
 29         compare expected-grapheme, 0x20
 30         break-if-!=
 31         compare g, 0
 32         break-if-= $check-screen-row-from:compare-graphemes
 33       }
 34       # if (g == expected-grapheme) print "."
 35       compare g, expected-grapheme
 36       {
 37         break-if-!=
 38         draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ".", 3/fg/cyan, 0/bg
 39         break $check-screen-row-from:compare-graphemes
 40       }
 41       # otherwise print an error
 42       count-test-failure
 43       draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, msg, 3/fg/cyan, 0/bg
 44       draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ": expected '", 3/fg/cyan, 0/bg
 45       draw-grapheme-at-cursor 0/screen, expected-grapheme, 3/cyan, 0/bg
 46       move-cursor-rightward-and-downward 0/screen, 0/xmin, 0x80/xmax=screen-width
 47       draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "' at (", 3/fg/cyan, 0/bg
 48       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, x, 3/fg/cyan, 0/bg
 49       draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ", ", 3/fg/cyan, 0/bg
 50       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, y, 3/fg/cyan, 0/bg
 51       draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ") but observed '", 3/fg/cyan, 0/bg
 52       draw-grapheme-at-cursor 0/screen, g, 3/cyan, 0/bg
 53       move-cursor-rightward-and-downward 0/screen, 0/xmin, 0x80/xmax=screen-width
 54       draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "'", 3/fg/cyan, 0/bg
 55       move-cursor-to-left-margin-of-next-line 0/screen
 56     }
 57     idx <- increment
 58     increment x
 59     loop
 60   }
 61 }
 62 
 63 # various variants by screen-cell attribute; spaces in the 'expected' data should not match the attribute
 64 
 65 fn check-screen-row-in-color screen: (addr screen), fg: int, y: int, expected: (addr array byte), msg: (addr array byte) {
 66   check-screen-row-in-color-from screen, fg, y, 0/x, expected, msg
 67 }
 68 
 69 fn check-screen-row-in-color-from screen-on-stack: (addr screen), fg: int, y: int, x: int, expected: (addr array byte), msg: (addr array byte) {
 70   var screen/esi: (addr screen) <- copy screen-on-stack
 71   var idx/ecx: int <- screen-cell-index screen, x, y
 72   # compare 'expected' with the screen contents starting at 'idx', grapheme by grapheme
 73   var e: (stream byte 0x100)
 74   var e-addr/edx: (addr stream byte) <- address e
 75   write e-addr, expected
 76   {
 77     var done?/eax: boolean <- stream-empty? e-addr
 78     compare done?, 0
 79     break-if-!=
 80     var _g/eax: grapheme <- screen-grapheme-at-idx screen, idx
 81     var g/ebx: grapheme <- copy _g
 82     var _expected-grapheme/eax: grapheme <- read-grapheme e-addr
 83     var expected-grapheme/edi: grapheme <- copy _expected-grapheme
 84     $check-screen-row-in-color-from:compare-cells: {
 85       # if expected-grapheme is space, null grapheme is also ok
 86       {
 87         compare expected-grapheme, 0x20
 88         break-if-!=
 89         compare g, 0
 90         break-if-= $check-screen-row-in-color-from:compare-cells
 91       }
 92       # if expected-grapheme is space, a different color is ok
 93       {
 94         compare expected-grapheme, 0x20
 95         break-if-!=
 96         var color/eax: int <- screen-color-at-idx screen, idx
 97         compare color, fg
 98         break-if-!= $check-screen-row-in-color-from:compare-cells
 99       }
100       # compare graphemes
101       $check-screen-row-in-color-from:compare-graphemes: {
102         # if (g == expected-grapheme) print "."
103         compare g, expected-grapheme
104         {
105           break-if-!=
106           draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ".", 3/fg/cyan, 0/bg
107           break $check-screen-row-in-color-from:compare-graphemes
108         }
109         # otherwise print an error
110         count-test-failure
111         draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, msg, 3/fg/cyan, 0/bg
112         draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ": expected '", 3/fg/cyan, 0/bg
113         draw-grapheme-at-cursor 0/screen, expected-grapheme, 3/cyan, 0/bg
114         move-cursor-rightward-and-downward 0/screen, 0/xmin, 0x80/xmax=screen-width
115         draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "' at (", 3/fg/cyan, 0/bg
116         draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, x, 3/fg/cyan, 0/bg
117         draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ", ", 3/fg/cyan, 0/bg
118         draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, y, 3/fg/cyan, 0/bg
119         draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ") but observed '", 3/fg/cyan, 0/bg
120         draw-grapheme-at-cursor 0/screen, g, 3/cyan, 0/bg
121         move-cursor-rightward-and-downward 0/screen, 0/xmin, 0x80/xmax=screen-width
122         draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "'", 3/fg/cyan, 0/bg
123         move-cursor-to-left-margin-of-next-line 0/screen
124       }
125       $check-screen-row-in-color-from:compare-colors: {
126         var color/eax: int <- screen-color-at-idx screen, idx
127         compare fg, color
128         {
129           break-if-!=
130           draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ".", 3/fg/cyan, 0/bg
131           break $check-screen-row-in-color-from:compare-colors
132         }
133         # otherwise print an error
134         count-test-failure
135         draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, msg, 3/fg/cyan, 0/bg
136         draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ": expected '", 3/fg/cyan, 0/bg
137         draw-grapheme-at-cursor 0/screen, expected-grapheme, 3/cyan, 0/bg
138         move-cursor-rightward-and-downward 0/screen, 0/xmin, 0x80/xmax=screen-width
139         draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "' at (", 3/fg/cyan, 0/bg
140         draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, x, 3/fg/cyan, 0/bg
141         draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ", ", 3/fg/cyan, 0/bg
142         draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, y, 3/fg/cyan, 0/bg
143         draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ") in color ", 3/fg/cyan, 0/bg
144         draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, fg, 3/fg/cyan, 0/bg
145         draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, " but observed color ", 3/fg/cyan, 0/bg
146         draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, color, 3/fg/cyan, 0/bg
147         move-cursor-to-left-margin-of-next-line 0/screen
148       }
149     }
150     idx <- increment
151     increment x
152     loop
153   }
154 }
155 
156 fn check-screen-row-in-background-color screen: (addr screen), bg: int, y: int, expected: (addr array byte), msg: (addr array byte) {
157   check-screen-row-in-background-color-from screen, bg, y, 0/x, expected, msg
158 }
159 
160 fn check-screen-row-in-background-color-from screen-on-stack: (addr screen), bg: int, y: int, x: int, expected: (addr array byte), msg: (addr array byte) {
161   var screen/esi: (addr screen) <- copy screen-on-stack
162   var idx/ecx: int <- screen-cell-index screen, x, y
163   # compare 'expected' with the screen contents starting at 'idx', grapheme by grapheme
164   var e: (stream byte 0x100)
165   var e-addr/edx: (addr stream byte) <- address e
166   write e-addr, expected
167   {
168     var done?/eax: boolean <- stream-empty? e-addr
169     compare done?, 0
170     break-if-!=
171     var _g/eax: grapheme <- screen-grapheme-at-idx screen, idx
172     var g/ebx: grapheme <- copy _g
173     var _expected-grapheme/eax: grapheme <- read-grapheme e-addr
174     var expected-grapheme/edi: grapheme <- copy _expected-grapheme
175     $check-screen-row-in-background-color-from:compare-cells: {
176       # if expected-grapheme is space, null grapheme is also ok
177       {
178         compare expected-grapheme, 0x20
179         break-if-!=
180         compare g, 0
181         break-if-= $check-screen-row-in-background-color-from:compare-cells
182       }
183       # if expected-grapheme is space, a different background-color is ok
184       {
185         compare expected-grapheme, 0x20
186         break-if-!=
187         var background-color/eax: int <- screen-background-color-at-idx screen, idx
188         compare background-color, bg
189         break-if-!= $check-screen-row-in-background-color-from:compare-cells
190       }
191       # compare graphemes
192       $check-screen-row-in-background-color-from:compare-graphemes: {
193         # if (g == expected-grapheme) print "."
194         compare g, expected-grapheme
195         {
196           break-if-!=
197           draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ".", 3/fg/cyan, 0/bg
198           break $check-screen-row-in-background-color-from:compare-graphemes
199         }
200         # otherwise print an error
201         count-test-failure
202         draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, msg, 3/fg/cyan, 0/bg
203         draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ": expected '", 3/fg/cyan, 0/bg
204         draw-grapheme-at-cursor 0/screen, expected-grapheme, 3/cyan, 0/bg
205         move-cursor-rightward-and-downward 0/screen, 0/xmin, 0x80/xmax=screen-width
206         draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "' at (", 3/fg/cyan, 0/bg
207         draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, x, 3/fg/cyan, 0/bg
208         draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ", ", 3/fg/cyan, 0/bg
209         draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, y, 3/fg/cyan, 0/bg
210         draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ") but observed '", 3/fg/cyan, 0/bg
211         draw-grapheme-at-cursor 0/screen, g, 3/cyan, 0/bg
212         move-cursor-rightward-and-downward 0/screen, 0/xmin, 0x80/xmax=screen-width
213         draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "'", 3/fg/cyan, 0/bg
214         move-cursor-to-left-margin-of-next-line 0/screen
215         break $check-screen-row-in-background-color-from:compare-graphemes
216       }
217       $check-screen-row-in-background-color-from:compare-background-colors: {
218         var background-color/eax: int <- screen-background-color-at-idx screen, idx
219         compare bg, background-color
220         {
221           break-if-!=
222           draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ".", 3/fg/cyan, 0/bg
223           break $check-screen-row-in-background-color-from:compare-background-colors
224         }
225         # otherwise print an error
226         count-test-failure
227         draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, msg, 3/fg/cyan, 0/bg
228         draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ": expected '", 3/fg/cyan, 0/bg
229         draw-grapheme-at-cursor 0/screen, expected-grapheme, 3/cyan, 0/bg
230         move-cursor-rightward-and-downward 0/screen, 0/xmin, 0x80/xmax=screen-width
231         draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "' at (", 3/fg/cyan, 0/bg
232         draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, x, 3/fg/cyan, 0/bg
233         draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ", ", 3/fg/cyan, 0/bg
234         draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, y, 3/fg/cyan, 0/bg
235         draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ") in background-color ", 3/fg/cyan, 0/bg
236         draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, bg, 3/fg/cyan, 0/bg
237         draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, " but observed background-color ", 3/fg/cyan, 0/bg
238         draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, background-color, 3/fg/cyan, 0/bg
239         move-cursor-to-left-margin-of-next-line 0/screen
240       }
241     }
242     idx <- increment
243     increment x
244     loop
245   }
246 }
247 
248 # helpers for checking just background color, not screen contents
249 # these can validate bg for spaces
250 
251 fn check-background-color-in-screen-row screen: (addr screen), bg: int, y: int, expected-bitmap: (addr array byte), msg: (addr array byte) {
252   check-background-color-in-screen-row-from screen, bg, y, 0/x, expected-bitmap, msg
253 }
254 
255 fn check-background-color-in-screen-row-from screen-on-stack: (addr screen), bg: int, y: int, x: int, expected-bitmap: (addr array byte), msg: (addr array byte) {
256   var screen/esi: (addr screen) <- copy screen-on-stack
257   var idx/ecx: int <- screen-cell-index screen, x, y
258   # compare background color where 'expected-bitmap' is a non-space
259   var e: (stream byte 0x100)
260   var e-addr/edx: (addr stream byte) <- address e
261   write e-addr, expected-bitmap
262   {
263     var done?/eax: boolean <- stream-empty? e-addr
264     compare done?, 0
265     break-if-!=
266     var _expected-bit/eax: grapheme <- read-grapheme e-addr
267     var expected-bit/edi: grapheme <- copy _expected-bit
268     $check-background-color-in-screen-row-from:compare-cells: {
269       var background-color/eax: int <- screen-background-color-at-idx screen, idx
270       # if expected-bit is space, assert that background is NOT bg
271       compare expected-bit, 0x20
272       {
273         break-if-!=
274         compare background-color, bg
275         break-if-!= $check-background-color-in-screen-row-from:compare-cells
276         count-test-failure
277         draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, msg, 3/fg/cyan, 0/bg
278         draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ": expected (", 3/fg/cyan, 0/bg
279         draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, x, 3/fg/cyan, 0/bg
280         draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ", ", 3/fg/cyan, 0/bg
281         draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, y, 3/fg/cyan, 0/bg
282         draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ") to not be in background-color ", 3/fg/cyan, 0/bg
283         draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, bg, 3/fg/cyan, 0/bg
284         move-cursor-to-left-margin-of-next-line 0/screen
285         break $check-background-color-in-screen-row-from:compare-cells
286       }
287       # otherwise assert that background IS bg
288       compare background-color, bg
289       break-if-= $check-background-color-in-screen-row-from:compare-cells
290       count-test-failure
291       draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, msg, 3/fg/cyan, 0/bg
292       draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ": expected (", 3/fg/cyan, 0/bg
293       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, x, 3/fg/cyan, 0/bg
294       draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ", ", 3/fg/cyan, 0/bg
295       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, y, 3/fg/cyan, 0/bg
296       draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, ") in background-color ", 3/fg/cyan, 0/bg
297       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, bg, 3/fg/cyan, 0/bg
298       draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, " but observed background-color ", 3/fg/cyan, 0/bg
299       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, background-color, 3/fg/cyan, 0/bg
300       move-cursor-to-left-margin-of-next-line 0/screen
301     }
302     idx <- increment
303     increment x
304     loop
305   }
306 }
307 
308 fn test-draw-single-grapheme {
309   var screen-on-stack: screen
310   var screen/esi: (addr screen) <- address screen-on-stack
311   initialize-screen screen, 5, 4, 0/no-pixel-graphics
312   draw-code-point screen, 0x61/a, 0/x, 0/y, 1/fg, 2/bg
313   check-screen-row screen, 0/y, "a", "F - test-draw-single-grapheme"  # top-left corner of the screen
314   check-screen-row-in-color screen, 1/fg, 0/y, "a", "F - test-draw-single-grapheme-fg"
315   check-screen-row-in-background-color screen, 2/bg, 0/y, "a", "F - test-draw-single-grapheme-bg"
316   check-background-color-in-screen-row screen, 2/bg, 0/y, "x ", "F - test-draw-single-grapheme-bg2"
317 }
318 
319 fn test-draw-multiple-graphemes {
320   var screen-on-stack: screen
321   var screen/esi: (addr screen) <- address screen-on-stack
322   initialize-screen screen, 0x10/rows, 4/cols, 0/no-pixel-graphics
323   draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, "Hello, 世界", 1/fg, 2/bg
324   check-screen-row screen, 0/y, "Hello, 世界", "F - test-draw-multiple-graphemes"
325   check-screen-row-in-color screen, 1/fg, 0/y, "Hello, 世界", "F - test-draw-multiple-graphemes-fg"
326   check-background-color-in-screen-row screen, 2/bg, 0/y, "xxxxxxxxx ", "F - test-draw-multiple-graphemes-bg2"
327 }