GlslReduce { public static void mainHelper( String[] args, FuzzerServiceManager.Iface managerOverride) throws ArgumentParserException, IOException, ParseTimeoutException, InterruptedException, GlslParserException { ArgumentParser parser = getParser(); Namespace ns = parser.parseArgs(args); final File workDir = ns.get("output"); ShaderJobFileOperations fileOps = new ShaderJobFileOperations(); fileOps.mkdir(workDir); final File inputShaderJobFile = ns.get("shader_job"); String shaderJobShortName = FilenameUtils.removeExtension(inputShaderJobFile.getName()); try { if (ns.get("reduction_kind").equals(ReductionKind.VALIDATOR_ERROR) && ns.get("error_string") == null) { throw new ArgumentParserException( "If reduction kind is " + ReductionKind.VALIDATOR_ERROR + " then --error-string must be provided.", parser); } ReductionKind reductionKind = null; try { reductionKind = ReductionKind.valueOf(((String) ns.get("reduction_kind")).toUpperCase()); } catch (IllegalArgumentException exception) { throw new ArgumentParserException( "unknown reduction kind argument found: " + ns.get("reduction_kind"), parser); } ImageComparisonMetric metric = null; try { metric = ImageComparisonMetric.valueOf(((String) ns.get("metric")).toUpperCase()); } catch (IllegalArgumentException exception) { throw new ArgumentParserException( "unknown metric argument found: " + ns.get("metric"), parser); } final double threshold = ns.get("threshold"); @SuppressWarnings("unused") Integer timeout = ns.get("timeout"); final Integer maxSteps = ns.get("max_steps"); final Integer retryLimit = ns.get("retry_limit"); final Boolean verbose = ns.get("verbose"); final boolean skipRender = ns.get("skip_render"); final IRandom random = new RandomWrapper(ArgsUtil.getSeedArgument(ns)); final String errorString = ns.get("error_string"); final boolean reduceEverywhere = !ns.getBoolean("preserve_semantics"); final boolean stopOnError = ns.get("stop_on_error"); final String server = ns.get("server"); final String worker = ns.get("worker"); final boolean usingSwiftshader = ns.get("swiftshader"); final boolean continuePreviousReduction = ns.get("continue_previous_reduction"); final boolean literalsToUniforms = ns.get("literals_to_uniforms"); if (managerOverride != null && (server == null || worker == null)) { throw new ArgumentParserException( "Must supply server (dummy string) and worker when executing in server process.", parser); } if (server != null && worker == null) { throw new ArgumentParserException("If --server is used then --worker is required", parser); } if (server == null && worker != null) { LOGGER.warn("Warning: --worker ignored, as it is used without --server"); } if (server != null && usingSwiftshader) { LOGGER.warn("Warning: --swiftshader ignored, as --server is being used"); } final File referenceResultFile = ns.get("reference"); final List<String> customJudgeScript = ns.get("interestingness_test"); if (reductionKind == ReductionKind.CUSTOM) { if (server != null) { throwExceptionForCustomReduction("server"); } if (worker != null) { throwExceptionForCustomReduction("worker"); } if (errorString != null) { throwExceptionForCustomReduction("error-string"); } if (referenceResultFile != null) { throwExceptionForCustomReduction("reference"); } if (customJudgeScript.isEmpty()) { throw new RuntimeException("A custom reduction requires an interestingness test to be " + "specified."); } File scriptFile = new File(customJudgeScript.get(0)); if (scriptFile.exists() && !scriptFile.canExecute()) { throw new RuntimeException("Custom judge script must be executable."); } } else { if (!customJudgeScript.isEmpty()) { throw new RuntimeException("An interestingness test is only supported when a custom " + "reduction is used."); } } fileOps.assertShaderJobRequiredFilesExist(inputShaderJobFile); if (referenceResultFile != null) { fileOps.doesShaderJobResultFileExist(referenceResultFile); } if (continuePreviousReduction) { fileOps.assertExists(new File(workDir, Constants.REDUCTION_INCOMPLETE)); } File copiedShaderJobFile = new File(workDir, inputShaderJobFile.getName()); fileOps.copyShaderJobFileTo(inputShaderJobFile, copiedShaderJobFile, true); File referenceResultFileCopy = null; if (referenceResultFile != null) { referenceResultFileCopy = new File(workDir, "reference_image.info.json"); fileOps.copyShaderJobResultFileTo(referenceResultFile, referenceResultFileCopy, true); } IFileJudge fileJudge; IShaderDispatcher imageGenerator = server == null || server.isEmpty() || server.equals(".") ? new LocalShaderDispatcher( usingSwiftshader, fileOps, new File(workDir, "temp")) : new RemoteShaderDispatcher( server + "/manageAPI", worker, managerOverride, new AtomicLong(), retryLimit); File corpus = new File(workDir, "corpus"); switch (reductionKind) { case CUSTOM: fileJudge = new CustomFileJudge(customJudgeScript); break; case NO_IMAGE: fileJudge = new ImageGenErrorShaderFileJudge( (errorString == null || errorString.isEmpty() ? null : errorString), imageGenerator, skipRender, stopOnError, fileOps); break; case NOT_IDENTICAL: fileJudge = new ImageShaderFileJudge( referenceResultFileCopy, imageGenerator, stopOnError, new ExactImageFileComparator(false, fileOps), fileOps); break; case IDENTICAL: fileJudge = new ImageShaderFileJudge( referenceResultFileCopy, imageGenerator, stopOnError, new ExactImageFileComparator(true, fileOps), fileOps); break; case BELOW_THRESHOLD: fileJudge = new ImageShaderFileJudge( referenceResultFileCopy, imageGenerator, stopOnError, new MetricImageFileComparator(threshold, false, metric, fileOps), fileOps); break; case ABOVE_THRESHOLD: fileJudge = new ImageShaderFileJudge( referenceResultFileCopy, imageGenerator, stopOnError, new MetricImageFileComparator(threshold, true, metric, fileOps), fileOps); break; case VALIDATOR_ERROR: fileJudge = new ValidatorErrorShaderFileJudge(errorString.isEmpty() ? null : errorString); break; case ALWAYS_REDUCE: fileJudge = (shaderJobFile, shaderResultFile) -> true; break; case FUZZ: fileJudge = new FuzzingFileJudge(corpus, imageGenerator); break; default: throw new ArgumentParserException( "Unsupported reduction kind: " + reductionKind, parser); } doReductionHelper( inputShaderJobFile, shaderJobShortName, random, fileJudge, workDir, maxSteps, reduceEverywhere, continuePreviousReduction, literalsToUniforms, verbose, fileOps); } catch (Throwable throwable) { final File exceptionFile = ReductionProgressHelper.getReductionExceptionFile(workDir, shaderJobShortName); fileOps.writeStringToFile( exceptionFile, ExceptionUtils.getStackTrace(throwable) ); throw throwable; } }  static void main(String[] args); static void mainHelper(
        String[] args,
        FuzzerServiceManager.Iface managerOverride); static void doReductionHelper(
      File initialShaderJobFile,
      String outputShortName,
      IRandom random,
      IFileJudge fileJudge,
      File workDir,
      int stepLimit,
      boolean reduceEverywhere,
      boolean continuePreviousReduction,
      boolean literalsToUniforms,
      boolean verbose,
      ShaderJobFileOperations fileOps); static final String METRICS_HELP_SHARED; }
