https://github.com/akkartik/mu1/blob/master/edit/009-sandbox-test.mu
  1 ## clicking on sandbox results to 'fix' them and turn sandboxes into tests
  2 
  3 scenario sandbox-click-on-result-toggles-color-to-green [
  4   local-scope
  5   trace-until 100/app  # trace too long
  6   assume-screen 100/width, 20/height
  7   # basic recipe
  8   assume-resources [
  9     [lesson/recipes.mu] <- [
 10       |recipe foo [|
 11       |  reply 4|
 12       |]|
 13     ]
 14   ]
 15   env:&:environment <- new-programming-environment resources, screen, [foo]
 16   render-all screen, env, render
 17   # run it
 18   assume-console [
 19     press F4
 20   ]
 21   event-loop screen, console, env, resources
 22   screen-should-contain [
 23     .                                                                                 run (F4)           .
 24     .recipe foo [                                      ╎                                                 .
 25     .  reply 4                                         ╎─────────────────────────────────────────────────.
 26     .]                                                 ╎0   edit       copy       to recipe    delete    .
 27     .                                                  ╎foo                                              .
 28     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎4                                                .
 29     .                                                  ╎─────────────────────────────────────────────────.
 30     .                                                  ╎                                                 .
 31   ]
 32   # click on the '4' in the result
 33   $clear-trace
 34   assume-console [
 35     left-click 5, 51
 36   ]
 37   run [
 38     event-loop screen, console, env, resources
 39   ]
 40   # color toggles to green
 41   screen-should-contain-in-color 2/green, [
 42     .                                                                                                    .
 43     .                                                                                                    .
 44     .                                                                                                    .
 45     .                                                                                                    .
 46     .                                                                                                    .
 47     .                                                   4                                                .
 48     .                                                                                                    .
 49     .                                                                                                    .
 50   ]
 51   # don't render entire sandbox side
 52   check-trace-count-for-label-lesser-than 250, [print-character]  # say 5 sandbox lines
 53   # cursor should remain unmoved
 54   run [
 55     cursor:char <- copy 9251/␣
 56     print screen, cursor
 57   ]
 58   screen-should-contain [
 59     .                                                                                 run (F4)           .
 60     .␣ecipe foo [                                      ╎                                                 .
 61     .  reply 4                                         ╎─────────────────────────────────────────────────.
 62     .]                                                 ╎0   edit       copy       to recipe    delete    .
 63     .                                                  ╎foo                                              .
 64     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎4                                                .
 65     .                                                  ╎─────────────────────────────────────────────────.
 66     .                                                  ╎                                                 .
 67   ]
 68   # now change the result
 69   # then rerun
 70   assume-console [
 71     left-click 2, 11  # cursor to end of line
 72     press backspace
 73     type [3]
 74     press F4
 75   ]
 76   run [
 77     event-loop screen, console, env, resources
 78   ]
 79   # result turns red
 80   screen-should-contain-in-color 1/red, [
 81     .                                                                                                    .
 82     .                                                                                                    .
 83     .                                                                                                    .
 84     .                                                                                                    .
 85     .                                                                                                    .
 86     .                                                   3                                                .
 87     .                                                                                                    .
 88     .                                                                                                    .
 89   ]
 90 ]
 91 
 92 # this requires tracking a couple more things
 93 container sandbox [
 94   response-starting-row-on-screen:num
 95   expected-response:text
 96 ]
 97 
 98 # include expected response when saving or restoring a sandbox
 99 before <end-save-sandbox> [
100   {
101     expected-response:text <- get *sandbox, expected-response:offset
102     break-unless expected-response
103     filename <- append filename, [.out]
104     resources <- dump resources, filename, expected-response
105   }
106 ]
107 
108 before <end-restore-sandbox> [
109   {
110     filename <- append filename, [.out]
111     contents <- slurp resources, filename
112     break-unless contents
113     *curr <- put *curr, expected-response:offset, contents
114   }
115 ]
116 
117 # clicks on sandbox responses save it as 'expected'
118 after <global-touch> [
119   # check if it's inside the output of any sandbox
120   {
121     sandbox-left-margin:num <- get *current-sandbox, left:offset
122     click-column:num <- get t, column:offset
123     on-sandbox-side?:bool <- greater-or-equal click-column, sandbox-left-margin
124     break-unless on-sandbox-side?
125     first-sandbox:&:sandbox <- get *env, sandbox:offset
126     break-unless first-sandbox
127     first-sandbox-begins:num <- get *first-sandbox, starting-row-on-screen:offset
128     click-row:num <- get t, row:offset
129     below-sandbox-editor?:bool <- greater-or-equal click-row, first-sandbox-begins
130     break-unless below-sandbox-editor?
131     # identify the sandbox whose output is being clicked on
132     sandbox:&:sandbox, sandbox-index:num <- find-click-in-sandbox-output env, click-row
133     break-unless sandbox
134     # update it
135     sandbox <- toggle-expected-response sandbox
136     # minimal update to disk
137     save-sandbox resources, sandbox, sandbox-index
138     # minimal update to screen
139     sandbox-right-margin:num <- get *current-sandbox, right:offset
140     row:num <- render-sandbox-response screen, sandbox, sandbox-left-margin, sandbox-right-margin
141     {
142       height:num <- screen-height screen
143       at-bottom?:bool <- greater-or-equal row, height
144       break-if at-bottom?
145       draw-horizontal screen, row, sandbox-left-margin, sandbox-right-margin
146     }
147     screen <- update-cursor screen, recipes, current-sandbox, sandbox-in-focus?, env
148     loop +next-event
149   }
150 ]
151 
152 def find-click-in-sandbox-output env:&:environment, click-row:num -> sandbox:&:sandbox, sandbox-index:num [
153   local-scope
154   load-inputs
155   # assert click-row >= sandbox.starting-row-on-screen
156   sandbox:&:sandbox <- get *env, sandbox:offset
157   start:num <- get *sandbox, starting-row-on-screen:offset
158   clicked-on-sandboxes?:bool <- greater-or-equal click-row, start
159   assert clicked-on-sandboxes?, [extract-sandbox called on click to sandbox editor]
160   # while click-row < sandbox.next-sandbox.starting-row-on-screen
161   sandbox-index <- copy 0
162   {
163     next-sandbox:&:sandbox <- get *sandbox, next-sandbox:offset
164     break-unless next-sandbox
165     next-start:num <- get *next-sandbox, starting-row-on-screen:offset
166     found?:bool <- lesser-than click-row, next-start
167     break-if found?
168     sandbox <- copy next-sandbox
169     sandbox-index <- add sandbox-index, 1
170     loop
171   }
172   # return sandbox if click is in its output region
173   response-starting-row:num <- get *sandbox, response-starting-row-on-screen:offset
174   return-unless response-starting-row, null/no-click-in-sandbox-output, 0/sandbox-index
175   click-in-response?:bool <- greater-or-equal click-row, response-starting-row
176   return-unless click-in-response?, null/no-click-in-sandbox-output, 0/sandbox-index
177   return sandbox, sandbox-index
178 ]
179 
180 def toggle-expected-response sandbox:&:sandbox -> sandbox:&:sandbox [
181   local-scope
182   load-inputs
183   expected-response:text <- get *sandbox, expected-response:offset
184   {
185     # if expected-response is set, reset
186     break-unless expected-response
187     *sandbox <- put *sandbox, expected-response:offset, null
188   }
189   {
190     # if not, set expected response to the current response
191     break-if expected-response
192     response:text <- get *sandbox, response:offset
193     *sandbox <- put *sandbox, expected-response:offset, response
194   }
195 ]
196 
197 # when rendering a sandbox, color it in red/green if expected response exists
198 after <render-sandbox-response> [
199   {
200     break-unless sandbox-response
201     *sandbox <- put *sandbox, response-starting-row-on-screen:offset, row
202     row <- render-sandbox-response screen, sandbox, left, right
203     jump +render-sandbox-end
204   }
205 ]
206 
207 def render-sandbox-response screen:&:screen, sandbox:&:sandbox, left:num, right:num -> row:num, screen:&:screen [
208   local-scope
209   load-inputs
210   sandbox-response:text <- get *sandbox, response:offset
211   expected-response:text <- get *sandbox, expected-response:offset
212   row:num <- get *sandbox response-starting-row-on-screen:offset
213   {
214     break-if expected-response
215     row <- render-text screen, sandbox-response, left, right, 245/grey, row
216     return
217   }
218   response-is-expected?:bool <- equal expected-response, sandbox-response
219   {
220     break-if response-is-expected?
221     row <- render-text screen, sandbox-response, left, right, 1/red, row
222   }
223   {
224     break-unless response-is-expected?:bool
225     row <- render-text screen, sandbox-response, left, right, 2/green, row
226   }
227 ]
228 
229 before <end-render-sandbox-reset-hidden> [
230   *sandbox <- put *sandbox, response-starting-row-on-screen:offset, 0
231 ]