Skip to content

test: Cover float comparison ops and WhileStmt float condition#239

Open
Jaskirat-s7 wants to merge 11 commits intoarxlang:mainfrom
Jaskirat-s7:feature-issue-38-phase3
Open

test: Cover float comparison ops and WhileStmt float condition#239
Jaskirat-s7 wants to merge 11 commits intoarxlang:mainfrom
Jaskirat-s7:feature-issue-38-phase3

Conversation

@Jaskirat-s7
Copy link
Contributor

PR Title

test(#38): cover float comparison operators and WhileStmt float condition


⚠️ Stacked PR — This branch builds on top of the Phase 2 PR (test/issue-38-uninit-var-fix) which is already under review. The Phase 2 changes (IfStmt float coverage + VariableDeclaration zero-init) are in that PR. Only the 2 commits below are new in Phase 3:

  • bf4c7aa — test_while_float_condition (WhileStmt float condition via fcmp_ordered)
  • e85c85f — Float comparison operators <=, >=, ==, != with Float32

PR Description

What this PR does

This PR adds 8 new tests across 2 test files that cover previously untested float-type code paths in LLVMLiteIRVisitor. No production code was changed — only test files.

Newly covered compiler paths

Test Operator Lines covered What it proves
test_binary_op_float_le Float32 <= 1115–1117 fcmp_ordered <= emitted correctly
test_binary_op_float_ge Float32 >= 1125–1127 fcmp_ordered >= emitted correctly
test_binary_op_float_eq Float32 == 1165–1168 fcmp_ordered == emitted correctly
test_binary_op_float_ne Float32 != 1188–1190 fcmp_ordered != emitted correctly
test_while_float_condition WhileStmt + Float32 1362–1363 Float loop condition cast to i1 correctly

All 5 of these code paths previously had 0% coverage.

Why the overall percentage looks unchanged

The 86% figure is a rounded integer over 1,793 tracked lines. Each additional covered line moves the raw percentage by ~0.056%. Covering 5 new lines moves it from 85.96%86.24% — both display as 86%. The raw missed line count dropped from 211 → 208 in llvmliteir.py.

To be transparent: a visible percentage jump to 87% requires covering ~18 lines in one PR. These tests cover 5 lines but specifically target previously untested float comparison semantics.

Why these tests matter (not just coverage)

The original issue (#38) was partly about tests that only verified the compiler didn't crash, not that it produced correct output. Every test in this PR:

  • Compiles the AST → LLVM IR → native binary via Clang
  • Executes the binary and asserts the printed string matches the expected result
  • Tests both branches (e.g., 2.0 <= 3.0 → "le_true" AND 3.0 <= 2.0 → "le_false")

For example, test_binary_op_float_le proves that 2.0 <= 3.0 produces "le_true" at runtime — not just that it compiles without crashing.

Test count

  • Before: 210 tests
  • After: 218 tests (all passing)

Files changed

  • tests/test_binary_op.py — +211 lines (4 new test functions, 8 parametrized cases)
  • tests/test_while.py — +54 lines (1 new test function)

@pytest.mark.parametrize(
"a_val, b_val, op, expected",
[
(2.0, 3.0, "<=", "le_true"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is le here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

le is short for "less than or equal" (<=). le_true is printed when the condition holds (2.0 <= 3.0), and le_false
when it doesn't (3.0 <= 2.0). I am happy to rename to something clearer like "yes" / "no" or "lte_pass" / "lte_fail" if preferred.



@pytest.mark.parametrize("builder_class", [LLVMLiteIR])
def test_while_float_condition(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can parametrize it with the basic while functionality, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added Float32 and Float64 to test_while_expr. One issue came up during implementation was that the original increment used UnaryOp("++"), which generates an integer add instruction in LLVM IR. When floats were added, this produced add double which is invalid — floats need fadd.
It was fixed by replacing the increment with VariableAssignment + BinaryOp("+"), which correctly dispatches to fadd for floats and add for integers. All 6 types now pass (Int8/Int16/Int32/Int64/Float32/Float64).

module = builder.module()

cond = astx.BinaryOp(
op_code=">=",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't understand why are we hardcoding operator, op should be accepted as parameter

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have merged all 4 functions (test_binary_op_float_le, test_binary_op_float_ge, test_binary_op_float_eq, test_binary_op_float_ne) into a single test_binary_op_float_comparison that accepts op_code as a parameter. The parametrize list now covers all 4 float comparison operators (<=, >=, ==, !=) with both true and false branches , 8 cases total, all are passing.



@pytest.mark.parametrize(
"a_val, b_val, expected",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pytest.mark.parametrize(
"a_val, b_val, op, true_label, false_label",
[
(2.0, 3.0, "<=", "le_true", "le_false"),
(3.0, 2.0, ">=", "ge_true", "ge_false"),
(2.0, 2.0, "==", "eq_true", "eq_false"),
(1.0, 2.0, "!=", "ne_true", "ne_false"),
],
)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i have updated to use your suggested structure exactly. 4 rows with a_val, b_val, op, true_label, false_label as separate parameters. true_label is used in the then block and asserted as output, false_label in the else
block. All 4 cases pass.

@Jaskirat-s7 Jaskirat-s7 force-pushed the feature-issue-38-phase3 branch 2 times, most recently from fe37ecd to 18f6ff6 Compare March 19, 2026 19:39
@Jaskirat-s7
Copy link
Contributor Author

@yuvimittal ,I have addressed all 4 review comments in the latest push:

1."what is le here?" — le/ ge/eq/ne are standard abbreviations for "less than or equal", "greater than or equal", etc. I have kept the labels as-is since they're consistent with operator naming conventions.
2."op should be accepted as parameter" — i have merged the 4 separate float comparison functions (float_le, float_ge, float_eq, float_ne) into a single test_binary_op_float_comparison accepting op as a parameter.
3.Suggested parametrize structure with true_label, false_label — i have updated to your exact suggestion: 4 rows with a_val, b_val, op, true_label, false_label as explicit parameters.
4."we can parametrize it with basic while functionality, right?" — i have added Float32/Float64 to test_while_expr. One issue: UnaryOp("++") generates add double (invalid for floats — needs fadd) , fixed by replacing the increment with VariableAssignment + BinaryOp("+"), which correctly dispatches fadd/add by type. All 6 types now pass (Int8/16/32/64 + Float32/64).

Jaskirat-s7 and others added 11 commits March 20, 2026 01:49
…n zero-init paths

- Add Float32 to test_if_else_stmt and test_if_only_stmt parametrize lists
- Add test_variable_declaration_no_initializer for Int32 and Float32
- Fix: VariableDeclaration visitor checked 'node.value is not None' but astx defaults
  value to astx.Undefined(); added isinstance check so uninitialized vars reach
  the zero-init path (llvmliteir.py lines 3023-3059)
…tion_no_initializer

Add PrintExpr and expected_output assertion ('0' for Int32,
'0.000000' for Float32/Float64) so the test proves default-to-zero
behavior at runtime, not just that the build does not crash.
Also add Float64 to the parametrize list.
… review

- add PrintExpr + expected_output assertion ('0' for ints, '0.000000'
  for floats) to prove zero-default at runtime, not just build success
- remove unused literal_type parameter from signature and parametrize
- add Int8, Int16, Int64 alongside Int32/Float32/Float64 for consistency
Add test_while_float_condition to test_while.py which:
- Initializes a Float32 variable to 3.0
- Uses it directly as the WhileStmt condition (triggers fcmp_ordered conversion)
- Decrements it each iteration until 0.0
- Asserts 'done' is printed proving the loop ran and terminated

Covers previously uncovered lines 1362-1363 in llvmliteir.py (fcmp_ordered
float-to-i1 cast path inside WhileStmt visitor).
Add 4 parametrized tests to test_binary_op.py targeting previously
uncovered float comparison paths in LLVMLiteIRVisitor:

  - test_binary_op_float_le  -> covers line 1115 (fcmp_ordered <=)
  - test_binary_op_float_ge  -> covers line 1125 (fcmp_ordered >=)
  - test_binary_op_float_eq  -> covers line 1165 (fcmp_ordered ==)
  - test_binary_op_float_ne  -> covers line 1188 (fcmp_ordered !=)

Each test has two parametrized cases (true/false branches) with
expected_output assertions proving semantically correct output.
…le tests

- merge test_binary_op_float_le/ge/eq/ne into single parametrized
  test_binary_op_float_comparison accepting op, true_label, false_label
- replace UnaryOp('++') with VariableAssignment+BinaryOp('+') in
  test_while_expr to support Float32/Float64 (++ generates 'add' not
  'fadd' for floats causing invalid LLVM IR)
- add Float32 and Float64 to test_while_expr parametrize list
@Jaskirat-s7 Jaskirat-s7 force-pushed the feature-issue-38-phase3 branch from 18f6ff6 to 8c9278a Compare March 19, 2026 20:19
@Jaskirat-s7 Jaskirat-s7 requested a review from yuvimittal March 21, 2026 08:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants