001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.rng.examples.stress;
018
019import picocli.CommandLine;
020import picocli.CommandLine.RunLast;
021
022/**
023 * Executes testing utilities for the random number generators in the Commons RNG library.
024 *
025 * <p>The principle action is testing a generator by piping the values returned by its
026 * {@link org.apache.commons.rng.UniformRandomProvider#nextInt()
027 * UniformRandomProvider.nextInt()} method to a program that reads {@code int} values from
028 * its standard input and writes an analysis report to standard output. The <a
029 * href="http://www.phy.duke.edu/~rgb/General/dieharder.php"> "Dieharder"</a> test suite
030 * is such a software.</p>
031 *
032 * <p>Example of command line, assuming that "examples-stress.jar" specifies this
033 * class as the "main" class (see {@link #main(String[]) main} method):</p>
034 *
035 * <pre>{@code $ java -jar examples-stress.jar stress /usr/bin/dieharder -a -g 200 -Y 1 -k 2}</pre>
036 *
037 * <p>Other functionality includes:</p>
038 *
039 * <ul>
040 *   <li>Listing all the available generators
041 *   <li>Collating results from known stress test applications
042 *   <li>Outputting data from a random generator
043 *   <li>Showing the platform native byte order
044 *   <li>Testing data transfer to an application sub-process via its standard input
045 * </ul>
046 */
047public final class ExamplesStressApplication {
048    /** No public constructor. */
049    private ExamplesStressApplication() {}
050
051    /**
052     * Run the RNG examples stress command line application.
053     *
054     * @param args Application's arguments.
055     */
056    public static void main(String[] args) {
057        // Build the command line manually so we can configure options.
058        final CommandLine cmd = new CommandLine(new ExamplesStressCommand())
059                .addSubcommand("bridge",  new CommandLine(new BridgeTestCommand())
060                                                          .setStopAtPositional(true))
061                .addSubcommand("endian",  new EndianessCommand())
062                .addSubcommand("list",    new ListCommand())
063                .addSubcommand("output",  new CommandLine(new OutputCommand())
064                                                          // Allow the input seed using hex (0x, 0X, #)
065                                                          // or octal (starting with 0)
066                                                          .registerConverter(Long.class, Long::decode))
067                .addSubcommand("results", new ResultsCommand())
068                .addSubcommand("stress",  new CommandLine(new StressTestCommand())
069                                                          .setStopAtPositional(true))
070                // Call last to apply to all sub-commands
071                .setCaseInsensitiveEnumValuesAllowed(true);
072
073        try {
074            // Parse the command line and invokes the Callable program (RNGUtilities)
075            cmd.parseWithHandler(new RunLast(), args);
076        } catch (final CommandLine.ExecutionException ex) {
077            final Throwable cause = ex.getCause();
078            if (cause != null) {
079                // If this was an exception generated by the application then the full
080                // stack trace is not needed depending on log level. This limits stack
081                // trace output to unexpected errors in the common use case.
082                if (cause instanceof ApplicationException &&
083                        !LogUtils.isLoggable(LogUtils.LogLevel.DEBUG)) {
084                    LogUtils.error(cause.getMessage());
085                } else {
086                    LogUtils.error(cause, cause.getMessage());
087                }
088                System.exit(1);
089            }
090            // No cause so re-throw. This may be a Picocli parsing error.
091            throw ex;
092        }
093    }
094}