java-topology/test/jdk/java/lang/IO/IO.java
russell@unturf.com 0a580b313d undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com

Patches, unit tests, benchmarks, whitepaper, and outreach briefs.
Public domain — no copyright claimed. Use freely.
2026-03-26 17:11:57 -04:00

199 lines
8.1 KiB
Java

/*
* Copyright (c) 2023, 2025, 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.
*/
import java.io.Writer;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.stream.Stream;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledOnOs;
import org.junit.jupiter.api.condition.OS;
import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import static org.junit.jupiter.api.Assertions.*;
/*
* @test
* @bug 8305457 8342936 8351435 8344706 8361613
* @summary java.lang.IO tests
* @library /test/lib
* @run junit IO
*/
@ExtendWith(IO.TimingExtension.class)
public class IO {
@Nested
@EnabledOnOs({OS.LINUX, OS.MAC})
public class OSSpecificTests {
private static Path expect;
@BeforeAll
public static void prepareTTY() {
expect = Paths.get("/usr/bin/expect"); // os-specific path
if (!Files.exists(expect) || !Files.isExecutable(expect)) {
Assumptions.abort("'" + expect + "' not found");
}
try {
var outputAnalyzer = ProcessTools.executeProcess(
expect.toAbsolutePath().toString(), "-version");
outputAnalyzer.reportDiagnosticSummary();
} catch (Exception _) { }
}
@ParameterizedTest
@ValueSource(strings = {"println", "print"})
public void outputTestInteractive(String mode) throws Exception {
var testSrc = System.getProperty("test.src", ".");
OutputAnalyzer output = ProcessTools.executeProcess(
expect.toString(),
Path.of(testSrc, "output.exp").toAbsolutePath().toString(),
System.getProperty("test.jdk") + "/bin/java",
Path.of(testSrc, "Output.java").toAbsolutePath().toString(),
mode);
assertEquals(0, output.getExitValue());
assertTrue(output.getStderr().isEmpty());
output.reportDiagnosticSummary();
String out = output.getStdout();
// The first half of the output is produced by Console, the second
// half is produced by IO: those halves must match.
// Executing Console and IO in the same VM (as opposed to
// consecutive VM runs, which are cleaner) to be able to compare string
// representation of objects.
assertFalse(out.isBlank());
assertEquals(out.substring(0, out.length() / 2),
out.substring(out.length() / 2));
}
/*
* This tests simulates terminal interaction (isatty), to check that the
* prompt is output.
*
* To simulate a terminal, the test currently uses the EXPECT(1) Unix
* command, which does not work for Windows. Later, a library like pty4j
* or JPty might be used instead of EXPECT, to cover both Unix and Windows.
*/
@ParameterizedTest
@MethodSource("args")
public void inputTestInteractive(String prompt) throws Exception {
var testSrc = System.getProperty("test.src", ".");
var command = new ArrayList<String>();
command.add(expect.toString());
String expectInputName = PROMPT_NONE.equals(prompt) ? "input-no-prompt"
: "input";
command.add(Path.of(testSrc, expectInputName + ".exp").toAbsolutePath().toString());
command.add(System.getProperty("test.jdk") + "/bin/java");
command.add(Path.of(testSrc, "Input.java").toAbsolutePath().toString());
command.add(prompt == null ? "0" : PROMPT_NONE.equals(prompt) ? "2" : "1");
command.add(String.valueOf(prompt));
OutputAnalyzer output = ProcessTools.executeProcess(command.toArray(new String[]{}));
output.reportDiagnosticSummary();
assertEquals(0, output.getExitValue());
}
private static final String PROMPT_NONE = "prompt-none";
public static Stream<Arguments> args() {
// prompts
return Stream.of(null, "?", "%s", PROMPT_NONE).map(Arguments::of);
}
}
@Test //JDK-8342936
public void printlnNoParamsTest() throws Exception {
var file = Path.of("PrintlnNoParams.java");
try (Writer w = Files.newBufferedWriter(file)) {
w.write("""
import static java.lang.IO.*;
void main() {
print("1 ");
print("2 ");
print("3 ");
println();
}
""");
}
var pb = ProcessTools.createTestJavaProcessBuilder("-Xlog:aot=off", "-Xlog:cds=off", file.toString());
OutputAnalyzer output = ProcessTools.executeProcess(pb);
assertEquals(0, output.getExitValue());
assertTrue(output.getStderr().isEmpty());
output.reportDiagnosticSummary();
String out = output.getStdout();
String nl = System.getProperty("line.separator");
assertEquals("1 2 3 " + nl, out);
}
// adapted from https://junit.org/junit5/docs/current/user-guide/#extensions-lifecycle-callbacks-timing-extension
// remove after CODETOOLS-7903752 propagates to jtreg that this test is routinely run by
public static class TimingExtension implements BeforeTestExecutionCallback,
AfterTestExecutionCallback {
private static final System.Logger logger = System.getLogger(
TimingExtension.class.getName());
private static final String START_TIME = "start time";
@Override
public void beforeTestExecution(ExtensionContext context) {
getStore(context).put(START_TIME, time());
}
@Override
public void afterTestExecution(ExtensionContext context) {
Method testMethod = context.getRequiredTestMethod();
long startTime = getStore(context).remove(START_TIME, long.class);
long duration = time() - startTime;
logger.log(System.Logger.Level.INFO, () ->
String.format("Method [%s] took %s ms.", testMethod.getName(), duration));
}
private ExtensionContext.Store getStore(ExtensionContext context) {
return context.getStore(ExtensionContext.Namespace.create(getClass(),
context.getRequiredTestMethod()));
}
private long time() {
return System.nanoTime() / 1_000_000;
}
}
}