Skip to content

Commit

Permalink
8285452: Add a new test library API to replace a file content using F…
Browse files Browse the repository at this point in the history
…ileUtils.java

Backport-of: 0462d5a25258458ec7f40d76b9d910ee529f3647
  • Loading branch information
GoeLin committed Mar 14, 2024
1 parent 4b9ab0d commit 0499df3
Show file tree
Hide file tree
Showing 2 changed files with 163 additions and 1 deletion.
124 changes: 124 additions & 0 deletions test/lib-test/jdk/test/lib/FileUtilsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
* Copyright (c) 2022, 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 8285452
* @summary Unit Test for a common Test API in jdk.test.lib.util.FileUtils
* @library .. /test/lib
* @run main FileUtilsTest
*/
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import jdk.test.lib.Asserts;
import jdk.test.lib.util.FileUtils;

public class FileUtilsTest {

public static void main(String[] args) throws Exception {
// Replace with same line
test("a", 1, 1, null, "a", "a\n");
// Replace with different line
test("a", 1, 1, null, "z", "z\n");
// Replace with same line based on line match
test("a", 1, 1, "a", "a", "a\n");
// Replace with different line based on line match
test("a", 1, 1, "a", "z", "z\n");
// Replace single line with multiple lines
test("a", 1, 1, null, "x\ny\nz", "x\ny\nz\n");
// Replace single line with multiple lines based on lines match
test("a", 1, 1, "a", "x\ny\nz", "x\ny\nz\n");
// Replace all lines
test("a\nb\nc", 1, 3, null, "x\ny\nz", "x\ny\nz\n");
// Replace all lines based on lines match
test("a\nb\nc", 1, 3, "a\nb\nc", "x\ny\nz", "x\ny\nz\n");
// Replace all lines with single line based on lines match
test("a\nb\nc", 1, 3, "a\nb\nc", "z", "z\n");
// Replace single line
test("a\nb\nc", 1, 1, null, "z", "z\nb\nc\n");
// Replace single line based on line match
test("a\nb\nc", 1, 1, "a", "z", "z\nb\nc\n");
// Replace multiple lines
test("a\nb\nc", 1, 2, null, "z", "z\nc\n");
// Replace multiple lines based on line match
test("a\nb\nc", 1, 2, "a\nb", "z", "z\nc\n");
// Replace multiple lines based on line match
test("a\nb\nc", 1, 2, "a\nb", "x\ny\nz", "x\ny\nz\nc\n");

// Test with space characters
// Replace with space line
test("\n", 1, 1, null, " ", " \n");
// Replace empty line with another line based on line match
test("\n", 1, 1, " ", "a", "a\n");
// Replace with space line
test(" \na\nb\nc", 1, 1, null, "a", "a\na\nb\nc\n");
// Replace empty line with different line based on line match
test(" \na\nb\nc", 1, 1, " ", "a", "a\na\nb\nc\n");
// Replace range of lines with space to different lines based on line match
test(" \na\nb\nc", 1, 2, " \na", "x\ny\nz", "x\ny\nz\nb\nc\n");

test("a\nb\nc\n", 1, 2, "a\nb", "1\n2", "1\n2\nc\n");
test("a\nb\nc\n", 1, 2, " a\nb", "1\n2", "1\n2\nc\n"); // free to add spaces around a line in from
test("a\nb\nc\n", 1, 2, "a \nb", "1\n2", "1\n2\nc\n");
test("a\nb\nc\n", 1, 2, "a\n b ", "1\n2", "1\n2\nc\n");
test("a\nb\nc\n", 1, 2, "\na\nb", "1\n2", "1\n2\nc\n"); // free to add empty lines at both ends of from
test("a\nb\nc\n", 1, 2, "a\nb\n", "1\n2", "1\n2\nc\n");
test("a\nb\nc\n", 1, 2, "\na\nb\n", "1\n2", "1\n2\nc\n");
test("a\nb\nc\n", 1, 2, "\n\na\nb\n\n", "1\n2", "1\n2\nc\n");
test("a\nb\nc\n", 1, 2, "a\nb", "1\n2\n", "1\n2\nc\n"); // ok to add a new line at end of to
test("a\nb\nc\n", 1, 2, "a\nb", "1\n2\n\n", "1\n2\n\nc\n"); // more will change result
test("a\nb\nc\n", 1, 2, "a\nb", "\n1\n2", "\n1\n2\nc\n");
test("a\nb\nc\n", 1, 2, "a\n\nb", "1\n2\n", null); // no extra new line inside
test("a\nb\nc\n", 1, 2, "ab", "1\n2\n", null); // new line inside must preserve
test("a\nb\nc\n", 1, 2, "a", "1\n2\n", null); // must be all
test("a\nb\nc\n", 1, 2, "b", "1\n2\n", null); // must be all

test("a\nb\nc\n", 1, 2, "a\nb", "", "c\n"); // just remove
test("a\nb\nc\n", 1, 0, "", "1\n2", "1\n2\na\nb\nc\n"); // just add

// Mismatched range with "replace" lines Tests
// Replace all lines with mismatched line
test("a", 1, 1, "z", "z", null);
// Replace all lines with mismatched lines
test("a\nb\nc", 1, 3, "x\ny\nz", "x\ny\nz", null);
// Replace single line with mismatched line
test("a\nb\nc", 1, 1, "z", "z", null);
// Replace a range of lines with mismatched lines
test("a\nb\nc", 1, 3, "ab", "x\ny\nz", null);
}

private static void test(String content, int from, int to, String replace,
String replaceTo, String expected) throws IOException {
String name = "Test-" + new Exception().getStackTrace()[1].getLineNumber();
Path path = Files.writeString(Paths.get(name), content);
String output = null;
try {
FileUtils.patch(path, from, to, replace, replaceTo);
output = Files.readString(path);
} catch (IOException e) {
// output is null
}
Asserts.assertEQ(output, (expected != null) ? expected.replaceAll("\n", System.lineSeparator()) : null);
}
}
40 changes: 39 additions & 1 deletion test/lib/jdk/test/lib/util/FileUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

