Theory: Reading Code: the CalcEngine Walkthrough
After the break the class did a different kind of exercise. The professor handed out a short Java file, CalcEngine.java, under 100 lines long. The task: find the bug by reading the code, with no help, in about 20 minutes. Afterwards some groups asked an AI as well, and the professor asked them to keep the two attempts separate, so they could see what they had found themselves and what the AI added.
The exercise teaches three things: how to read code like a tester, how to answer the question that was actually asked, and how to work with an AI without handing over your judgement.
Answer the Question That Was Asked
The professor used the exercise to make a point about answering, and said it will be expected in the exam. Several groups were asked to find the bug and wrote the fix instead. That was not the question.
How to write the answer:
- Answer exactly what the question asks. “Find the bug” means describe the bug: what input, what goes wrong. Do not write the fix unless the question asks for it.
- If the question asks why, write “This happens because…”. The words after because are your answer.
- If the question is yes or no, write yes or no first, then explain.
- If you are not sure what to write, repeat the question inside your answer. It keeps you on target.
The Code
CalcEngine is the brain of a small calculator: the keys call its methods, and the screen shows displayValue.
public class CalcEngine {
private int displayValue;
private char previousOperator;
private int leftOperand;
public void numberPressed(int number) {
displayValue = displayValue * 10 + number;
}
public void plus() {
applyPreviousOperator();
previousOperator = '+';
displayValue = 0;
}
public void equals() {
if (previousOperator == '+') {
displayValue = leftOperand + displayValue;
} else {
displayValue = leftOperand - displayValue;
}
leftOperand = 0;
}
private void applyPreviousOperator() {
if (previousOperator == '+') {
leftOperand += displayValue;
} else if (previousOperator == '-') {
leftOperand -= displayValue;
} else {
leftOperand = displayValue;
}
}
}
It has three variables:
| Variable | Meaning |
|---|
displayValue | The number on the screen. |
leftOperand | The number the calculator keeps on the left side of the operation. |
previousOperator | The last operator key the user pressed. In a new calculator it is the empty character '\u0000' (Java’s default for a char field). |
numberPressed builds the number digit by digit: pressing 4 then 2 gives 0×10+4=4, then 4×10+2=42. In this version only the + key is wired up; the '-' branches are there for a minus key.
Logic Errors and the Walkthrough
The bug here is a logic error: the program runs and does not crash, but the answer is wrong. Logic errors are hard to see, even in a short program, because nothing complains. To find one we use a walkthrough: you play the computer, running the code slowly on paper and writing down the value of every variable after every step.
Walkthrough of 5 + 3 =
| Key pressed | displayValue | leftOperand | previousOperator | What happens |
|---|
| (start) | 0 | 0 | none | A new calculator. |
| 5 | 5 | 0 | none | numberPressed: 0×10+5=5. |
| + | 0 | 5 | + | applyPreviousOperator takes the else branch: leftOperand = 5. Then the operator is stored and the screen number cleared. |
| 3 | 3 | 5 | + | numberPressed: 0×10+3=3. |
| = | 8 | 0 | + | equals: leftOperand + displayValue =5+3=8. |
The normal case works. So the bugs must hide in special cases, and good testers always go looking for special cases.
Special Case 1: A Very Big Number
A Java int can hold values only up to 2,147,483,647, about two billion. Go past that and Java does not stop or warn: the value wraps around to a large negative number. Think of the odometer of an old car: after 999999 km it rolls back to 000000.
Type the digits 2-1-4-7-4-8-3-6-4-8. The last numberPressed computes 214,748,364×10+8=2,147,483,648, one more than the maximum, and the screen shows −2,147,483,648, with no warning. No 9-digit number can overflow, but any 10-digit number from 2,147,483,648 upward does, and so does every longer one. A sum can overflow the same way, for example 2147483647 + 1 =.
Special Case 2: No Operator Was Pressed
On a new calculator, press 7 and then =. No operator was pressed, so previousOperator is still empty. In equals, the test previousOperator == '+' is false, so the program falls into the else branch and computes leftOperand - displayValue, that is 0−7. The screen shows −7 instead of 7. This is a real bug and very easy to reproduce: two key presses.
The root of it is that equals treats “not plus” as “minus”, although “no operator at all” is a third possibility.
What a Good Tester Asks
Two more behaviours are worth tracing, because a careful tester should check them, not assume.
- Pressing + several times. Trace
5 + + 3 =: the first + sets leftOperand = 5; the second + adds the current displayValue, which is 0, so leftOperand stays 5; then 3 and = give 8. Pressing + again just adds zero. This is not a bug.
- Typing a digit after =. After
5 + 3 = the screen shows 8, and displayValue is still 8. Press 2 and numberPressed computes 8×10+2=82. Most calculators would start a new number and show 2. Is this a bug? It depends on the specification. If the specification says a digit after = starts a new number, it is a bug; if it says nothing, a good tester asks rather than guesses.
Common mistake: declaring something a bug without tracing it. “Pressing + twice must break it” sounds plausible, but the walkthrough shows the second + adds 0 and the answer stays correct. Suspicion is where testing starts; the trace is the evidence.
The AI Part
The professor asked the groups that used AI two questions: Did you read the AI’s answer, or just copy it? Did you check it? The rule is simple: always check the AI’s result yourself. AI can suggest test cases, and it can even run tests, but you must stay in the loop. The case above is the reason: a confident but untraced claim of a bug looks exactly like a real one until someone walks through the code.
The professor added two remarks about where AI stops helping:
- This exercise was easy for AI, because you could give it the code. Many real situations are harder, for example an old (legacy) product where you do not have the code. There, the human tester remains essential.
- Nobody can test real software well without tools. AI is now one of the modern tools, a powerful one, but it has limits like any other.
Worked Examples
Exercise 1
On a new CalcEngine, the user presses 7 then =. Find the bug.
Show solution ↓Hide solution ↑
Solution
Walk through it:
| Key | displayValue | leftOperand | previousOperator |
|---|
| (start) | 0 | 0 | none |
| 7 | 7 | 0 | none |
| = | −7 | 0 | none |
At =, previousOperator == '+' is false, so equals runs the else branch: leftOperand - displayValue =0−7=−7.
The bug: pressing a number and then = with no operator shows −7 instead of 7, because equals treats “no operator” as subtraction.
(The question says find the bug, so the answer describes the bug. It does not write the fix.)
Exercise 2
A user types the digits 2-1-4-7-4-8-3-6-4-8 into a new CalcEngine. What does the screen show, and why?
Show solution ↓Hide solution ↑
Solution
The screen shows −2,147,483,648.
This happens because displayValue is a Java int, whose maximum is 2,147,483,647. After the first nine digits displayValue is 214,748,364. The last numberPressed computes 214,748,364×10+8=2,147,483,648, one more than the maximum, so the value wraps around to the most negative int, −2,147,483,648. Java gives no warning, so the calculator silently shows a wrong number: a logic error.
Exercise 3
Does pressing + twice break the calculator? Trace 5 + + 3 = to decide.
Show solution ↓Hide solution ↑
Solution
No.
| Key | displayValue | leftOperand | previousOperator | What happens |
|---|
| (start) | 0 | 0 | none | |
| 5 | 5 | 0 | none | 0×10+5 |
| + | 0 | 5 | + | else branch: leftOperand = 5 |
| + | 0 | 5 | + | leftOperand += 0 stays 5 |
| 3 | 3 | 5 | + | 0×10+3 |
| = | 8 | 0 | + | 5+3=8 |
The second + applies the previous operator to the current displayValue, which is 0, so it adds zero. The result is 8, which is correct ✓.
Exercise 4
Do a walkthrough of 5 + 3 + 2 = on a new CalcEngine. Is the result correct?
Show solution ↓Hide solution ↑
Solution
| Key | displayValue | leftOperand | previousOperator | What happens |
|---|
| (start) | 0 | 0 | none | |
| 5 | 5 | 0 | none | 0×10+5 |
| + | 0 | 5 | + | else branch: leftOperand = 5 |
| 3 | 3 | 5 | + | 0×10+3 |
| + | 0 | 8 | + | leftOperand += 3 gives 8 |
| 2 | 2 | 8 | + | 0×10+2 |
| = | 10 | 0 | + | 8+2=10 |
Yes, the result is 10, which is correct ✓. Chained additions work; the bugs are in the special cases (no operator, overflow).
Exercise 5
After 5 + 3 = the screen shows 8. The user then presses 2 and the screen shows 82. Is this a bug?
Show solution ↓Hide solution ↑
Solution
Trace it: after =, displayValue is still 8, so numberPressed(2) computes 8×10+2=82.
Whether this is a bug depends on the specification. A bug is a difference from the expected behaviour, and the expected behaviour comes from the specification, not from the tester’s taste. If the specification says a digit typed after = starts a new number (as on most calculators), then showing 82 instead of 2 is a bug. If the specification says nothing, the right move is to ask the people who own the specification, not to guess.
Exercise 6
The exam asks: “Find the bug in equals().” A student writes: “Change the else into else if (previousOperator == '-') and add an else that keeps displayValue.” Will this earn full marks? Write a better answer.
Show solution ↓Hide solution ↑
Solution
No. The question asks to find the bug, and the student wrote the fix. The fix even hints at the right problem, but it never states what goes wrong.
A better answer: “The bug is that equals() treats every operator that is not + as subtraction, including no operator at all. On a new calculator, pressing 7 then = shows −7 instead of 7, because previousOperator is empty, the if test is false, and the else branch computes 0−7.”
Exercise 7
A group pastes CalcEngine into an AI, which replies that “pressing + many times gives a wrong result.” The group copies this into its answer. What did the group do wrong, and what does the professor expect?
Show solution ↓Hide solution ↑
Solution
The group did not check the AI’s result. A walkthrough of 5 + + 3 = shows the second + adds 0 and the answer is still 8, so the claimed bug does not exist. Copying it means reporting a false bug.
The professor expects the tester to stay in the loop: read the AI’s answer, then verify each claim yourself, here with a walkthrough. AI can suggest test cases and even run them, but the tester is responsible for the conclusion. Keeping the human attempt and the AI attempt separate also shows which findings are really yours.