tests: test_syntax now reports other errors before the pkgconfig error
[nit.git] / lib / mnit / mnit_injected_input.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Injection of input events that are read frm a file.
16 # This cloud be used to replay an execution of a mnit application to debug or to benchmark it.
17 #
18 # The input file is given through the environment variable `MNIT_READ_INPUT`
19 #
20 # In order to reproduce executions, the behavior of the application must be deterministic
21 # for a given sequence of inputs.
22 # The main source of differences in executions is caused by the `rand` function,
23 # Set the environment variable `NIT_SRAND` to a value to force srand to be initialized with this value.
24 #
25 # The input event file is made of event descriptions, one event by line.
26 #
27 # ~~~raw
28 # 10 click 10.0 20.0
29 # 20 quit
30 # ~~~
31 #
32 # The first field, an integer, is the delay (in frame count) since the previous event
33 # 0 means the event is launched in the same frame that the previous one.
34 #
35 # The second field, a string, is the kind of the event.
36 # Currently only `click` for PointerEvent and `quit` for QuitEvent are recognized.
37 #
38 # The following fields are the arguments that specific for each kind of event.
39 #
40 # * `quit` does not have arguments
41 # * `click` has 2 float arguments: `PointerEvent::x` and `PointerEvent::y`
42 module mnit_injected_input
43
44 import mnit_app
45
46 # Concrete event objects that are manually instantiated.
47 # Most InputEvent are extern classes and specific to one platform.
48 #
49 # However, subclasses of this `DummyInputEvent` are genuine Nit
50 # classes and can be instantiated, and more easily manipulated by the programmer.
51 interface DummyInputEvent
52 super InputEvent
53 end
54
55 # A concrete QuitEvent
56 class DummyQuitEvent
57 super DummyInputEvent
58 super QuitEvent
59 redef fun to_s do return "quit"
60 end
61
62 # A concrete PointerEvent
63 class DummyPointerEvent
64 super DummyInputEvent
65 super PointerEvent
66 redef var x: Float
67 redef var y: Float
68 redef fun pressed do return true
69 redef fun to_s do return "click {x} {y}"
70 end
71
72 redef class App
73 # The stream where injected inputs are read
74 private var injected_input_stream: nullable Reader = null
75
76 redef fun setup
77 do
78 var input = "MNIT_READ_INPUT".environ
79 if input != "" then
80 injected_input_stream = new FileReader.open(input)
81 print "GET injected_input_stream {input}"
82 end
83
84 super
85 end
86
87 # Number of frames before the next injected input
88 private var wait_next_input = 0
89
90 # What is the input to inject when `wait_next_input` become 0
91 private var next_input: nullable DummyInputEvent
92
93 redef fun full_frame
94 do
95 if injected_input_stream != null then generate_injected_input
96 super
97 end
98
99 # Internal method to generate injected input events
100 # Is called before each frame
101 # Return `true` is an input event was injexted
102 fun generate_injected_input: Bool
103 do
104 var res = false
105 loop
106 if wait_next_input > 0 then
107 wait_next_input -= 1
108 return res
109 end
110 var n = next_input
111 if n != null then
112 print "INPUT {n}"
113 res = true
114 input(n)
115 end
116 var l = injected_input_stream.read_line
117 if l == "" then
118 print "END OF INPUTS"
119 injected_input_stream.close
120 injected_input_stream = null
121 input(new DummyQuitEvent)
122 return true
123 end
124 print "read {l}"
125 var fs = l.split(" ")
126 if fs.length < 2 then
127 print "BAD EVENT SPEC {l}"
128 res = true
129 input(new DummyQuitEvent)
130 end
131 wait_next_input = fs[0].to_i
132 if fs[1] == "click" then
133 next_input = new DummyPointerEvent(fs[2].to_f, fs[3].to_f)
134 else if fs[1] == "quit" then
135 next_input = new DummyQuitEvent
136 else
137 print "UNKNOWN EVENT {fs[1]} (on {l})"
138 res = true
139 input(new DummyQuitEvent)
140 return true
141 end
142 print "WAIT {wait_next_input} for {next_input.to_s}"
143 end
144 end
145 end