import java.util.stream.Collectors;
import jdk.test.lib.Platform;

import com.sun.management.UnixOperatingSystemMXBean;
Expand Down Expand Up @@ -377,6 +377,44 @@ public static long getProcessHandleCount() {
}
}

/**
* Patches a part of a file.
*
* @param path the file
* @param fromLine the first line to patch. This is the number you see in an editor, 1-based, inclusive.
* @param toLine the last line to patch. This is the number you see in an editor, inclusive.
* Set {@code toLine} to {@code fromLine - 1} if you only want to insert lines.
* @param from lines to remove, used to ensure the correct lines are removed. Can be multiple lines or empty.
* It's compared to existing lines with all lines trimmed and no new lines at both ends. Ignored if null.
* @param to the newly added lines, can be multiple lines or empty. New line at end is optional. Cannot be null.
* @throws IOException if there's an I/O error or {@code from} does not match the existing lines
* @throws IndexOutOfBoundsException if {@code fromLine} or {@code toLine} is invalid
*/
public static void patch(Path path, int fromLine, int toLine, String from, String to) throws IOException {
var lines = Files.readAllLines(path);
// The next line does a from/to as well
var subList = lines.subList(fromLine - 1, toLine);
if (from != null) {
// Each line is trimmed so caller needs not care about indentation.
// Caller also needs not care about new lines on both ends.
// New lines inside are preserved.
String actuallyRemoved = subList.stream()
.map(String::trim)
.collect(Collectors.joining("\n")).trim();
String wantToRemove = from.lines()
.map(String::trim)
.collect(Collectors.joining("\n")).trim();
if (!actuallyRemoved.equals(wantToRemove)) {
throw new IOException("Removed not the same: ["
+ String.join("\\n", subList) + "] and ["
+ from.replaceAll("\\n", "\\\\n") + "]");
}
}
subList.clear();
lines.addAll(fromLine - 1, to.lines().toList());
Files.write(path, lines);
}

private static native long getWinProcessHandleCount();

// Possible command locations and arguments
Expand Down

1 comment on commit 0499df3

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.