blob: 034cebe205ed6a19869d248ebbfff97693903d8d [file] [log] [blame]
Akronf3efc9e2026-06-03 10:46:37 +02001use strict;
2use warnings;
3use Test::More;
4use Test::Script;
5use Test::TempDir::Tiny;
6
7my $UNZIP = `sh -c 'command -v unzip'`;
8chomp $UNZIP;
9
10if ($UNZIP eq '') {
11 plan skip_all => 'No unzip executable found in PATH.';
12}
13
14my $offset_data = <<'CONLLU';
15# filename = TEST/TEST/TEST_OFF_001/base/tokens.xml
16# text_id = TEST_TEST.TEST_OFF_001
17# text = Geras rytas.
181 Geras geras ADJ bdv. Case=Nom 2 amod _ _
192 rytas rytas NOUN dkt. Case=Nom 0 root _ _
203 . . PUNCT skyr. _ 2 punct _ SpaceAfter=No
21
22# text = Kaip sekasi?
231 Kaip kaip ADV prv. _ 2 advmod _ _
242 sekasi sektis VERB vksm. _ 0 root _ SpaceAfter=No
253 ? ? PUNCT skyr. _ 2 punct _ _
26
27CONLLU
28
Akrondb3d0cb2026-06-03 15:53:57 +020029my $misc_data = <<'CONLLU';
30# filename = TEST/TEST/TEST_MISC_001/base/tokens.xml
31# text_id = TEST_TEST.TEST_MISC_001
32# start_offsets = 0 0 6 11
33# end_offsets = 12 5 11 12
341 Geras geras ADJ bdv. Case=Nom 2 amod _ SpaceAfter=No|Tag=NNN
352 rytas rytas NOUN dkt. Case=Nom 0 root _ Tag=LLL
363 . . PUNCT skyr. _ 2 punct _ SpaceAfter=No
37
38# start_offsets = 12 13 18 24
39# end_offsets = 25 17 24 25
401 Kaip kaip ADV prv. _ 2 advmod _ SpacesAfter=\n|Tag=MMM
412 sekasi sektis VERB vksm. _ 0 root _ _
423 ? ? PUNCT skyr. _ 2 punct _ _
43
44CONLLU
45
Akron4f542622026-06-03 14:20:20 +020046my $struct_data = <<'CONLLU';
47# filename = TEST/TEST/TEST_STR_001/base/tokens.xml
48# text_id = TEST_TEST.TEST_STR_001
49# newpar id = p1
50# sent_id = s1.1
51# start_offsets = 0 0 6 11
52# end_offsets = 12 5 11 12
531 Geras geras ADJ bdv. Case=Nom 2 amod _ _
542 rytas rytas NOUN dkt. Case=Nom 0 root _ _
553 . . PUNCT skyr. _ 2 punct _ SpaceAfter=No
56
57# sent_id = s1.2
58# start_offsets = 12 13 18 24
59# end_offsets = 25 17 24 25
601 Kaip kaip ADV prv. _ 2 advmod _ _
612 sekasi sektis VERB vksm. _ 0 root _ SpaceAfter=No
623 ? ? PUNCT skyr. _ 2 punct _ _
63
64# newpar id = p2
65# sent_id = s2.1
66# start_offsets = 25 26 32
67# end_offsets = 39 31 39
681 Labas labas ADJ bdv. Case=Nom 0 root _ _
692 vakaras vakaras NOUN dkt. Case=Nom 1 nmod _ _
70
71CONLLU
72
Akronf3efc9e2026-06-03 10:46:37 +020073my $test_tempdir = tempdir();
74my $offset_file = "$test_tempdir/test_offset.conllu";
75{
76 open(my $ofh, '>:encoding(UTF-8)', $offset_file)
77 or die "Cannot write test file: $!";
78 print $ofh $offset_data;
79 close($ofh);
80}
81
82my $zipcontent_off = '';
83script_runs(
84 [ 'script/conllu2korapxml', '-f', 'ud', $offset_file ],
85 { stdout => \$zipcontent_off },
86 "conllu2korapxml computes offsets from # text"
87);
88
89my $zipfile_off = "$test_tempdir/test_offset.zip";
90if ($zipcontent_off) {
91 open(my $zfh, '>:raw', $zipfile_off) or die "Cannot write zip: $!";
92 print $zfh $zipcontent_off;
93 close($zfh);
94
95 my $morpho_xml = `$UNZIP -p $zipfile_off 'TEST/TEST/TEST_OFF_001/ud/morpho.xml' 2>/dev/null`;
96
97 # Sentence 1: "Geras rytas." (12 chars, starts at 0)
98 # Geras: 0-5, rytas: 6-11, .: 11-12
99 like($morpho_xml,
100 qr/id="s1_n1" from="0" to="5"/,
101 "Computed offset: token 'Geras' at 0..5");
102 like($morpho_xml,
103 qr/id="s1_n2" from="6" to="11"/,
104 "Computed offset: token 'rytas' at 6..11");
105 like($morpho_xml,
106 qr/id="s1_n3" from="11" to="12"/,
107 "Computed offset: token '.' at 11..12 (SpaceAfter=No)");
108
109 # Sentence 2: "Kaip sekasi?" (12 chars, starts at 13 = 12 + 1 space)
110 # Kaip: 13-17, sekasi: 18-24, ?: 24-25
111 like($morpho_xml,
112 qr/id="s2_n1" from="13" to="17"/,
113 "Computed offset: token 'Kaip' at 13..17 (sentence 2)");
114 like($morpho_xml,
115 qr/id="s2_n2" from="18" to="24"/,
116 "Computed offset: token 'sekasi' at 18..24");
117 like($morpho_xml,
118 qr/id="s2_n3" from="24" to="25"/,
119 "Computed offset: token '?' at 24..25 (SpaceAfter=No)");
120
121 # Verify dependency XML has correct offsets (head references)
122 my $dep_xml = `$UNZIP -p $zipfile_off 'TEST/TEST/TEST_OFF_001/ud/dependency.xml' 2>/dev/null`;
123
124 # Token 1 "Geras" (0..5) -> head token 2 "rytas" (6..11)
125 like($dep_xml,
126 qr/id="s1_n1" from="0" to="5".*?<span from="6" to="11"/s,
127 "Dependency: 'Geras' head points to 'rytas' offsets");
128
129 # Token 2 "rytas" (6..11) -> head 0 = sentence span (0..12)
130 like($dep_xml,
131 qr/id="s1_n2" from="6" to="11".*?<span from="0" to="12"/s,
132 "Dependency: 'rytas' head points to sentence span");
133}
134else {
135 fail("Computed offset: token 'Geras' at 0..5");
136 fail("Computed offset: token 'rytas' at 6..11");
137 fail("Computed offset: token '.' at 11..12 (SpaceAfter=No)");
138 fail("Computed offset: token 'Kaip' at 13..17 (sentence 2)");
139 fail("Computed offset: token 'sekasi' at 18..24");
140 fail("Computed offset: token '?' at 24..25 (SpaceAfter=No)");
141 fail("Dependency: 'Geras' head points to 'rytas' offsets");
142 fail("Dependency: 'rytas' head points to sentence span");
143}
144
145# No SpaceAfter at all - every token has normal space separation
146my $no_spaceafter_data = <<'CONLLU';
147# filename = TEST/TEST/TEST_NSA_001/base/tokens.xml
148# text_id = TEST_TEST.TEST_NSA_001
149# text = One two three
1501 One one NUM num. _ 0 root _ _
1512 two two NUM num. _ 1 flat _ _
1523 three three NUM num. _ 1 flat _ _
153
154CONLLU
155
156my $nsa_file = "$test_tempdir/test_no_spaceafter.conllu";
157{
158 open(my $nfh, '>:encoding(UTF-8)', $nsa_file)
159 or die "Cannot write test file: $!";
160 print $nfh $no_spaceafter_data;
161 close($nfh);
162}
163
164my $zipcontent_nsa = '';
165script_runs(
166 [ 'script/conllu2korapxml', '-f', 'ud', $nsa_file ],
167 { stdout => \$zipcontent_nsa },
168 "conllu2korapxml handles tokens with no SpaceAfter"
169);
170
171my $zipfile_nsa = "$test_tempdir/test_no_spaceafter.zip";
172if ($zipcontent_nsa) {
173 open(my $zfh, '>:raw', $zipfile_nsa) or die "Cannot write zip: $!";
174 print $zfh $zipcontent_nsa;
175 close($zfh);
176
177 my $morpho_nsa = `$UNZIP -p $zipfile_nsa 'TEST/TEST/TEST_NSA_001/ud/morpho.xml' 2>/dev/null`;
178
179 # "One two three" = 13 chars
180 # One: 0-3, two: 4-7, three: 8-13
181 like($morpho_nsa,
182 qr/id="s1_n1" from="0" to="3"/,
183 "No SpaceAfter: token 'One' at 0..3");
184 like($morpho_nsa,
185 qr/id="s1_n2" from="4" to="7"/,
186 "No SpaceAfter: token 'two' at 4..7");
187 like($morpho_nsa,
188 qr/id="s1_n3" from="8" to="13"/,
189 "No SpaceAfter: token 'three' at 8..13");
190}
191else {
192 fail("No SpaceAfter: token 'One' at 0..3");
193 fail("No SpaceAfter: token 'two' at 4..7");
194 fail("No SpaceAfter: token 'three' at 8..13");
195}
196
197# Adjacent tokens - no space between consecutive tokens (SpaceAfter=No)
198my $adjacent_data = <<'CONLLU';
199# filename = TEST/TEST/TEST_ADJ_001/base/tokens.xml
200# text_id = TEST_TEST.TEST_ADJ_001
201# text = foo(bar)baz end
2021 foo foo NOUN n. _ 0 root _ SpaceAfter=No
2032 ( ( PUNCT skyr. _ 3 punct _ SpaceAfter=No
2043 bar bar NOUN n. _ 1 appos _ SpaceAfter=No
2054 ) ) PUNCT skyr. _ 3 punct _ SpaceAfter=No
2065 baz baz NOUN n. _ 1 conj _ _
2076 end end NOUN n. _ 1 conj _ _
208
209CONLLU
210
211my $adj_file = "$test_tempdir/test_adjacent.conllu";
212{
213 open(my $afh, '>:encoding(UTF-8)', $adj_file)
214 or die "Cannot write test file: $!";
215 print $afh $adjacent_data;
216 close($afh);
217}
218
219my $zipcontent_adj = '';
220script_runs(
221 [ 'script/conllu2korapxml', '-f', 'ud', $adj_file ],
222 { stdout => \$zipcontent_adj },
223 "conllu2korapxml handles adjacent tokens without spaces"
224);
225
226my $zipfile_adj = "$test_tempdir/test_adjacent.zip";
227if ($zipcontent_adj) {
228 open(my $zfh, '>:raw', $zipfile_adj) or die "Cannot write zip: $!";
229 print $zfh $zipcontent_adj;
230 close($zfh);
231
232 my $morpho_adj = `$UNZIP -p $zipfile_adj 'TEST/TEST/TEST_ADJ_001/ud/morpho.xml' 2>/dev/null`;
233
234 # "foo(bar)baz end" = 15 chars
235 # foo: 0-3, (: 3-4, bar: 4-7, ): 7-8, baz: 8-11, end: 12-15
236 like($morpho_adj,
237 qr/id="s1_n1" from="0" to="3"/,
238 "Adjacent: token 'foo' at 0..3");
239 like($morpho_adj,
240 qr/id="s1_n2" from="3" to="4"/,
241 "Adjacent: token '(' at 3..4 (no space before)");
242 like($morpho_adj,
243 qr/id="s1_n3" from="4" to="7"/,
244 "Adjacent: token 'bar' at 4..7 (no space before)");
245 like($morpho_adj,
246 qr/id="s1_n4" from="7" to="8"/,
247 "Adjacent: token ')' at 7..8 (no space before)");
248 like($morpho_adj,
249 qr/id="s1_n5" from="8" to="11"/,
250 "Adjacent: token 'baz' at 8..11 (no space before)");
251 like($morpho_adj,
252 qr/id="s1_n6" from="12" to="15"/,
253 "Adjacent: token 'end' at 12..15 (space before)");
254}
255else {
256 fail("Adjacent: token 'foo' at 0..3");
257 fail("Adjacent: token '(' at 3..4 (no space before)");
258 fail("Adjacent: token 'bar' at 4..7 (no space before)");
259 fail("Adjacent: token ')' at 7..8 (no space before)");
260 fail("Adjacent: token 'baz' at 8..11 (no space before)");
261 fail("Adjacent: token 'end' at 12..15 (space before)");
262}
263
264# Repeated token form - same word appears multiple times in sentence
265my $repeat_data = <<'CONLLU';
266# filename = TEST/TEST/TEST_REP_001/base/tokens.xml
267# text_id = TEST_TEST.TEST_REP_001
268# text = the cat and the dog
2691 the the DET det. _ 2 det _ _
2702 cat cat NOUN n. _ 0 root _ _
2713 and and CCONJ cc. _ 5 cc _ _
2724 the the DET det. _ 5 det _ _
2735 dog dog NOUN n. _ 2 conj _ _
274
275CONLLU
276
277my $rep_file = "$test_tempdir/test_repeat.conllu";
278{
279 open(my $rfh, '>:encoding(UTF-8)', $rep_file)
280 or die "Cannot write test file: $!";
281 print $rfh $repeat_data;
282 close($rfh);
283}
284
285my $zipcontent_rep = '';
286script_runs(
287 [ 'script/conllu2korapxml', '-f', 'ud', $rep_file ],
288 { stdout => \$zipcontent_rep },
289 "conllu2korapxml handles repeated token forms"
290);
291
292my $zipfile_rep = "$test_tempdir/test_repeat.zip";
293if ($zipcontent_rep) {
294 open(my $zfh, '>:raw', $zipfile_rep) or die "Cannot write zip: $!";
295 print $zfh $zipcontent_rep;
296 close($zfh);
297
298 my $morpho_rep = `$UNZIP -p $zipfile_rep 'TEST/TEST/TEST_REP_001/ud/morpho.xml' 2>/dev/null`;
299
300 # "the cat and the dog" = 19 chars
301 # the: 0-3, cat: 4-7, and: 8-11, the: 12-15, dog: 16-19
302 like($morpho_rep,
303 qr/id="s1_n1" from="0" to="3"/,
304 "Repeated: first 'the' at 0..3");
305 like($morpho_rep,
306 qr/id="s1_n2" from="4" to="7"/,
307 "Repeated: 'cat' at 4..7");
308 like($morpho_rep,
309 qr/id="s1_n4" from="12" to="15"/,
310 "Repeated: second 'the' at 12..15 (not matching first)");
311 like($morpho_rep,
312 qr/id="s1_n5" from="16" to="19"/,
313 "Repeated: 'dog' at 16..19");
314}
315else {
316 fail("Repeated: first 'the' at 0..3");
317 fail("Repeated: 'cat' at 4..7");
318 fail("Repeated: second 'the' at 12..15 (not matching first)");
319 fail("Repeated: 'dog' at 16..19");
320}
321
322# Single-token sentence - minimal sentence with only one word
323my $single_data = <<'CONLLU';
324# filename = TEST/TEST/TEST_SNG_001/base/tokens.xml
325# text_id = TEST_TEST.TEST_SNG_001
326# text = Hello
3271 Hello hello INTJ intj. _ 0 root _ _
328
329# text = World
3301 World world NOUN n. _ 0 root _ _
331
332CONLLU
333
334my $sng_file = "$test_tempdir/test_single.conllu";
335{
336 open(my $sfh, '>:encoding(UTF-8)', $sng_file)
337 or die "Cannot write test file: $!";
338 print $sfh $single_data;
339 close($sfh);
340}
341
342my $zipcontent_sng = '';
343script_runs(
344 [ 'script/conllu2korapxml', '-f', 'ud', $sng_file ],
345 { stdout => \$zipcontent_sng },
346 "conllu2korapxml handles single-token sentences"
347);
348
349my $zipfile_sng = "$test_tempdir/test_single.zip";
350if ($zipcontent_sng) {
351 open(my $zfh, '>:raw', $zipfile_sng) or die "Cannot write zip: $!";
352 print $zfh $zipcontent_sng;
353 close($zfh);
354
355 my $morpho_sng = `$UNZIP -p $zipfile_sng 'TEST/TEST/TEST_SNG_001/ud/morpho.xml' 2>/dev/null`;
356
357 # Sentence 1: "Hello" (5 chars, starts at 0)
358 # Sentence 2: "World" (5 chars, starts at 6 = 5 + 1 space)
359 like($morpho_sng,
360 qr/id="s1_n1" from="0" to="5"/,
361 "Single-token: 'Hello' at 0..5");
362 like($morpho_sng,
363 qr/id="s2_n1" from="6" to="11"/,
364 "Single-token: 'World' at 6..11 (after space separator)");
365}
366else {
367 fail("Single-token: 'Hello' at 0..5");
368 fail("Single-token: 'World' at 6..11 (after space separator)");
369}
370
371# Explicit offsets take precedence over # text when both are present
372my $explicit_wins_data = <<'CONLLU';
373# filename = TEST/TEST/TEST_EXP_001/base/tokens.xml
374# text_id = TEST_TEST.TEST_EXP_001
375# text = Geras rytas.
376# start_offsets = 0 100 200 300
377# end_offsets = 999 199 299 399
3781 Geras geras ADJ bdv. Case=Nom 2 amod _ _
3792 rytas rytas NOUN dkt. Case=Nom 0 root _ _
3803 . . PUNCT skyr. _ 2 punct _ _
381
382CONLLU
383
384my $exp_file = "$test_tempdir/test_explicit.conllu";
385{
386 open(my $efh, '>:encoding(UTF-8)', $exp_file)
387 or die "Cannot write test file: $!";
388 print $efh $explicit_wins_data;
389 close($efh);
390}
391
392my $zipcontent_exp = '';
393script_runs(
394 [ 'script/conllu2korapxml', '-f', 'ud', $exp_file ],
395 { stdout => \$zipcontent_exp },
396 "conllu2korapxml uses explicit offsets when both # text and offsets present"
397);
398
399my $zipfile_exp = "$test_tempdir/test_explicit.zip";
400if ($zipcontent_exp) {
401 open(my $zfh, '>:raw', $zipfile_exp) or die "Cannot write zip: $!";
402 print $zfh $zipcontent_exp;
403 close($zfh);
404
405 my $morpho_exp = `$UNZIP -p $zipfile_exp 'TEST/TEST/TEST_EXP_001/ud/morpho.xml' 2>/dev/null`;
406 like($morpho_exp,
407 qr/id="s1_n1" from="100" to="199"/,
408 "Explicit offsets win: token uses from=100 (not computed 0)");
409}
410else {
411 fail("Explicit offsets win: token uses from=100 (not computed 0)");
412}
413
Akron8dabdc12026-06-02 16:36:07 +0200414# -------------------------------------------------------------------
415# Inline test data: minimal UD CoNLL-U with explicit offsets.
416# Uses "# newdoc id" (UD style) instead of "# filename" / "# text_id"
417# (KorAP style). Explicit offsets are provided so this test does not
418# depend on automatic offset computation.
419# -------------------------------------------------------------------
420
421my $ud_conllu_data = <<'CONLLU';
422# newdoc id = TEST_LIT_001
423# start_offsets = 0 0 6 11
424# end_offsets = 12 5 11 12
4251 Geras geras ADJ bdv. Case=Nom 2 amod _ _
4262 rytas rytas NOUN dkt. Case=Nom 0 root _ _
4273 . . PUNCT skyr. _ 2 punct _ _
428
429CONLLU
430
431my $conllu_file = "$test_tempdir/test_ud.conllu";
432{
433 open(my $fh, '>:encoding(UTF-8)', $conllu_file)
434 or die "Cannot write test file: $!";
435 print $fh $ud_conllu_data;
436 close($fh);
437}
438
439my $zipcontent = '';
440script_runs(
441 [ 'script/conllu2korapxml', '-f', 'ud',
442 '--text-sigle', 'TEST/TEST/{ID}', $conllu_file ],
443 { stdout => \$zipcontent },
444 "conllu2korapxml accepts --text-sigle with UD CoNLL-U input"
445);
446
447my $zipfile = "$test_tempdir/test_newdoc.zip";
448if ($zipcontent) {
449 open(my $zfh, '>:raw', $zipfile) or die "Cannot write zip: $!";
450 print $zfh $zipcontent;
451 close($zfh);
452
453 my $ziplist = `$UNZIP -l $zipfile 2>/dev/null`;
454 like($ziplist,
455 qr@TEST/TEST/TEST_LIT_001/ud/morpho\.xml@,
456 "Zip contains morpho.xml at path derived from newdoc id");
457 like($ziplist,
458 qr@TEST/TEST/TEST_LIT_001/ud/dependency\.xml@,
459 "Zip contains dependency.xml at path derived from newdoc id");
460
461 my $zipdata = `$UNZIP -c $zipfile 2>/dev/null`;
462 like($zipdata,
463 qr/docid="TEST_TEST\.TEST_LIT_001"/,
464 "docid correctly derived from text-sigle template and newdoc id");
465}
466else {
467 fail("Zip contains morpho.xml at path derived from newdoc id");
468 fail("Zip contains dependency.xml at path derived from newdoc id");
469 fail("docid correctly derived from text-sigle template and newdoc id");
470}
471
472my $zipcontent_lc = '';
473script_runs(
474 [ 'script/conllu2korapxml', '-f', 'ud',
475 '--text-sigle', 'TEST/TEST/{id}', $conllu_file ],
476 { stdout => \$zipcontent_lc },
477 "text-sigle template accepts {id} (lowercase)"
478);
479
480if ($zipcontent_lc) {
481 my $zipfile_lc = "$test_tempdir/test_lc.zip";
482 open(my $zfh, '>:raw', $zipfile_lc) or die "Cannot write zip: $!";
483 print $zfh $zipcontent_lc;
484 close($zfh);
485
486 my $zipdata_lc = `$UNZIP -c $zipfile_lc 2>/dev/null`;
487 like($zipdata_lc,
488 qr/docid="TEST_TEST\.TEST_LIT_001"/,
489 "docid correct with lowercase {id} template");
490}
491else {
492 fail("docid correct with lowercase {id} template");
493}
494
495my $zipcontent_mc = '';
496script_runs(
497 [ 'script/conllu2korapxml', '-f', 'ud',
498 '--text-sigle', 'TEST/TEST/{Id}', $conllu_file ],
499 { stdout => \$zipcontent_mc },
500 "text-sigle template accepts {Id} (mixed case)"
501);
502
503if ($zipcontent_mc) {
504 my $zipfile_mc = "$test_tempdir/test_mc.zip";
505 open(my $zfh, '>:raw', $zipfile_mc) or die "Cannot write zip: $!";
506 print $zfh $zipcontent_mc;
507 close($zfh);
508
509 my $zipdata_mc = `$UNZIP -c $zipfile_mc 2>/dev/null`;
510 like($zipdata_mc,
511 qr/docid="TEST_TEST\.TEST_LIT_001"/,
512 "docid correct with mixed-case {Id} template");
513}
514else {
515 fail("docid correct with mixed-case {Id} template");
516}
517
518# Template with only 2 parts (missing corpus level)
519my $err_2parts = `$^X script/conllu2korapxml -f ud --text-sigle 'TEST/{ID}' $conllu_file 2>&1`;
520isnt($? >> 8, 0, "Rejects template with only 2 parts (non-zero exit)");
521like($err_2parts, qr/ERROR/, "Error message for 2-part template");
522
523# Template with 4 parts (too many levels)
524my $err_4parts = `$^X script/conllu2korapxml -f ud --text-sigle 'A/B/C/{ID}' $conllu_file 2>&1`;
525isnt($? >> 8, 0, "Rejects template with 4 parts (non-zero exit)");
526like($err_4parts, qr/ERROR/, "Error message for 4-part template");
527
528# Template with empty middle part
529my $err_empty = `$^X script/conllu2korapxml -f ud --text-sigle 'TEST//{ID}' $conllu_file 2>&1`;
530isnt($? >> 8, 0, "Rejects template with empty part (non-zero exit)");
531like($err_empty, qr/ERROR/, "Error message for empty-part template");
532
533# Template with only 1 part (no slashes)
534my $err_1part = `$^X script/conllu2korapxml -f ud --text-sigle '{ID}' $conllu_file 2>&1`;
535isnt($? >> 8, 0, "Rejects template with only 1 part (non-zero exit)");
536like($err_1part, qr/ERROR/, "Error message for 1-part template");
537
538my $bad_id_data = <<'CONLLU';
539# newdoc id = BAD/SLASH_ID
540# start_offsets = 0 0
541# end_offsets = 4 4
5421 Test test NOUN NN _ 0 root _ _
543
544CONLLU
545
546my $bad_id_file = "$test_tempdir/bad_id.conllu";
547{
548 open(my $bfh, '>:encoding(UTF-8)', $bad_id_file)
549 or die "Cannot write test file: $!";
550 print $bfh $bad_id_data;
551 close($bfh);
552}
553
554my $err_slash = `$^X script/conllu2korapxml -f ud --text-sigle 'A/B/{ID}' $bad_id_file 2>&1`;
555isnt($? >> 8, 0,
556 "Rejects newdoc id with slash (expanded sigle has wrong part count)");
557like($err_slash, qr/ERROR/,
558 "Error message for newdoc id containing slash");
Akron982dd1e2026-06-03 11:52:48 +0200559
560my $base_text_data = <<'CONLLU';
561# filename = TEST/TEST/TEST_BTX_001/base/tokens.xml
562# text_id = TEST_TEST.TEST_BTX_001
563# text = Geras rytas.
564# start_offsets = 0 0 6 11
565# end_offsets = 12 5 11 12
5661 Geras geras ADJ bdv. Case=Nom 2 amod _ _
5672 rytas rytas NOUN dkt. Case=Nom 0 root _ _
5683 . . PUNCT skyr. _ 2 punct _ SpaceAfter=No
569
570# text = Kaip sekasi?
571# start_offsets = 12 13 18 24
572# end_offsets = 25 17 24 25
5731 Kaip kaip ADV prv. _ 2 advmod _ _
5742 sekasi sektis VERB vksm. _ 0 root _ SpaceAfter=No
5753 ? ? PUNCT skyr. _ 2 punct _ _
576
577CONLLU
578
579my $btx_file = "$test_tempdir/test_base_text.conllu";
580{
581 open(my $bfh, '>:encoding(UTF-8)', $btx_file)
582 or die "Cannot write test file: $!";
583 print $bfh $base_text_data;
584 close($bfh);
585}
586
587# Run with --base-text to generate data.xml
588my $zipcontent_btx = '';
589script_runs(
590 [ 'script/conllu2korapxml', '-f', 'ud', '--base-text', $btx_file ],
591 { stdout => \$zipcontent_btx },
592 "conllu2korapxml accepts --base-text option"
593);
594
595my $zipfile_btx = "$test_tempdir/test_base_text.zip";
596if ($zipcontent_btx) {
597 open(my $zfh, '>:raw', $zipfile_btx) or die "Cannot write zip: $!";
598 print $zfh $zipcontent_btx;
599 close($zfh);
600
601 my $ziplist = `$UNZIP -l $zipfile_btx 2>/dev/null`;
602 like($ziplist,
603 qr@TEST/TEST/TEST_BTX_001/data\.xml@,
604 "Zip contains data.xml at correct path");
605
606 my $data_xml = `$UNZIP -p $zipfile_btx 'TEST/TEST/TEST_BTX_001/data.xml' 2>/dev/null`;
607
608 like($data_xml,
609 qr/docid="TEST_TEST\.TEST_BTX_001"/,
610 "data.xml has correct docid attribute");
611
612 like($data_xml,
613 qr/<raw_text\b/,
614 "data.xml contains <raw_text> element");
615
616 like($data_xml,
617 qr{<text>Geras rytas\. Kaip sekasi\?</text>},
618 "data.xml text is sentences joined by single space");
619
620 like($data_xml,
621 qr/xmlns="http:\/\/ids-mannheim\.de\/ns\/KorAP"/,
622 "data.xml has correct namespace");
623
624 like($data_xml,
625 qr/<\?xml-model href="text\.rng"/,
626 "data.xml has correct processing instruction");
627}
628else {
629 fail("Zip contains data.xml at correct path");
630 fail("data.xml has correct docid attribute");
631 fail("data.xml contains <raw_text> element");
632 fail("data.xml text is sentences joined by single space");
633 fail("data.xml has correct namespace");
634 fail("data.xml has correct processing instruction");
635}
636
637# Run WITHOUT --base-text to verify data.xml is NOT generated
638my $zipcontent_no_btx = '';
639script_runs(
640 [ 'script/conllu2korapxml', '-f', 'ud', $btx_file ],
641 { stdout => \$zipcontent_no_btx },
642 "conllu2korapxml runs without --base-text"
643);
644
645my $zipfile_no_btx = "$test_tempdir/test_no_base_text.zip";
646if ($zipcontent_no_btx) {
647 open(my $zfh, '>:raw', $zipfile_no_btx) or die "Cannot write zip: $!";
648 print $zfh $zipcontent_no_btx;
649 close($zfh);
650
651 my $ziplist_no = `$UNZIP -l $zipfile_no_btx 2>/dev/null`;
652 unlike($ziplist_no,
653 qr/data\.xml/,
654 "Zip does NOT contain data.xml when --base-text is omitted");
655}
656else {
657 fail("Zip does NOT contain data.xml when --base-text is omitted");
658}
659
660# Test XML escaping: text containing &, <, > characters
661my $escape_data = <<'CONLLU';
662# filename = TEST/TEST/TEST_ESC_001/base/tokens.xml
663# text_id = TEST_TEST.TEST_ESC_001
664# text = A & B < C > D
665# start_offsets = 0 0 2 4 6 8 10 12
666# end_offsets = 13 1 3 5 7 9 11 13
6671 A a NOUN n. _ 0 root _ _
6682 & & PUNCT p. _ 1 punct _ _
6693 B b NOUN n. _ 1 conj _ _
6704 < < PUNCT p. _ 3 punct _ _
6715 C c NOUN n. _ 1 conj _ _
6726 > > PUNCT p. _ 5 punct _ _
6737 D d NOUN n. _ 1 conj _ _
674
675CONLLU
676
677my $esc_file = "$test_tempdir/test_escape.conllu";
678{
679 open(my $efh, '>:encoding(UTF-8)', $esc_file)
680 or die "Cannot write test file: $!";
681 print $efh $escape_data;
682 close($efh);
683}
684
685my $zipcontent_esc = '';
686script_runs(
687 [ 'script/conllu2korapxml', '-f', 'ud', '--base-text', $esc_file ],
688 { stdout => \$zipcontent_esc },
689 "conllu2korapxml handles XML special chars in text"
690);
691
692my $zipfile_esc = "$test_tempdir/test_escape.zip";
693if ($zipcontent_esc) {
694 open(my $zfh, '>:raw', $zipfile_esc) or die "Cannot write zip: $!";
695 print $zfh $zipcontent_esc;
696 close($zfh);
697
698 my $data_esc = `$UNZIP -p $zipfile_esc 'TEST/TEST/TEST_ESC_001/data.xml' 2>/dev/null`;
699 like($data_esc,
700 qr{<text>A &amp; B &lt; C &gt; D</text>},
701 "data.xml escapes &, <, > in full text string");
702}
703else {
704 fail("data.xml escapes &, <, > in full text string");
705}
706
707# Test single-sentence document (no joining needed)
708my $single_sent_data = <<'CONLLU';
709# filename = TEST/TEST/TEST_SST_001/base/tokens.xml
710# text_id = TEST_TEST.TEST_SST_001
711# text = Hello world
712# start_offsets = 0 0 6
713# end_offsets = 11 5 11
7141 Hello hello INTJ intj. _ 0 root _ _
7152 world world NOUN n. _ 1 flat _ _
716
717CONLLU
718
719my $sst_file = "$test_tempdir/test_single_sent.conllu";
720{
721 open(my $sfh, '>:encoding(UTF-8)', $sst_file)
722 or die "Cannot write test file: $!";
723 print $sfh $single_sent_data;
724 close($sfh);
725}
726
727my $zipcontent_sst = '';
728script_runs(
729 [ 'script/conllu2korapxml', '-f', 'ud', '--base-text', $sst_file ],
730 { stdout => \$zipcontent_sst },
731 "conllu2korapxml generates data.xml for single-sentence document"
732);
733
734my $zipfile_sst = "$test_tempdir/test_single_sent.zip";
735if ($zipcontent_sst) {
736 open(my $zfh, '>:raw', $zipfile_sst) or die "Cannot write zip: $!";
737 print $zfh $zipcontent_sst;
738 close($zfh);
739
740 my $data_sst = `$UNZIP -p $zipfile_sst 'TEST/TEST/TEST_SST_001/data.xml' 2>/dev/null`;
741 like($data_sst,
742 qr{<text>Hello world</text>},
743 "data.xml single sentence: text matches exactly");
744}
745else {
746 fail("data.xml single sentence: text matches exactly");
747}
748
Akroncdabed82026-06-03 15:35:39 +0200749# Test: --base-tokens produces base/tokens.xml
750my $zipcontent_tok = '';
751script_runs(
752 [ 'script/conllu2korapxml', '-f', 'ud', '--base-tokens', $btx_file ],
753 { stdout => \$zipcontent_tok },
754 "conllu2korapxml runs with --base-tokens"
755);
756
757my $zipfile_tok = "$test_tempdir/test_tokens.zip";
758if ($zipcontent_tok) {
759 open(my $zfh, '>:raw', $zipfile_tok) or die "Cannot write zip: $!";
760 print $zfh $zipcontent_tok;
761 close($zfh);
762
763 my $ziplist = `$UNZIP -l $zipfile_tok 2>/dev/null`;
764 like($ziplist,
765 qr@TEST/TEST/TEST_BTX_001/base/tokens\.xml@,
766 "Zip contains base/tokens.xml at correct path");
767
768 my $tokens_xml = `$UNZIP -p $zipfile_tok 'TEST/TEST/TEST_BTX_001/base/tokens.xml' 2>/dev/null`;
769
770 like($tokens_xml,
771 qr/docid="TEST_TEST\.TEST_BTX_001"/,
772 "tokens.xml has correct docid attribute");
773
774 like($tokens_xml,
775 qr/xmlns="http:\/\/ids-mannheim\.de\/ns\/KorAP"/,
776 "tokens.xml has correct namespace");
777
778 like($tokens_xml,
779 qr/<\?xml-model href="span\.rng"/,
780 "tokens.xml has correct processing instruction");
781
782 like($tokens_xml,
783 qr/version="KorAP-0\.4"/,
784 "tokens.xml has correct version");
785
786 # Token spans with sequential IDs and correct offsets
787 # Sentence 1: Geras(0..5) rytas(6..11) .(11..12)
788 like($tokens_xml,
789 qr/<span id="t_0" from="0" to="5"\/>/,
790 "Token t_0: Geras from=0 to=5");
791
792 like($tokens_xml,
793 qr/<span id="t_1" from="6" to="11"\/>/,
794 "Token t_1: rytas from=6 to=11");
795
796 like($tokens_xml,
797 qr/<span id="t_2" from="11" to="12"\/>/,
798 "Token t_2: period from=11 to=12");
799
800 # Sentence 2: Kaip(13..17) sekasi(18..24) ?(24..25)
801 like($tokens_xml,
802 qr/<span id="t_3" from="13" to="17"\/>/,
803 "Token t_3: Kaip from=13 to=17");
804
805 like($tokens_xml,
806 qr/<span id="t_4" from="18" to="24"\/>/,
807 "Token t_4: sekasi from=18 to=24");
808
809 like($tokens_xml,
810 qr/<span id="t_5" from="24" to="25"\/>/,
811 "Token t_5: question mark from=24 to=25");
812
813 # Self-closing span elements (no child fs element)
814 unlike($tokens_xml,
815 qr/<span id="t_0"[^\/]*>.*?<\/span>/s,
816 "Token spans are self-closing (no child elements)");
817}
818else {
819 fail("Zip contains base/tokens.xml at correct path");
820 fail("tokens.xml has correct docid attribute");
821 fail("tokens.xml has correct namespace");
822 fail("tokens.xml has correct processing instruction");
823 fail("tokens.xml has correct version");
824 fail("Token t_0: Geras from=0 to=5");
825 fail("Token t_1: rytas from=6 to=11");
826 fail("Token t_2: period from=11 to=12");
827 fail("Token t_3: Kaip from=13 to=17");
828 fail("Token t_4: sekasi from=18 to=24");
829 fail("Token t_5: question mark from=24 to=25");
830 fail("Token spans are self-closing (no child elements)");
831}
832
833# Test: without --base-tokens, no base/tokens.xml is produced
834my $zipcontent_notok = '';
835script_runs(
836 [ 'script/conllu2korapxml', '-f', 'ud', $btx_file ],
837 { stdout => \$zipcontent_notok },
838 "conllu2korapxml runs without --base-tokens"
839);
840
841my $zipfile_notok = "$test_tempdir/test_notokens.zip";
842if ($zipcontent_notok) {
843 open(my $zfh, '>:raw', $zipfile_notok) or die "Cannot write zip: $!";
844 print $zfh $zipcontent_notok;
845 close($zfh);
846
847 my $ziplist_notok = `$UNZIP -l $zipfile_notok 2>/dev/null`;
848 unlike($ziplist_notok,
849 qr/base\/tokens\.xml/,
850 "Zip does NOT contain base/tokens.xml when --base-tokens omitted");
851}
852else {
853 fail("Zip does NOT contain base/tokens.xml when --base-tokens omitted");
854}
855
Akrondb3d0cb2026-06-03 15:53:57 +0200856my $misc_file = "$test_tempdir/test_misc.conllu";
857{
858 open(my $mfh, '>:encoding(UTF-8)', $misc_file)
859 or die "Cannot write test file: $!";
860 print $mfh $misc_data;
861 close($mfh);
862}
863
864my $zipcontent_misc = '';
865script_runs(
866 [ 'script/conllu2korapxml', '-f', 'ud', $misc_file ],
867 { stdout => \$zipcontent_misc },
868 "conllu2korapxml runs with MISC column data"
869);
870
871my $zipfile_misc = "$test_tempdir/test_misc.zip";
872if ($zipcontent_misc) {
873 open(my $zfh, '>:raw', $zipfile_misc) or die "Cannot write zip: $!";
874 print $zfh $zipcontent_misc;
875 close($zfh);
876
877 my $morpho_xml = `$UNZIP -p $zipfile_misc 'TEST/TEST/TEST_MISC_001/ud/morpho.xml' 2>/dev/null`;
878
879 # SpaceAfter=No|Tag=NNN -> only Tag=NNN kept
880 like($morpho_xml,
881 qr/<f name="misc">Tag=NNN<\/f>/,
882 "MISC: SpaceAfter=No filtered, Tag=NNN kept");
883
884 unlike($morpho_xml,
885 qr/<f name="misc">SpaceAfter=No\|Tag=NNN<\/f>/,
886 "MISC: raw SpaceAfter=No|Tag not stored verbatim");
887
888 # Pure Tag=LLL (no SpaceAfter) -> preserved unchanged
889 like($morpho_xml,
890 qr/<f name="misc">Tag=LLL<\/f>/,
891 "MISC: pure non-SpaceAfter value preserved unchanged");
892
893 # SpaceAfter=No alone -> no misc element at all
894 my ($s1_n3_block) = $morpho_xml =~ /(id="s1_n3".*?<\/span>)/s;
895 ok(defined $s1_n3_block, "Found span block for s1_n3");
896 unlike($s1_n3_block // '',
897 qr/<f name="misc">/,
898 "MISC: SpaceAfter=No alone produces no misc element");
899 unlike($s1_n3_block // '',
900 qr/<f name="certainty">/,
901 "MISC: SpaceAfter=No alone produces no certainty element");
902
903 # SpacesAfter=\n|Tag=MMM -> only Tag=MMM kept
904 like($morpho_xml,
905 qr/<f name="misc">Tag=MMM<\/f>/,
906 "MISC: SpacesAfter filtered, Tag=MMM kept");
907
908 unlike($morpho_xml,
909 qr/SpacesAfter/,
910 "MISC: no SpacesAfter value appears in output");
911
912 unlike($morpho_xml,
913 qr/SpaceAfter/,
914 "MISC: no SpaceAfter value appears in output at all");
915}
916else {
917 fail("MISC: SpaceAfter=No filtered, Tag=NNN kept");
918 fail("MISC: raw SpaceAfter=No|Tag not stored verbatim");
919 fail("MISC: pure non-SpaceAfter value preserved unchanged");
920 fail("Found span block for s1_n3");
921 fail("MISC: SpaceAfter=No alone produces no misc element");
922 fail("MISC: SpaceAfter=No alone produces no certainty element");
923 fail("MISC: SpacesAfter filtered, Tag=MMM kept");
924 fail("MISC: no SpacesAfter value appears in output");
925 fail("MISC: no SpaceAfter value appears in output at all");
926}
927
Akron4f542622026-06-03 14:20:20 +0200928my $struct_file = "$test_tempdir/test_struct.conllu";
929{
930 open(my $sfh, '>:encoding(UTF-8)', $struct_file)
931 or die "Cannot write test file: $!";
932 print $sfh $struct_data;
933 close($sfh);
934}
935
936my $zipcontent_str = '';
937script_runs(
938 [ 'script/conllu2korapxml', '-f', 'ud', $struct_file ],
939 { stdout => \$zipcontent_str },
940 "conllu2korapxml generates struct.xml when sent_id present"
941);
942
943my $zipfile_str = "$test_tempdir/test_struct.zip";
944if ($zipcontent_str) {
945 open(my $zfh, '>:raw', $zipfile_str) or die "Cannot write zip: $!";
946 print $zfh $zipcontent_str;
947 close($zfh);
948
949 my $ziplist = `$UNZIP -l $zipfile_str 2>/dev/null`;
950 like($ziplist,
951 qr@TEST/TEST/TEST_STR_001/base/struct\.xml@,
952 "Zip contains struct.xml at correct path");
953
954 my $struct_xml = `$UNZIP -p $zipfile_str 'TEST/TEST/TEST_STR_001/base/struct.xml' 2>/dev/null`;
955
956 like($struct_xml,
957 qr/docid="TEST_TEST\.TEST_STR_001"/,
958 "struct.xml has correct docid attribute");
959
960 like($struct_xml,
961 qr/xmlns="http:\/\/ids-mannheim\.de\/ns\/KorAP"/,
962 "struct.xml has correct namespace");
963
964 like($struct_xml,
965 qr/<\?xml-model href="span\.rng"/,
966 "struct.xml has correct processing instruction");
967
968 # Sentence spans: s1 (0..12), s2 (12..25), s3 (25..39)
969 like($struct_xml,
970 qr/id="s1" from="0" to="12".*?<f name="name">s<\/f>/s,
971 "Sentence span s1: from=0 to=12");
972
973 like($struct_xml,
974 qr/id="s2" from="12" to="25".*?<f name="name">s<\/f>/s,
975 "Sentence span s2: from=12 to=25");
976
977 like($struct_xml,
978 qr/id="s3" from="25" to="39".*?<f name="name">s<\/f>/s,
979 "Sentence span s3: from=25 to=39");
980
981 # Paragraph spans: p1 (0..25), p2 (25..39)
982 like($struct_xml,
983 qr/id="p1" from="0" to="25".*?<f name="name">p<\/f>/s,
984 "Paragraph span p1: from=0 to=25");
985
986 like($struct_xml,
987 qr/id="p2" from="25" to="39".*?<f name="name">p<\/f>/s,
988 "Paragraph span p2: from=25 to=39");
989
990 # All struct spans use the TEI namespace
991 like($struct_xml,
992 qr/<fs type="struct" xmlns="http:\/\/www\.tei-c\.org\/ns\/1\.0">/,
993 "Struct spans use TEI namespace");
994
995 # Spans are sorted by from ascending, then to ascending (mixed s and p)
996 my @span_order;
997 while ($struct_xml =~ /id="([sp]\d+)" from="(\d+)" to="(\d+)"/g) {
998 push @span_order, [$1, $2, $3];
999 }
1000 my $sorted_ok = 1;
1001 for my $j (1 .. $#span_order) {
1002 if ($span_order[$j]->[1] < $span_order[$j-1]->[1] ||
1003 ($span_order[$j]->[1] == $span_order[$j-1]->[1] &&
1004 $span_order[$j]->[2] < $span_order[$j-1]->[2])) {
1005 $sorted_ok = 0;
1006 last;
1007 }
1008 }
1009 ok($sorted_ok, "Struct spans are sorted by from then to");
1010}
1011else {
1012 fail("Zip contains struct.xml at correct path");
1013 fail("struct.xml has correct docid attribute");
1014 fail("struct.xml has correct namespace");
1015 fail("struct.xml has correct processing instruction");
1016 fail("Sentence span s1: from=0 to=12");
1017 fail("Sentence span s2: from=12 to=25");
1018 fail("Sentence span s3: from=25 to=39");
1019 fail("Paragraph span p1: from=0 to=25");
1020 fail("Paragraph span p2: from=25 to=39");
1021 fail("Struct spans use TEI namespace");
1022}
1023
1024my $sent_only_data = <<'CONLLU';
1025# filename = TEST/TEST/TEST_SNO_001/base/tokens.xml
1026# text_id = TEST_TEST.TEST_SNO_001
1027# sent_id = x1
1028# start_offsets = 0 0 6
1029# end_offsets = 11 5 11
10301 Hello hello INTJ intj. _ 0 root _ _
10312 world world NOUN n. _ 1 flat _ _
1032
1033# sent_id = x2
1034# start_offsets = 11 12 18
1035# end_offsets = 23 17 23
10361 Good good ADJ adj. _ 2 amod _ _
10372 night night NOUN n. _ 0 root _ _
1038
1039CONLLU
1040
1041my $sno_file = "$test_tempdir/test_sent_only.conllu";
1042{
1043 open(my $sfh, '>:encoding(UTF-8)', $sno_file)
1044 or die "Cannot write test file: $!";
1045 print $sfh $sent_only_data;
1046 close($sfh);
1047}
1048
1049my $zipcontent_sno = '';
1050script_runs(
1051 [ 'script/conllu2korapxml', '-f', 'ud', $sno_file ],
1052 { stdout => \$zipcontent_sno },
1053 "conllu2korapxml generates struct.xml with sent_id only (no newpar)"
1054);
1055
1056my $zipfile_sno = "$test_tempdir/test_sent_only.zip";
1057if ($zipcontent_sno) {
1058 open(my $zfh, '>:raw', $zipfile_sno) or die "Cannot write zip: $!";
1059 print $zfh $zipcontent_sno;
1060 close($zfh);
1061
1062 my $struct_sno = `$UNZIP -p $zipfile_sno 'TEST/TEST/TEST_SNO_001/base/struct.xml' 2>/dev/null`;
1063
1064 like($struct_sno,
1065 qr/id="s1" from="0" to="11".*?<f name="name">s<\/f>/s,
1066 "Sent-only: sentence span s1 from=0 to=11");
1067
1068 like($struct_sno,
1069 qr/id="s2" from="11" to="23".*?<f name="name">s<\/f>/s,
1070 "Sent-only: sentence span s2 from=11 to=23");
1071
1072 unlike($struct_sno,
1073 qr/>p<\/f>/,
1074 "Sent-only: no paragraph spans when newpar absent");
1075}
1076else {
1077 fail("Sent-only: sentence span s1 from=0 to=11");
1078 fail("Sent-only: sentence span s2 from=11 to=23");
1079 fail("Sent-only: no paragraph spans when newpar absent");
1080}
1081
1082my $no_struct_data = <<'CONLLU';
1083# filename = TEST/TEST/TEST_NOS_001/base/tokens.xml
1084# text_id = TEST_TEST.TEST_NOS_001
1085# start_offsets = 0 0 4
1086# end_offsets = 7 3 7
10871 foo foo NOUN n. _ 0 root _ _
10882 bar bar NOUN n. _ 1 flat _ _
1089
1090CONLLU
1091
1092my $nos_file = "$test_tempdir/test_no_struct.conllu";
1093{
1094 open(my $nfh, '>:encoding(UTF-8)', $nos_file)
1095 or die "Cannot write test file: $!";
1096 print $nfh $no_struct_data;
1097 close($nfh);
1098}
1099
1100my $zipcontent_nos = '';
1101script_runs(
1102 [ 'script/conllu2korapxml', '-f', 'ud', $nos_file ],
1103 { stdout => \$zipcontent_nos },
1104 "conllu2korapxml runs without sent_id (no struct.xml)"
1105);
1106
1107my $zipfile_nos = "$test_tempdir/test_no_struct.zip";
1108if ($zipcontent_nos) {
1109 open(my $zfh, '>:raw', $zipfile_nos) or die "Cannot write zip: $!";
1110 print $zfh $zipcontent_nos;
1111 close($zfh);
1112
1113 my $ziplist_nos = `$UNZIP -l $zipfile_nos 2>/dev/null`;
1114 unlike($ziplist_nos,
1115 qr/struct\.xml/,
1116 "Zip does NOT contain struct.xml when sent_id absent");
1117}
1118else {
1119 fail("Zip does NOT contain struct.xml when sent_id absent");
1120}
1121
1122my $bare_newpar_data = <<'CONLLU';
1123# filename = TEST/TEST/TEST_BNP_001/base/tokens.xml
1124# text_id = TEST_TEST.TEST_BNP_001
1125# newpar
1126# sent_id = a1
1127# start_offsets = 0 0 4
1128# end_offsets = 7 3 7
11291 one one NUM num. _ 0 root _ _
11302 two two NUM num. _ 1 flat _ _
1131
1132# newpar
1133# sent_id = a2
1134# start_offsets = 7 8 14
1135# end_offsets = 17 13 17
11361 three three NUM num. _ 0 root _ _
11372 four four NUM num. _ 1 flat _ _
1138
1139CONLLU
1140
1141my $bnp_file = "$test_tempdir/test_bare_newpar.conllu";
1142{
1143 open(my $bfh, '>:encoding(UTF-8)', $bnp_file)
1144 or die "Cannot write test file: $!";
1145 print $bfh $bare_newpar_data;
1146 close($bfh);
1147}
1148
1149my $zipcontent_bnp = '';
1150script_runs(
1151 [ 'script/conllu2korapxml', '-f', 'ud', $bnp_file ],
1152 { stdout => \$zipcontent_bnp },
1153 "conllu2korapxml handles bare newpar (without id)"
1154);
1155
1156my $zipfile_bnp = "$test_tempdir/test_bare_newpar.zip";
1157if ($zipcontent_bnp) {
1158 open(my $zfh, '>:raw', $zipfile_bnp) or die "Cannot write zip: $!";
1159 print $zfh $zipcontent_bnp;
1160 close($zfh);
1161
1162 my $struct_bnp = `$UNZIP -p $zipfile_bnp 'TEST/TEST/TEST_BNP_001/base/struct.xml' 2>/dev/null`;
1163
1164 like($struct_bnp,
1165 qr/id="p1" from="0" to="7".*?<f name="name">p<\/f>/s,
1166 "Bare newpar: paragraph p1 from=0 to=7");
1167
1168 like($struct_bnp,
1169 qr/id="p2" from="7" to="17".*?<f name="name">p<\/f>/s,
1170 "Bare newpar: paragraph p2 from=7 to=17");
1171}
1172else {
1173 fail("Bare newpar: paragraph p1 from=0 to=7");
1174 fail("Bare newpar: paragraph p2 from=7 to=17");
1175}
1176
Akronf3efc9e2026-06-03 10:46:37 +02001177done_testing;