PERFORM THRU Fall-Through Prints a Summary Once per Customer
Atlas Freight's invoice-print program contains paragraph 200-PROCESS-CUSTOMER, which ends with no EXIT and is followed by 300-PRINT-SUMMARY and then 400-EXIT. The main routine uses PERFORM 200-PROCESS-CUSTOMER THRU 400-EXIT. This worked when the summary paragraph did not exist. A maintenance change inserted 300-PRINT-SUMMARY between the process paragraph and the exit paragraph. Now the summary prints once per customer instead of once per run, page totals are multiplied, and the job occasionally runs out of spool space. Unit testing with a three-customer input still passed because the summary output looked plausible. The developer says the PERFORM range is correct because 400-EXIT is the exit. The change must be corrected without altering the summary's once-per-run behavior.
1. Explain the control flow that causes the summary to print once per customer.
Read the worked answer
PERFORM 200-PROCESS-CUSTOMER THRU 400-EXIT executes every paragraph in the inclusive range from 200 through 400. The range now contains 300-PRINT-SUMMARY. In addition, 200-PROCESS-CUSTOMER has no EXIT or GO TO, so after its last statement control falls through into 300-PRINT-SUMMARY. The summary paragraph then falls through into 400-EXIT, which returns control to the PERFORM.
Because the main routine performs this range once for each customer, the summary runs once per customer. The exit paragraph is not a boundary that skips intervening paragraphs; THRU deliberately includes them. The unit test passed because a plausible-looking summary was produced and no one asserted that it appeared exactly once. The page-total multiplication follows directly from accumulating totals repeatedly.
2. Propose a correction and compare it with at least one alternative.
Read the worked answer
The clean correction is to make the performed unit contain only the customer processing, then invoke the summary once outside the loop:
PERFORM 200-PROCESS-CUSTOMER THRU 200-EXIT
UNTIL WS-EOF = 'Y'
PERFORM 300-PRINT-SUMMARY
Place 200-EXIT. EXIT. immediately after 200-PROCESS-CUSTOMER, or use PERFORM 200-PROCESS-CUSTOMER without THRU and add an explicit EXIT for readability. Move 300-PRINT-SUMMARY outside the performed range. If the summary needs detail totals, accumulate them in working-storage during processing and have the summary paragraph read those totals.
An alternative is to move 300-PRINT-SUMMARY below 400-EXIT so it is outside the range, but paragraph order then becomes fragile for future maintenance. Another alternative is to add a GO TO 400-EXIT at the end of 200, but that preserves a wide and dangerous PERFORM range. The first correction is safer because the performed range is narrow and the once-per-run invocation is explicit.
3. How would you test this change so the original defect is caught, not hidden?
Read the worked answer
Use an input with at least three customers and assert the summary line count is exactly one, not merely that a summary exists. Compare the summary totals to the sum of the detail lines independently. Run the job with a single customer, multiple customers, and a customer that causes an error path, because fall-through defects often appear only on some paths.
Instrument the program with a counter incremented in 300-PRINT-SUMMARY and display it at end of job, or use the debugger to confirm that 300 is entered once. Review the source for every PERFORM THRU range and verify that no paragraph inside a range is intended to run once per run. Also check whether the summary paragraph has its own EXIT and whether any GO TO crosses PERFORM boundaries. The test should fail if the summary count is greater than one, even if the output looks business-plausible.