8379432: jpackage: Make default equals() in jdk.jpackage.test.CannedFormattedString class work as expected

Reviewed-by: almatvee
This commit is contained in:
Alexey Semenyuk 2026-03-10 23:46:53 +00:00
parent a4daabbbfa
commit 9d04c8a8af
8 changed files with 218 additions and 46 deletions

View File

@ -0,0 +1,39 @@
/*
* Copyright (c) 2026, 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.
*/
package jdk.jpackage.test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.nio.file.Path;
import jdk.jpackage.test.CannedFormattedStringTest.Formatter;
import org.junit.jupiter.api.Test;
class CannedArgumentTest {
@Test
void test_cannedAbsolutePath() {
var a = Formatter.MESSAGE_FORMAT.create("Current directory: {0}", CannedArgument.cannedAbsolutePath("foo"));
assertEquals("Current directory: " + Path.of("foo").toAbsolutePath(), a.getValue());
assertEquals("Current directory: {0}+[AbsolutePath(foo)]", a.toString());
}
}

View File

@ -0,0 +1,99 @@
/*
* Copyright (c) 2026, 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.
*/
package jdk.jpackage.test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import java.text.MessageFormat;
import java.util.List;
import java.util.function.BiFunction;
import org.junit.jupiter.api.Test;
class CannedFormattedStringTest {
@Test
void test_getValue() {
var a = Formatter.MESSAGE_FORMAT.create("Hello {0}! Bye {0}", "Duke");
assertEquals("Hello Duke! Bye Duke", a.getValue());
assertEquals("Hello {0}! Bye {0}+[Duke]", a.toString());
var b = Formatter.MESSAGE_FORMAT.create("Hello {0}! Bye {0}");
assertEquals("Hello {0}! Bye {0}", b.getValue());
assertEquals("Hello {0}! Bye {0}", b.toString());
}
@Test
void test_equals() {
var a = Formatter.MESSAGE_FORMAT.create("Hello {0}! Bye {0}", "Duke");
var b = Formatter.MESSAGE_FORMAT.create("Hello {0}! Bye {0}", "Duke");
assertEquals(a, b);
a = Formatter.MESSAGE_FORMAT.create("Hello {0}! Bye {0}", "Duke");
b = Formatter.MESSAGE_FORMAT.create("Hello {0}! Bye {0}", "Java");
assertNotEquals(a, b);
}
@Test
void test_addPrefix() {
var a = Formatter.MESSAGE_FORMAT.create("Hello {0}! Bye {0}", "Duke");
var b = a.addPrefix("{0} and {0}").addPrefix("They say: {0}");
var str = "Hello Duke! Bye Duke";
assertEquals(str, a.getValue());
assertEquals("They say: " + str + " and " + str, b.getValue());
assertEquals("They say: {0}+[{0} and {0}, Hello {0}! Bye {0}, Duke]", b.toString());
var c = a.addPrefix("{0} and {0}").addPrefix("They say: {0}");
assertEquals(c, b);
assertNotSame(b, c);
}
@Test
void test_mapArgs() {
var a = Formatter.MESSAGE_FORMAT.create("Hello {0}! Bye {0}", "Duke");
var b = a.mapArgs(arg -> {
assertEquals("Duke", arg);
return "Java";
});
assertEquals("Hello Duke! Bye Duke", a.getValue());
assertEquals("Hello Java! Bye Java", b.getValue());
}
enum Formatter implements BiFunction<String, Object[], String> {
MESSAGE_FORMAT {
@Override
public String apply(String format, Object[] formatArgs) {
return MessageFormat.format(format, formatArgs);
}
},
;
CannedFormattedString create(String format, Object ... formatArgs) {
return new CannedFormattedString(this, format, List.of(formatArgs));
}
}
}

View File

