View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.rng.core.source32;
18  
19  import java.io.ByteArrayOutputStream;
20  import java.io.IOException;
21  import java.io.ObjectInputStream;
22  import java.io.ObjectOutputStream;
23  import java.io.Serializable;
24  import java.util.Random;
25  
26  import org.apache.commons.rng.RandomProviderState;
27  import org.apache.commons.rng.core.RandomProviderDefaultState;
28  import org.apache.commons.rng.core.util.NumberFactory;
29  import org.junit.Assert;
30  import org.junit.Test;
31  
32  public class JDKRandomTest {
33      /**
34       * A class that is Serializable.
35       * It contains member fields so there is something to serialize and malicious
36       * deserialization code.
37       */
38      static class SerializableTestObject implements Serializable {
39          private static final long serialVersionUID = 1L;
40  
41          private int state0;
42          private double state1;
43          private long state2;
44          private boolean stte3;
45  
46          /**
47           * This simulates doing something malicious when deserializing.
48           *
49           * @param input Input stream.
50           * @throws IOException if an error occurs.
51           * @throws ClassNotFoundException if an error occurs.
52           */
53          private void readObject(ObjectInputStream input)
54                  throws IOException,
55                         ClassNotFoundException {
56              Assert.fail("This should not be run during the test");
57          }
58      }
59  
60      @Test
61      public void testReferenceCode() {
62          final long refSeed = -1357111213L;
63          final JDKRandom rng = new JDKRandom(refSeed);
64          final Random jdk = new Random(refSeed);
65  
66          // This is a trivial test since "JDKRandom" delegates to "Random".
67  
68          final int numRepeats = 1000;
69          for (int r = 0; r < numRepeats; r++) {
70              Assert.assertEquals(r + " nextInt", jdk.nextInt(), rng.nextInt());
71          }
72      }
73  
74      /**
75       * Test the state can be used to restore a new instance that has not previously had a call
76       * to save the state.
77       */
78      @Test
79      public void testRestoreToNewInstance()  {
80          final long seed = 8796746234L;
81          final JDKRandom rng1 = new JDKRandom(seed);
82          final JDKRandom rng2 = new JDKRandom(seed + 1);
83  
84          // Ensure different
85          final int numRepeats = 10;
86          for (int r = 0; r < numRepeats; r++) {
87              Assert.assertNotEquals(r + " nextInt", rng1.nextInt(), rng2.nextInt());
88          }
89  
90          final RandomProviderState state = rng1.saveState();
91          // This second instance will not know the state size required to write
92          // java.util.Random to serialized form. This is only known when the saveState
93          // method is called.
94          rng2.restoreState(state);
95  
96          // Ensure the same
97          for (int r = 0; r < numRepeats; r++) {
98              Assert.assertEquals(r + " nextInt", rng1.nextInt(), rng2.nextInt());
99          }
100     }
101 
102     /**
103      * Test the deserialization code identifies bad states that do not contain a Random instance.
104      * This test exercises the code that uses a custom deserialization ObjectInputStream.
105      *
106      * @throws IOException Signals that an I/O exception has occurred.
107      */
108     @Test(expected = IllegalStateException.class)
109     public void testRestoreWithInvalidClass() throws IOException  {
110         // Serialize something
111         final ByteArrayOutputStream bos = new ByteArrayOutputStream();
112         final ObjectOutputStream oos = new ObjectOutputStream(bos);
113         oos.writeObject(new SerializableTestObject());
114         oos.close();
115 
116         // Compose the size with the state.
117         // This is what is expected by the JDKRandom class.
118         final byte[] state = bos.toByteArray();
119         final int stateSize = state.length;
120         final byte[] sizeAndState = new byte[4 + stateSize];
121         System.arraycopy(NumberFactory.makeByteArray(stateSize), 0, sizeAndState, 0, 4);
122         System.arraycopy(state, 0, sizeAndState, 4, stateSize);
123 
124         final RandomProviderDefaultState dummyState = new RandomProviderDefaultState(sizeAndState);
125 
126         final JDKRandom rng = new JDKRandom(13L);
127         // This should throw
128         rng.restoreState(dummyState);
129     }
130 }