[Git][java-team/kdgcommons-java][upstream] New upstream version 1.0.17
Andreas Tille (@tille)
gitlab at salsa.debian.org
Sun Feb 2 08:10:03 GMT 2025
Andreas Tille pushed to branch upstream at Debian Java Maintainers / kdgcommons-java
Commits:
2ca9bfea by Andreas Tille at 2025-02-02T09:04:56+01:00
New upstream version 1.0.17
- - - - -
23 changed files:
- pom.xml
- src/main/java/net/sf/kdgcommons/collections/CollectionUtil.java
- src/main/java/net/sf/kdgcommons/collections/HashMultimap.java
- src/main/java/net/sf/kdgcommons/io/IOUtil.java
- src/main/java/net/sf/kdgcommons/lang/ClassUtil.java
- src/main/java/net/sf/kdgcommons/lang/StringUtil.java
- src/main/java/net/sf/kdgcommons/sql/JDBCUtil.java
- src/main/java/net/sf/kdgcommons/test/NumericAsserts.java
- + src/main/java/net/sf/kdgcommons/test/SelfMock.java
- src/main/java/net/sf/kdgcommons/test/StringAsserts.java
- src/main/java/net/sf/kdgcommons/util/Counters.java
- src/main/java/net/sf/kdgcommons/util/ReadThroughCache.java
- src/site/changes.xml
- src/site/findbugs-filter.xml
- + src/test/java/net/sf/kdgcommons/alt/TestSelfMockAlt.java
- + src/test/java/net/sf/kdgcommons/alt/package.html
- src/test/java/net/sf/kdgcommons/collections/TestCollectionUtil.java
- src/test/java/net/sf/kdgcommons/lang/TestClassUtil.java
- src/test/java/net/sf/kdgcommons/sql/TestJDBCUtil.java
- src/test/java/net/sf/kdgcommons/test/TestNumericAsserts.java
- + src/test/java/net/sf/kdgcommons/test/TestSelfMock.java
- src/test/java/net/sf/kdgcommons/test/TestStringAsserts.java
- src/test/java/net/sf/kdgcommons/util/TestCounters.java
Changes:
=====================================
pom.xml
=====================================
@@ -2,15 +2,9 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.sonatype.oss</groupId>
- <artifactId>oss-parent</artifactId>
- <version>7</version>
- </parent>
-
<groupId>net.sf.kdgcommons</groupId>
<artifactId>kdgcommons</artifactId>
- <version>1.0.15</version>
+ <version>1.0.17</version>
<name>KDG Commons</name>
<packaging>jar</packaging>
@@ -32,6 +26,13 @@
</licenses>
+ <scm>
+ <connection>scm:svn:svn://svn.code.sf.net/p/kdgcommons/code</connection>
+ <developerConnection>scm:svn:svn+ssh://kdgregory@svn.code.sf.ner/p/kdgcommons/code</developerConnection>
+ <url>http://kdgcommons.svn.sourceforge.net/viewvc/kdgcommons/</url>
+ </scm>
+
+
<developers>
<developer>
<id>kdgregory</id>
@@ -54,6 +55,13 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+
+ <maven-compiler-plugin.version>3.1</maven-compiler-plugin.version>
+ <maven-javadoc-plugin.version>3.0.1</maven-javadoc-plugin.version>
+ <maven-site-plugin.version>3.7.1</maven-site-plugin.version>
+ <maven-changes-plugin.version>2.11</maven-changes-plugin.version>
+ <findbugs-plugin.version>3.0.5</findbugs-plugin.version>
+ <cobertura-plugin.version>2.7</cobertura-plugin.version>
</properties>
@@ -62,32 +70,51 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
+ <version>${maven-compiler-plugin.version}</version>
<configuration>
<source>1.5</source>
<target>1.5</target>
<compilerArgument>-g</compilerArgument>
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>${maven-javadoc-plugin.version}</version>
+ <configuration>
+ <doclint>none</doclint>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-site-plugin</artifactId>
+ <version>${maven-site-plugin.version}</version>
+ </plugin>
</plugins>
</build>
+
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
+ <version>${maven-javadoc-plugin.version}</version>
<configuration>
<bottom>
<a
href="http://sourceforge.net/projects/kdgcommons/">
<img
src="http://sflogo.sourceforge.net/sflogo.php?group_id=234884&type=3">
- </a> </bottom>
+ </a>
+ </bottom>
+ <doclint>none</doclint>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId>
+ <version>${cobertura-plugin.version}</version>
<configuration>
<instrumentation>
<excludes>
@@ -107,7 +134,7 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
- <version>2.4.0</version>
+ <version>${findbugs-plugin.version}</version>
<configuration>
<excludeFilterFile>src/site/findbugs-filter.xml</excludeFilterFile>
</configuration>
@@ -115,7 +142,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-changes-plugin</artifactId>
- <version>2.3</version>
+ <version>${maven-changes-plugin.version}</version>
<configuration>
<xmlPath>${basedir}/src/site/changes.xml</xmlPath>
</configuration>
@@ -142,13 +169,6 @@
</dependencies>
- <scm>
- <connection>scm:svn:svn://svn.code.sf.net/p/kdgcommons/code</connection>
- <developerConnection>scm:svn:svn+ssh://kdgregory@svn.code.sf.ner/p/kdgcommons/code</developerConnection>
- <url>http://kdgcommons.svn.sourceforge.net/viewvc/kdgcommons/</url>
- </scm>
-
-
<distributionManagement>
<repository>
<id>build</id>
=====================================
src/main/java/net/sf/kdgcommons/collections/CollectionUtil.java
=====================================
@@ -18,9 +18,11 @@ import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
@@ -211,10 +213,14 @@ public class CollectionUtil
/**
* Returns the last element of the passed list, <code>null</code> if
- * the list is empty or null.
+ * the list is empty or null. Uses an indexed get unless the list is
+ * a subclass of <code>java.util.LinkedList</code>
*/
public static <T> T last(List<T> list) {
- return isNotEmpty(list) ? list.get(list.size() - 1) : null;
+ if (isEmpty(list)) return null;
+
+ if (list instanceof LinkedList<?>) return ((LinkedList<T>)list).getLast();
+ return list.get(list.size() - 1);
}
@@ -443,6 +449,28 @@ public class CollectionUtil
}
+ /**
+ * Returns <code>true</code> if the passed map is either <code>null</code>
+ * or has size 0.
+ */
+ public static boolean isEmpty(Map<?,?> m)
+ {
+ return (m == null)
+ ? true
+ : (m.size() == 0);
+ }
+
+
+ /**
+ * Returns <code>true</code> if the passed map is not <code>null</code>
+ * and has size > 0.
+ */
+ public static boolean isNotEmpty(Map<?,?> m)
+ {
+ return (m != null) && (m.size() > 0);
+ }
+
+
/**
* Compares two collections of <code>Comparable</code> elements. The two collections are
* iterated, and the first not-equal <code>compareTo()</code> result is returned. If the
@@ -763,6 +791,70 @@ public class CollectionUtil
}
+ /**
+ * Partitions the passed iterable into N sublists, each of which has
+ * at most <code>maxSize</code> elements.
+ */
+ public static <T> List<List<T>> partition(Iterable<T> source, int maxSize)
+ {
+ if (source == null) return Collections.emptyList();
+
+ List<List<T>> result = new ArrayList<List<T>>();
+ List<T> sublist = new ArrayList<T>(maxSize);
+ int count = 0;
+ for (T item : source)
+ {
+ sublist.add(item);
+ count++;
+ if (count >= maxSize)
+ {
+ result.add(sublist);
+ sublist = new ArrayList<T>(maxSize);
+ count = 0;
+ }
+ }
+ if (sublist.size() > 0)
+ {
+ result.add(sublist);
+ }
+ return result;
+ }
+
+
+ /**
+ * Returns a map that contains all keys in the specified collection.
+ * <p>
+ * The returned map is a HashMap; see variant for choosing map type.
+ */
+ public static <K,V> Map<K,V> submap(Map<K,V> src, Collection<K> keys)
+ {
+ return submap(src, keys, new HashMap<K,V>());
+ }
+
+
+ /**
+ * Extracts all mappings from the source map that correspond to the passed
+ * keys, and stores them in the destination map. Returns the destination
+ * map as a convenience.
+ */
+ public static <K,V> Map<K,V> submap(Map<K,V> src, Collection<K> keys, Map<K,V> dest)
+ {
+ if ((src == null) || (keys == null) || (dest == null))
+ {
+ return dest;
+ }
+
+ for (K key : keys)
+ {
+ if (src.containsKey(key))
+ {
+ dest.put(key, src.get(key));
+ }
+ }
+ return dest;
+ }
+
+
//----------------------------------------------------------------------------
// Supporting Objects
//----------------------------------------------------------------------------
=====================================
src/main/java/net/sf/kdgcommons/collections/HashMultimap.java
=====================================
@@ -60,7 +60,7 @@ implements Serializable
/**
* Controls the handling of equal key-value pairs.
*/
- enum Behavior { LIST, SET }
+ public enum Behavior { LIST, SET }
//----------------------------------------------------------------------------
// Instance variables and Constructors
=====================================
src/main/java/net/sf/kdgcommons/io/IOUtil.java
=====================================
@@ -215,7 +215,7 @@ public class IOUtil
/**
- * Repeatedly calls <code>skip()/code> on the underlying stream, until either the
+ * Repeatedly calls <code>skip()</code> on the underlying stream, until either the
* desired number of bytes have been read or EOF is reached. Returns the number
* of bytes actually skipped.
*
=====================================
src/main/java/net/sf/kdgcommons/lang/ClassUtil.java
=====================================
@@ -15,6 +15,7 @@
package net.sf.kdgcommons.lang;
import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
@@ -309,6 +310,38 @@ public class ClassUtil
}
+ /**
+ * Looks for the specified field in the class or its superclasses, returning
+ * its value if it exists. Throws <code>NoSuchFieldException</code> if unable
+ * to find the field in the class hierarchy, or if an exception occurred while
+ * retrieving its value.
+ * <p>
+ * Note: does not verify that actual field value matches <code>expectedClass</code>
+ * (this gets tricky when dealing with primitive wrapper classes).
+ */
+ public static <T> T getFieldValue(Object obj, String fieldName, Class<T> expectedClass)
+ throws NoSuchFieldException
+ {
+ for (Class<?> objKlass = obj.getClass() ; objKlass != null ; objKlass = objKlass.getSuperclass())
+ {
+ Field field = null;
+ try
+ {
+ field = objKlass.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ return (T)field.get(obj);
+ }
+ catch (Exception ignored)
+ {
+ // most likely a NoSuchFieldException, but could be a security exception
+ // from setAccessible(); in either case we'll just try superclass
+ }
+ }
+
+ throw new NoSuchFieldException("unable to retrieve field " + fieldName + " from object of class " + obj.getClass().getName());
+ }
+
+
//----------------------------------------------------------------------------
// Internals
//----------------------------------------------------------------------------
=====================================
src/main/java/net/sf/kdgcommons/lang/StringUtil.java
=====================================
@@ -362,8 +362,10 @@ public class StringUtil
/**
- * Generates a random string consisting of characters from the passed
- * string.
+ * Generates a (non-cryptographicaly-) random string consisting of characters
+ * from the passed string. Useful for generating bogus string fields.
+ * <p>
+ * Warning: not threadsafe; uses a shared instance of <code>java.util.Random</code>.
*
* @param chars Defines the set of characters used to create the
* returned string.
@@ -381,8 +383,10 @@ public class StringUtil
/**
- * Generates a string containing random ASCII alphabetic characters
- * (A-Za-z).
+ * Generates a string containing (non-cryptographicaly-) random ASCII alphabetic
+ * characters (A-Za-z). Useful for generating bogus string fields.
+ * <p>
+ * Warning: not threadsafe; uses a shared instance of <code>java.util.Random</code>.
*
* @param minLength Minimum length of the returned string.
* @param maxLength Maximum length of the returned string.
=====================================
src/main/java/net/sf/kdgcommons/sql/JDBCUtil.java
=====================================
@@ -14,10 +14,11 @@
package net.sf.kdgcommons.sql;
-import java.sql.Connection;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
+import java.sql.*;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
/**
@@ -25,6 +26,102 @@ import java.sql.Statement;
*/
public class JDBCUtil
{
+ /**
+ * Executes a query and returns the results, ensuring that the created statement
+ * and resultset are closed.
+ *
+ * @param args Parameters for the query. May be empty.
+ *
+ * @return A list of maps, where each entry in the list represents a row from the
+ * results, and the keys in the map represent the column names.
+ */
+ public static List<Map<String,Object>> executeQuery(Connection cxt, String sql, Object... args)
+ throws SQLException
+ {
+ PreparedStatement stmt = null;
+ ResultSet rslt = null;
+ try
+ {
+ stmt = prepare(cxt, sql, args);
+ rslt = stmt.executeQuery();
+ return retrieve(rslt);
+ }
+ finally
+ {
+ closeQuietly(stmt);
+ closeQuietly(rslt);
+ }
+ }
+
+
+ /**
+ * Executes an update and returns the results, ensuring that the created statement is closed.
+ *
+ * @param args Parameters for the query. May be empty.
+ *
+ * @return The number of rows updated by this statement.
+ */
+ public static int executeUpdate(Connection cxt, String sql, Object... args)
+ throws SQLException
+ {
+ PreparedStatement stmt = null;
+ try
+ {
+ stmt = prepare(cxt, sql, args);
+ return stmt.executeUpdate();
+ }
+ finally
+ {
+ closeQuietly(stmt);
+ }
+ }
+
+
+ /**
+ * Creates a <code>PreparedStatement</code> from the provided connection.
+ *
+ * @param args Parameters for the query. May be empty.
+ */
+ public static PreparedStatement prepare(Connection cxt, String sql, Object... args)
+ throws SQLException
+ {
+ PreparedStatement stmt = cxt.prepareStatement(sql);
+ for (int ii = 0 ; ii < args.length ; ii++)
+ {
+ stmt.setObject(ii + 1, args[ii]);
+ }
+ return stmt;
+ }
+
+
+ /**
+ * Iterates through the passed <code>ResultSet</code>, converting each row into a
+ * <code>Map</code>, where keys are the column names as retrieved from metadata,
+ * and values are the result of calling <code>getObject()</code>.
+ * <p>
+ * Caller is responsible for closing the <code>ResultSet</code>.
+ */
+ public static List<Map<String,Object>> retrieve(ResultSet rslt)
+ throws SQLException
+ {
+ // we use a LinkedList because it has simpler memory allocation characteristics
+ List<Map<String,Object>> results = new LinkedList<Map<String,Object>>();
+
+ ResultSetMetaData meta = rslt.getMetaData();
+ while (rslt.next())
+ {
+ Map<String,Object> row = new HashMap<String,Object>();
+ for (int ii = 1 ; ii <= meta.getColumnCount() ; ii++)
+ {
+ row.put(meta.getColumnName(ii), rslt.getObject(ii));
+ }
+ results.add(row);
+ }
+
+ return results;
+ }
+
+
/**
* Closes the passed <code>Connection</code> ignoring exceptions. This is usually
* called in a <code>finally</code> block, and throwing an exception there would
=====================================
src/main/java/net/sf/kdgcommons/test/NumericAsserts.java
=====================================
@@ -23,15 +23,147 @@ import junit.framework.Assert;
public class NumericAsserts
{
/**
- * Asserts that the actual value is within the expected, plus/minus
- * the specified percentage.
+ * Asserts that the actual value is within the expected, plus/minus the
+ * specified percentage (useful for probabilistic testing).
*/
public static void assertApproximate(int expected, int actual, int deltaPercent)
+ {
+ assertApproximate(null, expected, actual, deltaPercent);
+ }
+
+
+ /**
+ * Asserts that the actual value is within the expected, plus/minus the
+ * specified percentage (useful for probabilistic testing). On failure,
+ * prepends the supplied message (if any) to a description of the failure.
+ */
+ public static void assertApproximate(String message, int expected, int actual, int deltaPercent)
{
int delta = (int)(((long)expected * deltaPercent) / 100);
- int loBound = expected - delta;
- Assert.assertTrue("expected >= " + loBound + ", was " + actual, actual >= loBound);
- int hiBound = expected + delta;
- Assert.assertTrue("expected <= " + hiBound + ", was " + actual, actual <= hiBound);
+ assertInRange(message, expected - delta, expected + delta, actual);
+ }
+
+
+ /**
+ * Asserts that the actual value is within the expected, plus/minus the
+ * specified percentage (useful for probabilistic testing).
+ */
+ public static void assertApproximate(long expected, long actual, int deltaPercent)
+ {
+ assertApproximate(null, expected, actual, deltaPercent);
+ }
+
+ /**
+ * Asserts that the actual value is within the expected, plus/minus the
+ * specified percentage (useful for probabilistic testing). On failure,
+ * prepends the supplied message (if any) to a description of the failure.
+ */
+ public static void assertApproximate(String message, long expected, long actual, int deltaPercent)
+ {
+ // to avoid overflow, we swap the divide and multiply depending on the size of
+ // the value -- assumption is that range of error is minimal compared to delta
+ long delta = (expected > Integer.MAX_VALUE * 100)
+ ? (expected / 100) * deltaPercent
+ : (expected * deltaPercent) / 100;
+ assertInRange(message, expected - delta, expected + delta, actual);
+ }
+
+
+ /**
+ * Asserts that the actual value is within the expected, plus/minus the
+ * specified percentage (useful for probabilistic testing).
+ */
+ public static void assertApproximate(double expected, double actual, double deltaPercent)
+ {
+ assertApproximate(null, expected, actual, deltaPercent);
+ }
+
+
+ /**
+ * Asserts that the actual value is within the expected, plus/minus the
+ * specified percentage (useful for probabilistic testing). On failure,
+ * prepends the supplied message (if any) to a description of the failure.
+ */
+ public static void assertApproximate(String message, double expected, double actual, double deltaPercent)
+ {
+ double delta = (expected * deltaPercent) / 100;
+ assertInRange(message, expected - delta, expected + delta, actual);
+ }
+
+
+ /**
+ * Asserts that the actual value is within an arbitrary range +/- the expected value.
+ */
+ public static void assertInRange(int expectedLow, int expectedHigh, int actual)
+ {
+ assertInRange(null, expectedLow, expectedHigh, actual);
+ }
+
+
+ /**
+ * Asserts that the actual value is within an arbitrary range +/- the expected value.
+ * On failure, prepends the supplied message (if any) to a description of the failure.
+ */
+ public static void assertInRange(String message, int expectedLow, int expectedHigh, int actual)
+ {
+ if ((actual < expectedLow) || (actual > expectedHigh))
+ {
+ String baseMessage = "value not in expected range: was " + actual + ", expected between " + expectedLow + " and " + expectedHigh;
+ String actualMessage = (message != null)
+ ? message + ": " + baseMessage
+ : baseMessage;
+ Assert.fail(actualMessage);
+ }
+ }
+
+
+ /**
+ * Asserts that the actual value is within an arbitrary range +/- the expected value.
+ */
+ public static void assertInRange(long expectedLow, long expectedHigh, long actual)
+ {
+ assertInRange(null, expectedLow, expectedHigh, actual);
+ }
+
+
+ /**
+ * Asserts that the actual value is within an arbitrary range +/- the expected value.
+ * On failure, prepends the supplied message (if any) to a description of the failure.
+ */
+ public static void assertInRange(String message, long expectedLow, long expectedHigh, long actual)
+ {
+ if ((actual < expectedLow) || (actual > expectedHigh))
+ {
+ String baseMessage = "value not in expected range: was " + actual + ", expected between " + expectedLow + " and " + expectedHigh;
+ String actualMessage = (message != null)
+ ? message + ": " + baseMessage
+ : baseMessage;
+ Assert.fail(actualMessage);
+ }
+ }
+
+
+ /**
+ * Asserts that the actual value is within an arbitrary range +/- the expected value.
+ */
+ public static void assertInRange(double expectedLow, double expectedHigh, double actual)
+ {
+ assertInRange(null, expectedLow, expectedHigh, actual);
+ }
+
+
+ /**
+ * Asserts that the actual value is within an arbitrary range +/- the expected value.
+ */
+ public static void assertInRange(String message, double expectedLow, double expectedHigh, double actual)
+ {
+ if ((actual < expectedLow) || (actual > expectedHigh))
+ {
+ String baseMessage = "value not in expected range: was " + actual + ", expected between " + expectedLow + " and " + expectedHigh;
+ String actualMessage = (message != null)
+ ? message + ": " + baseMessage
+ : baseMessage;
+ Assert.fail(actualMessage);
+ }
}
}
=====================================
src/main/java/net/sf/kdgcommons/test/SelfMock.java
=====================================
@@ -0,0 +1,199 @@
+// Copyright Keith D Gregory
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package net.sf.kdgcommons.test;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.concurrent.ConcurrentHashMap;
+
+import net.sf.kdgcommons.util.Counters;
+
+
+/**
+ * A reflection-based proxy that invokes requests on itself. This class is
+ * intended to be subclassed, with the subclass implementing the methods
+ * that are to be mocked.
+ * <p>
+ * To use, construct with the interface to be mocked (only one allowed, due to
+ * Java parameterization), then call {@link #getInstance} to create the proxy
+ * instance.
+ * <p>
+ * By default, all invocations are counted and the invocation arguments retained.
+ * See {@link #getInvocationCount} and {@link #getInvocationArgument} for more
+ * information.
+ */
+public abstract class SelfMock<MockedType>
+implements InvocationHandler
+{
+ private Class<MockedType> klass;
+
+ private Counters<String> invocationCounts = new Counters<String>();
+ private ConcurrentHashMap<String,ArrayList<Object[]>> invocationArgs = new ConcurrentHashMap<String,ArrayList<Object[]>>();
+
+
+ public SelfMock(Class<MockedType> klass)
+ {
+ this.klass = klass;
+ }
+
+//----------------------------------------------------------------------------
+// Public API
+//----------------------------------------------------------------------------
+
+ /**
+ * Returns a proxy instance that passes invocations to this mock. Multiple
+ * calls will return different proxy instances.
+ */
+ public MockedType getInstance()
+ {
+ return klass.cast(
+ Proxy.newProxyInstance(
+ this.getClass().getClassLoader(),
+ new Class[] {klass},
+ this));
+ }
+
+
+ /**
+ * Returns the number of times the named function was invoked. This does
+ * not differentiate between overloaded methods: all methods with the same
+ * name is counted together.
+ */
+ public int getInvocationCount(String methodName)
+ {
+ return invocationCounts.getInt(methodName);
+ }
+
+
+ /**
+ * Returns the arguments passed to a particular invocation of the named method
+ * (using zero-based counting). This is primarily useful when dealing with
+ * overloaded methods, where you might not know what types the arguments are.
+ * If you know the arguments, {@link #getInvocationArgAs} is a better choice.
+ * <p>
+ * Note: this method returns the actual argument array that was passed to the
+ * invocation handler. Don't modify it unless you want to invalidate your tests.
+ *
+ * @throws IndexOutOfBoundsException if accessing a call that was never made.
+ */
+ public Object[] getInvocationArgs(String methodName, int invocationIndex)
+ {
+ return invocationHistoryFor(methodName).get(invocationIndex);
+ }
+
+
+ /**
+ * Returns a specific invocation argument, cast to a particular type. Both
+ * indexes are zero-based.
+ */
+ public <T> T getInvocationArg(String methodName, int invocationIndex, int argumentIndex, Class<T> argType)
+ {
+ return argType.cast(getInvocationArgs(methodName, invocationIndex)[argumentIndex]);
+ }
+
+
+ /**
+ * Returns the arguments passed to the most recent invocation of the named
+ * method.This is primarily useful when dealing with overloaded methods,
+ * where you might not know what types the arguments are. If you know the
+ * arguments, {@link #getInvocationArgAs} is a better choice.
+ * <p>
+ * Note: this method returns the actual argument array that was passed to the
+ * invocation handler. Don't modify it unless you want to invalidate your tests.
+ */
+ public Object[] getMostRecentInvocationArgs(String methodName)
+ {
+ int index = getInvocationCount(methodName) - 1;
+ return (index < 0)
+ ? null
+ : getInvocationArgs(methodName, index);
+ }
+
+
+ /**
+ * The a specific argument from the most recent invocation, cast to a particular
+ * type.
+ */
+ public <T> T getMostRecentInvocationArg(String methodName, int argumentIndex, Class<T> argType)
+ {
+ Object[] args = getMostRecentInvocationArgs(methodName);
+ return (args == null)
+ ? null
+ : argType.cast(args[argumentIndex]);
+ }
+
+//----------------------------------------------------------------------------
+// Internals
+//----------------------------------------------------------------------------
+
+ public Object invoke(Object proxy, Method method, Object[] args)
+ throws Throwable
+ {
+ String methodName = method.getName();
+ invocationCounts.increment(methodName);
+
+ synchronized (this)
+ {
+ invocationHistoryFor(methodName).add(args);
+ }
+
+ try
+ {
+ Method selfMethod = getClass().getMethod(methodName, method.getParameterTypes());
+ selfMethod.setAccessible(true);
+ return selfMethod.invoke(this, args);
+ }
+ catch (NoSuchMethodException ex)
+ {
+ throw new UnsupportedOperationException("mock does not implement method: " + methodName
+ + "(" + Arrays.asList(method.getParameterTypes()) + ")");
+ }
+ catch (SecurityException ex)
+ {
+ throw new RuntimeException("security exception when invoking: " + methodName, ex);
+ }
+ catch (IllegalAccessException ex)
+ {
+ throw new RuntimeException("illegal access exception when invoking: " + methodName, ex);
+ }
+ catch (InvocationTargetException ex)
+ {
+ // this is an exception thrown by the mock instance, which is probably intentional
+ throw ex.getCause();
+ }
+ }
+
+
+ /**
+ * Returns the list of historical arguments for the named method, creating it
+ * if necessary. May be called without synchronization, although changes to the
+ * returned array should be synchronized.
+ */
+ private ArrayList<Object[]> invocationHistoryFor(String methodName)
+ {
+ ArrayList<Object[]> history = invocationArgs.get(methodName);
+ if (history == null)
+ {
+ // this could be invoked concurrently, and someone else could create the list
+ invocationArgs.putIfAbsent(methodName, new ArrayList<Object[]>());
+ history = invocationArgs.get(methodName);
+ }
+ return history;
+ }
+}
=====================================
src/main/java/net/sf/kdgcommons/test/StringAsserts.java
=====================================
@@ -27,6 +27,30 @@ import junit.framework.Assert;
*/
public class StringAsserts
{
+ /**
+ * Asserts that the passed string is not empty or null.
+ */
+ public static void assertNotEmpty(String value)
+ {
+ assertNotEmpty(null, value);
+ }
+
+
+ /**
+ * Asserts that the passed string is not empty or null. On failure, reports
+ * the specified message along with a descriptive error message.
+ */
+ public static void assertNotEmpty(String message, String value)
+ {
+ String baseMessage = (message == null)
+ ? "expected not-empty"
+ : message + ": expected not-empty";
+
+ if (value == null) Assert.fail(baseMessage + ", was null");
+ if (value.length() == 0) Assert.fail(baseMessage);
+ }
+
+
/**
* Asserts that a given string contains N instances of a substring.
*
@@ -58,7 +82,7 @@ public class StringAsserts
int actual = 0;
for (int idx = str.indexOf(sub) ; (idx >= 0) && (idx < str.length()) ; )
{
- actual += (idx >= 0) ? 1 : 0;
+ actual++;
idx = str.indexOf(sub, idx + 1);
}
Assert.assertEquals(message + ": count(" + sub + ")",
=====================================
src/main/java/net/sf/kdgcommons/util/Counters.java
=====================================
@@ -49,6 +49,32 @@ implements Map<K,Long>, Iterable<Map.Entry<K,Long>>
{
private ConcurrentHashMap<K,AtomicLong> _map = new ConcurrentHashMap<K,AtomicLong>();
+//----------------------------------------------------------------------------
+// Object overrides
+//----------------------------------------------------------------------------
+
+ /**
+ * Outputs all counters in the format "[ NAME: VALUE, ...]".
+ */
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder(16384);
+ sb.append("[");
+ for (Map.Entry<K,AtomicLong> entry : _map.entrySet())
+ {
+ if (sb.length() > 1)
+ sb.append(", ");
+
+ sb.append(String.valueOf(entry.getKey()))
+ .append(": ")
+ .append(entry.getValue().longValue());
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+
//----------------------------------------------------------------------------
// Implementation of Map
@@ -166,13 +192,17 @@ implements Map<K,Long>, Iterable<Map.Entry<K,Long>>
/**
- * Returns the current keys in the map. This operation is performed directly on the
- * underlying map, so does not involve a performance penalty (other than what the
- * map incurs).
+ * Returns the current keys in the map. Note that this represents a point-in-time
+ * view of the map, so if you are concurrently adding or removing counters it may
+ * be missing keys or contain keys that are no longer in the map.
*/
public Set<K> keySet()
{
- return _map.keySet();
+ // in Java 8, ConcurrentHashMap broke binary compatibility for keySet(), returning
+ // a different concrete class; this cast forces the compiler to use an invokeinterface
+ // rather than invokevirtual, so the bytecode remains compatible
+
+ return ((Map<K,AtomicLong>)_map).keySet();
}
@@ -208,7 +238,6 @@ implements Map<K,Long>, Iterable<Map.Entry<K,Long>>
return entries;
}
-
//----------------------------------------------------------------------------
// Additional Public Methods
//----------------------------------------------------------------------------
@@ -230,7 +259,7 @@ implements Map<K,Long>, Iterable<Map.Entry<K,Long>>
/**
- * Retrieves the value of the specified key as a primitive. If there is no
+ * Retrieves the value of the specified key as a primitive long. If there is no
* mapping for the key, returns 0.
*/
public long getLong(K key)
@@ -240,6 +269,19 @@ implements Map<K,Long>, Iterable<Map.Entry<K,Long>>
}
+ /**
+ * Retrieves the value of the specified key as a primitive int. If there is no
+ * mapping for the key, returns 0. This is only valid if you know that your
+ * counters will remain in integer range (but often works better with other
+ * variables in your code).
+ */
+ public int getInt(K key)
+ {
+ AtomicLong mapping = _map.get(key);
+ return (mapping == null) ? 0 : (int)mapping.get();
+ }
+
+
/**
* Sets the mapping to the specified value. This is equivalent to calling
* {@link #put}, with the same caveats regarding concurent access.
=====================================
src/main/java/net/sf/kdgcommons/util/ReadThroughCache.java
=====================================
@@ -149,7 +149,7 @@ public class ReadThroughCache<K,V>
@Override
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
- return size() > size;
+ return this.size() > size;
}
};
}
=====================================
src/site/changes.xml
=====================================
@@ -4,7 +4,61 @@
</properties>
<body>
- <release version="1.0.15" date="TBD"
+ <release version="1.0.17" date="2019-08-03"
+ description="TBD">
+ <action dev='kdgregory' type='add'>
+ Counters: add toString()
+ </action>
+ <action dev='kdgregory' type='add'>
+ JDBCUtil: add functions to support simple SQL operations
+ </action>
+ <action dev='kdgregory' type='update'>
+ CollectionUtil.last(): optimization for linked lists
+ </action>
+ <action dev='kdgregory' type='update'>
+ Counters: work-around signature change in ConcurrentHashMap
+ (was producing an invalid artifact when compiling with JDK 8)
+ </action>
+ <action dev='kdgregory' type='update'>
+ HashMultimap: Behavior enum was not public
+ </action>
+ <action dev='kdgregory' type='update'>
+ SelfMock: retain invocation arguments
+ </action>
+ </release>
+
+ <release version="1.0.16" date="2018-07-22"
+ description="grab-bag of additions">
+ <action dev='kdgregory' type='add'>
+ ClassUtil.getFieldValue(): retrieves a field's value from wherever it's defined in
+ the class hierarchy
+ </action>
+ <action dev='kdgregory' type='add'>
+ CollectionUtil.isEmpty(), isNotEmpty(): now accept Maps
+ </action>
+ <action dev='kdgregory' type='add'>
+ CollectionUtil.partition(): breaks a passed collection into a list of max-size lists
+ </action>
+ <action dev='kdgregory' type='add'>
+ CollectionUtil.submap(): extracts mappings for a set of keys.
+ </action>
+ <action dev='kdgregory' type='add'>
+ NumericAsserts: add assertApproximate() for long and double values
+ </action>
+ <action dev='kdgregory' type='add'>
+ NumericAsserts.assertInRange(): allows caller to assert that the actual value is
+ within an arbitrary range +/- of the expected value
+ </action>
+ <action dev='kdgregory' type='add'>
+ StringAsserts.assertNotEmpty()
+ </action>
+ <action dev='kdgregory' type='add'>
+ SelfMock: a reflection-based mock object that invokes methods on itself; used to mock
+ a portion of an interface without a long if-else chain in the invocation handler
+ </action>
+ </release>
+
+ <release version="1.0.15" date="2017-01-22"
description="A variety of new functions, across utils">
<action dev='kdgregory' type='add'>
BufferUtil.toArray: returns the entire contents of a ByteBuffer as an array
=====================================
src/site/findbugs-filter.xml
=====================================
@@ -47,4 +47,11 @@
<!-- all paths are covered; FindBugs is being paranoid -->
</Match>
+ <Match>
+ <Class name='net.sf.kdgcommons.lang.ObjectUtil' />
+ <Method name='equals' />
+ <Bug pattern='RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE' />
+ <!-- intentional: as written I consider the conditions more obvious -->
+ </Match>
+
</FindBugsFilter>
=====================================
src/test/java/net/sf/kdgcommons/alt/TestSelfMockAlt.java
=====================================
@@ -0,0 +1,40 @@
+// Copyright Keith D Gregory
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package net.sf.kdgcommons.alt;
+
+import junit.framework.TestCase;
+
+import net.sf.kdgcommons.test.SelfMock;
+
+
+public class TestSelfMockAlt extends TestCase
+{
+ // there was a bug where methods in anonymous classes in a different package
+ // (ie, not net.sf.kdgcommons.test) were unaccessible and needed to be set
+ // accessible to be invoked
+ public void testAnonymousImplementationClassInDifferentPackage()
+ {
+ CharSequence instance = new SelfMock<CharSequence>(CharSequence.class)
+ {
+ @SuppressWarnings("unused")
+ public int length()
+ {
+ return 123;
+ }
+ }.getInstance();
+
+ assertEquals(123, instance.length());
+ }
+}
=====================================
src/test/java/net/sf/kdgcommons/alt/package.html
=====================================
@@ -0,0 +1,4 @@
+<body>
+This package exists because some testcases need to run in a different package
+than the classes under test to verify protection problems.
+</body>
=====================================
src/test/java/net/sf/kdgcommons/collections/TestCollectionUtil.java
=====================================
@@ -24,6 +24,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.TreeMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -197,18 +198,40 @@ public class TestCollectionUtil extends TestCase
List<String> l4 = null;
assertEquals("first(), list is null", null, CollectionUtil.first(l4));
assertEquals("last(), list is null", null, CollectionUtil.last(l4));
+
+ // this test is just here for coverage; we don't verify behavior
+ List<String> l5 = new LinkedList<String>(Arrays.asList("foo", "bar", "baz"));
+ assertEquals("first(), LinkedList", "foo", CollectionUtil.first(l5));
+ assertEquals("last(), LinkedList", "baz", CollectionUtil.last(l5));
}
public void testIsEmpty() throws Exception
{
- assertTrue(CollectionUtil.isEmpty(null));
- assertTrue(CollectionUtil.isEmpty(Arrays.asList()));
- assertFalse(CollectionUtil.isEmpty(Arrays.asList("foo")));
+ List<String> list1 = null;
+ assertTrue(CollectionUtil.isEmpty(list1));
+ assertFalse(CollectionUtil.isNotEmpty(list1));
+
+ List<String> list2 = new ArrayList<String>();
+ assertTrue(CollectionUtil.isEmpty(list2));
+ assertFalse(CollectionUtil.isNotEmpty(list2));
+
+ List<String> list3 = Arrays.asList("foo");
+ assertFalse(CollectionUtil.isEmpty(list3));
+ assertTrue(CollectionUtil.isNotEmpty(list3));
- assertFalse(CollectionUtil.isNotEmpty(null));
- assertFalse(CollectionUtil.isNotEmpty(Arrays.asList()));
- assertTrue(CollectionUtil.isNotEmpty(Arrays.asList("foo")));
+ Map<String,String> map1 = null;
+ assertTrue(CollectionUtil.isEmpty(map1));
+ assertFalse(CollectionUtil.isNotEmpty(map1));
+
+ Map<String,String> map2 = new HashMap<String,String>();
+ assertTrue(CollectionUtil.isEmpty(map2));
+ assertFalse(CollectionUtil.isNotEmpty(map2));
+
+ Map<String,String> map3 = new HashMap<String,String>();
+ map3.put("foo", "bar");
+ assertFalse(CollectionUtil.isEmpty(list3));
+ assertTrue(CollectionUtil.isNotEmpty(map3));
}
@@ -856,4 +879,56 @@ public class TestCollectionUtil extends TestCase
}
}
+
+ public void testPartition() throws Exception
+ {
+ assertEquals(
+ "null source",
+ Collections.<Integer>emptyList(),
+ CollectionUtil.partition(null, 2));
+
+ assertEquals(
+ "empty source list",
+ Collections.<Integer>emptyList(),
+ CollectionUtil.partition(Collections.<Integer>emptyList(), 2));
+
+ assertEquals(
+ "non-empty source array",
+ Arrays.asList(Arrays.asList(1,2), Arrays.asList(3,4), Arrays.asList(5)),
+ CollectionUtil.partition(Arrays.asList(1,2,3,4,5), 2));
+ }
+
+
+ public void testSubmap() throws Exception
+ {
+ Map<Integer,String> source = new HashMap<Integer,String>();
+ source.put(1, "foo");
+ source.put(2, "bar");
+ source.put(3, "baz");
+
+ assertEquals("null source",
+ Collections.emptyMap(),
+ CollectionUtil.submap(null, Arrays.asList(1, 2, 3)));
+
+ assertEquals("null keylist",
+ Collections.emptyMap(),
+ CollectionUtil.submap(source, null));
+
+ assertEquals("null destination",
+ null,
+ CollectionUtil.submap(source, Arrays.asList(1,2), null));
+
+ Map<Integer,String> expected = new HashMap<Integer,String>();
+ expected.put(2, "bar");
+
+ assertEquals("normal operation",
+ expected,
+ CollectionUtil.submap(source, Arrays.asList(2)));
+
+ Map<Integer,String> dest = new TreeMap<Integer,String>();
+
+ assertSame("explicit destination",
+ dest,
+ CollectionUtil.submap(source, Arrays.asList(2), dest));
+ }
}
=====================================
src/test/java/net/sf/kdgcommons/lang/TestClassUtil.java
=====================================
@@ -48,27 +48,70 @@ public class TestClassUtil extends TestCase
public static class Parent
{
- public int foo() { return 1; }
+ protected int parentField;
+ protected int fieldToBeShadowed;
+
+ public Parent()
+ {}
+
+ public Parent(int fieldValue, int shadowValue)
+ {
+ parentField = fieldValue;
+ fieldToBeShadowed = shadowValue;
+ }
+
+ public int foo()
+ { return 1; }
@Bar
- public int bar() { return 2; }
+ public int bar()
+ { return 2; }
}
public static class Child
extends Parent
{
+ // leaving this private to verify that we can make it accessible
+ @SuppressWarnings("unused")
+ private int childField;
+
+ public Child()
+ {}
+
+ public Child(int parentValue, int shadowValue, int fieldValue)
+ {
+ super(parentValue, shadowValue);
+ childField = fieldValue;
+ }
+
@Override
- public int bar() { return 3; }
+ public int bar()
+ { return 3; }
@Foo
- public int baz() { return 3; }
+ public int baz()
+ { return 3; }
}
public static class Grandchild extends Child
{
- public int bar(int param) { return 4; }
+ public int grandchildField;
+ public int fieldToBeShadowed;
+
+ public Grandchild()
+ {}
+
+ public Grandchild(int parentValue, int parentShadowValue, int childValue, int fieldValue, int shadowValue)
+ {
+ super(parentValue, parentShadowValue, childValue);
+ grandchildField = fieldValue;
+ fieldToBeShadowed = shadowValue;
+ }
+
+ public int bar(int param)
+ { return 4; }
}
@@ -76,6 +119,7 @@ public class TestClassUtil extends TestCase
{
public void foo(Object val) { /* nothing here */ }
+ @SuppressWarnings("unused")
private void bar(Object val) { /* nothing here */ }
protected void baz(Object val) { /* nothing here */ }
@@ -513,4 +557,34 @@ public class TestClassUtil extends TestCase
assertEquals("override: parameters", Arrays.asList(Object.class),
Arrays.asList(m3.getParameterTypes()));
}
+
+
+ public void testGetFieldValue() throws Exception
+ {
+ Grandchild obj1 = new Grandchild(1, 2, 3, 4, 5);
+ assertEquals("parentField", Integer.valueOf(1), ClassUtil.getFieldValue(obj1, "parentField", Integer.TYPE));
+ assertEquals("childField", Integer.valueOf(3), ClassUtil.getFieldValue(obj1, "childField", Integer.TYPE));
+ assertEquals("grandchildField", Integer.valueOf(4), ClassUtil.getFieldValue(obj1, "grandchildField", Integer.TYPE));
+ assertEquals("fieldToBeShadowed", Integer.valueOf(5), ClassUtil.getFieldValue(obj1, "fieldToBeShadowed", Integer.TYPE));
+
+ Child obj2 = new Child(1, 2, 3);
+ assertEquals("parentField", Integer.valueOf(1), ClassUtil.getFieldValue(obj2, "parentField", Integer.TYPE));
+ assertEquals("childField", Integer.valueOf(3), ClassUtil.getFieldValue(obj2, "childField", Integer.TYPE));
+ assertEquals("fieldToBeShadowed", Integer.valueOf(2), ClassUtil.getFieldValue(obj2, "fieldToBeShadowed", Integer.TYPE));
+
+ try
+ {
+ ClassUtil.getFieldValue(obj2, "grandchildField", Integer.TYPE);
+ fail("did not throw when retrieving non-existent field");
+ }
+ catch (NoSuchFieldException ex)
+ {
+ String message = ex.getMessage();
+ assertTrue("exception message includes field name (was: \"" + message + "\")",
+ message.contains("grandchildField"));
+ assertTrue("exception message includes class name (was: \"" + message + "\")",
+ message.contains("Child"));
+ }
+ }
+
}
=====================================
src/test/java/net/sf/kdgcommons/sql/TestJDBCUtil.java
=====================================
@@ -14,14 +14,18 @@
package net.sf.kdgcommons.sql;
-import java.sql.Connection;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
+import java.sql.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
import junit.framework.TestCase;
+import net.sf.kdgcommons.collections.CollectionUtil;
import net.sf.kdgcommons.test.ExceptionMock;
+import net.sf.kdgcommons.test.SelfMock;
import net.sf.kdgcommons.test.SimpleMock;
@@ -31,22 +35,247 @@ import net.sf.kdgcommons.test.SimpleMock;
*/
public class TestJDBCUtil extends TestCase
{
-
public TestJDBCUtil(String testName)
{
super(testName);
}
-
//----------------------------------------------------------------------------
// Support Code
//----------------------------------------------------------------------------
+ private final static List<? extends Map<Object,Object>> SAMPLE_DATA = Arrays.asList(
+ CollectionUtil.asMap("foo", "123", "argle", Integer.valueOf(123)),
+ CollectionUtil.asMap("foo", "baz", "argle", Integer.valueOf(456)),
+ CollectionUtil.asMap("foo", null, "argle", Integer.valueOf(789))
+ );
+
+ private final static String[] SAMPLE_DATA_COLNAMES = new String[] { "foo", "argle" };
+
+
+ private static class MockConnection
+ extends SelfMock<Connection>
+ {
+ public boolean isOpen = true;
+ public String lastPrepareSql;
+ public MockPreparedStatement lastPrepareMock;
+
+ public MockConnection()
+ {
+ super(Connection.class);
+ }
+
+ @SuppressWarnings("unused")
+ public void close()
+ {
+ isOpen = false;
+ }
+
+ @SuppressWarnings("unused")
+ public PreparedStatement prepareStatement(String sql)
+ {
+ lastPrepareSql = sql;
+ lastPrepareMock = new MockPreparedStatement();
+ return lastPrepareMock.getInstance();
+ }
+ }
+
+
+ private static class MockPreparedStatement
+ extends SelfMock<PreparedStatement>
+ {
+ // these can be changed if necessary
+ public String[] queryColumnNames = SAMPLE_DATA_COLNAMES;
+ public List<? extends Map<Object,Object>> queryData = SAMPLE_DATA;
+ public int rowsUpdated = 1;
+
+ // note: first element will always be null; objects are stored
+ // at the index specified in the call
+ public ArrayList<Object> parameters = new ArrayList<Object>();
+ public boolean isOpen = true;
+ public MockResultSet lastResultMock;
+
+ public MockPreparedStatement()
+ {
+ super(PreparedStatement.class);
+ }
+
+ @SuppressWarnings("unused")
+ public void close()
+ {
+ isOpen = false;
+ }
+
+ @SuppressWarnings("unused")
+ public int executeUpdate()
+ {
+ return rowsUpdated;
+ }
+
+ @SuppressWarnings("unused")
+ public ResultSet executeQuery()
+ {
+ lastResultMock = new MockResultSet(queryColumnNames, queryData);
+ return lastResultMock.getInstance();
+ }
+
+ @SuppressWarnings("unused")
+ public void setObject(int idx, Object obj)
+ {
+ if (parameters.size() > idx)
+ parameters.set(idx, obj);
+ else
+ {
+ while (parameters.size() < idx)
+ parameters.add(null);
+ parameters.add(obj);
+ }
+ }
+ }
+
+
+ private static class MockResultSet
+ extends SelfMock<ResultSet>
+ {
+ private String[] columnNames;
+
+ public int getMetaDataInvocationCount;
+ public int nextInvocationCount;
+ public boolean isOpen = true;
+
+ private Iterator<? extends Map<Object,Object>> rowItx;
+ private Map<Object,Object> currentRow;
+
+ public MockResultSet(String[] columnNames, List<? extends Map<Object,Object>> data)
+ {
+ super(ResultSet.class);
+ this.columnNames = columnNames;
+ this.rowItx = data.iterator();
+ }
+
+ @SuppressWarnings("unused")
+ public void close()
+ {
+ isOpen = false;
+ }
+
+ @SuppressWarnings("unused")
+ public ResultSetMetaData getMetaData()
+ {
+ getMetaDataInvocationCount++;
+ return new MockResultSetMetaData(columnNames).getInstance();
+ }
+
+ @SuppressWarnings("unused")
+ public boolean next()
+ {
+ nextInvocationCount++;
+ if (rowItx.hasNext())
+ {
+ currentRow = rowItx.next();
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ @SuppressWarnings("unused")
+ public Object getObject(int idx)
+ {
+ return currentRow.get(columnNames[idx - 1]);
+ }
+ }
+
+
+ private static class MockResultSetMetaData
+ extends SelfMock<ResultSetMetaData>
+ {
+ private String[] columnNames;
+
+ public MockResultSetMetaData(String[] columnNames)
+ {
+ super(ResultSetMetaData.class);
+ this.columnNames = columnNames;
+ }
+
+ @SuppressWarnings("unused")
+ public int getColumnCount()
+ {
+ return columnNames.length;
+ }
+
+ @SuppressWarnings("unused")
+ public String getColumnName(int idx)
+ {
+ return columnNames[idx - 1];
+ }
+ }
+
//----------------------------------------------------------------------------
// Test cases
//----------------------------------------------------------------------------
+ public void testExecuteQuery() throws Exception
+ {
+ final String sql = "select * from foo where bar = ?";
+
+ MockConnection cxtMock = new MockConnection();
+ List<? extends Map<String,Object>> result = JDBCUtil.executeQuery(cxtMock.getInstance(), sql, "baz");
+
+ assertEquals("SQL", sql, cxtMock.lastPrepareSql);
+ assertEquals("parameters", Arrays.asList(null, "baz"), cxtMock.lastPrepareMock.parameters);
+ assertEquals("results", SAMPLE_DATA, result);
+ assertTrue("connection still open", cxtMock.isOpen);
+ assertFalse("statment not open", cxtMock.lastPrepareMock.isOpen);
+ assertFalse("resultset not open", cxtMock.lastPrepareMock.lastResultMock.isOpen);
+ }
+
+
+ public void testExecuteUpdate() throws Exception
+ {
+ final String sql = "insert into foo values(?, ?)";
+
+ MockConnection cxtMock = new MockConnection();
+ int count = JDBCUtil.executeUpdate(cxtMock.getInstance(), sql, "bar", "baz");
+
+ assertEquals("SQL", sql, cxtMock.lastPrepareSql);
+ assertEquals("parameters", Arrays.asList(null, "bar", "baz"), cxtMock.lastPrepareMock.parameters);
+ assertEquals("results", 1, count);
+ assertTrue("connection still open", cxtMock.isOpen);
+ assertFalse("statment not open", cxtMock.lastPrepareMock.isOpen);
+ }
+
+
+ public void testPrepare() throws Exception
+ {
+ final String sql = "select * from foo where bar = ?";
+
+ MockConnection cxtMock = new MockConnection();
+ PreparedStatement stmt = JDBCUtil.prepare(cxtMock.getInstance(), sql, "baz");
+
+ assertNotNull("created statement", stmt);
+ assertEquals("SQL", sql, cxtMock.lastPrepareSql);
+ assertEquals("parameters", Arrays.asList(null, "baz"), cxtMock.lastPrepareMock.parameters);
+ assertTrue("statement open", cxtMock.lastPrepareMock.isOpen);
+ assertTrue("connection still open", cxtMock.isOpen);
+ }
+
+
+ public void testRetrieve() throws Exception
+ {
+ MockResultSet mock = new MockResultSet(SAMPLE_DATA_COLNAMES, SAMPLE_DATA);
+ List<Map<String,Object>> result = JDBCUtil.retrieve(mock.getInstance());
+
+ assertEquals("result equivalent to source data", SAMPLE_DATA, result);
+ assertEquals("getMetaData() invocation count", 1, mock.getMetaDataInvocationCount);
+ assertEquals("next() invocation count", 4, mock.nextInvocationCount);
+ assertTrue("resultSet still open", mock.isOpen);
+ }
+
+
public void testCloseQuietly()
throws Exception
{
=====================================
src/test/java/net/sf/kdgcommons/test/TestNumericAsserts.java
=====================================
@@ -26,26 +26,125 @@ public class TestNumericAsserts extends TestCase
NumericAsserts.assertApproximate(100, 101, 1);
NumericAsserts.assertApproximate(100, 99, 1);
- AssertionFailedError last = null;
+ // note: we can't use fail() inside the try block (because it throws
+ // AssertionFailedError) so must capture the exception and
+ // assert on it afterward
+ AssertionFailedError lastAssertionResult;
+
+ try
+ {
+ NumericAsserts.assertApproximate("example", 100, 98, 1);
+ lastAssertionResult = null;
+ }
+ catch (AssertionFailedError ee)
+ {
+ lastAssertionResult = ee;
+ }
+ assertNotNull("did not assert for < delta %", lastAssertionResult);
+ assertTrue("message contained passed title", lastAssertionResult.getMessage().contains("example: "));
+ assertTrue("message identified original value", lastAssertionResult.getMessage().contains("98"));
+ assertTrue("message identified expected low", lastAssertionResult.getMessage().contains("99"));
+ assertTrue("message identified expected high", lastAssertionResult.getMessage().contains("101"));
+
+ try
+ {
+ NumericAsserts.assertApproximate("example", 100, 102, 1);
+ lastAssertionResult = null;
+ }
+ catch (AssertionFailedError ee)
+ {
+ lastAssertionResult = ee;
+ }
+ assertNotNull("did not assert for > delta %", lastAssertionResult);
+ assertTrue("message contained passed title", lastAssertionResult.getMessage().contains("example: "));
+ assertTrue("message identified original value", lastAssertionResult.getMessage().contains("102"));
+ assertTrue("message identified expected low", lastAssertionResult.getMessage().contains("99"));
+ assertTrue("message identified expected high", lastAssertionResult.getMessage().contains("101"));
+ }
+
+
+ public void testAssertApproximateLong() throws Exception
+ {
+ NumericAsserts.assertApproximate(100L, 100L, 0);
+ NumericAsserts.assertApproximate(100L, 101L, 1);
+ NumericAsserts.assertApproximate(100L, 99L, 1);
+
+ // note: we can't use fail() inside the try block (because it throws
+ // AssertionFailedError) so must capture the exception and
+ // assert on it afterward
+ AssertionFailedError lastAssertionResult;
+
try
{
- NumericAsserts.assertApproximate(100, 98, 1);
+ NumericAsserts.assertApproximate("example", 100L, 98L, 1);
+ lastAssertionResult = null;
}
catch (AssertionFailedError ee)
{
- last = ee;
+ lastAssertionResult = ee;
}
- assertNotNull("did not assert for < delta %", last);
+ assertNotNull("did not assert for < delta %", lastAssertionResult);
+ assertTrue("message contained passed title", lastAssertionResult.getMessage().contains("example: "));
+ assertTrue("message identified original value", lastAssertionResult.getMessage().contains("98"));
+ assertTrue("message identified expected low", lastAssertionResult.getMessage().contains("99"));
+ assertTrue("message identified expected high", lastAssertionResult.getMessage().contains("101"));
try
{
- NumericAsserts.assertApproximate(100, 102, 1);
+ NumericAsserts.assertApproximate("example", 100L, 102L, 1);
+ lastAssertionResult = null;
}
catch (AssertionFailedError ee)
{
- last = ee;
+ lastAssertionResult = ee;
}
- assertNotNull("did not assert for > delta %", last);
+ assertNotNull("did not assert for > delta %", lastAssertionResult);
+ assertTrue("message contained passed title", lastAssertionResult.getMessage().contains("example: "));
+ assertTrue("message identified original value", lastAssertionResult.getMessage().contains("102"));
+ assertTrue("message identified expected low", lastAssertionResult.getMessage().contains("99"));
+ assertTrue("message identified expected high", lastAssertionResult.getMessage().contains("101"));
}
+
+ public void testAssertApproximateDouble() throws Exception
+ {
+ NumericAsserts.assertApproximate(100.0, 100.0, 0);
+ NumericAsserts.assertApproximate(100.0, 101.0, 1);
+ NumericAsserts.assertApproximate(100.0, 99.0, 1);
+
+ // note: we can't use fail() inside the try block (because it throws
+ // AssertionFailedError) so must capture the exception and
+ // assert on it afterward
+ AssertionFailedError lastAssertionResult;
+
+ try
+ {
+ NumericAsserts.assertApproximate("example", 100.0, 98.0, 1);
+ lastAssertionResult = null;
+ }
+ catch (AssertionFailedError ee)
+ {
+ lastAssertionResult = ee;
+ }
+ assertNotNull("did not assert for < delta %", lastAssertionResult);
+ assertTrue("message contained passed title", lastAssertionResult.getMessage().contains("example: "));
+ assertTrue("message identified original value", lastAssertionResult.getMessage().contains("98"));
+ assertTrue("message identified expected low", lastAssertionResult.getMessage().contains("99"));
+ assertTrue("message identified expected high", lastAssertionResult.getMessage().contains("101"));
+
+ try
+ {
+ NumericAsserts.assertApproximate("example", 100.0, 102.0, 1);
+ lastAssertionResult = null;
+ }
+ catch (AssertionFailedError ee)
+ {
+ lastAssertionResult = ee;
+ }
+ assertNotNull("did not assert for > delta %", lastAssertionResult);
+ assertTrue("message contained passed title", lastAssertionResult.getMessage().contains("example: "));
+ assertTrue("message identified original value", lastAssertionResult.getMessage().contains("102"));
+ assertTrue("message identified expected low", lastAssertionResult.getMessage().contains("99"));
+ assertTrue("message identified expected high", lastAssertionResult.getMessage().contains("101"));
+ }
}
=====================================
src/test/java/net/sf/kdgcommons/test/TestSelfMock.java
=====================================
@@ -0,0 +1,113 @@
+// Copyright Keith D Gregory
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package net.sf.kdgcommons.test;
+
+import junit.framework.TestCase;
+
+
+public class TestSelfMock extends TestCase
+{
+ // note that we don't implement all methods of the interface
+ public static class TestMock
+ extends SelfMock<CharSequence>
+ {
+ public TestMock()
+ {
+ super(CharSequence.class);
+ }
+
+ // tests normal invocation -- can be called with different arguments
+ public char charAt(int index)
+ {
+ return (char)('0' + (index % 10));
+ }
+
+ // tests invocation exceptions
+ public int length()
+ {
+ throw new IllegalStateException("testing");
+ }
+
+ }
+
+
+ public void testNormalOperation() throws Exception
+ {
+ TestMock mock = new TestMock();
+ CharSequence instance = mock.getInstance();
+
+ assertEquals("count before invocations", 0, mock.getInvocationCount("charAt"));
+
+ // two invocations so that we can verify history
+
+ assertEquals('3', instance.charAt(3));
+ assertEquals('5', instance.charAt(15));
+
+ assertEquals("count after invocations", 2, mock.getInvocationCount("charAt"));
+ assertEquals("argument count, call 0", 1, mock.getInvocationArgs("charAt", 0).length);
+ assertEquals("argument value, call 0", Integer.valueOf(3), mock.getInvocationArgs("charAt", 0)[0]);
+ assertEquals("as-type value, call 0", 3, mock.getInvocationArg("charAt", 0, 0, Integer.class).intValue());
+ assertEquals("argument count, call 1", 1, mock.getInvocationArgs("charAt", 1).length);
+ assertEquals("argument value, call 1", Integer.valueOf(15), mock.getInvocationArgs("charAt", 1)[0]);
+ assertEquals("as-type value, call 1", 15, mock.getInvocationArg("charAt", 1, 0, Integer.class).intValue());
+
+ assertEquals("argument to most recent call", Integer.valueOf(15), mock.getMostRecentInvocationArg("charAt", 0, Integer.class));
+ }
+
+
+ public void testExceptionInMock() throws Exception
+ {
+ TestMock mock = new TestMock();
+ CharSequence instance = mock.getInstance();
+
+ try
+ {
+ instance.length();
+ fail("successful invocation of method that was supposed to throw");
+ }
+ catch (IllegalStateException ex)
+ {
+ assertEquals("count incremented even though method throws", 1, mock.getInvocationCount("length"));
+ assertEquals("history recorded even though method throws", null, mock.getInvocationArgs("length", 0));
+ }
+ }
+
+
+ public void testMissingMethod() throws Exception
+ {
+ TestMock mock = new TestMock();
+ CharSequence instance = mock.getInstance();
+
+ try
+ {
+ instance.subSequence(1, 10);
+ fail("successful invocation of method that doesn't exist");
+ }
+ catch (UnsupportedOperationException ex)
+ {
+ assertEquals("count incremented even though method doesn't exist", 1, mock.getInvocationCount("subSequence"));
+ }
+ }
+
+
+ public void testInvokeInheritedFunctions() throws Exception
+ {
+ TestMock mock = new TestMock() { /* nothing new here */ };
+ CharSequence instance = mock.getInstance();
+
+ assertEquals('0', instance.charAt(10));
+ }
+
+}
=====================================
src/test/java/net/sf/kdgcommons/test/TestStringAsserts.java
=====================================
@@ -20,6 +20,50 @@ import junit.framework.TestCase;
public class TestStringAsserts extends TestCase
{
+ public void testAsserNotEmpty() throws Exception
+ {
+ StringAsserts.assertNotEmpty("this succeeds");
+
+ AssertionFailedError fail1 = null;
+ try
+ {
+ StringAsserts.assertNotEmpty(null);
+ }
+ catch (AssertionFailedError ex)
+ {
+ fail1 = ex;
+ assertEquals("expected exception to indicate value was null", "expected not-empty, was null", ex.getMessage());
+ }
+ assertNotNull("null did not cause assertion failure", fail1);
+
+ AssertionFailedError fail2 = null;
+ try
+ {
+ StringAsserts.assertNotEmpty("");
+ }
+ catch (AssertionFailedError ex)
+ {
+ fail2 = ex;
+ assertEquals("expected exception to indicate value was empty", "expected not-empty", ex.getMessage());
+ }
+ assertNotNull("empty string did not cause assertion failure", fail2);
+
+ AssertionFailedError fail3 = null;
+ try
+ {
+ StringAsserts.assertNotEmpty("example", "");
+ }
+ catch (AssertionFailedError ex)
+ {
+ fail3 = ex;
+ assertEquals("expected exception to start with user message", "example: expected not-empty", ex.getMessage());
+ }
+ assertNotNull("null did not cause assertion failure", fail3);
+
+
+ }
+
+
public void testAssertSubstringCount0() throws Exception
{
StringAsserts.assertSubstringCount("foo", "bar", 0);
=====================================
src/test/java/net/sf/kdgcommons/util/TestCounters.java
=====================================
@@ -37,21 +37,24 @@ extends TestCase
{
Counters<String> counters = new Counters<String>();
- assertEquals("object get from new instance", null, counters.get("foo"));
- assertEquals("primitive get from new instance", 0, counters.getLong("foo"));
+ assertEquals("object get from new instance", null, counters.get("foo"));
+ assertEquals("primitive long get from new instance", 0, counters.getLong("foo"));
+ assertEquals("primitive int get from new instance", 0, counters.getInt("foo"));
- assertEquals("return from put, new mapping", null, counters.put("foo", Long.valueOf(12)));
+ assertEquals("return from put, new mapping", null, counters.put("foo", Long.valueOf(12)));
- assertEquals("object get, existing mapping", Long.valueOf(12), counters.get("foo"));
- assertEquals("primitive get, existing mapping", 12, counters.getLong("foo"));
+ assertEquals("object get, existing mapping", Long.valueOf(12), counters.get("foo"));
+ assertEquals("primitive long get, existing mapping", 12, counters.getLong("foo"));
+ assertEquals("primitive int get, existing mapping", 12, counters.getInt("foo"));
counters.putLong("foo", 13);
- assertEquals("primitive get after primitive put", 13, counters.getLong("foo"));
- assertEquals("return value from remove()", Long.valueOf(13), counters.remove("foo"));
+ assertEquals("primitive get after primitive put", 13, counters.getLong("foo"));
- assertEquals("object get after remove()", null, counters.get("foo"));
- assertEquals("primitive get after remove()", 0, counters.getLong("foo"));
+ assertEquals("return value from remove()", Long.valueOf(13), counters.remove("foo"));
+
+ assertEquals("object get after remove()", null, counters.get("foo"));
+ assertEquals("primitive get after remove()", 0, counters.getLong("foo"));
}
@@ -60,19 +63,23 @@ extends TestCase
Counters<String> counters = new Counters<String>();
assertEquals("increment creates counter", 1, counters.increment("foo"));
- assertEquals("post-increment primitive get", 1, counters.getLong("foo"));
- assertEquals("post-increment object get", Long.valueOf(1), counters.get("foo"));
+ assertEquals("post-create primitive long get", 1, counters.getLong("foo"));
+ assertEquals("post-create primitive int get", 1, counters.getInt("foo"));
+ assertEquals("post-create object get", Long.valueOf(1), counters.get("foo"));
- assertEquals("increment of existing counte", 2, counters.increment("foo"));
- assertEquals("post-increment primitive get", 2, counters.getLong("foo"));
+ assertEquals("increment of existing counter", 2, counters.increment("foo"));
+ assertEquals("post-increment primitive long get", 2, counters.getLong("foo"));
+ assertEquals("post-increment primitive int get", 2, counters.getInt("foo"));
assertEquals("post-increment object get", Long.valueOf(2), counters.get("foo"));
assertEquals("decrement creates counter", -1, counters.decrement("bar"));
- assertEquals("post-decrement primitive get", -1, counters.getLong("bar"));
- assertEquals("post-decrement object get", Long.valueOf(-1), counters.get("bar"));
+ assertEquals("post-create primitive long get", -1, counters.getLong("bar"));
+ assertEquals("post-create primitive int get", -1, counters.getInt("bar"));
+ assertEquals("post-create object get", Long.valueOf(-1), counters.get("bar"));
assertEquals("decrement of existing counter", -2, counters.decrement("bar"));
- assertEquals("post-decrement primitive get", -2, counters.getLong("bar"));
+ assertEquals("post-decrement primitive long get", -2, counters.getLong("bar"));
+ assertEquals("post-decrement primitive int get", -2, counters.getInt("bar"));
assertEquals("post-decrement object get", Long.valueOf(-2), counters.get("bar"));
}
@@ -129,6 +136,7 @@ extends TestCase
}
+ @SuppressWarnings("unlikely-arg-type")
public void testContains() throws Exception
{
Counters<String> counters = new Counters<String>();
@@ -220,4 +228,25 @@ extends TestCase
}
+ public void testToString() throws Exception
+ {
+ Counters<String> counters = new Counters<String>();
+
+ assertEquals("empty counters", "[]", counters.toString());
+
+ counters.put("foo", 12L);
+ assertEquals("single element", "[foo: 12]", counters.toString());
+
+ counters.put("bar", 13L);
+
+ // note: since the map is hashed, the order of elements may change depending
+ // on Java version, so we'll accept either order
+ String value = counters.toString();
+ boolean order1 = value.equals("[foo: 12, bar: 13]");
+ boolean order2 = value.equals("[bar: 13, foo: 12]");
+ assertTrue("multiple elements (was: " + value + ")", order1 || order2);
+ }
+
+
+
}
View it on GitLab: https://salsa.debian.org/java-team/kdgcommons-java/-/commit/2ca9bfea7c63763440bede2d56a4c43bbf832619
--
View it on GitLab: https://salsa.debian.org/java-team/kdgcommons-java/-/commit/2ca9bfea7c63763440bede2d56a4c43bbf832619
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20250202/f5d794e6/attachment.htm>
More information about the pkg-java-commits
mailing list