v4 / vlib / v / builder / c_error_report_test.v
277 lines · 256 sloc · 8.55 KB · 26ed1f73bf37184b83bd2bb6de2f10f5b69b508e
Raw
1module builder
2
3import os
4import v.pref
5
6fn restore_env_var(name string, old_value ?string) {
7 if value := old_value {
8 os.setenv(name, value, true)
9 } else {
10 os.unsetenv(name)
11 }
12}
13
14fn restore_c_error_bug_report_url_env(old_url ?string) {
15 restore_env_var('V_C_ERROR_BUG_REPORT_URL', old_url)
16}
17
18fn restore_c_error_bug_report_disabled_env(old_value ?string) {
19 restore_env_var(c_error_bug_report_disabled_env, old_value)
20}
21
22fn test_c_error_location_for_generated_c_parses_gcc_output() {
23 loc := c_error_location_for_generated_c('/tmp/program.tmp.c:42:7: error: unknown type name',
24 '/tmp/program.tmp.c') or {
25 assert false
26 return
27 }
28 assert loc.line == 42
29}
30
31fn test_c_error_location_for_generated_c_parses_msvc_output() {
32 loc := c_error_location_for_generated_c('C:\\tmp\\program.tmp.c(19): error C2143: syntax error',
33 'C:\\tmp\\program.tmp.c') or {
34 assert false
35 return
36 }
37 assert loc.line == 19
38}
39
40fn test_numbered_context_lines_returns_five_lines_each_side() {
41 lines := ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12']
42 context := numbered_context_lines(lines, 6, 5)
43 assert context.len == 11
44 assert context.first().line == 1
45 assert context.last().line == 11
46 assert context[5].line == 6
47 assert context[5].text == '6'
48}
49
50fn test_v_source_location_mapping_from_line_directives() {
51 c_lines := [
52 '#line 10 "/tmp/source.v"',
53 'int a = 1;',
54 'int b = missing;',
55 '#line 999 "/tmp/program.tmp.c"',
56 'int main(void) { return 0; }',
57 ]
58 loc := v_source_location_for_c_line(c_lines, 3, '/tmp/program.tmp.c') or {
59 assert false
60 return
61 }
62 assert loc.file == '/tmp/source.v'
63 assert loc.line == 11
64 c_line := generated_c_line_for_source_location(c_lines, CErrorReportLocation{
65 file: '/tmp/source.v'
66 line: 11
67 }, '/tmp/program.tmp.c') or {
68 assert false
69 return
70 }
71 assert c_line == 3
72}
73
74fn test_generated_c_reset_line_is_not_reported_as_v_source() {
75 c_lines := [
76 '#line 40 "/tmp/program.tmp.c"',
77 'int generated = missing;',
78 ]
79 if _ := v_source_location_for_c_line(c_lines, 2, '/tmp/program.tmp.c') {
80 assert false
81 }
82}
83
84fn test_generated_c_line_for_source_location_prefers_non_empty_line() {
85 c_lines := [
86 '#line 5 "/tmp/source.v"',
87 'void main__main(void) {',
88 '',
89 '#line 6 "/tmp/source.v"',
90 '{NoSuchType _ = ((NoSuchType){E_STRUCT});}',
91 '}',
92 ]
93 c_line := generated_c_line_for_source_location(c_lines, CErrorReportLocation{
94 file: '/tmp/source.v'
95 line: 6
96 }, '/tmp/program.tmp.c') or {
97 assert false
98 return
99 }
100 assert c_line == 5
101}
102
103fn test_c_error_bug_report_url_uses_override_without_trailing_slash() {
104 assert c_error_bug_report_url(' http://127.0.0.1:19090/bug-report/ ') == 'http://127.0.0.1:19090/bug-report'
105}
106
107fn test_c_error_bug_report_url_uses_bugs_domain_by_default() {
108 old_url := os.getenv_opt('V_C_ERROR_BUG_REPORT_URL')
109 os.unsetenv('V_C_ERROR_BUG_REPORT_URL')
110 defer {
111 restore_c_error_bug_report_url_env(old_url)
112 }
113 assert c_error_bug_report_url('') == 'https://bugs.vlang.io/bug-report'
114}
115
116fn test_should_submit_c_error_bug_report_allows_default_outside_github_ci() {
117 old_github_actions := os.getenv_opt('GITHUB_ACTIONS')
118 old_github_job := os.getenv_opt('GITHUB_JOB')
119 old_disabled := os.getenv_opt(c_error_bug_report_disabled_env)
120 os.unsetenv('GITHUB_ACTIONS')
121 os.unsetenv('GITHUB_JOB')
122 os.unsetenv(c_error_bug_report_disabled_env)
123 defer {
124 restore_env_var('GITHUB_ACTIONS', old_github_actions)
125 restore_env_var('GITHUB_JOB', old_github_job)
126 restore_c_error_bug_report_disabled_env(old_disabled)
127 }
128 assert should_submit_c_error_bug_report('')
129}
130
131fn test_should_submit_c_error_bug_report_skips_bugs_domain_in_github_ci() {
132 old_github_actions := os.getenv_opt('GITHUB_ACTIONS')
133 old_github_job := os.getenv_opt('GITHUB_JOB')
134 old_url := os.getenv_opt('V_C_ERROR_BUG_REPORT_URL')
135 old_disabled := os.getenv_opt(c_error_bug_report_disabled_env)
136 os.setenv('GITHUB_ACTIONS', 'true', true)
137 os.unsetenv('GITHUB_JOB')
138 os.unsetenv('V_C_ERROR_BUG_REPORT_URL')
139 os.unsetenv(c_error_bug_report_disabled_env)
140 defer {
141 restore_env_var('GITHUB_ACTIONS', old_github_actions)
142 restore_env_var('GITHUB_JOB', old_github_job)
143 restore_c_error_bug_report_url_env(old_url)
144 restore_c_error_bug_report_disabled_env(old_disabled)
145 }
146 assert !should_submit_c_error_bug_report('')
147 assert !should_submit_c_error_bug_report(' https://bugs.vlang.io/bug-report/ ')
148}
149
150fn test_should_submit_c_error_bug_report_uses_custom_url_in_github_ci() {
151 old_github_actions := os.getenv_opt('GITHUB_ACTIONS')
152 old_github_job := os.getenv_opt('GITHUB_JOB')
153 old_url := os.getenv_opt('V_C_ERROR_BUG_REPORT_URL')
154 old_disabled := os.getenv_opt(c_error_bug_report_disabled_env)
155 os.unsetenv('GITHUB_ACTIONS')
156 os.setenv('GITHUB_JOB', 'test', true)
157 os.setenv('V_C_ERROR_BUG_REPORT_URL', 'http://127.0.0.1:19090/bug-report', true)
158 os.unsetenv(c_error_bug_report_disabled_env)
159 defer {
160 restore_env_var('GITHUB_ACTIONS', old_github_actions)
161 restore_env_var('GITHUB_JOB', old_github_job)
162 restore_c_error_bug_report_url_env(old_url)
163 restore_c_error_bug_report_disabled_env(old_disabled)
164 }
165 assert should_submit_c_error_bug_report('')
166 assert should_submit_c_error_bug_report('http://127.0.0.1:19091/bug-report')
167}
168
169fn test_should_submit_c_error_bug_report_can_be_disabled_by_env() {
170 old_github_actions := os.getenv_opt('GITHUB_ACTIONS')
171 old_github_job := os.getenv_opt('GITHUB_JOB')
172 old_disabled := os.getenv_opt(c_error_bug_report_disabled_env)
173 os.unsetenv('GITHUB_ACTIONS')
174 os.unsetenv('GITHUB_JOB')
175 defer {
176 restore_env_var('GITHUB_ACTIONS', old_github_actions)
177 restore_env_var('GITHUB_JOB', old_github_job)
178 restore_c_error_bug_report_disabled_env(old_disabled)
179 }
180 for value in ['1', 'true', 'yes', 'on'] {
181 os.setenv(c_error_bug_report_disabled_env, value, true)
182 assert !should_submit_c_error_bug_report('')
183 assert !should_submit_c_error_bug_report('http://127.0.0.1:19090/bug-report')
184 }
185 os.setenv(c_error_bug_report_disabled_env, '0', true)
186 assert should_submit_c_error_bug_report('')
187 disable_c_error_bug_reports()
188 assert !should_submit_c_error_bug_report('')
189}
190
191fn test_bounded_c_error_bug_report_keeps_encoded_body_under_limit() {
192 long_output := 'C compiler diagnostic '.repeat(12000)
193 long_c_line := 'generated C line '.repeat(1000)
194 long_v_line := 'source V line '.repeat(1000)
195 report := CErrorBugReport{
196 kind: 'v-c-compiler-error'
197 v_version: 'V test'
198 target_os: 'linux'
199 target_backend: 'c'
200 ccompiler: 'cc'
201 c_error: long_output
202 c_file: '/tmp/program.tmp.c'
203 c_line: 12
204 c_context: [
205 CErrorReportLine{
206 line: 12
207 text: long_c_line
208 },
209 ]
210 v_file: '/tmp/source.v'
211 v_line: 4
212 v_context: [
213 CErrorReportLine{
214 line: 4
215 text: long_v_line
216 },
217 ]
218 }
219 bounded := bounded_c_error_bug_report(report, 4096)
220 encoded := c_error_bug_report_json(bounded)
221 assert encoded.len <= 4096
222 assert bounded.c_error.len < report.c_error.len
223 assert bounded.c_context[0].line == 12
224 assert bounded.c_context[0].text.len < report.c_context[0].text.len
225 assert bounded.v_context[0].line == 4
226 assert bounded.v_context[0].text.len < report.v_context[0].text.len
227}
228
229fn test_c_error_bug_report_json_escapes_strings() {
230 report := CErrorBugReport{
231 kind: 'v-c-compiler-error'
232 v_version: 'V "test"\n'
233 c_context: [
234 CErrorReportLine{
235 line: 1
236 text: 'tab\tslash\\'
237 },
238 ]
239 }
240 encoded := c_error_bug_report_json(report)
241 assert encoded.contains('"v_version":"V \\"test\\"\\n"')
242 assert encoded.contains('"text":"tab\\tslash\\\\"')
243}
244
245fn test_truncated_report_text_preserves_start_and_end_when_space_allows() {
246 text := 'start-' + 'x'.repeat(100) + '-end'
247 truncated := truncated_report_text(text, 80)
248 assert truncated.len <= 80
249 assert truncated.starts_with('start-')
250 assert truncated.contains('report truncated before upload')
251 assert truncated.ends_with('-end')
252}
253
254fn test_new_c_error_bug_report_with_vlines_is_skipped_when_already_vlines() {
255 // When the program is already compiled with -g, the original `.tmp.c` already has
256 // `#line` directives, so there is nothing to regenerate.
257 mut b := Builder{
258 pref: &pref.Preferences{
259 is_vlines: true
260 }
261 }
262 if _ := b.new_c_error_bug_report_with_vlines('cc') {
263 assert false, 'expected none when the C source is already #line annotated'
264 }
265}
266
267fn test_new_c_error_bug_report_with_vlines_is_skipped_without_a_recorded_command() {
268 // Without a recorded C compiler command (e.g. -parallel-cc, or a Windows MSVC build),
269 // there is no command to rerun, so no V mapping can be produced this way.
270 mut b := Builder{
271 pref: &pref.Preferences{}
272 last_cc_cmd: ''
273 }
274 if _ := b.new_c_error_bug_report_with_vlines('cc') {
275 assert false, 'expected none when no C compiler command was recorded'
276 }
277}
278