c2d3ff3bf8
Reviewed-by: cjplummer, sspitsyn
510 lines
23 KiB
Java
510 lines
23 KiB
Java
/*
|
|
* Copyright (c) 2004, 2020, 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 4847959 6191402
|
|
* @summary Test newly-generified APIs
|
|
* @author Eamonn McManus
|
|
*
|
|
* @run clean GenericTest
|
|
* @run build GenericTest
|
|
* @run main GenericTest
|
|
*/
|
|
|
|
import java.lang.management.ManagementFactory;
|
|
import java.lang.reflect.*;
|
|
import java.util.*;
|
|
import java.util.stream.Stream;
|
|
import javax.management.*;
|
|
import javax.management.openmbean.*;
|
|
import javax.management.relation.*;
|
|
import javax.management.timer.Timer;
|
|
import javax.management.timer.TimerMBean;
|
|
|
|
public class GenericTest {
|
|
private static int failures;
|
|
|
|
public static void main(String[] args) throws Exception {
|
|
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
|
|
|
|
// Check we are really using the generified version
|
|
boolean generic;
|
|
Method findmbs = MBeanServerFactory.class.getMethod("findMBeanServer",
|
|
String.class);
|
|
Type findmbstype = findmbs.getGenericReturnType();
|
|
if (!(findmbstype instanceof ParameterizedType)) {
|
|
System.out.println("FAILURE: API NOT GENERIC!");
|
|
System.out.println(" MBeanServerFactory.findMBeanServer -> " +
|
|
findmbstype);
|
|
failures++;
|
|
generic = false;
|
|
} else {
|
|
System.out.println("OK: this API is generic");
|
|
generic = true;
|
|
}
|
|
|
|
ArrayList<MBeanServer> mbsList1 =
|
|
MBeanServerFactory.findMBeanServer(null);
|
|
checked(mbsList1, MBeanServer.class);
|
|
ArrayList mbsList2 = MBeanServerFactory.findMBeanServer(null);
|
|
check("ArrayList<MBeanServer> findMBeanServer", mbsList1.size() == 1);
|
|
check("ArrayList findMBeanServer", mbsList1.equals(mbsList2));
|
|
|
|
boolean isSecondAttempt = false;
|
|
Set<ObjectName> names1 = null;
|
|
while (true) {
|
|
names1 = checked(mbs.queryNames(null, null), ObjectName.class);
|
|
Set names2 = mbs.queryNames(null, null);
|
|
Set<ObjectName> names3 =
|
|
checked(((MBeanServerConnection) mbs).queryNames(null, null),
|
|
ObjectName.class);
|
|
// If new MBean (e.g. Graal MBean) is registered while the test is running, names1,
|
|
// names2, and names3 will have different sizes. Repeat the test in this case.
|
|
if (sameSize(names1, names2, names3) || isSecondAttempt) {
|
|
check("Set<ObjectName> MBeanServer.queryNames", names1.size() >= 1);
|
|
check("Set MBeanServer.queryNames", names2.size() >= 1);
|
|
check("Set<ObjectName> MBeanServerConnection.queryNames",
|
|
names3.size() >= 1);
|
|
check("queryNames sets same",
|
|
names1.equals(names2) && names2.equals(names3));
|
|
break;
|
|
}
|
|
isSecondAttempt = true;
|
|
System.out.println("queryNames sets have different size, retrying...");
|
|
}
|
|
|
|
isSecondAttempt = false;
|
|
while (true) {
|
|
Set<ObjectInstance> mbeans1 =
|
|
checked(mbs.queryMBeans(null, null), ObjectInstance.class);
|
|
Set mbeans2 = mbs.queryMBeans(null, null);
|
|
Set<ObjectInstance> mbeans3 =
|
|
checked(((MBeanServerConnection) mbs).queryMBeans(null, null),
|
|
ObjectInstance.class);
|
|
// If new MBean (e.g. Graal MBean) is registered while the test is running, mbeans1,
|
|
// mbeans2, and mbeans3 will have different sizes. Repeat the test in this case.
|
|
if (sameSize(mbeans1, mbeans2, mbeans3) || isSecondAttempt) {
|
|
check("Set<ObjectInstance> MBeanServer.queryMBeans",
|
|
mbeans1.size() >= 1);
|
|
check("Set MBeanServer.queryMBeans", mbeans2.size() >= 1);
|
|
check("Set<ObjectInstance> MBeanServerConnection.queryMBeans",
|
|
mbeans3.size() >= 1);
|
|
check("queryMBeans sets same",
|
|
mbeans1.equals(mbeans2) && mbeans2.equals(mbeans3));
|
|
break;
|
|
}
|
|
isSecondAttempt = true;
|
|
System.out.println("queryMBeans sets have different size, retrying...");
|
|
}
|
|
|
|
AttributeChangeNotificationFilter acnf =
|
|
new AttributeChangeNotificationFilter();
|
|
acnf.enableAttribute("foo");
|
|
Vector<String> acnfs = acnf.getEnabledAttributes();
|
|
checked(acnfs, String.class);
|
|
check("Vector<String> AttributeChangeNotificationFilter.getEnabled" +
|
|
"Attributes", acnfs.equals(Arrays.asList(new String[] {"foo"})));
|
|
|
|
if (generic) {
|
|
Attribute a = new Attribute("foo", "bar");
|
|
AttributeList al1 = new AttributeList();
|
|
al1.add(a);
|
|
AttributeList al2 =
|
|
new AttributeList(Arrays.asList(new Attribute[] {a}));
|
|
check("new AttributeList(List<Attribute>)", al1.equals(al2));
|
|
List<Attribute> al3 = checked(al1.asList(), Attribute.class);
|
|
al3.remove(a);
|
|
check("List<Attribute> AttributeList.asList()",
|
|
al1.equals(al3) && al1.isEmpty());
|
|
}
|
|
|
|
List<ObjectName> namelist1 = new ArrayList<ObjectName>(names1);
|
|
Role role = new Role("rolename", namelist1);
|
|
List<ObjectName> namelist2 =
|
|
checked(role.getRoleValue(), ObjectName.class);
|
|
check("new Role(String,List<ObjectName>).getRoleValue() -> " +
|
|
"List<ObjectName>", namelist1.equals(namelist2));
|
|
|
|
RoleList rl1 = new RoleList();
|
|
rl1.add(role);
|
|
RoleList rl2 = new RoleList(Arrays.asList(new Role[] {role}));
|
|
check("new RoleList(List<Role>)", rl1.equals(rl2));
|
|
if (generic) {
|
|
List<Role> rl3 = checked(rl1.asList(), Role.class);
|
|
rl3.remove(role);
|
|
check("List<Role> RoleList.asList()",
|
|
rl1.equals(rl3) && rl1.isEmpty());
|
|
}
|
|
|
|
RoleUnresolved ru =
|
|
new RoleUnresolved("rolename", namelist1,
|
|
RoleStatus.LESS_THAN_MIN_ROLE_DEGREE);
|
|
List<ObjectName> namelist3 =
|
|
checked(ru.getRoleValue(), ObjectName.class);
|
|
check("new RoleUnresolved(...List<ObjectName>...).getRoleValue() -> " +
|
|
"List<ObjectName>", namelist1.equals(namelist3));
|
|
|
|
RoleUnresolvedList rul1 = new RoleUnresolvedList();
|
|
rul1.add(ru);
|
|
RoleUnresolvedList rul2 =
|
|
new RoleUnresolvedList(Arrays.asList(new RoleUnresolved[] {ru}));
|
|
check("new RoleUnresolvedList(List<RoleUnresolved>", rul1.equals(rul2));
|
|
if (generic) {
|
|
List<RoleUnresolved> rul3 =
|
|
checked(rul1.asList(), RoleUnresolved.class);
|
|
rul3.remove(ru);
|
|
check("List<RoleUnresolved> RoleUnresolvedList.asList()",
|
|
rul1.equals(rul3) && rul1.isEmpty());
|
|
}
|
|
|
|
// This case basically just tests that we can compile this sort of thing
|
|
OpenMBeanAttributeInfo ombai1 =
|
|
new OpenMBeanAttributeInfoSupport("a", "a descr",
|
|
SimpleType.INTEGER,
|
|
true, true, false);
|
|
CompositeType ct =
|
|
new CompositeType("ct", "ct descr", new String[] {"item1"},
|
|
new String[] {"item1 descr"},
|
|
new OpenType[] {SimpleType.INTEGER});
|
|
OpenMBeanAttributeInfo ombai2 =
|
|
new OpenMBeanAttributeInfoSupport("a", "a descr",
|
|
ct, true, true, false);
|
|
TabularType tt =
|
|
new TabularType("tt", "tt descr", ct, new String[] {"item1"});
|
|
OpenMBeanAttributeInfo ombai3 =
|
|
new OpenMBeanAttributeInfoSupport("a", "a descr",
|
|
tt, true, true, false);
|
|
ArrayType<String[][]> at =
|
|
new ArrayType<String[][]>(2, SimpleType.STRING);
|
|
OpenMBeanAttributeInfo ombai4 =
|
|
new OpenMBeanAttributeInfoSupport("a", "a descr",
|
|
at, true, true, false);
|
|
OpenMBeanAttributeInfo ombai4a =
|
|
new OpenMBeanAttributeInfoSupport("a", "a descr",
|
|
(ArrayType) at,
|
|
true, true, false);
|
|
OpenMBeanAttributeInfo ombai5 =
|
|
new OpenMBeanAttributeInfoSupport("a", "a descr",
|
|
SimpleType.INTEGER,
|
|
true, true, false,
|
|
5, 1, 9);
|
|
OpenMBeanAttributeInfo ombai6 =
|
|
new OpenMBeanAttributeInfoSupport("a", "a descr",
|
|
SimpleType.INTEGER,
|
|
true, true, false,
|
|
5, new Integer[] {1, 5});
|
|
|
|
OpenMBeanInfo ombi =
|
|
new OpenMBeanInfoSupport("a.a", "a.a descr",
|
|
new OpenMBeanAttributeInfo[] {
|
|
ombai1, ombai2, ombai3, ombai4,
|
|
ombai5, ombai6,
|
|
},
|
|
null, null, null);
|
|
|
|
Map<String,Integer> itemMap =
|
|
checked(singletonMap("item1", 5),
|
|
String.class, Integer.class);
|
|
CompositeData cd =
|
|
new CompositeDataSupport(ct, itemMap);
|
|
check("CompositeDataSupport(CompositeType, Map<String,?>",
|
|
cd.get("item1").equals(5));
|
|
|
|
Set<String> ctkeys = checked(ct.keySet(), String.class);
|
|
check("Set<String> CompositeType.keySet()",
|
|
ctkeys.equals(singleton("item1")));
|
|
|
|
List<String> ttindex = checked(tt.getIndexNames(), String.class);
|
|
check("Set<String> TabularType.getIndexNames()",
|
|
ttindex.equals(singletonList("item1")));
|
|
|
|
TabularData td = new TabularDataSupport(tt);
|
|
td.putAll(new CompositeData[] {cd});
|
|
List<Integer> tdkey = checked(singletonList(5), Integer.class);
|
|
Set<List<Integer>> tdkeys = checked(singleton(tdkey),
|
|
(Class<List<Integer>>) tdkey.getClass());
|
|
Collection<CompositeData> tdvalues = checked(singleton(cd),
|
|
CompositeData.class);
|
|
check("Set<List<?>> TabularDataSupport.keySet()",
|
|
td.keySet().equals(tdkeys));
|
|
check("Collection<CompositeData> TabularDataSupport.values()",
|
|
td.values().iterator().next().equals(tdvalues.iterator().next()));
|
|
|
|
ObjectName stupidName = new ObjectName("stupid:a=b");
|
|
mbs.registerMBean(new Stupid(), stupidName);
|
|
StupidMBean proxy =
|
|
MBeanServerInvocationHandler.newProxyInstance(mbs,
|
|
stupidName,
|
|
StupidMBean.class,
|
|
false);
|
|
check("MBeanServerInvocationHandler.newProxyInstance",
|
|
proxy.getFive() == 5);
|
|
mbs.unregisterMBean(stupidName);
|
|
|
|
mbs.registerMBean(new StandardMBean(new Stupid(), StupidMBean.class),
|
|
stupidName);
|
|
check("<T> StandardMBean(T impl, Class<T> intf)",
|
|
proxy.getFive() == 5);
|
|
|
|
// Following is based on the package.html for javax.management.relation
|
|
// Create the Relation Service MBean
|
|
ObjectName relSvcName = new ObjectName(":type=RelationService");
|
|
RelationService relSvcObject = new RelationService(true);
|
|
mbs.registerMBean(relSvcObject, relSvcName);
|
|
|
|
// Create an MBean proxy for easier access to the Relation Service
|
|
RelationServiceMBean relSvc =
|
|
MBeanServerInvocationHandler.newProxyInstance(mbs, relSvcName,
|
|
RelationServiceMBean.class,
|
|
false);
|
|
|
|
// Define the DependsOn relation type
|
|
RoleInfo[] dependsOnRoles = {
|
|
new RoleInfo("dependent", Module.class.getName()),
|
|
new RoleInfo("dependedOn", Module.class.getName())
|
|
};
|
|
relSvc.createRelationType("DependsOn", dependsOnRoles);
|
|
|
|
// Now define a relation instance "moduleA DependsOn moduleB"
|
|
|
|
ObjectName moduleA = new ObjectName(":type=Module,name=A");
|
|
ObjectName moduleB = new ObjectName(":type=Module,name=B");
|
|
|
|
// Following two lines added to example:
|
|
mbs.registerMBean(new Module(), moduleA);
|
|
mbs.registerMBean(new Module(), moduleB);
|
|
|
|
Role dependent = new Role("dependent", singletonList(moduleA));
|
|
Role dependedOn = new Role("dependedOn", singletonList(moduleB));
|
|
Role[] roleArray = {dependent, dependedOn};
|
|
RoleList roles = new RoleList(Arrays.asList(roleArray));
|
|
relSvc.createRelation("A-DependsOn-B", "DependsOn", roles);
|
|
|
|
// Query the Relation Service to find what modules moduleA depends on
|
|
Map<ObjectName,List<String>> dependentAMap =
|
|
relSvc.findAssociatedMBeans(moduleA, "DependsOn", "dependent");
|
|
Set<ObjectName> dependentASet = dependentAMap.keySet();
|
|
dependentASet = checked(dependentASet, ObjectName.class);
|
|
// Set of ObjectName containing moduleB
|
|
check("Map<ObjectName,List<String>> RelationService.findAssociatedMBeans",
|
|
dependentAMap.size() == 1 &&
|
|
dependentASet.equals(singleton(moduleB)));
|
|
|
|
Map<String,List<String>> refRels =
|
|
relSvc.findReferencingRelations(moduleA, "DependsOn", "dependent");
|
|
List<String> refRoles =
|
|
checked(refRels.get("A-DependsOn-B"), String.class);
|
|
check("Map<String,List<String>> RelationService.findReferencingRelations",
|
|
refRoles.equals(singletonList("dependent")));
|
|
|
|
List<String> relsOfType = relSvc.findRelationsOfType("DependsOn");
|
|
relsOfType = checked(relsOfType, String.class);
|
|
check("List<String> RelationService.findRelationsOfType",
|
|
relsOfType.equals(singletonList("A-DependsOn-B")));
|
|
|
|
List<String> allRelIds = relSvc.getAllRelationIds();
|
|
allRelIds = checked(allRelIds, String.class);
|
|
check("List<String> RelationService.getAllRelationIds()",
|
|
allRelIds.equals(singletonList("A-DependsOn-B")));
|
|
|
|
List<String> allRelTypes = relSvc.getAllRelationTypeNames();
|
|
allRelTypes = checked(allRelTypes, String.class);
|
|
check("List<String> RelationService.getAllRelationTypeNames",
|
|
allRelTypes.equals(singletonList("DependsOn")));
|
|
|
|
Map<ObjectName,List<String>> refdMBeans =
|
|
relSvc.getReferencedMBeans("A-DependsOn-B");
|
|
check("Map<ObjectName,List<String>> RelationService.getReferencedMBeans",
|
|
refdMBeans.get(moduleA).equals(singletonList("dependent")) &&
|
|
refdMBeans.get(moduleB).equals(singletonList("dependedOn")));
|
|
|
|
List<ObjectName> roleContents =
|
|
checked(relSvc.getRole("A-DependsOn-B", "dependent"),
|
|
ObjectName.class);
|
|
check("List<ObjectName> RelationService.getRole",
|
|
roleContents.equals(singletonList(moduleA)));
|
|
|
|
RoleInfo roleInfoDependent =
|
|
relSvc.getRoleInfo("DependsOn", "dependent");
|
|
RoleInfo roleInfoDependedOn =
|
|
relSvc.getRoleInfo("DependsOn", "dependedOn");
|
|
List<RoleInfo> expectedRoleInfos =
|
|
Arrays.asList(new RoleInfo[] {roleInfoDependent, roleInfoDependedOn});
|
|
List<RoleInfo> roleInfos =
|
|
checked(relSvc.getRoleInfos("DependsOn"), RoleInfo.class);
|
|
check("List<RoleInfo> RelationService.getRoleInfos",
|
|
equalListContents(expectedRoleInfos, roleInfos));
|
|
|
|
RelationType relType =
|
|
new RelationTypeSupport("DependsOn", dependsOnRoles);
|
|
List<RoleInfo> relTypeRoleInfos =
|
|
checked(relType.getRoleInfos(), RoleInfo.class);
|
|
// Since there's no RoleInfo.equals and since the RelationTypeSupport
|
|
// constructor clones the RoleInfos passed to it, it's tricky to
|
|
// test equality here so we check type and size and have done with it
|
|
check("List<RoleInfo> RelationType.getRoleInfos",
|
|
relTypeRoleInfos.size() == 2);
|
|
|
|
MBeanServerNotificationFilter mbsnf =
|
|
new MBeanServerNotificationFilter();
|
|
mbsnf.enableObjectName(moduleA);
|
|
check("Vector<ObjectName> MBeanServerNotificationFilter." +
|
|
"getEnabledObjectNames",
|
|
mbsnf.getEnabledObjectNames().equals(Arrays.asList(moduleA)));
|
|
mbsnf.enableAllObjectNames();
|
|
mbsnf.disableObjectName(moduleB);
|
|
check("Vector<ObjectName> MBeanServerNotificationFilter." +
|
|
"getDisabledObjectNames",
|
|
mbsnf.getDisabledObjectNames().equals(Arrays.asList(moduleB)));
|
|
|
|
RelationService unusedRelSvc = new RelationService(false);
|
|
RelationNotification rn1 =
|
|
new RelationNotification(RelationNotification.RELATION_MBEAN_REMOVAL,
|
|
unusedRelSvc, 0L, 0L, "yo!",
|
|
"A-DependsOn-B", "DependsOn", null,
|
|
singletonList(moduleA));
|
|
List<ObjectName> toUnreg =
|
|
checked(rn1.getMBeansToUnregister(), ObjectName.class);
|
|
check("List<ObjectName> RelationNotification.getMBeansToUnregister",
|
|
toUnreg.equals(singletonList(moduleA)));
|
|
|
|
RelationNotification rn2 =
|
|
new RelationNotification(RelationNotification.RELATION_MBEAN_UPDATE,
|
|
unusedRelSvc, 0L, 0L, "yo!",
|
|
"A-DependsOn-B", "DependsOn", null,
|
|
"dependent", singletonList(moduleA),
|
|
singletonList(moduleB));
|
|
check("List<ObjectName> RelationNotification.getOldRoleValue",
|
|
checked(rn2.getOldRoleValue(), ObjectName.class)
|
|
.equals(singletonList(moduleB)));
|
|
check("List<ObjectName> RelationNotification.getNewRoleValue",
|
|
checked(rn2.getNewRoleValue(), ObjectName.class)
|
|
.equals(singletonList(moduleA)));
|
|
|
|
ObjectName timerName = new ObjectName(":type=timer");
|
|
mbs.registerMBean(new Timer(), timerName);
|
|
TimerMBean timer =
|
|
MBeanServerInvocationHandler.newProxyInstance(mbs,
|
|
timerName,
|
|
TimerMBean.class,
|
|
false);
|
|
Date doomsday = new Date(Long.MAX_VALUE);
|
|
int timer1 = timer.addNotification("one", "one", null, doomsday);
|
|
int timer2 = timer.addNotification("two", "two", null, doomsday);
|
|
Vector<Integer> idsOne = timer.getNotificationIDs("one");
|
|
check("Vector<Integer> TimerMBean.getNotificationIDs",
|
|
idsOne.equals(singletonList(timer1)));
|
|
Vector<Integer> allIds = timer.getAllNotificationIDs();
|
|
check("Vector<Integer> TimerMBean.getAllNotificationIDs",
|
|
equalListContents(allIds,
|
|
Arrays.asList(new Integer[]{timer1, timer2})));
|
|
|
|
// ADD NEW TEST CASES ABOVE THIS COMMENT
|
|
|
|
if (failures == 0)
|
|
System.out.println("All tests passed");
|
|
else {
|
|
System.out.println("TEST FAILURES: " + failures);
|
|
System.exit(1);
|
|
}
|
|
|
|
// DO NOT ADD NEW TEST CASES HERE, ADD THEM ABOVE THE PREVIOUS COMMENT
|
|
}
|
|
|
|
public static interface StupidMBean {
|
|
public int getFive();
|
|
}
|
|
|
|
public static class Stupid implements StupidMBean {
|
|
public int getFive() {
|
|
return 5;
|
|
}
|
|
}
|
|
|
|
public static class Module extends StandardMBean implements StupidMBean {
|
|
public Module() throws NotCompliantMBeanException {
|
|
super(StupidMBean.class);
|
|
}
|
|
|
|
public int getFive() {
|
|
return 5;
|
|
}
|
|
}
|
|
|
|
private static <E> List<E> singletonList(E value) {
|
|
return Collections.singletonList(value);
|
|
}
|
|
|
|
private static <E> Set<E> singleton(E value) {
|
|
return Collections.singleton(value);
|
|
}
|
|
|
|
private static <K,V> Map<K,V> singletonMap(K key, V value) {
|
|
return Collections.singletonMap(key, value);
|
|
}
|
|
|
|
private static <E> List<E> checked(List<E> c, Class<E> type) {
|
|
List<E> unchecked = new ArrayList<E>();
|
|
List<E> checked = Collections.checkedList(unchecked, type);
|
|
checked.addAll(c);
|
|
return Collections.checkedList(c, type);
|
|
}
|
|
|
|
private static <E> Set<E> checked(Set<E> c, Class<E> type) {
|
|
Set<E> unchecked = new HashSet<E>();
|
|
Set<E> checked = Collections.checkedSet(unchecked, type);
|
|
checked.addAll(c);
|
|
return Collections.checkedSet(c, type);
|
|
}
|
|
|
|
private static <K,V> Map<K,V> checked(Map<K,V> m,
|
|
Class<K> keyType,
|
|
Class<V> valueType) {
|
|
Map<K,V> unchecked = new HashMap<K,V>();
|
|
Map<K,V> checked = Collections.checkedMap(unchecked, keyType, valueType);
|
|
checked.putAll(m);
|
|
return Collections.checkedMap(m, keyType, valueType);
|
|
}
|
|
|
|
/* The fact that we have to call this method is a clear signal that
|
|
* the API says List where it means Set.
|
|
*/
|
|
private static <E> boolean equalListContents(List<E> l1, List<E> l2) {
|
|
return new HashSet<E>(l1).equals(new HashSet<E>(l2));
|
|
}
|
|
|
|
private static void check(String what, boolean cond) {
|
|
if (cond)
|
|
System.out.println("OK: " + what);
|
|
else {
|
|
System.out.println("FAILED: " + what);
|
|
failures++;
|
|
}
|
|
}
|
|
|
|
private static boolean sameSize(Set ... sets) {
|
|
return Stream.of(sets).map(s -> s.size()).distinct().count() == 1;
|
|
}
|
|
}
|