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