Java程序  |  125行  |  4.27 KB

// Copyright 2011 Google Inc. All Rights Reserved.

package com.google.common.hash;

import com.google.common.collect.ImmutableList;

import junit.framework.TestCase;

import java.util.Arrays;

/**
 * Tests for HashCodes, especially making sure that their endianness promises (big-endian)
 * are upheld. 
 *  
 * @author andreou@google.com (Dimitris Andreou)
 */
public class HashCodesTest extends TestCase {
  // note: asInt(), asLong() are in little endian
  private static final ImmutableList<ExpectedHashCode> expectedHashCodes = ImmutableList.of(
      new ExpectedHashCode(new byte[] { 
        (byte) 0xef, (byte) 0xcd, (byte) 0xab, (byte) 0x89,
        (byte) 0x67, (byte) 0x45, (byte) 0x23, (byte) 0x01},
        0x89abcdef, 0x0123456789abcdefL, "efcdab8967452301"),
        
      new ExpectedHashCode(new byte[] { 
        (byte) 0xef, (byte) 0xcd, (byte) 0xab, (byte) 0x89,
        (byte) 0x67, (byte) 0x45, (byte) 0x23, (byte) 0x01, // up to here, same bytes as above
        (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04,
        (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08},
        0x89abcdef, 0x0123456789abcdefL, // asInt/asLong as above, due to equal eight first bytes
        "efcdab89674523010102030405060708"), 
        
      new ExpectedHashCode(new byte[] { (byte) 0xdf, (byte) 0x9b, (byte) 0x57, (byte) 0x13 },
        0x13579bdf, null, "df9b5713"),
        
      new ExpectedHashCode(new byte[] { 
          (byte) 0xcd, (byte) 0xab, (byte) 0x00, (byte) 0x00},
          0x0000abcd, null, "cdab0000"),
      
      new ExpectedHashCode(new byte[] { 
          (byte) 0xef, (byte) 0xcd, (byte) 0xab, (byte) 0x00,
          (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00},
          0x00abcdef, 0x0000000000abcdefL, "efcdab0000000000")
    );
  
  // expectedHashCodes must contain at least one hash code with 4 bytes
  public void testFromInt() {
    for (ExpectedHashCode expected : expectedHashCodes) {
      if (expected.bytes.length == 4) { 
        HashCode fromInt = HashCodes.fromInt(expected.asInt);
        assertExpectedHashCode(expected, fromInt);
      }
    }
  }
  
  // expectedHashCodes must contain at least one hash code with 8 bytes
  public void testFromLong() {
    for (ExpectedHashCode expected : expectedHashCodes) {
      if (expected.bytes.length == 8) { 
        HashCode fromLong = HashCodes.fromLong(expected.asLong);
        assertExpectedHashCode(expected, fromLong);
      }
    }
  }
  
  public void testFromBytes() {
    for (ExpectedHashCode expected : expectedHashCodes) {
      HashCode fromBytes = HashCodes.fromBytes(expected.bytes);
      assertExpectedHashCode(expected, fromBytes);
    }
  }
  
  private void assertExpectedHashCode(ExpectedHashCode expected, HashCode hash) {
    assertTrue(Arrays.equals(expected.bytes, hash.asBytes()));
    byte[] bb = new byte[hash.bits() / 8];
    hash.writeBytesTo(bb, 0, bb.length);
    assertTrue(Arrays.equals(expected.bytes, bb));
    assertEquals(expected.asInt, hash.asInt());
    if (expected.asLong == null) {
      try {
        hash.asLong();
        fail();
      } catch (IllegalStateException ok) {}
    } else {
      assertEquals(expected.asLong.longValue(), hash.asLong());
    }
    assertEquals(expected.toString, hash.toString());
    assertSideEffectFree(hash);
    assertReadableBytes(hash);
  }
  
  private void assertSideEffectFree(HashCode hash) {
    byte[] original = hash.asBytes();
    byte[] mutated = hash.asBytes();
    mutated[0]++;
    assertTrue(Arrays.equals(original, hash.asBytes()));
  }
  
  private void assertReadableBytes(HashCode hashCode) {
    assertTrue(hashCode.bits() >= 32); // sanity
    byte[] hashBytes = hashCode.asBytes();
    int totalBytes = hashCode.bits() / 8;
    
    for (int bytes = 0; bytes < totalBytes; bytes++) {
      byte[] bb = new byte[bytes];
      hashCode.writeBytesTo(bb, 0, bb.length);
      
      assertTrue(Arrays.equals(Arrays.copyOf(hashBytes, bytes), bb));
    }
  }
  
  private static class ExpectedHashCode {
    final byte[] bytes;
    final int asInt;
    final Long asLong; // null means that asLong should throw an exception
    final String toString;
    ExpectedHashCode(byte[] bytes, int asInt, Long asLong, String toString) {
      this.bytes = bytes;
      this.asInt = asInt;
      this.asLong = asLong;
      this.toString = toString;
    }
  }
}