As with RESCHR, we will write RESOUT using test-driven development TDD). The first problem is how to figure out whether something is printing correctly. The simple answer is to just print it to the screen and look at it (and frankly is the very first approach I use). The problem with this approach is that it requires us to look at the output for every test every time we run them. This gets old in a hurry. To be really effective, unit tests must be quick and easy to run. The more effort required to run and evaluate them, the less often they will be used. So, we want the testing framework to automatically figure out whether or not the right thing is being printed out.
To make this automatic, we need to print to a place other than the default output and then compare what was written to our expectation. Most modern languages have some sort of internal representation of a string or file. In FORTRAN 66, we could use Hollerith fields encoded into integer arrays to achieve that effect, but it seems way too much effort of this particular job (we'll tackle that another day). Instead, I'm going to take the easy way out. I'll create a scratch file, write out to it, read the results back in, and compare them to my expectation. After a couple of false starts, here is the test that I came up with:
C
C TEST CASE FOR THE ROUTINE THAT PRINTS OUT THE
C CHARACTER BASED ON THE VALUE OF THE TEST
SUBROUTINE TSTOUT()
INTEGER I
INTEGER PASS
INTEGER FAIL
INTEGER RESULT
DIMENSION RESULT(2)
DATA PASS/1H./, FAIL/1HF/
OPEN(UNIT=10, STATUS='SCRATCH')
CALL RESOUT(.TRUE.)
CALL RESOUT(.FALSE.)
REWIND(10)
READ(10, 3000) (RESULT(I), I = 1, 2)
IF (RESULT(1) .EQ. PASS) GO TO 100
WRITE(6, 1000)
GO TO 200
100 CONTINUE
WRITE(6, 2000)
200 CONTINUE
IF (RESULT(2) .EQ. FAIL) GO TO 300
WRITE(6, 1000)
GO TO 400
300 CONTINUE
WRITE(6, 2000)
400 CONTINUE
1000 FORMAT(1HF, $)
2000 FORMAT(1H., $)
3000 FORMAT(2A1)
RETURN
END
The next TDD step is to write our minimal code that will compile, and cause the test to fail:
C WRITES OUT '.' IF THE ARGUMENT IS .TRUE.
C 'F' OTHERWISE
SUBROUTINE RESOUT(A)
LOGICAL A
WRITE(10, 100)
100 FORMAT(2HXX)
RETURN
END
We run it, and see the expected output:
FF
Next, we remove the hard-coded unit (10), and take it an argument to the routine, and revise the code to make the tests pass (I actually do these in separate steps, but from the last post you get the idea of TDD, so I'm picking up the pace a bit):
C
C WRITES OUT '.' IF THE ARGUMENT IS .TRUE.
C 'F' OTHERWISE
SUBROUTINE RESOUT(A, OUTPUT)
LOGICAL A
INTEGER OUTPUT
IF (A) GO TO 200
WRITE(OUTPUT, 100)
100 FORMAT(1HF, $)
GO TO 400
200 CONTINUE
WRITE(OUTPUT, 300)
300 FORMAT(1H., $)
400 CONTINUE
RETURN
END
And the output becomes:
..
indicating that both of the tests now pass.
Now, we can leave this version as it is, and some purists would say we should. However, those purists would be wrong. As it stands, RESOUT replicates the logic of RESCHR (choosing which character to print, based on the value of the LOGICAL argument A. The replication is a violation of the "Don't repeat yourself" rule (know as DRY, from The Pragmatic Programmer). There is no reason to have that code twice, so we modify RESOUT to make use of RESCHR routine that we developed last time. So, our final version of RESOUT becomes:
C
C WRITES OUT '.' IF THE ARGUMENT IS .TRUE.
C 'F' OTHERWISE
C
C LOGICAL A: WHETHER OR NOT THE TEST PASSED
C INTEGER OUTPUT: THE UNIT TO WRITE THE RESULT TO
C
SUBROUTINE RESOUT(A, OUTPUT)
LOGICAL A
INTEGER OUTPUT
INTEGER RESCHR
INTEGER OUTCHR
OUTCHR = RESCHR(A)
WRITE(OUTPUT, 100) OUTCHR
100 FORMAT(A1, $)
RETURN
END
We run this, and again the tests pass.
Finally, we tighten up the test code:
C
C TEST CASE FOR THE ROUTINE THAT PRINTS OUT THE
C CHARACTER BASED ON THE VALUE OF THE TEST
SUBROUTINE TSTOUT()
INTEGER I
INTEGER OUTPUT
INTEGER EXPECT
INTEGER RESULT
DIMENSION RESULT(2)
DATA (EXPECT(I = 1 ,2))/1H., 1HF/, OUTPUT/10/
OPEN(UNIT=OUTPUT, STATUS='SCRATCH')
CALL RESOUT(.TRUE., OUTPUT)
CALL RESOUT(.FALSE., OUTPUT)
REWIND(10)
READ(10, 100) (RESULT(I), I = 1, 2)
100 FORMAT(2A1)
DO 600 I = 1, 2
IF (EXPECT(I) .EQ. RESULT(I)) go to 300
WRITE(6, 200)
200 FORMAT(1HF, $)
GO TO 500
300 CONTINUE
WRITE(6, 400)
400 FORMAT(1H., $)
500 CONTINUE
600 CONTINUE
RETURN
END
No comments:
Post a Comment