JeffGrigg

Untitled

May 12th, 2023
664
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 5 26.43 KB | None | 0 0
  1. /*
  2.  
  3. GuessTheNumber.java = simple “guess the number” game
  4.  
  5. tests/GuessTheNumberTest.java = JUnit test for the game
  6. tests/SystemInputOutputTesterTest.java = test the main JUnit testing factory
  7.  
  8. library/RandomSource.java = creates "Random" instances (or mock) for the Program Under Test
  9. library/RandomCreatorInterface.java = interface to create "Random" instances (enables lambda expressions)
  10. library/AbstractMockRandomSwapper.java = swaps mock Random subclasses in on construction and out on close
  11. library/MockRandomSingleValue.java = mock implementation for single-use random number
  12.  
  13. library/SystemInputOutputTester.java = Builder for console I/O testing
  14. library/MockStandardOutputStream.java = Capture Standard Output (or Error)
  15. library/MockInputStream.java = Provide input when needed. Calls back to SystemInputOutputTester to validate intervening output and to provide the input data.
  16. library/ExpectedInputOutputStepBase.java = common base class for input/output expectations
  17. library/ExpectedTextOutputStep.java = Expect that the given text output is written to Standard Output (or Error).
  18. library/ProvideLineInputStep.java = Provide line of Console Input at that point in the expectations list.
  19.  
  20. */
  21.  
  22. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  23. //
  24. // File Name:   GuessTheNumber.java
  25. //
  26. package p20230510_TestConsoleIO;
  27.  
  28. import p20230510_TestConsoleIO.library.RandomSource;
  29.  
  30. import java.util.Scanner;
  31.  
  32. public class GuessTheNumber {
  33.  
  34.     public static void main(final String[] args) {
  35.         System.out.println("Welcome to the number guessing program.");
  36.         System.out.println("I am thinking of a number in the range of 1 to 100, inclusive.");
  37.         System.out.println("You need to guess this number, with a minimum number of guesses.");
  38.         System.out.println("Each time you guess, I will tell you if my number is HIGHER or LOWER than your guess.");
  39.  
  40.         final var random = RandomSource.create();
  41.         final int numberToGuess = random.nextInt(100);
  42.         final var scanner = new Scanner(System.in);
  43.         var numberOfGuesses = 0;
  44.  
  45.         while (true) {
  46.             System.out.println("What is your guess?");
  47.             final var userGuess = scanner.nextInt();
  48.             ++numberOfGuesses;
  49.             if (userGuess < numberToGuess) {
  50.                 System.out.println("Your guess of " + userGuess + " is TOO LOW.");
  51.             } else if (userGuess > numberToGuess) {
  52.                 System.out.println("Your guess of " + userGuess + " is TOO HIGH.");
  53.             } else {
  54.                 System.out.println("Your guess of " + userGuess + " is CORRECT in "
  55.                         + (numberOfGuesses == 1 ? "one guess!!!" : (numberOfGuesses + " guesses!")));
  56.                 break;
  57.             }
  58.         }
  59.     }
  60.  
  61. }
  62.  
  63. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  64. //
  65. // File Name:   tests/GuessTheNumberTest.java
  66. //
  67. package p20230510_TestConsoleIO.tests;
  68.  
  69. import org.junit.Test;
  70. import p20230510_TestConsoleIO.GuessTheNumber;
  71. import p20230510_TestConsoleIO.library.MockRandomSingleValue;
  72. import p20230510_TestConsoleIO.library.SystemInputOutputTester;
  73.  
  74. import java.io.IOException;
  75.  
  76. public class GuessTheNumberTest {
  77.  
  78.     @Test
  79.     public void testHighsAndLowsTo33() throws IOException {
  80.  
  81.         final var systemNumberWeAreTryingToGuess = 33;
  82.  
  83.         try (final var mockRandomNumberGenerator = new MockRandomSingleValue(systemNumberWeAreTryingToGuess)) {
  84.             SystemInputOutputTester
  85.                     .whenCallingThisMethod(() -> {
  86.                         GuessTheNumber.main(null);
  87.                     })
  88.                     .assertTextOutput("Welcome to the number guessing program.")
  89.                     //
  90.                     .assertTextOutput("I am thinking of a number in the range of 1 to 100, inclusive.")
  91.                     .assertTextOutput("You need to guess this number, with a minimum number of guesses.")
  92.                     .assertTextOutput("Each time you guess, I will tell you if my number is HIGHER or LOWER than your guess.")
  93.                     //
  94.                     .assertTextOutput("What is your guess?")
  95.                     .provideLineInput("50")
  96.                     .assertTextOutput("Your guess of 50 is TOO HIGH.")
  97.                     //
  98.                     .assertTextOutput("What is your guess?")
  99.                     .provideLineInput("25")
  100.                     .assertTextOutput("Your guess of 25 is TOO LOW.")
  101.                     //
  102.                     .assertTextOutput("What is your guess?")
  103.                     .provideLineInput("37")
  104.                     .assertTextOutput("Your guess of 37 is TOO HIGH.")
  105.                     //
  106.                     .assertTextOutput("What is your guess?")
  107.                     .provideLineInput("31")
  108.                     .assertTextOutput("Your guess of 31 is TOO LOW.")
  109.                     //
  110.                     .assertTextOutput("What is your guess?")
  111.                     .provideLineInput("34")
  112.                     .assertTextOutput("Your guess of 34 is TOO HIGH.")
  113.                     //
  114.                     .assertTextOutput("What is your guess?")
  115.                     .provideLineInput("32")
  116.                     .assertTextOutput("Your guess of 32 is TOO LOW.")
  117.                     //
  118.                     .assertTextOutput("What is your guess?")
  119.                     .provideLineInput("33")
  120.                     .assertTextOutput("Your guess of 33 is CORRECT in 7 guesses!")
  121.                     //
  122.                     .assertThatTheMethodReturnsHere();
  123.         }
  124.     }
  125.  
  126.     @Test
  127.     public void testMaximumGuesses() throws IOException {
  128.  
  129.         final var systemNumberWeAreTryingToGuess = 100;
  130.  
  131.         try (final var mockRandomNumberGenerator = new MockRandomSingleValue(systemNumberWeAreTryingToGuess)) {
  132.             SystemInputOutputTester
  133.                     .whenCallingThisMethod(() -> {
  134.                         GuessTheNumber.main(null);
  135.                     })
  136.                     .assertTextOutput("Welcome to the number guessing program.")
  137.                     //
  138.                     .assertTextOutput("I am thinking of a number in the range of 1 to 100, inclusive.")
  139.                     .assertTextOutput("You need to guess this number, with a minimum number of guesses.")
  140.                     .assertTextOutput("Each time you guess, I will tell you if my number is HIGHER or LOWER than your guess.")
  141.                     //
  142.                     .assertTextOutput("What is your guess?")
  143.                     .provideLineInput("50")
  144.                     .assertTextOutput("Your guess of 50 is TOO LOW.")
  145.                     //
  146.                     .assertTextOutput("What is your guess?")
  147.                     .provideLineInput("75")
  148.                     .assertTextOutput("Your guess of 75 is TOO LOW.")
  149.                     //
  150.                     .assertTextOutput("What is your guess?")
  151.                     .provideLineInput("88")
  152.                     .assertTextOutput("Your guess of 88 is TOO LOW.")
  153.                     //
  154.                     .assertTextOutput("What is your guess?")
  155.                     .provideLineInput("94")
  156.                     .assertTextOutput("Your guess of 94 is TOO LOW.")
  157.                     //
  158.                     .assertTextOutput("What is your guess?")
  159.                     .provideLineInput("97")
  160.                     .assertTextOutput("Your guess of 97 is TOO LOW.")
  161.                     //
  162.                     .assertTextOutput("What is your guess?")
  163.                     .provideLineInput("99")
  164.                     .assertTextOutput("Your guess of 99 is TOO LOW.")
  165.                     //
  166.                     .assertTextOutput("What is your guess?")
  167.                     .provideLineInput("100")
  168.                     .assertTextOutput("Your guess of 100 is CORRECT in 7 guesses!")
  169.                     //
  170.                     .assertThatTheMethodReturnsHere();
  171.         }
  172.     }
  173.  
  174.     @Test
  175.     public void testFirstGuessIsCorrect() throws IOException {
  176.  
  177.         final var systemNumberWeAreTryingToGuess = 24;
  178.  
  179.         try (final var mockRandomNumberGenerator = new MockRandomSingleValue(systemNumberWeAreTryingToGuess)) {
  180.             SystemInputOutputTester
  181.                     .whenCallingThisMethod(() -> {
  182.                         GuessTheNumber.main(null);
  183.                     })
  184.                     .assertTextOutput("Welcome to the number guessing program.")
  185.                     //
  186.                     .assertTextOutput("I am thinking of a number in the range of 1 to 100, inclusive.")
  187.                     .assertTextOutput("You need to guess this number, with a minimum number of guesses.")
  188.                     .assertTextOutput("Each time you guess, I will tell you if my number is HIGHER or LOWER than your guess.")
  189.                     //
  190.                     .assertTextOutput("What is your guess?")
  191.                     .provideLineInput("24")
  192.                     .assertTextOutput("Your guess of 24 is CORRECT in one guess!!!")
  193.                     //
  194.                     .assertThatTheMethodReturnsHere();
  195.         }
  196.     }
  197.  
  198.     @Test
  199.     public void testSecondGuessIsCorrect() throws IOException {
  200.  
  201.         final var systemNumberWeAreTryingToGuess = 33;
  202.  
  203.         try (final var mockRandomNumberGenerator = new MockRandomSingleValue(systemNumberWeAreTryingToGuess)) {
  204.             SystemInputOutputTester
  205.                     .whenCallingThisMethod(() -> {
  206.                         GuessTheNumber.main(null);
  207.                     })
  208.                     .assertTextOutput("Welcome to the number guessing program.")
  209.                     //
  210.                     .assertTextOutput("I am thinking of a number in the range of 1 to 100, inclusive.")
  211.                     .assertTextOutput("You need to guess this number, with a minimum number of guesses.")
  212.                     .assertTextOutput("Each time you guess, I will tell you if my number is HIGHER or LOWER than your guess.")
  213.                     //
  214.                     .assertTextOutput("What is your guess?")
  215.                     .provideLineInput("86")
  216.                     .assertTextOutput("Your guess of 86 is TOO HIGH.")
  217.                     //
  218.                     .assertTextOutput("What is your guess?")
  219.                     .provideLineInput("33")
  220.                     .assertTextOutput("Your guess of 33 is CORRECT in 2 guesses!")
  221.                     //
  222.                     .assertThatTheMethodReturnsHere();
  223.         }
  224.     }
  225.  
  226. }
  227.  
  228. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  229. //
  230. // File Name:   tests/SystemInputOutputTesterTest.java
  231. //
  232. package p20230510_TestConsoleIO.tests;
  233.  
  234. import junit.framework.AssertionFailedError;
  235. import org.junit.Assert;
  236. import org.junit.Test;
  237. import p20230510_TestConsoleIO.library.SystemInputOutputTester;
  238.  
  239. public class SystemInputOutputTesterTest {
  240.  
  241.     private static class DoNothingProgram {
  242.         public static void main(String[] args) {
  243.             // Intentionally do NOTHING here.
  244.         }
  245.     }
  246.  
  247.     @Test
  248.     public void testDoNothingProgram() {
  249.         SystemInputOutputTester
  250.                 .whenCallingThisMethod(() -> {
  251.                     DoNothingProgram.main(null);})
  252.                 .assertThatTheMethodReturnsHere();
  253.     }
  254.  
  255.     @Test
  256.     public void testMissingMessage() {
  257.         try {
  258.             SystemInputOutputTester
  259.                     .whenCallingThisMethod(() -> {
  260.                         DoNothingProgram.main(null);})
  261.                     .assertTextOutput("This program does not and should not produce this output.")
  262.                     .assertThatTheMethodReturnsHere();
  263.             throw new ExpectedException("Expected AssertionFailedError to be thrown by the code above.");
  264.         } catch (final AssertionFailedError ex) {
  265.             Assert.assertEquals("Failed to find <This program does not and should not produce this output.> in <>", ex.getMessage());
  266.         }
  267.     }
  268.  
  269.     @Test
  270.     public void testProgramTerminatesWithMoreInputToProcess() {
  271.         try {
  272.             SystemInputOutputTester
  273.                     .whenCallingThisMethod(() -> {
  274.                         DoNothingProgram.main(null);})
  275.                     .provideLineInput("Unused Input Line")
  276.                     .assertThatTheMethodReturnsHere();
  277.             throw new ExpectedException("Expected AssertionFailedError to be thrown by the code above.");
  278.         } catch (final AssertionFailedError ex) {
  279.             Assert.assertEquals("Program has exited, but the tests say that we should further input:  ProvideLineInputStep('Unused Input Line')", ex.getMessage());
  280.         }
  281.     }
  282.  
  283.     @Test
  284.     public void testHelloWorldProgram() {
  285.         SystemInputOutputTester
  286.                 .whenCallingThisMethod(() -> {
  287.                     HelloWorldProgram.main(null);})
  288.                 .assertTextOutput("Hello world!")
  289.                 .assertThatTheMethodReturnsHere();
  290.     }
  291.  
  292.     private static class HelloWorldProgram {
  293.         public static void main(String[] args) {
  294.             System.out.println("Hello world!");
  295.         }
  296.     }
  297.  
  298.     private static class ExpectedException extends Error {
  299.         public ExpectedException(final String message) {
  300.             super(message);
  301.         }
  302.     }
  303.  
  304. }
  305.  
  306. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  307. //
  308. // File Name:   library/RandomSource.java
  309. //
  310. package p20230510_TestConsoleIO.library;
  311.  
  312. import p20230510_TestConsoleIO.GuessTheNumber;
  313.  
  314. import java.util.Random;
  315.  
  316. abstract public class RandomSource {
  317.  
  318.     private RandomSource() {
  319.         throw new IllegalStateException("Not expecting to create instances of this class.");
  320.     }
  321.  
  322.     private static RandomCreatorInterface _creator = () -> {
  323.         return new Random();
  324.     };
  325.  
  326.     public static Random create() {
  327.         return _creator.create();
  328.     }
  329.  
  330.     public static RandomCreatorInterface swapRandomConstructor(final RandomCreatorInterface newRandomCreator) {
  331.         final var oldRandomCreator = _creator;
  332.         _creator = newRandomCreator;
  333.         return oldRandomCreator;
  334.     }
  335.  
  336. }
  337.  
  338. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  339. //
  340. // File Name:   library/RandomCreatorInterface.java
  341. //
  342. package p20230510_TestConsoleIO.library;
  343.  
  344. import java.util.Random;
  345.  
  346. public interface RandomCreatorInterface {
  347.  
  348.     Random create();
  349.  
  350. }
  351.  
  352. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  353. //
  354. // File Name:   library/AbstractMockRandomSwapper.java
  355. //
  356. package p20230510_TestConsoleIO.library;
  357.  
  358. import java.io.Closeable;
  359. import java.io.IOException;
  360. import java.util.Random;
  361.  
  362. abstract public class AbstractMockRandomSwapper extends Random implements Closeable {
  363.  
  364.     private final RandomCreatorInterface _oldRandomConstructor;
  365.  
  366.     protected AbstractMockRandomSwapper() {
  367.         _oldRandomConstructor = RandomSource.swapRandomConstructor(() -> {
  368.             return this;
  369.         });
  370.     }
  371.  
  372.     @Override
  373.     public void close() throws IOException {
  374.         final var randomConstructorSwappedOut = RandomSource.swapRandomConstructor(_oldRandomConstructor);
  375.     }
  376.  
  377. }
  378.  
  379. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  380. //
  381. // File Name:   library/MockRandomSingleValue.java
  382. //
  383. package p20230510_TestConsoleIO.library;
  384.  
  385. public class MockRandomSingleValue extends AbstractMockRandomSwapper {
  386.  
  387.     private int _theFixedRandomNumberValue;
  388.  
  389.     public MockRandomSingleValue(final int theFixedRandomNumberValue) {
  390.         _theFixedRandomNumberValue = theFixedRandomNumberValue;
  391.     }
  392.  
  393.     @Override
  394.     public int nextInt(final int bound) {
  395.         final var returnValue = _theFixedRandomNumberValue;
  396.         _theFixedRandomNumberValue = -1;    // Clear value, as it's only good for one use.
  397.         return returnValue;
  398.     }
  399.  
  400. }
  401.  
  402. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  403. //
  404. // File Name:   library/SystemInputOutputTester.java
  405. //
  406. package p20230510_TestConsoleIO.library;
  407.  
  408. import junit.framework.AssertionFailedError;
  409.  
  410. import java.io.PrintStream;
  411. import java.util.ArrayList;
  412. import java.util.Iterator;
  413. import java.util.List;
  414.  
  415. public class SystemInputOutputTester {
  416.  
  417.     private final Runnable _callback;
  418.     private final List<ExpectedInputOutputStepBase> _expectedInputOutputSteps = new ArrayList<ExpectedInputOutputStepBase>();
  419.     private Iterator<ExpectedInputOutputStepBase> _expectedInputOutputStepIterator = null;
  420.     private final MockInputStream _mockInputStream = new MockInputStream(this);
  421.     private final MockStandardOutputStream _mockStandardOutputStream = new MockStandardOutputStream();
  422.     //protected final MockStandardOutputStream _mockStandardErrorStream = new MockStandardOutputStream(this);
  423.  
  424.     public SystemInputOutputTester(final Runnable callback) {
  425.         _callback = callback;
  426.     }
  427.  
  428.     public static SystemInputOutputTester whenCallingThisMethod(final Runnable callback) {
  429.         return new SystemInputOutputTester(callback);
  430.     }
  431.  
  432.     public SystemInputOutputTester assertTextOutput(final String expectedOutputMessage) {
  433.         _expectedInputOutputSteps.add(new ExpectedTextOutputStep(_mockStandardOutputStream, expectedOutputMessage));
  434.         return this;
  435.     }
  436.  
  437.     public SystemInputOutputTester provideLineInput(final String lineOfInputText) {
  438.         _expectedInputOutputSteps.add(new ProvideLineInputStep(lineOfInputText));
  439.         return this;
  440.     }
  441.  
  442.     public void assertThatTheMethodReturnsHere() {
  443.         final var oldInput = System.in;
  444.         final var oldOutput = System.out;
  445.         //final var oldError = System.err;
  446.         try {
  447.             System.setIn(_mockInputStream);
  448.             System.setOut(new PrintStream(_mockStandardOutputStream));
  449.             //System.setErr(new PrintStream(_mockStandardErrorStream));
  450.  
  451.             _expectedInputOutputStepIterator = _expectedInputOutputSteps.iterator();
  452.  
  453.             _callback.run();
  454.  
  455.             System.out.flush();
  456.             System.err.flush();
  457.             while (_expectedInputOutputStepIterator.hasNext()) {
  458.                 final var step = _expectedInputOutputStepIterator.next();
  459.                 final var isAfterExit = true;
  460.                 final var didProvideNewUserInput = step.validate(this);
  461.                 if (didProvideNewUserInput) {
  462.                     throw new AssertionFailedError(
  463.                             "Program has exited, but the tests say that we should further input:  " + step.toString());
  464.                 }
  465.             }
  466.  
  467.         } finally {
  468.             System.setIn(oldInput);
  469.             System.setOut(oldOutput);
  470.             //System.setErr(oldError);
  471.         }
  472.     }
  473.  
  474.     public void validateToAndIncludingNextInput() {
  475.         System.out.flush();
  476.         System.err.flush();
  477.         while (_expectedInputOutputStepIterator.hasNext()) {
  478.             final var step = _expectedInputOutputStepIterator.next();
  479.             final var isAfterExit = false;
  480.             final var didProvideNewUserInput = step.validate(this);
  481.             if (didProvideNewUserInput) {
  482.                 break;
  483.             }
  484.         }
  485.     }
  486.  
  487.     protected void provideLineOfInput(final String lineOfInputText) {
  488.         _mockInputStream.provideLineOfInput(lineOfInputText);
  489.     }
  490.  
  491. }
  492.  
  493. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  494. //
  495. // File Name:   library/MockStandardOutputStream.java
  496. //
  497. package p20230510_TestConsoleIO.library;
  498.  
  499. import org.jetbrains.annotations.NotNull;
  500. import org.junit.Assert;
  501.  
  502. import java.io.IOException;
  503. import java.io.OutputStream;
  504.  
  505. public class MockStandardOutputStream extends OutputStream {
  506.  
  507.     private final StringBuilder _sequenceOfByteValues = new StringBuilder();
  508.     private final StringBuilder _accumulatedOutput = new StringBuilder();
  509.  
  510.     @Override
  511.     public void write(final int byteValue) throws IOException {
  512.         _sequenceOfByteValues.append((char) byteValue);
  513.     }
  514.  
  515.     public StringBuilder getUpdatedStringBuilder() {
  516.  
  517.         if (_sequenceOfByteValues.length() > 0) {
  518.             final String stringValue = getStringValue();
  519.             _accumulatedOutput.append(stringValue);
  520.         }
  521.  
  522.         return _accumulatedOutput;
  523.     }
  524.  
  525.     @NotNull
  526.     private String getStringValue() {
  527.         final var bytes = new byte[_sequenceOfByteValues.length()];
  528.         final var charArray = _sequenceOfByteValues.toString().toCharArray();
  529.         Assert.assertEquals(bytes.length, charArray.length);
  530.         for (int idx = 0; idx < bytes.length; ++idx) {
  531.             bytes[idx] = (byte) charArray[idx];
  532.         }
  533.         _sequenceOfByteValues.setLength(0);
  534.         final var stringValue = new String(bytes);
  535.         return stringValue;
  536.     }
  537.  
  538. }
  539.  
  540. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  541. //
  542. // File Name:   library/MockInputStream.java
  543. //
  544. package p20230510_TestConsoleIO.library;
  545.  
  546. import java.io.IOException;
  547. import java.io.InputStream;
  548.  
  549. public class MockInputStream extends InputStream {
  550.  
  551.     private final SystemInputOutputTester _systemInputOutputTester;
  552.     private final StringBuilder _inputToProvide = new StringBuilder();
  553.  
  554.     public MockInputStream(final SystemInputOutputTester systemInputOutputTester) {
  555.         _systemInputOutputTester = systemInputOutputTester;
  556.     }
  557.  
  558.     @Override
  559.     public int read() throws IOException {
  560.  
  561.         if (_inputToProvide.length() == 0) {
  562.             _systemInputOutputTester.validateToAndIncludingNextInput();
  563.         }
  564.  
  565.         if (_inputToProvide.length() == 0) {
  566.             return -1;  // = End Of File
  567.         } else {
  568.             final var firstCharacter = _inputToProvide.charAt(0);
  569.             _inputToProvide.delete(0, 1);
  570.             return firstCharacter;
  571.         }
  572.     }
  573.  
  574.     @Override
  575.     public int read(final byte byteArray[], final int offset, final int length) throws IOException {
  576.  
  577.         if (_inputToProvide.length() == 0) {
  578.             _systemInputOutputTester.validateToAndIncludingNextInput();
  579.         }
  580.  
  581.         if (_inputToProvide.length() == 0) {
  582.             return -1;  // = End Of File
  583.         } else {
  584.             int charsRead = 0;
  585.             for (; charsRead < length && _inputToProvide.length() > 0; ++charsRead) {
  586.                 final var firstCharacter = _inputToProvide.charAt(0);
  587.                 _inputToProvide.delete(0, 1);
  588.                 byteArray[offset + charsRead] = (byte) firstCharacter;
  589.             }
  590.             return charsRead;
  591.         }
  592.     }
  593.  
  594.     public void provideLineOfInput(final String lineOfInputText) {
  595.         _inputToProvide.append(lineOfInputText);
  596.         _inputToProvide.append('\n');
  597.     }
  598.  
  599. }
  600.  
  601. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  602. //
  603. // File Name:   library/ExpectedInputOutputStepBase.java
  604. //
  605. package p20230510_TestConsoleIO.library;
  606.  
  607. import junit.framework.AssertionFailedError;
  608.  
  609. abstract public class ExpectedInputOutputStepBase {
  610.  
  611.     protected final AssertionFailedError _locationOfAssertionInitialization = new AssertionFailedError(
  612.             "Location of assertion initialization. [Look up in the Test's chained method calls.]");
  613.  
  614.     protected ExpectedInputOutputStepBase() {
  615.     }
  616.  
  617.     public abstract boolean validate(final SystemInputOutputTester systemInputOutputTester);
  618.  
  619. }
  620.  
  621. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  622. //
  623. // File Name:   library/ExpectedTextOutputStep.java
  624. //
  625. package p20230510_TestConsoleIO.library;
  626.  
  627. import junit.framework.AssertionFailedError;
  628.  
  629. public class ExpectedTextOutputStep extends ExpectedInputOutputStepBase {
  630.  
  631.     private final MockStandardOutputStream _mockOutputStream;
  632.     private final String _expectedOutputMessage;
  633.  
  634.     public ExpectedTextOutputStep(final MockStandardOutputStream mockOutputStream, final String expectedOutputMessage) {
  635.         _mockOutputStream = mockOutputStream;
  636.         _expectedOutputMessage = expectedOutputMessage;
  637.     }
  638.  
  639.     @Override
  640.     public boolean validate(final SystemInputOutputTester systemInputOutputTester) {
  641.         final var stringBuilder = _mockOutputStream.getUpdatedStringBuilder();
  642.         final var currentStringValue = stringBuilder.toString();
  643.         final var foundAtIndex = currentStringValue.indexOf(_expectedOutputMessage);
  644.         if (foundAtIndex >= 0) {
  645.             // Remove the string value we just found:
  646.             stringBuilder.delete(0, foundAtIndex + _expectedOutputMessage.length());
  647.             // If followed immediately by a new line, remove that too:
  648.             final var newlineCharacterSequence = System.lineSeparator();
  649.             if (stringBuilder.toString().startsWith(newlineCharacterSequence)) {
  650.                 stringBuilder.delete(0, newlineCharacterSequence.length());
  651.             }
  652.             return false;   // Did NOT provide new *input* data.
  653.         } else {
  654.             final var remainingOutputWithNewlinesVisible = currentStringValue
  655.                     .replace("\r\n", "[NL]")
  656.                     .replace("\r", "\\r")
  657.                     .replace("\n", "\\n");
  658.             final var failEx = new AssertionFailedError("Failed to find <" + _expectedOutputMessage + "> in <" + remainingOutputWithNewlinesVisible + ">");
  659.             failEx.initCause(super._locationOfAssertionInitialization);
  660.             throw failEx;
  661.         }
  662.     }
  663. }
  664.  
  665. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  666. //
  667. // File Name:   library/ProvideLineInputStep.java
  668. //
  669. package p20230510_TestConsoleIO.library;
  670.  
  671. public class ProvideLineInputStep extends ExpectedInputOutputStepBase {
  672.  
  673.     private final String _lineOfInputText;
  674.  
  675.     public ProvideLineInputStep(final String lineOfInputText) {
  676.         _lineOfInputText = lineOfInputText;
  677.     }
  678.  
  679.     @Override
  680.     public boolean validate(final SystemInputOutputTester systemInputOutputTester) {
  681.         systemInputOutputTester.provideLineOfInput(_lineOfInputText);
  682.         return true;   // *DID* provide new *input* data.
  683.     }
  684.  
  685.     @Override
  686.     public String toString() {
  687.         return "ProvideLineInputStep('" + _lineOfInputText + "')";
  688.     }
  689.  
  690. }
  691.  
Add Comment
Please, Sign In to add comment