Skip to content

Commit 59fba90

Browse files
committed
test: Improve test coverage for PythonVersion and add edge case tests
- Add comprehensive tests for PythonVersion methods: - supportsPatternMatching() - supportsTypeParameters() - supportsPython314Features() - getValue() for all versions - Add edge case tests for: - Nested patterns in match statements - As patterns in match statements - Star patterns and double star patterns - Complex type parameters with multiple bounds - Type aliases with type parameters - T-strings with complex expressions - All tests pass (40+ test cases total) - Improves code coverage for PythonVersion.java
1 parent 3387692 commit 59fba90

File tree

1 file changed

+201
-0
lines changed

1 file changed

+201
-0
lines changed

chapi-ast-python/src/test/kotlin/chapi/ast/pythonast/Python314FeaturesTest.kt

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,4 +453,205 @@ class Python314FeaturesTest {
453453
assertEquals("get_type", func.Name)
454454
}
455455
}
456+
457+
// ==================== PythonVersion Tests ====================
458+
459+
@Nested
460+
inner class PythonVersionTests {
461+
462+
@Test
463+
fun `should support pattern matching for Python310 and above`() {
464+
val version310 = chapi.ast.antlr.PythonVersion.Python310
465+
val version312 = chapi.ast.antlr.PythonVersion.Python312
466+
val version314 = chapi.ast.antlr.PythonVersion.Python314
467+
val version3 = chapi.ast.antlr.PythonVersion.Python3
468+
val autodetect = chapi.ast.antlr.PythonVersion.Autodetect
469+
470+
assertTrue(version310.supportsPatternMatching())
471+
assertTrue(version312.supportsPatternMatching())
472+
assertTrue(version314.supportsPatternMatching())
473+
assertTrue(version3.supportsPatternMatching())
474+
assertTrue(autodetect.supportsPatternMatching())
475+
476+
val version2 = chapi.ast.antlr.PythonVersion.Python2
477+
assertFalse(version2.supportsPatternMatching())
478+
}
479+
480+
@Test
481+
fun `should support type parameters for Python312 and above`() {
482+
val version312 = chapi.ast.antlr.PythonVersion.Python312
483+
val version314 = chapi.ast.antlr.PythonVersion.Python314
484+
val autodetect = chapi.ast.antlr.PythonVersion.Autodetect
485+
486+
assertTrue(version312.supportsTypeParameters())
487+
assertTrue(version314.supportsTypeParameters())
488+
assertTrue(autodetect.supportsTypeParameters())
489+
490+
val version310 = chapi.ast.antlr.PythonVersion.Python310
491+
val version3 = chapi.ast.antlr.PythonVersion.Python3
492+
val version2 = chapi.ast.antlr.PythonVersion.Python2
493+
assertFalse(version310.supportsTypeParameters())
494+
assertFalse(version3.supportsTypeParameters())
495+
assertFalse(version2.supportsTypeParameters())
496+
}
497+
498+
@Test
499+
fun `should support Python314 features only for Python314`() {
500+
val version314 = chapi.ast.antlr.PythonVersion.Python314
501+
val autodetect = chapi.ast.antlr.PythonVersion.Autodetect
502+
503+
assertTrue(version314.supportsPython314Features())
504+
assertTrue(autodetect.supportsPython314Features())
505+
506+
val version312 = chapi.ast.antlr.PythonVersion.Python312
507+
val version310 = chapi.ast.antlr.PythonVersion.Python310
508+
val version3 = chapi.ast.antlr.PythonVersion.Python3
509+
val version2 = chapi.ast.antlr.PythonVersion.Python2
510+
assertFalse(version312.supportsPython314Features())
511+
assertFalse(version310.supportsPython314Features())
512+
assertFalse(version3.supportsPython314Features())
513+
assertFalse(version2.supportsPython314Features())
514+
}
515+
516+
@Test
517+
fun `should return correct value for each version`() {
518+
assertEquals(0, chapi.ast.antlr.PythonVersion.Autodetect.getValue())
519+
assertEquals(2, chapi.ast.antlr.PythonVersion.Python2.getValue())
520+
assertEquals(3, chapi.ast.antlr.PythonVersion.Python3.getValue())
521+
assertEquals(310, chapi.ast.antlr.PythonVersion.Python310.getValue())
522+
assertEquals(312, chapi.ast.antlr.PythonVersion.Python312.getValue())
523+
assertEquals(314, chapi.ast.antlr.PythonVersion.Python314.getValue())
524+
}
525+
}
526+
527+
// ==================== Additional Edge Cases ====================
528+
529+
@Nested
530+
inner class EdgeCaseTests {
531+
532+
@Test
533+
fun `should parse match with nested patterns`() {
534+
val code = """
535+
def process_data(data):
536+
match data:
537+
case {"items": [{"id": x, "value": y}]}:
538+
return (x, y)
539+
case {"items": []}:
540+
return None
541+
""".trimIndent()
542+
543+
val codeFile = PythonAnalyser().analysis(code, "test.py")
544+
val func = codeFile.DataStructures[0].Functions[0]
545+
546+
assertEquals("process_data", func.Name)
547+
}
548+
549+
@Test
550+
fun `should parse match with as pattern`() {
551+
val code = """
552+
def process_point(point):
553+
match point:
554+
case (x, y) as coord:
555+
return f"Coordinate: {coord}"
556+
case _:
557+
return "Unknown"
558+
""".trimIndent()
559+
560+
val codeFile = PythonAnalyser().analysis(code, "test.py")
561+
val func = codeFile.DataStructures[0].Functions[0]
562+
563+
assertEquals("process_point", func.Name)
564+
}
565+
566+
@Test
567+
fun `should parse complex type parameter with multiple bounds`() {
568+
val code = """
569+
class Processor[T: (int, str), U: float]:
570+
def process(self, item: T, value: U) -> T:
571+
return item
572+
""".trimIndent()
573+
574+
val codeFile = PythonAnalyser().analysis(code, "test.py")
575+
val cls = codeFile.DataStructures[0]
576+
577+
assertEquals("Processor", cls.NodeName)
578+
}
579+
580+
@Test
581+
fun `should parse type alias with type parameters`() {
582+
val code = """
583+
type Result[T, E] = tuple[T, E] | None
584+
585+
def create_result[T, E](value: T, error: E) -> Result[T, E]:
586+
return (value, error)
587+
""".trimIndent()
588+
589+
val codeFile = PythonAnalyser().analysis(code, "test.py")
590+
assertNotNull(codeFile)
591+
}
592+
593+
@Test
594+
fun `should parse match with star pattern`() {
595+
val code = """
596+
def process_list(items):
597+
match items:
598+
case [first, *rest]:
599+
return (first, rest)
600+
case []:
601+
return None
602+
""".trimIndent()
603+
604+
val codeFile = PythonAnalyser().analysis(code, "test.py")
605+
val func = codeFile.DataStructures[0].Functions[0]
606+
607+
assertEquals("process_list", func.Name)
608+
}
609+
610+
@Test
611+
fun `should parse match with double star pattern`() {
612+
val code = """
613+
def process_dict(data):
614+
match data:
615+
case {"name": name, **rest}:
616+
return (name, rest)
617+
case _:
618+
return None
619+
""".trimIndent()
620+
621+
val codeFile = PythonAnalyser().analysis(code, "test.py")
622+
val func = codeFile.DataStructures[0].Functions[0]
623+
624+
assertEquals("process_dict", func.Name)
625+
}
626+
627+
@Test
628+
fun `should parse t-string with nested f-string like syntax`() {
629+
val code = """
630+
def format_message(user, count):
631+
return t"User {user.name} has {count} items"
632+
""".trimIndent()
633+
634+
val codeFile = PythonAnalyser().analysis(code, "test.py")
635+
val func = codeFile.DataStructures[0].Functions[0]
636+
637+
assertEquals("format_message", func.Name)
638+
}
639+
640+
@Test
641+
fun `should parse except with except star`() {
642+
val code = """
643+
def handle_errors():
644+
try:
645+
risky_operation()
646+
except* ValueError:
647+
handle_value_error()
648+
except* TypeError, KeyError:
649+
handle_type_or_key_error()
650+
""".trimIndent()
651+
652+
// Note: except* is not yet in our grammar, but we should handle gracefully
653+
val codeFile = PythonAnalyser().analysis(code, "test.py")
654+
assertNotNull(codeFile)
655+
}
656+
}
456657
}

0 commit comments

Comments
 (0)