JeffGrigg

Untitled

May 12th, 2023
637
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 5 14.18 KB | None | 0 0
  1. /*
  2. GuessTheNumber.java = simple “guess the number” game
  3. RandomNumberGenerator.java = wrapper for random number generation
  4.  
  5. GuessTheNumberTest.java = JUnit test for the game
  6. MockRandomNumberGenerator.java = mock implementation for single-use random number
  7.  
  8. SystemInputOutputTester.java = Builder for console I/O testing
  9. MockStandardOutputStream.java = Capture Standard Output (or Error)
  10. MockInputStream.java = Provide input when needed. Calls back to SystemInputOutputTester to validate intervening output and to provide the input data.
  11. ExpectedInputOutputStepBase.java = common base class for input/output expectations
  12. ExpectedTextOutputStep.java = Expect that the given text output is written to Standard Output (or Error).
  13. ProvideLineInputStep.java = Provide line of Console Input at that point in the expectations list.
  14. */
  15.  
  16. //
  17. // File Name:   GuessTheNumber.java
  18. //
  19.  
  20. import java.util.Scanner;
  21.  
  22. public class GuessTheNumber {
  23.  
  24.     protected static RandomNumberGenerator _randomNumberGenerator = new RandomNumberGenerator();
  25.  
  26.     public static void main(final String[] args) {
  27.         System.out.println("Welcome to the number guessing program.");
  28.         System.out.println("I am thinking of a number in the range of 1 to 100, inclusive.");
  29.         System.out.println("You need to guess this number, with a minimum number of guesses.");
  30.         System.out.println("Each time you guess, I will tell you if my number is HIGHER or LOWER than your guess.");
  31.  
  32.         final int numberToGuess = _randomNumberGenerator.nextInt(100);
  33.         final var scanner = new Scanner(System.in);
  34.  
  35.         while (true) {
  36.             System.out.println("What is your guess?");
  37.             final var userGuess = scanner.nextInt();
  38.             if (userGuess < numberToGuess) {
  39.                 System.out.println("Your guess of " + userGuess + " is TOO LOW.");
  40.             } else if (userGuess > numberToGuess) {
  41.                 System.out.println("Your guess of " + userGuess + " is TOO HIGH.");
  42.             } else {
  43.                 System.out.println("Your guess of " + userGuess + " is CORRECT!");
  44.                 break;
  45.             }
  46.         }
  47.     }
  48.  
  49. }
  50.  
  51. //
  52. // File Name:   RandomNumberGenerator.java
  53. //
  54.  
  55. import java.util.Random;
  56.  
  57. public class RandomNumberGenerator {
  58.  
  59.     private final Random _randomNumberGenerator = new Random();
  60.  
  61.     public int nextInt(final int bound) {
  62.         return _randomNumberGenerator.nextInt(bound);
  63.     }
  64.  
  65. }
  66.  
  67. //
  68. // File Name:   GuessTheNumberTest.java
  69. //
  70.  
  71. import org.junit.Test;
  72.  
  73. public class GuessTheNumberTest {
  74.  
  75.     @Test
  76.     public void test() {
  77.         final var oldRandomNumberGenerator = GuessTheNumber._randomNumberGenerator;
  78.         GuessTheNumber._randomNumberGenerator = new MockRandomNumberGenerator(33);
  79.         try {
  80.             SystemInputOutputTester
  81.                     .whenCallingThisMethod(() -> {GuessTheNumber.main(null);})
  82.                     .assertTextOutput("Welcome to the number guessing program.")
  83.                     //
  84.                     .assertTextOutput("I am thinking of a number in the range of 1 to 100, inclusive.")
  85.                     .assertTextOutput("You need to guess this number, with a minimum number of guesses.")
  86.                     .assertTextOutput("Each time you guess, I will tell you if my number is HIGHER or LOWER than your guess.")
  87.                     //
  88.                     .assertTextOutput("What is your guess?")
  89.                     .provideLineInput("50")
  90.                     .assertTextOutput("Your guess of 50 is TOO HIGH.")
  91.                     //
  92.                     .assertTextOutput("What is your guess?")
  93.                     .provideLineInput("25")
  94.                     .assertTextOutput("Your guess of 25 is TOO LOW.")
  95.                     //
  96.                     .assertTextOutput("What is your guess?")
  97.                     .provideLineInput("37")
  98.                     .assertTextOutput("Your guess of 37 is TOO HIGH.")
  99.                     //
  100.                     .assertTextOutput("What is your guess?")
  101.                     .provideLineInput("31")
  102.                     .assertTextOutput("Your guess of 31 is TOO LOW.")
  103.                     //
  104.                     .assertTextOutput("What is your guess?")
  105.                     .provideLineInput("34")
  106.                     .assertTextOutput("Your guess of 34 is TOO HIGH.")
  107.                     //
  108.                     .assertTextOutput("What is your guess?")
  109.                     .provideLineInput("32")
  110.                     .assertTextOutput("Your guess of 32 is TOO LOW.")
  111.                     //
  112.                     .assertTextOutput("What is your guess?")
  113.                     .provideLineInput("33")
  114.                     .assertTextOutput("Your guess of 33 is CORRECT!")
  115.                     //
  116.                     .assertThatTheMethodReturnsHere();
  117.         } finally {
  118.             GuessTheNumber._randomNumberGenerator = oldRandomNumberGenerator;
  119.         }
  120.     }
  121.  
  122. }
  123.  
  124. //
  125. // File Name:   MockRandomNumberGenerator.java
  126. //
  127.  
  128. public class MockRandomNumberGenerator extends RandomNumberGenerator {
  129.  
  130.     private int _theFixedRandomNumberValue;
  131.  
  132.     public MockRandomNumberGenerator(final int theFixedRandomNumberValue) {
  133.         _theFixedRandomNumberValue = theFixedRandomNumberValue;
  134.     }
  135.  
  136.     @Override
  137.     public int nextInt(final int bound) {
  138.         final var returnValue = _theFixedRandomNumberValue;
  139.         _theFixedRandomNumberValue = -1;    // Clear value, as it's only good for one use.
  140.         return returnValue;
  141.     }
  142.  
  143. }
  144.  
  145. //
  146. // File Name:   SystemInputOutputTester.java
  147. //
  148.  
  149. import junit.framework.AssertionFailedError;
  150.  
  151. import java.io.PrintStream;
  152. import java.util.ArrayList;
  153. import java.util.Iterator;
  154. import java.util.List;
  155.  
  156. public class SystemInputOutputTester {
  157.  
  158.     private final Runnable _callback;
  159.     private final List<ExpectedInputOutputStepBase> _expectedInputOutputSteps = new ArrayList<ExpectedInputOutputStepBase>();
  160.     private Iterator<ExpectedInputOutputStepBase> _expectedInputOutputStepIterator = null;
  161.     protected final MockInputStream _mockInputStream = new MockInputStream(this);
  162.     protected final MockStandardOutputStream _mockStandardOutputStream = new MockStandardOutputStream();
  163.     //protected final MockStandardOutputStream _mockStandardErrorStream = new MockStandardOutputStream(this);
  164.  
  165.     public SystemInputOutputTester(final Runnable callback) {
  166.         _callback = callback;
  167.     }
  168.  
  169.     public static SystemInputOutputTester whenCallingThisMethod(final Runnable callback) {
  170.         return new SystemInputOutputTester(callback);
  171.     }
  172.  
  173.     public SystemInputOutputTester assertTextOutput(final String expectedOutputMessage) {
  174.         _expectedInputOutputSteps.add(new ExpectedTextOutputStep(expectedOutputMessage));
  175.         return this;
  176.     }
  177.  
  178.     public SystemInputOutputTester provideLineInput(final String lineOfInputText) {
  179.         _expectedInputOutputSteps.add(new ProvideLineInputStep(lineOfInputText));
  180.         return this;
  181.     }
  182.  
  183.     public void assertThatTheMethodReturnsHere() {
  184.         final var oldInput = System.in;
  185.         final var oldOutput = System.out;
  186.         //final var oldError = System.err;
  187.         try {
  188.             System.setIn(_mockInputStream);
  189.             System.setOut(new PrintStream(_mockStandardOutputStream));
  190.             //System.setErr(new PrintStream(_mockStandardErrorStream));
  191.  
  192.             _expectedInputOutputStepIterator = _expectedInputOutputSteps.iterator();
  193.  
  194.             _callback.run();
  195.  
  196.             System.out.flush();
  197.             System.err.flush();
  198.             while (_expectedInputOutputStepIterator.hasNext()) {
  199.                 final var step = _expectedInputOutputStepIterator.next();
  200.                 final var isAfterExit = true;
  201.                 final var didProvideNewUserInput = step.validate(this);
  202.                 if (didProvideNewUserInput) {
  203.                     throw new AssertionFailedError("Program has exited, but the tests say that we should further input: <" + ((ProvideLineInputStep) step)._lineOfInputText + ">.");
  204.                 }
  205.             }
  206.  
  207.         } finally {
  208.             System.setIn(oldInput);
  209.             System.setOut(oldOutput);
  210.             //System.setErr(oldError);
  211.         }
  212.     }
  213.  
  214.     public void validateToAndIncludingNextInput() {
  215.         System.out.flush();
  216.         System.err.flush();
  217.         while (_expectedInputOutputStepIterator.hasNext()) {
  218.             final var step = _expectedInputOutputStepIterator.next();
  219.             final var isAfterExit = false;
  220.             final var didProvideNewUserInput = step.validate(this);
  221.             if (didProvideNewUserInput) {
  222.                 break;
  223.             }
  224.         }
  225.     }
  226.  
  227. }
  228.  
  229. //
  230. // File Name:   MockStandardOutputStream.java
  231. //
  232.  
  233. import org.jetbrains.annotations.NotNull;
  234. import org.junit.Assert;
  235.  
  236. import java.io.IOException;
  237. import java.io.OutputStream;
  238.  
  239. public class MockStandardOutputStream extends OutputStream {
  240.  
  241.     private final StringBuilder _sequenceOfByteValues = new StringBuilder();
  242.     private final StringBuilder _accumulatedOutput = new StringBuilder();
  243.  
  244.     @Override
  245.     public void write(final int byteValue) throws IOException {
  246.         _sequenceOfByteValues.append((char) byteValue);
  247.     }
  248.  
  249.     public StringBuilder getUpdatedStringBuilder() {
  250.  
  251.         if (_sequenceOfByteValues.length() > 0) {
  252.             final String stringValue = getStringValue();
  253.             _accumulatedOutput.append(stringValue);
  254.         }
  255.  
  256.         return _accumulatedOutput;
  257.     }
  258.  
  259.     @NotNull
  260.     private String getStringValue() {
  261.         final var bytes = new byte[_sequenceOfByteValues.length()];
  262.         final var charArray = _sequenceOfByteValues.toString().toCharArray();
  263.         Assert.assertEquals(bytes.length, charArray.length);
  264.         for (int idx = 0; idx < bytes.length; ++idx) {
  265.             bytes[idx] = (byte) charArray[idx];
  266.         }
  267.         _sequenceOfByteValues.setLength(0);
  268.         final var stringValue = new String(bytes);
  269.         return stringValue;
  270.     }
  271.  
  272. }
  273.  
  274. //
  275. // File Name:   MockInputStream.java
  276. //
  277.  
  278. import java.io.IOException;
  279. import java.io.InputStream;
  280.  
  281. public class MockInputStream extends InputStream {
  282.  
  283.     private final SystemInputOutputTester _systemInputOutputTester;
  284.     private final StringBuilder _inputToProvide = new StringBuilder();
  285.  
  286.     public MockInputStream(final SystemInputOutputTester systemInputOutputTester) {
  287.         _systemInputOutputTester = systemInputOutputTester;
  288.     }
  289.  
  290.     @Override
  291.     public int read() throws IOException {
  292.  
  293.         if (_inputToProvide.length() == 0) {
  294.             _systemInputOutputTester.validateToAndIncludingNextInput();
  295.         }
  296.  
  297.         if (_inputToProvide.length() == 0) {
  298.             return -1;  // = End Of File
  299.         } else {
  300.             final var firstCharacter = _inputToProvide.charAt(0);
  301.             _inputToProvide.delete(0, 1);
  302.             return firstCharacter;
  303.         }
  304.     }
  305.  
  306.     @Override
  307.     public int read(final byte byteArray[], final int offset, final int length) throws IOException {
  308.  
  309.         if (_inputToProvide.length() == 0) {
  310.             _systemInputOutputTester.validateToAndIncludingNextInput();
  311.         }
  312.  
  313.         if (_inputToProvide.length() == 0) {
  314.             return -1;  // = End Of File
  315.         } else {
  316.             int charsRead = 0;
  317.             for (; charsRead < length && _inputToProvide.length() > 0; ++charsRead) {
  318.                 final var firstCharacter = _inputToProvide.charAt(0);
  319.                 _inputToProvide.delete(0, 1);
  320.                 byteArray[offset + charsRead] = (byte) firstCharacter;
  321.             }
  322.             return charsRead;
  323.         }
  324.     }
  325.  
  326.     public void provideLineOfInput(final String lineOfInputText) {
  327.         _inputToProvide.append(lineOfInputText);
  328.         _inputToProvide.append('\n');
  329.     }
  330.  
  331. }
  332.  
  333. //
  334. // File Name:   ExpectedInputOutputStepBase.java
  335. //
  336.  
  337. import junit.framework.AssertionFailedError;
  338.  
  339. abstract public class ExpectedInputOutputStepBase {
  340.  
  341.     protected final AssertionFailedError _locationOfAssertionInitialization = new AssertionFailedError(
  342.             "Location of assertion initialization. [Look up in the Test's chained method calls.]");
  343.  
  344.     protected ExpectedInputOutputStepBase() {
  345.     }
  346.  
  347.     public abstract boolean validate(final SystemInputOutputTester systemInputOutputTester);
  348.  
  349. }
  350.  
  351. //
  352. // File Name:   ExpectedTextOutputStep.java
  353. //
  354.  
  355. import junit.framework.AssertionFailedError;
  356.  
  357. public class ExpectedTextOutputStep extends ExpectedInputOutputStepBase {
  358.  
  359.     private final String _expectedOutputMessage;
  360.  
  361.     public ExpectedTextOutputStep(final String expectedOutputMessage) {
  362.         _expectedOutputMessage = expectedOutputMessage;
  363.     }
  364.  
  365.     @Override
  366.     public boolean validate(final SystemInputOutputTester systemInputOutputTester) {
  367.         final var stringBuilder = systemInputOutputTester._mockStandardOutputStream.getUpdatedStringBuilder();
  368.         final var currentStringValue = stringBuilder.toString();
  369.         final var foundAtIndex = currentStringValue.indexOf(_expectedOutputMessage);
  370.         if (foundAtIndex >= 0) {
  371.             stringBuilder.delete(0, foundAtIndex + _expectedOutputMessage.length());
  372.             return false;   // Did NOT provide new *input* data.
  373.         } else {
  374.             final var remainingOutputWithNewlinesVisible = currentStringValue
  375.                     .replace("\r\n", "[NL]")
  376.                     .replace("\r", "\\r")
  377.                     .replace("\n", "\\n")
  378.                     ;
  379.             final var failEx = new AssertionFailedError("Failed to find <" + _expectedOutputMessage + "> in <" + remainingOutputWithNewlinesVisible + ">");
  380.             failEx.initCause(super._locationOfAssertionInitialization);
  381.             throw failEx;
  382.         }
  383.     }
  384. }
  385.  
  386. //
  387. // File Name:   ProvideLineInputStep.java
  388. //
  389.  
  390. public class ProvideLineInputStep extends ExpectedInputOutputStepBase {
  391.  
  392.     protected final String _lineOfInputText;
  393.  
  394.     public ProvideLineInputStep(final String lineOfInputText) {
  395.         _lineOfInputText = lineOfInputText;
  396.     }
  397.  
  398.     @Override
  399.     public boolean validate(final SystemInputOutputTester systemInputOutputTester) {
  400.         systemInputOutputTester._mockInputStream.provideLineOfInput(_lineOfInputText);
  401.         return true;   // *DID* provide new *input* data.
  402.     }
  403. }
  404.  
Add Comment
Please, Sign In to add comment