Fortran · 8943 bytes Raw Blame History
1 module test_driver_module
2 use text_buffer_module
3 use editor_state_module
4 use command_handler_module
5 use mock_terminal_module
6 implicit none
7 private
8
9 public :: create_test_editor, destroy_test_editor
10 public :: simulate_typing, simulate_key, simulate_key_sequence
11 public :: get_buffer_text, get_cursor_position, get_line_text
12 public :: assert_buffer_equals, assert_cursor_at
13 public :: assert_line_equals
14
15 contains
16
17 subroutine create_test_editor(editor, buffer, initial_text)
18 type(editor_state_t), intent(out) :: editor
19 type(buffer_t), intent(out) :: buffer
20 character(len=*), intent(in), optional :: initial_text
21 integer :: i
22
23 ! Initialize buffer
24 call init_buffer(buffer)
25
26 ! Add initial text if provided
27 if (present(initial_text)) then
28 do i = 1, len(initial_text)
29 call buffer_insert(buffer, i, initial_text(i:i))
30 end do
31 end if
32
33 ! Initialize editor state
34 editor%screen_rows = 24
35 editor%screen_cols = 80
36 editor%viewport_line = 1
37 editor%viewport_column = 1
38 allocate(editor%cursors(1))
39 editor%cursors(1)%line = 1
40 editor%cursors(1)%column = 1
41 editor%cursors(1)%desired_column = 1
42 editor%cursors(1)%has_selection = .false.
43 editor%active_cursor = 1
44
45 ! Initialize command handler
46 call init_command_handler()
47 end subroutine create_test_editor
48
49 subroutine destroy_test_editor(editor, buffer)
50 type(editor_state_t), intent(inout) :: editor
51 type(buffer_t), intent(inout) :: buffer
52
53 call cleanup_buffer(buffer)
54 if (allocated(editor%cursors)) deallocate(editor%cursors)
55 if (allocated(editor%filename)) deallocate(editor%filename)
56 call cleanup_command_handler()
57 end subroutine destroy_test_editor
58
59 subroutine simulate_typing(editor, buffer, text)
60 type(editor_state_t), intent(inout) :: editor
61 type(buffer_t), intent(inout) :: buffer
62 character(len=*), intent(in) :: text
63 integer :: i
64 character(len=16) :: key_str
65 logical :: should_quit
66
67 do i = 1, len(text)
68 ! Convert character to key string
69 key_str = text(i:i) ! Don't use write statement, just assign directly
70 ! Don't trim spaces - they're valid input
71 if (text(i:i) == ' ') then
72 call handle_key_command(' ', editor, buffer, should_quit)
73 else
74 call handle_key_command(trim(key_str), editor, buffer, should_quit)
75 end if
76 end do
77 end subroutine simulate_typing
78
79 subroutine simulate_key(editor, buffer, key_name)
80 type(editor_state_t), intent(inout) :: editor
81 type(buffer_t), intent(inout) :: buffer
82 character(len=*), intent(in) :: key_name
83 logical :: should_quit
84
85 call handle_key_command(key_name, editor, buffer, should_quit)
86 end subroutine simulate_key
87
88 subroutine simulate_key_sequence(editor, buffer, keys)
89 type(editor_state_t), intent(inout) :: editor
90 type(buffer_t), intent(inout) :: buffer
91 character(len=*), dimension(:), intent(in) :: keys
92 integer :: i
93
94 do i = 1, size(keys)
95 call simulate_key(editor, buffer, keys(i))
96 end do
97 end subroutine simulate_key_sequence
98
99 function get_buffer_text(buffer) result(text)
100 type(buffer_t), intent(in) :: buffer
101 character(len=:), allocatable :: text
102 integer :: i, pos, logical_size
103 character :: ch
104
105 ! Calculate logical size (total size minus gap)
106 logical_size = buffer%size - (buffer%gap_end - buffer%gap_start)
107
108 if (logical_size <= 0) then
109 text = ''
110 return
111 end if
112
113 allocate(character(len=logical_size) :: text)
114 text = repeat(' ', logical_size) ! Initialize with spaces instead of empty string
115 pos = 0
116
117 ! Get text before gap
118 do i = 1, buffer%gap_start - 1
119 if (i <= len(buffer%data)) then
120 ch = buffer%data(i:i)
121 pos = pos + 1
122 if (pos <= len(text)) text(pos:pos) = ch
123 end if
124 end do
125
126 ! Get text after gap
127 do i = buffer%gap_end, buffer%size
128 if (i <= len(buffer%data)) then
129 ch = buffer%data(i:i)
130 if (ch /= char(0)) then ! Skip null characters
131 pos = pos + 1
132 if (pos <= len(text)) text(pos:pos) = ch
133 end if
134 end if
135 end do
136
137 ! Trim to actual content length
138 if (pos > 0) then
139 text = text(1:pos)
140 else
141 text = ''
142 end if
143 end function get_buffer_text
144
145 function get_line_text(buffer, line_num) result(text)
146 type(buffer_t), intent(in) :: buffer
147 integer, intent(in) :: line_num
148 character(len=:), allocatable :: text
149
150 text = buffer_get_line(buffer, line_num)
151 end function get_line_text
152
153 subroutine get_cursor_position(editor, line, column)
154 type(editor_state_t), intent(in) :: editor
155 integer, intent(out) :: line, column
156
157 line = editor%cursors(editor%active_cursor)%line
158 column = editor%cursors(editor%active_cursor)%column
159 end subroutine get_cursor_position
160
161 subroutine assert_buffer_equals(buffer, expected, test_name)
162 type(buffer_t), intent(in) :: buffer
163 character(len=*), intent(in) :: expected
164 character(len=*), intent(in), optional :: test_name
165 character(len=:), allocatable :: actual
166 character(len=256) :: msg
167
168 actual = get_buffer_text(buffer)
169
170 if (actual /= expected) then
171 if (present(test_name)) then
172 write(msg, '(A,A)') trim(test_name), ": Buffer content mismatch"
173 else
174 msg = "Buffer content mismatch"
175 end if
176 write(*, '(A)') trim(msg)
177 write(*, '(A,A)') "Expected: '", expected, "'"
178 write(*, '(A,A)') "Actual: '", actual, "'"
179 error stop "Test failed"
180 end if
181 end subroutine assert_buffer_equals
182
183 subroutine assert_cursor_at(editor, expected_line, expected_col, test_name)
184 type(editor_state_t), intent(in) :: editor
185 integer, intent(in) :: expected_line, expected_col
186 character(len=*), intent(in), optional :: test_name
187 integer :: actual_line, actual_col
188 character(len=256) :: msg
189
190 call get_cursor_position(editor, actual_line, actual_col)
191
192 if (actual_line /= expected_line .or. actual_col /= expected_col) then
193 if (present(test_name)) then
194 write(msg, '(A,A)') trim(test_name), ": Cursor position mismatch"
195 else
196 msg = "Cursor position mismatch"
197 end if
198 write(*, '(A)') trim(msg)
199 write(*, '(A,I0,A,I0,A)') "Expected: (", expected_line, ",", expected_col, ")"
200 write(*, '(A,I0,A,I0,A)') "Actual: (", actual_line, ",", actual_col, ")"
201 error stop "Test failed"
202 end if
203 end subroutine assert_cursor_at
204
205 subroutine assert_line_equals(buffer, line_num, expected, test_name)
206 type(buffer_t), intent(in) :: buffer
207 integer, intent(in) :: line_num
208 character(len=*), intent(in) :: expected
209 character(len=*), intent(in), optional :: test_name
210 character(len=:), allocatable :: actual
211 character(len=256) :: msg
212
213 actual = get_line_text(buffer, line_num)
214
215 if (actual /= expected) then
216 if (present(test_name)) then
217 write(msg, '(A,A,I0,A)') trim(test_name), ": Line ", line_num, " mismatch"
218 else
219 write(msg, '(A,I0,A)') "Line ", line_num, " mismatch"
220 end if
221 write(*, '(A)') trim(msg)
222 write(*, '(A,A)') "Expected: '", expected, "'"
223 write(*, '(A,A)') "Actual: '", actual, "'"
224 error stop "Test failed"
225 end if
226 end subroutine assert_line_equals
227
228 function buffer_get_char_at(buffer, position) result(ch)
229 type(buffer_t), intent(in) :: buffer
230 integer, intent(in) :: position
231 character :: ch
232 integer :: actual_pos
233
234 if (position < 1 .or. position > buffer%size) then
235 ch = char(0)
236 return
237 end if
238
239 if (position < buffer%gap_start) then
240 ch = buffer%data(position:position)
241 else
242 ! Adjust position for gap
243 actual_pos = position + (buffer%gap_end - buffer%gap_start)
244 if (actual_pos <= len(buffer%data)) then
245 ch = buffer%data(actual_pos:actual_pos)
246 else
247 ch = char(0)
248 end if
249 end if
250 end function buffer_get_char_at
251
252 end module test_driver_module