test/jdk/java/util/Collection/RandomizedIteration.java
author smarks
Fri, 25 May 2018 11:18:22 -0700
changeset 50272 dd2867f9e671
permissions -rw-r--r--
8201518: add test for randomized iteration order of unmodifiable Set and Map Reviewed-by: plevart

/*
 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

/*
 * @test
 * @bug     8201518
 * @key     randomness
 * @summary Ensure that randomized iteration order of unmodifiable sets
 *          and maps is actually randomized. Must be run othervm so that
 *          the per-VM-instance salt value differs.
 * @run main/othervm RandomizedIteration 0
 * @run main/othervm RandomizedIteration 1
 * @run main/othervm RandomizedIteration 2
 * @run main/othervm RandomizedIteration 3
 * @run main/othervm RandomizedIteration 4
 * @run main/othervm RandomizedIteration verify 5
 */

import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import static java.util.stream.Collectors.toUnmodifiableMap;

/**
 * Test of randomized iteration of unmodifiable sets and maps.
 *
 * Usage: RandomizedIteration n
 *            - writes files suffixed with 'n' containing set elements and map keys
 *              in iteration order
 *        RandomizedIteration "verify" count
 *            - reads files 0..count-1 and checks to ensure that their orders differ
 *
 * The idea is to generate several test files by invoking this test with an arg
 * of 0 through count-1. Then invoke the test once more with two args, the first being
 * the word "verify" and the second arg being the count. This will read all the generated
 * files and perform verification.
 *
 * The test is considered to pass if any of the runs result in different iteration
 * orders. The randomization is not actually very random, so over many runs there is
 * the possibility of a couple of the test files having the same order. That's ok, as
 * long as the iteration order is usually different. The test fails if *all* of the
 * iteration orders are the same.
 */
public class RandomizedIteration {
    /**
     * Generates a set and a map from the word array, and then writes
     * text files "set.#" and "map.#" containing the set elements and
     * map keys in iteration order.
     *
     * @param suffix number used for the file suffix
     */
    static void writeFiles(int suffix) throws IOException {
        try (PrintStream setOut = new PrintStream("set." + suffix)) {
            Set.of(WORDS)
               .forEach(setOut::println);
        }

        try (PrintStream mapOut = new PrintStream("map." + suffix)) {
            var map = Map.ofEntries(Arrays.stream(WORDS)
                                          .map(word -> Map.entry(word, ""))
                                          .toArray(Map.Entry<?, ?>[]::new));
            map.keySet()
               .forEach(mapOut::println);
        }
    }

    /**
     * Reads lines from each file derived from the prefix and index from 0..count-1
     * into a list, computes its hashcode, and returns a set of those hashcodes.
     * The hashcode of the list is order sensitive, so the same lines in a different
     * order should have different hashcodes.
     *
     * @param prefix the file prefix
     * @param count the number of files to read
     * @return a set of hashcodes of each file
     */
    static Set<Integer> readFiles(String prefix, int count) throws IOException {
        Set<Integer> hashes = new HashSet<>();
        for (int suffix = 0; suffix < count; suffix++) {
            String name = prefix + suffix;
            int hash = Files.readAllLines(Paths.get(name)).hashCode();
            System.out.println(name + ": " + hash);
            hashes.add(hash);
        }
        return hashes;
    }

    /**
     * Test main routine.
     *
     * @param args n | "verify" count
     * @throws IOException if an error occurred
     */
    public static void main(String[] args) throws IOException {
        if ("verify".equals(args[0])) {
            int count = Integer.parseInt(args[1]);
            System.out.println("Verifying " + count + " files.");
            Set<Integer> setHashes = readFiles("set.", count);
            Set<Integer> mapHashes = readFiles("map.", count);
            if (setHashes.size() > 1 && mapHashes.size() > 1) {
                System.out.println("Passed: differing iteration orders were detected.");
            } else {
                throw new AssertionError("FAILED: iteration order not randomized!");
            }
        } else {
            int suffix = Integer.parseInt(args[0]);
            System.out.println("Generating files: " + suffix);
            writeFiles(suffix);
        }
    }

    /**
     * List of 63 words of 22 or more letters from BSD /usr/share/dict/words.
     */
    static final String[] WORDS = {
        "anatomicophysiological",
        "anthropomorphologically",
        "aquopentamminecobaltic",
        "blepharoconjunctivitis",
        "blepharosphincterectomy",
        "cholecystenterorrhaphy",
        "cholecystoduodenostomy",
        "choledochoduodenostomy",
        "counterexcommunication",
        "dacryocystoblennorrhea",
        "dacryocystosyringotomy",
        "deanthropomorphization",
        "duodenocholecystostomy",
        "electroencephalography",
        "electrotelethermometer",
        "epididymodeferentectomy",
        "formaldehydesulphoxylate",
        "formaldehydesulphoxylic",
        "gastroenteroanastomosis",
        "hematospectrophotometer",
        "hexamethylenetetramine",
        "hexanitrodiphenylamine",
        "historicocabbalistical",
        "hydropneumopericardium",
        "hyperconscientiousness",
        "laparocolpohysterotomy",
        "lymphangioendothelioma",
        "macracanthrorhynchiasis",
        "microcryptocrystalline",
        "naphthylaminesulphonic",
        "nonrepresentationalism",
        "omnirepresentativeness",
        "pancreaticoduodenostomy",
        "pancreaticogastrostomy",
        "pathologicohistological",
        "pathologicopsychological",
        "pericardiomediastinitis",
        "phenolsulphonephthalein",
        "philosophicohistorical",
        "philosophicotheological",
        "photochronographically",
        "photospectroheliograph",
        "pneumohydropericardium",
        "pneumoventriculography",
        "polioencephalomyelitis",
        "Prorhipidoglossomorpha",
        "Pseudolamellibranchiata",
        "pseudolamellibranchiate",
        "pseudomonocotyledonous",
        "pyopneumocholecystitis",
        "scientificogeographical",
        "scientificophilosophical",
        "scleroticochorioiditis",
        "stereophotomicrography",
        "tetraiodophenolphthalein",
        "theologicoastronomical",
        "theologicometaphysical",
        "thymolsulphonephthalein",
        "thyroparathyroidectomize",
        "thyroparathyroidectomy",
        "transubstantiationalist",
        "ureterocystanastomosis",
        "zoologicoarchaeologist"
    };
}