From 21069b6f19fe53116c861e1bb7276b65251e26c6 Mon Sep 17 00:00:00 2001 From: Furgo <148809153+furgo16@users.noreply.github.com> Date: Fri, 23 Jan 2026 11:37:36 +0100 Subject: [PATCH] BIM: add regression and functional test for ArchWall's MakeBlock feature (#27002) Co-authored-by: Roy-043 <70520633+Roy-043@users.noreply.github.com> --- src/Mod/BIM/bimtests/TestArchWall.py | 104 +++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/src/Mod/BIM/bimtests/TestArchWall.py b/src/Mod/BIM/bimtests/TestArchWall.py index 48c3140a84..aa1bfe0cb5 100644 --- a/src/Mod/BIM/bimtests/TestArchWall.py +++ b/src/Mod/BIM/bimtests/TestArchWall.py @@ -369,3 +369,107 @@ class TestArchWall(TestArchBase.TestArchBase): delta=1e-6, msg="Placement.Rotation was not updated correctly.", ) + + def test_wall_makeblocks(self): + """Test the 'MakeBlocks' feature of Arch Wall. + This is a regression test for https://github.com/FreeCAD/FreeCAD/issues/26982, and + a basic, functional test for the MakeBlocks code path. + """ + operation = "Checking Arch Wall MakeBlocks functional correctness..." + self.printTestMessage(operation) + + # Block parameters + L, H, W = 1000.0, 600.0, 200.0 + BL, BH = 400.0, 200.0 # Block Length and Height + O1, O2 = 0.0, 200.0 # Row offsets + + # Create base line + p1 = App.Vector(0, 0, 0) + p2 = App.Vector(L, 0, 0) + line = Draft.makeLine(p1, p2) + self.document.recompute() + + # Create wall based on line and block parameters + wall = Arch.makeWall(line, width=W, height=H) + wall.BlockLength = BL + wall.BlockHeight = BH + wall.Joint = 0 # For test and volume calculation simplicity + wall.OffsetFirst = O1 + wall.OffsetSecond = O2 + + def calc_row(row_start): + """ + Simulates the 1D block-segmentation logic for a single horizontal course. + + This helper replicates the "sawing" algorithm found in _Wall.execute: + 1. It places the first vertical joint at 'row_start'. + 2. It advances the cutting position by 'BlockLength' (BL). + 3. It measures the resulting segments between joints. + 4. It classifies segments equal to 'BL' as 'Entire' and any + remainder (at the start or end of the row) as 'Broken'. + + Args: + row_start (float): The distance from the start of the wall to the first vertical + joint. + + Returns: + tuple (int, int): A pair of integers (entire_count, broken_count) + predicted for this specific row. + """ + row_entire, row_broken = 0, 0 + current_pos = row_start + last_pos = 0.0 + + # Mimic the logic in ArchWall: + # while offset < (Length - Joint): create cut at offset + # Perform the cuts and record the resulting segments + segments = [] + while current_pos < L: + if current_pos > 0: + segments.append(current_pos - last_pos) + last_pos = current_pos + current_pos += BL + if last_pos < L: + segments.append(L - last_pos) + + # Classify segments + for seg_len in segments: + if abs(seg_len - BL) < 0.1: # Threshold for "Entire" + row_entire += 1 + else: + row_broken += 1 + return row_entire, row_broken + + # Calculate expectations based on total courses (rows) + num_rows = int(H // BH) + expected_entire = 0 + expected_broken = 0 + + # Effectively "lay the bricks" in a running bond pattern: alternate offsets per even/odd row + for r in range(num_rows): + ent, brk = calc_row(O1 if r % 2 == 0 else O2) + expected_entire += ent + expected_broken += brk + + # Enable the feature, triggering the #26982 bug via the code path in _Wall.execute() + wall.MakeBlocks = True + self.document.recompute() + + # Regression check: did we crash? + self.assertFalse(wall.Shape.isNull(), "Wall shape should not be null") + + # Functional check: compare dynamic calculation to property values + self.assertEqual( + wall.CountEntire, + expected_entire, + f"Mismatch in Entire blocks. Expected {expected_entire}, got {wall.CountEntire}", + ) + self.assertEqual( + wall.CountBroken, + expected_broken, + f"Mismatch in Broken blocks. Expected {expected_broken}, got {wall.CountBroken}", + ) + + # Integrity check: volume correctness + expected_vol = L * W * H + self.assertAlmostEqual(wall.Shape.Volume, expected_vol, places=3)