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