You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by at...@apache.org on 2012/04/16 20:33:16 UTC
svn commit: r1326729 - in
/hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common: ./
src/main/java/org/apache/hadoop/util/ src/test/java/org/apache/hadoop/test/
src/test/java/org/apache/hadoop/util/
Author: atm
Date: Mon Apr 16 18:33:16 2012
New Revision: 1326729
URL: http://svn.apache.org/viewvc?rev=1326729&view=rev
Log:
HADOOP-8280. Move VersionUtil/TestVersionUtil and GenericTestUtils from HDFS into Common. Contributed by Ahmed Radwan.
Added:
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/VersionUtil.java
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/test/GenericTestUtils.java
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestVersionUtil.java
Modified:
hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common/CHANGES.txt
Modified: hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common/CHANGES.txt
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common/CHANGES.txt?rev=1326729&r1=1326728&r2=1326729&view=diff
==============================================================================
--- hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common/CHANGES.txt (original)
+++ hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common/CHANGES.txt Mon Apr 16 18:33:16 2012
@@ -142,6 +142,9 @@ Release 2.0.0 - UNRELEASED
HADOOP-8086. KerberosName silently sets defaultRealm to "" if the
Kerberos config is not found, it should log a WARN (tucu)
+ HADOOP-8280. Move VersionUtil/TestVersionUtil and GenericTestUtils from
+ HDFS into Common. (Ahmed Radwan
+
OPTIMIZATIONS
BUG FIXES
Added: hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/VersionUtil.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/VersionUtil.java?rev=1326729&view=auto
==============================================================================
--- hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/VersionUtil.java (added)
+++ hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/VersionUtil.java Mon Apr 16 18:33:16 2012
@@ -0,0 +1,101 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.hadoop.util;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+
+@InterfaceAudience.Private
+public abstract class VersionUtil {
+
+ private static final Pattern COMPONENT_GROUPS = Pattern.compile("(\\d+)|(\\D+)");
+
+ /**
+ * This function splits the two versions on "." and performs a
+ * naturally-ordered comparison of the resulting components. For example, the
+ * version string "0.3" is considered to precede "0.20", despite the fact that
+ * lexical comparison would consider "0.20" to precede "0.3". This method of
+ * comparison is similar to the method used by package versioning systems like
+ * deb and RPM.
+ *
+ * Version components are compared numerically whenever possible, however a
+ * version component can contain non-numeric characters. When a non-numeric
+ * group of characters is found in a version component, this group is compared
+ * with the similarly-indexed group in the other version component. If the
+ * other group is numeric, then the numeric group is considered to precede the
+ * non-numeric group. If both groups are non-numeric, then a lexical
+ * comparison is performed.
+ *
+ * If two versions have a different number of components, then only the lower
+ * number of components are compared. If those components are identical
+ * between the two versions, then the version with fewer components is
+ * considered to precede the version with more components.
+ *
+ * This function returns a negative integer if version1 precedes version2, a
+ * positive integer if version2 precedes version1, and 0 if and only if the
+ * two versions' components are identical in value and cardinality.
+ *
+ * @param version1
+ * the first version to compare
+ * @param version2
+ * the second version to compare
+ * @return a negative integer if version1 precedes version2, a positive
+ * integer if version2 precedes version1, and 0 if and only if the two
+ * versions are equal.
+ */
+ public static int compareVersions(String version1, String version2) {
+ String[] version1Parts = version1.split("\\.");
+ String[] version2Parts = version2.split("\\.");
+
+ for (int i = 0; i < version1Parts.length && i < version2Parts.length; i++) {
+ String component1 = version1Parts[i];
+ String component2 = version2Parts[i];
+ if (!component1.equals(component2)) {
+ Matcher matcher1 = COMPONENT_GROUPS.matcher(component1);
+ Matcher matcher2 = COMPONENT_GROUPS.matcher(component2);
+
+ while (matcher1.find() && matcher2.find()) {
+ String group1 = matcher1.group();
+ String group2 = matcher2.group();
+ if (!group1.equals(group2)) {
+ if (isNumeric(group1) && isNumeric(group2)) {
+ return Integer.parseInt(group1) - Integer.parseInt(group2);
+ } else if (!isNumeric(group1) && !isNumeric(group2)) {
+ return group1.compareTo(group2);
+ } else {
+ return isNumeric(group1) ? -1 : 1;
+ }
+ }
+ }
+ return component1.length() - component2.length();
+ }
+ }
+ return version1Parts.length - version2Parts.length;
+ }
+
+ private static boolean isNumeric(String s) {
+ try {
+ Integer.parseInt(s);
+ return true;
+ } catch (NumberFormatException nfe) {
+ return false;
+ }
+ }
+}
Added: hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/test/GenericTestUtils.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/test/GenericTestUtils.java?rev=1326729&view=auto
==============================================================================
--- hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/test/GenericTestUtils.java (added)
+++ hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/test/GenericTestUtils.java Mon Apr 16 18:33:16 2012
@@ -0,0 +1,289 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.hadoop.test;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeoutException;
+import java.util.regex.Pattern;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.impl.Log4JLogger;
+import org.apache.hadoop.fs.FileUtil;
+import org.apache.hadoop.util.StringUtils;
+import org.apache.log4j.Layout;
+import org.apache.log4j.Logger;
+import org.apache.log4j.WriterAppender;
+import org.junit.Assert;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Supplier;
+import com.google.common.collect.Sets;
+
+/**
+ * Test provides some very generic helpers which might be used across the tests
+ */
+public abstract class GenericTestUtils {
+
+ /**
+ * Extracts the name of the method where the invocation has happened
+ * @return String name of the invoking method
+ */
+ public static String getMethodName() {
+ return Thread.currentThread().getStackTrace()[2].getMethodName();
+ }
+
+ /**
+ * Assert that a given file exists.
+ */
+ public static void assertExists(File f) {
+ Assert.assertTrue("File " + f + " should exist", f.exists());
+ }
+
+ /**
+ * List all of the files in 'dir' that match the regex 'pattern'.
+ * Then check that this list is identical to 'expectedMatches'.
+ * @throws IOException if the dir is inaccessible
+ */
+ public static void assertGlobEquals(File dir, String pattern,
+ String ... expectedMatches) throws IOException {
+
+ Set<String> found = Sets.newTreeSet();
+ for (File f : FileUtil.listFiles(dir)) {
+ if (f.getName().matches(pattern)) {
+ found.add(f.getName());
+ }
+ }
+ Set<String> expectedSet = Sets.newTreeSet(
+ Arrays.asList(expectedMatches));
+ Assert.assertEquals("Bad files matching " + pattern + " in " + dir,
+ Joiner.on(",").join(found),
+ Joiner.on(",").join(expectedSet));
+ }
+
+ public static void assertExceptionContains(String string, Throwable t) {
+ String msg = t.getMessage();
+ Assert.assertTrue(
+ "Expected to find '" + string + "' but got unexpected exception:"
+ + StringUtils.stringifyException(t), msg.contains(string));
+ }
+
+ public static void waitFor(Supplier<Boolean> check,
+ int checkEveryMillis, int waitForMillis)
+ throws TimeoutException, InterruptedException
+ {
+ long st = System.currentTimeMillis();
+ do {
+ boolean result = check.get();
+ if (result) {
+ return;
+ }
+
+ Thread.sleep(checkEveryMillis);
+ } while (System.currentTimeMillis() - st < waitForMillis);
+ throw new TimeoutException("Timed out waiting for condition");
+ }
+
+ public static class LogCapturer {
+ private StringWriter sw = new StringWriter();
+ private WriterAppender appender;
+ private Logger logger;
+
+ public static LogCapturer captureLogs(Log l) {
+ Logger logger = ((Log4JLogger)l).getLogger();
+ LogCapturer c = new LogCapturer(logger);
+ return c;
+ }
+
+
+ private LogCapturer(Logger logger) {
+ this.logger = logger;
+ Layout layout = Logger.getRootLogger().getAppender("stdout").getLayout();
+ WriterAppender wa = new WriterAppender(layout, sw);
+ logger.addAppender(wa);
+ }
+
+ public String getOutput() {
+ return sw.toString();
+ }
+
+ public void stopCapturing() {
+ logger.removeAppender(appender);
+
+ }
+ }
+
+
+ /**
+ * Mockito answer helper that triggers one latch as soon as the
+ * method is called, then waits on another before continuing.
+ */
+ public static class DelayAnswer implements Answer<Object> {
+ private final Log LOG;
+
+ private final CountDownLatch fireLatch = new CountDownLatch(1);
+ private final CountDownLatch waitLatch = new CountDownLatch(1);
+ private final CountDownLatch resultLatch = new CountDownLatch(1);
+
+ // Result fields set after proceed() is called.
+ private volatile Throwable thrown;
+ private volatile Object returnValue;
+
+ public DelayAnswer(Log log) {
+ this.LOG = log;
+ }
+
+ /**
+ * Wait until the method is called.
+ */
+ public void waitForCall() throws InterruptedException {
+ fireLatch.await();
+ }
+
+ /**
+ * Tell the method to proceed.
+ * This should only be called after waitForCall()
+ */
+ public void proceed() {
+ waitLatch.countDown();
+ }
+
+ public Object answer(InvocationOnMock invocation) throws Throwable {
+ LOG.info("DelayAnswer firing fireLatch");
+ fireLatch.countDown();
+ try {
+ LOG.info("DelayAnswer waiting on waitLatch");
+ waitLatch.await();
+ LOG.info("DelayAnswer delay complete");
+ } catch (InterruptedException ie) {
+ throw new IOException("Interrupted waiting on latch", ie);
+ }
+ return passThrough(invocation);
+ }
+
+ protected Object passThrough(InvocationOnMock invocation) throws Throwable {
+ try {
+ Object ret = invocation.callRealMethod();
+ returnValue = ret;
+ return ret;
+ } catch (Throwable t) {
+ thrown = t;
+ throw t;
+ } finally {
+ resultLatch.countDown();
+ }
+ }
+
+ /**
+ * After calling proceed(), this will wait until the call has
+ * completed and a result has been returned to the caller.
+ */
+ public void waitForResult() throws InterruptedException {
+ resultLatch.await();
+ }
+
+ /**
+ * After the call has gone through, return any exception that
+ * was thrown, or null if no exception was thrown.
+ */
+ public Throwable getThrown() {
+ return thrown;
+ }
+
+ /**
+ * After the call has gone through, return the call's return value,
+ * or null in case it was void or an exception was thrown.
+ */
+ public Object getReturnValue() {
+ return returnValue;
+ }
+ }
+
+ /**
+ * An Answer implementation that simply forwards all calls through
+ * to a delegate.
+ *
+ * This is useful as the default Answer for a mock object, to create
+ * something like a spy on an RPC proxy. For example:
+ * <code>
+ * NamenodeProtocol origNNProxy = secondary.getNameNode();
+ * NamenodeProtocol spyNNProxy = Mockito.mock(NameNodeProtocol.class,
+ * new DelegateAnswer(origNNProxy);
+ * doThrow(...).when(spyNNProxy).getBlockLocations(...);
+ * ...
+ * </code>
+ */
+ public static class DelegateAnswer implements Answer<Object> {
+ private final Object delegate;
+
+ public DelegateAnswer(Object delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public Object answer(InvocationOnMock invocation) throws Throwable {
+ return invocation.getMethod().invoke(
+ delegate, invocation.getArguments());
+ }
+ }
+
+ /**
+ * An Answer implementation which sleeps for a random number of milliseconds
+ * between 0 and a configurable value before delegating to the real
+ * implementation of the method. This can be useful for drawing out race
+ * conditions.
+ */
+ public static class SleepAnswer implements Answer<Object> {
+ private final int maxSleepTime;
+ private static Random r = new Random();
+
+ public SleepAnswer(int maxSleepTime) {
+ this.maxSleepTime = maxSleepTime;
+ }
+
+ @Override
+ public Object answer(InvocationOnMock invocation) throws Throwable {
+ boolean interrupted = false;
+ try {
+ Thread.sleep(r.nextInt(maxSleepTime));
+ } catch (InterruptedException ie) {
+ interrupted = true;
+ }
+ try {
+ return invocation.callRealMethod();
+ } finally {
+ if (interrupted) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+
+ public static void assertMatches(String output, String pattern) {
+ Assert.assertTrue("Expected output to match /" + pattern + "/" +
+ " but got:\n" + output,
+ Pattern.compile(pattern).matcher(output).find());
+ }
+}
Added: hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestVersionUtil.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestVersionUtil.java?rev=1326729&view=auto
==============================================================================
--- hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestVersionUtil.java (added)
+++ hadoop/common/branches/branch-2/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestVersionUtil.java Mon Apr 16 18:33:16 2012
@@ -0,0 +1,63 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.hadoop.util;
+
+import static org.junit.Assert.*;
+
+import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.util.VersionUtil;
+import org.junit.Test;
+
+public class TestVersionUtil {
+
+ @Test
+ public void testCompareVersions() {
+ // Equal versions are equal.
+ assertEquals(0, VersionUtil.compareVersions("2.0.0", "2.0.0"));
+ assertEquals(0, VersionUtil.compareVersions("2.0.0a", "2.0.0a"));
+ assertEquals(0, VersionUtil.compareVersions("1", "1"));
+
+ // Assert that lower versions are lower, and higher versions are higher.
+ assertExpectedValues("1", "2.0.0");
+ assertExpectedValues("1.0.0", "2");
+ assertExpectedValues("1.0.0", "2.0.0");
+ assertExpectedValues("1.0", "2.0.0");
+ assertExpectedValues("1.0.0", "2.0.0");
+ assertExpectedValues("1.0.0", "1.0.0a");
+ assertExpectedValues("1.0.0.0", "2.0.0");
+ assertExpectedValues("1.0.0", "1.0.0-dev");
+ assertExpectedValues("1.0.0", "1.0.1");
+ assertExpectedValues("1.0.0", "1.0.2");
+ assertExpectedValues("1.0.0", "1.1.0");
+ assertExpectedValues("2.0.0", "10.0.0");
+ assertExpectedValues("1.0.0", "1.0.0a");
+ assertExpectedValues("1.0.2a", "1.0.10");
+ assertExpectedValues("1.0.2a", "1.0.2b");
+ assertExpectedValues("1.0.2a", "1.0.2ab");
+ assertExpectedValues("1.0.0a1", "1.0.0a2");
+ assertExpectedValues("1.0.0a2", "1.0.0a10");
+ assertExpectedValues("1.0", "1.a");
+ assertExpectedValues("1.0", "1.a0");
+ }
+
+ private static void assertExpectedValues(String lower, String higher) {
+ assertTrue(VersionUtil.compareVersions(lower, higher) < 0);
+ assertTrue(VersionUtil.compareVersions(higher, lower) > 0);
+ }
+
+}