@ -24,6 +24,7 @@ package jdk.jpackage.test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ -46,6 +47,24 @@ class JPackageStringBundleTest {
assertFalse(JPackageStringBundle.MAIN.cannedFormattedString("error.version-string-empty").getValue().isBlank());
}
@Test
void test_cannedFormattedString_equals() {
var a = JPackageStringBundle.MAIN.cannedFormattedString("error.version-string-empty");
var b = JPackageStringBundle.MAIN.cannedFormattedString("error.version-string-empty");
assertEquals(a, b);
a = JPackageStringBundle.MAIN.cannedFormattedString("message.error-header", "foo");
b = JPackageStringBundle.MAIN.cannedFormattedString("message.error-header", "foo");
assertEquals(a, b);
a = JPackageStringBundle.MAIN.cannedFormattedString("message.error-header", "foo");
b = JPackageStringBundle.MAIN.cannedFormattedString("message.error-header", "bar");
assertNotEquals(a, b);
}
@Test
void test_cannedFormattedStringAsPattern() {
var pred = JPackageStringBundle.MAIN.cannedFormattedStringAsPattern("error.version-string-empty", UNREACHABLE_FORMAT_ARG_MAPPER).asMatchPredicate();

View File

@ -22,7 +22,37 @@
*/
package jdk.jpackage.test;
import java.nio.file.Path;
import java.util.Objects;
import java.util.function.Supplier;
@FunctionalInterface
public interface CannedArgument {
public String getValue();
public static CannedArgument create(Supplier<Object> supplier, String label) {
Objects.requireNonNull(supplier);
Objects.requireNonNull(label);
return new CannedArgument() {
@Override
public String getValue() {
return supplier.get().toString();
}
@Override
public String toString( ) {
return label;
}
};
}
public static Object cannedAbsolutePath(Path v) {
return create(v::toAbsolutePath, String.format("AbsolutePath(%s)", v));
}
public static Object cannedAbsolutePath(String v) {
return cannedAbsolutePath(Path.of(v));
}
}

View File

@ -22,55 +22,29 @@
*/
package jdk.jpackage.test;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.stream.Stream;
public record CannedFormattedString(BiFunction<String, Object[], String> formatter, String key, Object[] args) implements CannedArgument {
public static CannedArgument cannedArgument(Supplier<Object> supplier, String label) {
Objects.requireNonNull(supplier);
Objects.requireNonNull(label);
return new CannedArgument() {
@Override
public String getValue() {
return supplier.get().toString();
}
@Override
public String toString( ) {
return label;
}
};
}
public static Object cannedAbsolutePath(Path v) {
return cannedArgument(() -> v.toAbsolutePath(), String.format("AbsolutePath(%s)", v));
}
public static Object cannedAbsolutePath(String v) {
return cannedAbsolutePath(Path.of(v));
}
public record CannedFormattedString(BiFunction<String, Object[], String> formatter, String key, List<Object> args) implements CannedArgument {
public CannedFormattedString mapArgs(UnaryOperator<Object> mapper) {
return new CannedFormattedString(formatter, key, Stream.of(args).map(mapper).toArray());
return new CannedFormattedString(formatter, key, args.stream().map(mapper).toList());
}
public CannedFormattedString {
Objects.requireNonNull(formatter);
Objects.requireNonNull(key);
Objects.requireNonNull(args);
List.of(args).forEach(Objects::requireNonNull);
args.forEach(Objects::requireNonNull);
}
@Override
public String getValue() {
return formatter.apply(key, Stream.of(args).map(arg -> {
return formatter.apply(key, args.stream().map(arg -> {
if (arg instanceof CannedArgument cannedArg) {
return cannedArg.getValue();
} else {
@ -80,19 +54,29 @@ public record CannedFormattedString(BiFunction<String, Object[], String> formatt
}
public CannedFormattedString addPrefix(String prefixKey) {
Objects.requireNonNull(prefixKey);
return new CannedFormattedString((theKey, theArgs) -> {
var str = formatter.apply((String)theArgs[0], Arrays.copyOfRange(theArgs, 1, theArgs.length));
return formatter.apply(theKey, new Object[] {str});
}, prefixKey, Stream.concat(Stream.of(key), Stream.of(args)).toArray());
return new CannedFormattedString(
new AddPrefixFormatter(formatter), prefixKey, Stream.concat(Stream.of(key), args.stream()).toList());
}
@Override
public String toString() {
if (args.length == 0) {
if (args.isEmpty()) {
return String.format("%s", key);
} else {
return String.format("%s+%s", key, List.of(args));
return String.format("%s+%s", key, args);
}
}
private record AddPrefixFormatter(BiFunction<String, Object[], String> formatter) implements BiFunction<String, Object[], String> {
AddPrefixFormatter {
Objects.requireNonNull(formatter);
}
@Override
public String apply(String format, Object[] formatArgs) {
var str = formatter.apply((String)formatArgs[0], Arrays.copyOfRange(formatArgs, 1, formatArgs.length));
return formatter.apply(format, new Object[] {str});
}
}
}

View File

@ -966,13 +966,13 @@ public class JPackageCommand extends CommandArguments<JPackageCommand> {
}
public String getValue(CannedFormattedString str) {
return new CannedFormattedString(str.formatter(), str.key(), Stream.of(str.args()).map(arg -> {
return new CannedFormattedString(str.formatter(), str.key(), str.args().stream().map(arg -> {
if (arg instanceof CannedArgument cannedArg) {
return cannedArg.value(this);
} else {
return arg;
}
}).toArray()).getValue();
}).toList()).getValue();
}
public JPackageCommand validateOut(CannedFormattedString... strings) {

View File

@ -31,6 +31,7 @@ import java.text.MessageFormat;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.regex.Pattern;
@ -48,6 +49,9 @@ public enum JPackageStringBundle {
} catch (ClassNotFoundException|NoSuchMethodException ex) {
throw toUnchecked(ex);
}
formatter = (String key, Object[] args) -> {
return new FormattedMessage(key, args).value();
};
}
/**
@ -61,12 +65,8 @@ public enum JPackageStringBundle {
}
}
private String getFormattedString(String key, Object[] args) {
return new FormattedMessage(key, args).value();
}
public CannedFormattedString cannedFormattedString(String key, Object ... args) {
return new CannedFormattedString(this::getFormattedString, key, args);
return new CannedFormattedString(formatter, key, List.of(args));
}
public Pattern cannedFormattedStringAsPattern(String key, Function<Object, Pattern> formatArgMapper, Object ... args) {
@ -153,4 +153,5 @@ public enum JPackageStringBundle {
private final Class<?> i18nClass;
private final Method i18nClass_getString;
private final BiFunction<String, Object[], String> formatter;
}

View File

@ -713,7 +713,7 @@ public final class ErrorTest {
private static void testMacSignWithoutIdentityWithNewTKitState(TestSpec spec) {
final Token keychainToken = spec.expectedMessages().stream().flatMap(cannedStr -> {
return Stream.of(cannedStr.args()).filter(Token.class::isInstance).map(Token.class::cast).filter(token -> {
return cannedStr.args().stream().filter(Token.class::isInstance).map(Token.class::cast).filter(token -> {
switch (token) {
case EMPTY_KEYCHAIN, KEYCHAIN_WITH_APP_IMAGE_CERT, KEYCHAIN_WITH_PKG_CERT -> {
return true;