| /* |
| * Copyright 2014-2015 Open Networking Laboratory |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.onlab.junit; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.io.Files; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.List; |
| import java.util.Random; |
| |
| import static com.google.common.base.Preconditions.checkArgument; |
| import static org.junit.Assert.fail; |
| |
| /** |
| * Utilities to aid in producing JUnit tests. |
| */ |
| public final class TestTools { |
| |
| private static final Random RANDOM = new Random(); |
| |
| // Prohibit construction |
| private TestTools() { |
| } |
| |
| public static void print(String msg) { |
| System.out.print(msg); |
| } |
| |
| /** |
| * Suspends the current thread for a specified number of millis. |
| * |
| * @param ms number of millis |
| */ |
| public static void delay(int ms) { |
| try { |
| Thread.sleep(ms); |
| } catch (InterruptedException e) { |
| fail("test interrupted"); |
| } |
| } |
| |
| /** |
| * Returns the current time in millis since epoch. |
| * |
| * @return current time |
| */ |
| public static long now() { |
| return System.currentTimeMillis(); |
| } |
| |
| /** |
| * Runs the specified runnable until it completes successfully or until the |
| * specified time expires. If the latter occurs, the first encountered |
| * assertion on the last attempt will be re-thrown. Errors other than |
| * assertion errors will be propagated immediately. |
| * <p> |
| * Assertions attempts will not be closer than 10 millis apart and no |
| * further than 50 millis. |
| * </p> |
| * |
| * @param delay number of millis to delay before the first attempt |
| * @param duration number of milliseconds beyond the current time |
| * @param assertions test assertions runnable |
| */ |
| public static void assertAfter(int delay, int duration, Runnable assertions) { |
| checkArgument(delay < duration, "delay >= duration"); |
| long start = now(); |
| int step = Math.max(Math.min((duration - delay) / 100, 50), 10); |
| |
| // Is there an initial delay? |
| if (delay > 0) { |
| delay(delay); |
| } |
| |
| // Keep going until the assertions succeed or until time runs-out. |
| while (true) { |
| try { |
| assertions.run(); |
| break; |
| } catch (AssertionError e) { |
| // If there was an error and time ran out, re-throw it. |
| if (now() - start > duration) { |
| throw e; |
| } |
| } |
| delay(step); |
| } |
| } |
| |
| /** |
| * Runs the specified runnable until it completes successfully or until the |
| * specified time expires. If the latter occurs, the first encountered |
| * assertion on the last attempt will be re-thrown. Errors other than |
| * assertion errors will be propagated immediately. |
| * <p> |
| * Assertions attempts will not be closer than 10 millis apart and no |
| * further than 50 millis. |
| * </p> |
| * |
| * @param duration number of milliseconds beyond the current time |
| * @param assertions test assertions runnable |
| */ |
| public static void assertAfter(int duration, Runnable assertions) { |
| assertAfter(0, duration, assertions); |
| } |
| |
| |
| /** |
| * Creates a directory tree of test files. To signify creating a directory |
| * file path should end with '/'. |
| * |
| * @param paths list of file paths |
| * @return list of created files |
| * @throws java.io.IOException if there is an issue |
| */ |
| public static List<File> createTestFiles(List<String> paths) throws IOException { |
| return createTestFiles(paths, 32, 1024); |
| } |
| |
| /** |
| * Creates a directory tree of test files. To signify creating a directory |
| * file path should end with '/'. |
| * |
| * @param paths list of file paths |
| * @param minSize minimum file size in bytes |
| * @param maxSize maximum file size in bytes |
| * @return list of created files |
| * @throws java.io.IOException if there is an issue |
| */ |
| public static List<File> createTestFiles(List<String> paths, |
| int minSize, int maxSize) throws IOException { |
| ImmutableList.Builder<File> files = ImmutableList.builder(); |
| for (String p : paths) { |
| File f = new File(p); |
| if (p.endsWith("/")) { |
| if (f.mkdirs()) { |
| files.add(f); |
| } |
| } else { |
| Files.createParentDirs(f); |
| if (f.createNewFile()) { |
| writeRandomFile(f, minSize, maxSize); |
| files.add(f); |
| } |
| } |
| } |
| return files.build(); |
| } |
| |
| /** |
| * Writes random binary content into the specified file. The number of |
| * bytes will be random between the given minimum and maximum. |
| * |
| * @param file file to write data to |
| * @param minSize minimum number of bytes to write |
| * @param maxSize maximum number of bytes to write |
| * @throws IOException if there is an issue |
| */ |
| public static void writeRandomFile(File file, int minSize, int maxSize) throws IOException { |
| int size = minSize + (minSize == maxSize ? 0 : RANDOM.nextInt(maxSize - minSize)); |
| byte[] data = new byte[size]; |
| tweakBytes(RANDOM, data, size / 4); |
| Files.write(data, file); |
| } |
| |
| |
| /** |
| * Tweaks the given number of bytes in a byte array. |
| * |
| * @param random random number generator |
| * @param data byte array to be tweaked |
| * @param count number of bytes to tweak |
| */ |
| public static void tweakBytes(Random random, byte[] data, int count) { |
| tweakBytes(random, data, count, 0, data.length); |
| } |
| |
| /** |
| * Tweaks the given number of bytes in the specified range of a byte array. |
| * |
| * @param random random number generator |
| * @param data byte array to be tweaked |
| * @param count number of bytes to tweak |
| * @param start index at beginning of range (inclusive) |
| * @param end index at end of range (exclusive) |
| */ |
| public static void tweakBytes(Random random, byte[] data, int count, |
| int start, int end) { |
| int len = end - start; |
| for (int i = 0; i < count; i++) { |
| data[start + random.nextInt(len)] = (byte) random.nextInt(); |
| } |
| } |
| |
| } |