blob: d058a7caf9dcb8eefb566a7770f9893b85366f6c [file] [log] [blame]
Akronb7e1f352025-05-16 15:45:23 +02001package matcher
2
3import (
4 "testing"
5
6 "github.com/KorAP/KoralPipe-TermMapper2/pkg/ast"
7 "github.com/stretchr/testify/assert"
8)
9
10func TestMatchSimplePattern(t *testing.T) {
11 // Create a simple pattern: match a term with DET
12 pattern := ast.Pattern{
13 Root: &ast.Term{
14 Foundry: "opennlp",
15 Key: "DET",
16 Layer: "p",
17 Match: ast.MatchEqual,
18 },
19 }
20
21 // Create a simple replacement
22 replacement := ast.Replacement{
23 Root: &ast.Term{
24 Foundry: "opennlp",
25 Key: "COMBINED_DET",
26 Layer: "p",
27 Match: ast.MatchEqual,
28 },
29 }
30
31 m := NewMatcher(pattern, replacement)
32
33 tests := []struct {
34 name string
35 input ast.Node
36 expected bool
37 }{
38 {
39 name: "Exact match",
40 input: &ast.Term{
41 Foundry: "opennlp",
42 Key: "DET",
43 Layer: "p",
44 Match: ast.MatchEqual,
45 },
46 expected: true,
47 },
48 {
49 name: "Different key",
50 input: &ast.Term{
51 Foundry: "opennlp",
52 Key: "NOUN",
53 Layer: "p",
54 Match: ast.MatchEqual,
55 },
56 expected: false,
57 },
58 {
59 name: "Different foundry",
60 input: &ast.Term{
61 Foundry: "different",
62 Key: "DET",
63 Layer: "p",
64 Match: ast.MatchEqual,
65 },
66 expected: false,
67 },
68 {
69 name: "Different match type",
70 input: &ast.Term{
71 Foundry: "opennlp",
72 Key: "DET",
73 Layer: "p",
74 Match: ast.MatchNotEqual,
75 },
76 expected: false,
77 },
78 {
79 name: "Wrong node type",
80 input: &ast.Token{
81 Wrap: &ast.Term{
82 Foundry: "opennlp",
83 Key: "DET",
84 Layer: "p",
85 Match: ast.MatchEqual,
86 },
87 },
88 expected: false,
89 },
90 }
91
92 for _, tt := range tests {
93 t.Run(tt.name, func(t *testing.T) {
94 result := m.Match(tt.input)
95 assert.Equal(t, tt.expected, result)
96 })
97 }
98}
99
100func TestMatchComplexPattern(t *testing.T) {
101 // Create a complex pattern: DET AND (AdjType=Pdt OR PronType=Ind)
102 pattern := ast.Pattern{
103 Root: &ast.Token{
104 Wrap: &ast.TermGroup{
105 Operands: []ast.Node{
106 &ast.Term{
107 Foundry: "opennlp",
108 Key: "DET",
109 Layer: "p",
110 Match: ast.MatchEqual,
111 },
112 &ast.TermGroup{
113 Operands: []ast.Node{
114 &ast.Term{
115 Foundry: "opennlp",
116 Key: "AdjType",
117 Layer: "m",
118 Match: ast.MatchEqual,
119 Value: "Pdt",
120 },
121 &ast.Term{
122 Foundry: "opennlp",
123 Key: "PronType",
124 Layer: "m",
125 Match: ast.MatchEqual,
126 Value: "Ind",
127 },
128 },
129 Relation: ast.OrRelation,
130 },
131 },
132 Relation: ast.AndRelation,
133 },
134 },
135 }
136
137 replacement := ast.Replacement{
138 Root: &ast.Token{
139 Wrap: &ast.Term{
140 Foundry: "opennlp",
141 Key: "COMBINED_DET",
142 Layer: "p",
143 Match: ast.MatchEqual,
144 },
145 },
146 }
147
148 m := NewMatcher(pattern, replacement)
149
150 tests := []struct {
151 name string
152 input ast.Node
153 expected bool
154 }{
155 {
156 name: "Match with AdjType=Pdt",
157 input: &ast.Token{
158 Wrap: &ast.TermGroup{
159 Operands: []ast.Node{
160 &ast.Term{
161 Foundry: "opennlp",
162 Key: "DET",
163 Layer: "p",
164 Match: ast.MatchEqual,
165 },
166 &ast.Term{
167 Foundry: "opennlp",
168 Key: "AdjType",
169 Layer: "m",
170 Match: ast.MatchEqual,
171 Value: "Pdt",
172 },
173 },
174 Relation: ast.AndRelation,
175 },
176 },
177 expected: true,
178 },
179 {
180 name: "Match with PronType=Ind",
181 input: &ast.Token{
182 Wrap: &ast.TermGroup{
183 Operands: []ast.Node{
184 &ast.Term{
185 Foundry: "opennlp",
186 Key: "DET",
187 Layer: "p",
188 Match: ast.MatchEqual,
189 },
190 &ast.Term{
191 Foundry: "opennlp",
192 Key: "PronType",
193 Layer: "m",
194 Match: ast.MatchEqual,
195 Value: "Ind",
196 },
197 },
198 Relation: ast.AndRelation,
199 },
200 },
201 expected: true,
202 },
203 {
204 name: "No match - missing DET",
205 input: &ast.Token{
206 Wrap: &ast.TermGroup{
207 Operands: []ast.Node{
208 &ast.Term{
209 Foundry: "opennlp",
210 Key: "NOUN",
211 Layer: "p",
212 Match: ast.MatchEqual,
213 },
214 &ast.Term{
215 Foundry: "opennlp",
216 Key: "AdjType",
217 Layer: "m",
218 Match: ast.MatchEqual,
219 Value: "Pdt",
220 },
221 },
222 Relation: ast.AndRelation,
223 },
224 },
225 expected: false,
226 },
227 {
228 name: "No match - wrong value",
229 input: &ast.Token{
230 Wrap: &ast.TermGroup{
231 Operands: []ast.Node{
232 &ast.Term{
233 Foundry: "opennlp",
234 Key: "DET",
235 Layer: "p",
236 Match: ast.MatchEqual,
237 },
238 &ast.Term{
239 Foundry: "opennlp",
240 Key: "AdjType",
241 Layer: "m",
242 Match: ast.MatchEqual,
243 Value: "Wrong",
244 },
245 },
246 Relation: ast.AndRelation,
247 },
248 },
249 expected: false,
250 },
251 }
252
253 for _, tt := range tests {
254 t.Run(tt.name, func(t *testing.T) {
255 result := m.Match(tt.input)
256 assert.Equal(t, tt.expected, result)
257 })
258 }
259}
260
261func TestReplace(t *testing.T) {
262 // Create pattern and replacement
263 pattern := ast.Pattern{
264 Root: &ast.TermGroup{
265 Operands: []ast.Node{
266 &ast.Term{
267 Foundry: "opennlp",
268 Key: "DET",
269 Layer: "p",
270 Match: ast.MatchEqual,
271 },
272 &ast.Term{
273 Foundry: "opennlp",
274 Key: "AdjType",
275 Layer: "m",
276 Match: ast.MatchEqual,
277 Value: "Pdt",
278 },
279 },
280 Relation: ast.AndRelation,
281 },
282 }
283
284 replacement := ast.Replacement{
285 Root: &ast.Term{
286 Foundry: "opennlp",
287 Key: "COMBINED_DET",
288 Layer: "p",
289 Match: ast.MatchEqual,
290 },
291 }
292
293 m := NewMatcher(pattern, replacement)
294
295 tests := []struct {
296 name string
297 input ast.Node
298 expected ast.Node
299 }{
300 {
301 name: "Replace matching pattern",
302 input: &ast.TermGroup{
303 Operands: []ast.Node{
304 &ast.Term{
305 Foundry: "opennlp",
306 Key: "DET",
307 Layer: "p",
308 Match: ast.MatchEqual,
309 },
310 &ast.Term{
311 Foundry: "opennlp",
312 Key: "AdjType",
313 Layer: "m",
314 Match: ast.MatchEqual,
315 Value: "Pdt",
316 },
317 },
318 Relation: ast.AndRelation,
319 },
320 expected: &ast.Term{
321 Foundry: "opennlp",
322 Key: "COMBINED_DET",
323 Layer: "p",
324 Match: ast.MatchEqual,
325 },
326 },
327 {
328 name: "No replacement for non-matching pattern",
329 input: &ast.TermGroup{
330 Operands: []ast.Node{
331 &ast.Term{
332 Foundry: "opennlp",
333 Key: "NOUN",
334 Layer: "p",
335 Match: ast.MatchEqual,
336 },
337 &ast.Term{
338 Foundry: "opennlp",
339 Key: "AdjType",
340 Layer: "m",
341 Match: ast.MatchEqual,
342 Value: "Pdt",
343 },
344 },
345 Relation: ast.AndRelation,
346 },
347 expected: &ast.TermGroup{
348 Operands: []ast.Node{
349 &ast.Term{
350 Foundry: "opennlp",
351 Key: "NOUN",
352 Layer: "p",
353 Match: ast.MatchEqual,
354 },
355 &ast.Term{
356 Foundry: "opennlp",
357 Key: "AdjType",
358 Layer: "m",
359 Match: ast.MatchEqual,
360 Value: "Pdt",
361 },
362 },
363 Relation: ast.AndRelation,
364 },
365 },
366 {
367 name: "Replace in nested structure",
368 input: &ast.Token{
369 Wrap: &ast.TermGroup{
370 Operands: []ast.Node{
371 &ast.TermGroup{
372 Operands: []ast.Node{
373 &ast.Term{
374 Foundry: "opennlp",
375 Key: "DET",
376 Layer: "p",
377 Match: ast.MatchEqual,
378 },
379 &ast.Term{
380 Foundry: "opennlp",
381 Key: "AdjType",
382 Layer: "m",
383 Match: ast.MatchEqual,
384 Value: "Pdt",
385 },
386 },
387 Relation: ast.AndRelation,
388 },
389 &ast.Term{
390 Foundry: "opennlp",
391 Key: "NOUN",
392 Layer: "p",
393 Match: ast.MatchEqual,
394 },
395 },
396 Relation: ast.AndRelation,
397 },
398 },
399 expected: &ast.Token{
400 Wrap: &ast.TermGroup{
401 Operands: []ast.Node{
402 &ast.Term{
403 Foundry: "opennlp",
404 Key: "COMBINED_DET",
405 Layer: "p",
406 Match: ast.MatchEqual,
407 },
408 &ast.Term{
409 Foundry: "opennlp",
410 Key: "NOUN",
411 Layer: "p",
412 Match: ast.MatchEqual,
413 },
414 },
415 Relation: ast.AndRelation,
416 },
417 },
418 },
419 }
420
421 for _, tt := range tests {
422 t.Run(tt.name, func(t *testing.T) {
423 result := m.Replace(tt.input)
424 assert.Equal(t, tt.expected, result)
425 })
426 }
427}
428
429func TestMatchNodeOrder(t *testing.T) {
430 // Test that operands can match in any order
431 pattern := ast.Pattern{
432 Root: &ast.TermGroup{
433 Operands: []ast.Node{
434 &ast.Term{
435 Foundry: "opennlp",
436 Key: "DET",
437 Layer: "p",
438 Match: ast.MatchEqual,
439 },
440 &ast.Term{
441 Foundry: "opennlp",
442 Key: "AdjType",
443 Layer: "m",
444 Match: ast.MatchEqual,
445 Value: "Pdt",
446 },
447 },
448 Relation: ast.AndRelation,
449 },
450 }
451
452 replacement := ast.Replacement{
453 Root: &ast.Term{
454 Foundry: "opennlp",
455 Key: "COMBINED_DET",
456 Layer: "p",
457 Match: ast.MatchEqual,
458 },
459 }
460
461 m := NewMatcher(pattern, replacement)
462
463 // Test with operands in different orders
464 input1 := &ast.TermGroup{
465 Operands: []ast.Node{
466 &ast.Term{
467 Foundry: "opennlp",
468 Key: "DET",
469 Layer: "p",
470 Match: ast.MatchEqual,
471 },
472 &ast.Term{
473 Foundry: "opennlp",
474 Key: "AdjType",
475 Layer: "m",
476 Match: ast.MatchEqual,
477 Value: "Pdt",
478 },
479 },
480 Relation: ast.AndRelation,
481 }
482
483 input2 := &ast.TermGroup{
484 Operands: []ast.Node{
485 &ast.Term{
486 Foundry: "opennlp",
487 Key: "AdjType",
488 Layer: "m",
489 Match: ast.MatchEqual,
490 Value: "Pdt",
491 },
492 &ast.Term{
493 Foundry: "opennlp",
494 Key: "DET",
495 Layer: "p",
496 Match: ast.MatchEqual,
497 },
498 },
499 Relation: ast.AndRelation,
500 }
501
502 assert.True(t, m.Match(input1), "Should match with original order")
503 assert.True(t, m.Match(input2), "Should match with reversed order")
504}