@Test public void noServerAllowedInCustomReduction() throws Exception { try { GlslReduce.mainHelper(new String[]{"--server", "some_server", "--worker", "some_worker", makeShaderJobAndReturnJsonFilename(), "--reduction-kind", "CUSTOM", "--output", temporaryFolder.getRoot().getAbsolutePath()}, null); fail(); } catch (RuntimeException exception) { checkOptionNotAllowed(exception, "server"); } }
@Test public void noWorkerNameAllowedInCustomReduction() throws Exception { try { GlslReduce.mainHelper(new String[]{"--worker", "some_worker", makeShaderJobAndReturnJsonFilename(), "--reduction-kind", "CUSTOM", "--output", temporaryFolder.getRoot().getAbsolutePath()}, null); assertTrue(false); } catch (RuntimeException exception) { checkOptionNotAllowed(exception, "worker"); } }
@Test public void noErrorStringAllowedInCustomReduction() throws Exception { try { GlslReduce.mainHelper(new String[]{"--error-string", "some_string", makeShaderJobAndReturnJsonFilename(), "--reduction-kind", "CUSTOM", "--output", temporaryFolder.getRoot().getAbsolutePath()}, null); assertTrue(false); } catch (RuntimeException exception) { checkOptionNotAllowed(exception, "error-string"); } }
@Test public void noReferenceAllowedInCustomReduction() throws Exception { try { GlslReduce.mainHelper(new String[]{"--reference", "reference.info.json", makeShaderJobAndReturnJsonFilename(), "--reduction-kind", "CUSTOM", "--output", temporaryFolder.getRoot().getAbsolutePath()}, null); assertTrue(false); } catch (RuntimeException exception) { checkOptionNotAllowed(exception, "reference"); } }
@Test public void noCustomJudgeAllowedInNonCustomReduction() throws Exception { try { GlslReduce.mainHelper(new String[]{makeShaderJobAndReturnJsonFilename(), "--reduction-kind", "NO_IMAGE", "somejudgescript", "--output", temporaryFolder.getRoot().getAbsolutePath()}, null); fail(); } catch (RuntimeException exception) { assertTrue(exception.getMessage().contains("An interestingness test is only supported when " + "a custom reduction is used.")); } }
@Test public void customJudgeRequiredInCustomReduction() throws Exception { try { GlslReduce.mainHelper(new String[]{makeShaderJobAndReturnJsonFilename(), "--reduction-kind", "CUSTOM", "--output", temporaryFolder.getRoot().getAbsolutePath()}, null); fail(); } catch (RuntimeException exception) { assertTrue(exception.getMessage().contains("A custom reduction requires an interestingness " + "test to be specified.")); } }
@Test public void checkCustomJudgeIsExecutable() throws Exception { if (ExecHelper.IS_WINDOWS) { return; } final File jsonFile = getShaderJobReady(); final File emptyFile = temporaryFolder.newFile(); try { GlslReduce.mainHelper(new String[]{ jsonFile.getAbsolutePath(), "--reduction-kind", "CUSTOM", emptyFile.getAbsolutePath(), "--output", temporaryFolder.getRoot().getAbsolutePath()}, null); fail("An exception should have been thrown as the judge script is not executable."); } catch (RuntimeException exception) { assertTrue(exception.getMessage().contains("judge script must be executable")); } }
@Test public void testAlwaysReduceJudgeMaximallyReduces() throws Exception { final File jsonFile = getShaderJobReady(); final File emptyFile = temporaryFolder.newFile("judge.bat"); emptyFile.setExecutable(true); GlslReduce.mainHelper(new String[]{ jsonFile.getAbsolutePath(), "--reduction-kind", "CUSTOM", emptyFile.getAbsolutePath(), "--output", temporaryFolder.getRoot().getAbsolutePath()}, null); final File[] reducedFinal = temporaryFolder.getRoot().listFiles((dir, name) -> name.contains( "reduced_final.frag")); assertEquals(1, reducedFinal.length); CompareAsts.assertEqualAsts("#version 100\nvoid main() { }", ParseHelper.parse(reducedFinal[0])); }