/* * Copyright 2015-present Facebook, Inc. * * 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 com.facebook.buck.json; import static org.junit.Assert.assertEquals; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.hash.Hasher; import com.google.common.hash.Hashing; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.junit.Before; import org.junit.Test; public class JsonObjectHashingTest { private Hasher hasher; @Before public void setupHasher() { hasher = Hashing.sha1().newHasher(); } @Test public void emptyMapSha1Hash() throws IOException { JsonObjectHashing.hashJsonObject(hasher, ImmutableMap.of()); assertEquals("29e24643a6328cb4ea893738b89c63b842ce24e7", hasher.hash().toString()); } @Test public void stringSha1Hash() throws IOException { JsonObjectHashing.hashJsonObject(hasher, ImmutableMap.of("k", "v")); assertEquals("fe58988370f1bac4597f7cffc04a315d3ea5493d", hasher.hash().toString()); } @Test public void booleanSha1Hash() throws IOException { JsonObjectHashing.hashJsonObject(hasher, ImmutableMap.of("k", true)); assertEquals("b3913145947a4a567ff95e9a321d4133cfbe602f", hasher.hash().toString()); } @Test public void shortSha1Hash() throws IOException { JsonObjectHashing.hashJsonObject(hasher, ImmutableMap.of("k", 4096L)); assertEquals("d052359a75b28c9f1097fdbcab6e9f2b52b47193", hasher.hash().toString()); } @Test public void intSha1Hash() throws IOException { JsonObjectHashing.hashJsonObject(hasher, ImmutableMap.of("k", 64738L)); assertEquals("a41891d41f003499d698a2e7d7aa3f0760a5c6d2", hasher.hash().toString()); } @Test public void longSha1Hash() throws IOException { JsonObjectHashing.hashJsonObject(hasher, ImmutableMap.of("k", 2147483648L)); assertEquals("5ec3ef74c52a97a02c1367767ae81a47554c36e4", hasher.hash().toString()); } @Test public void floatSha1Hash() throws IOException { JsonObjectHashing.hashJsonObject(hasher, ImmutableMap.of("k", 123.456)); assertEquals("c1c8a5cd9eddc08ffb1221d886eec398a4565bd9", hasher.hash().toString()); } @Test public void doubleSha1Hash() throws IOException { JsonObjectHashing.hashJsonObject(hasher, ImmutableMap.of("k", 1.79769313486231570e+308)); assertEquals("93e9a1600777d06a6c8e1d2926b7d8c6d601d4b8", hasher.hash().toString()); } @Test public void nullSha1Hash() throws IOException { Map<String, Object> map = new HashMap<>(); map.put("k", null); JsonObjectHashing.hashJsonObject(hasher, map); assertEquals("29e24643a6328cb4ea893738b89c63b842ce24e7", hasher.hash().toString()); } @Test public void arraySha1Hash() throws IOException { JsonObjectHashing.hashJsonObject(hasher, ImmutableMap.of("k", ImmutableList.of(1L, 2L, 3L))); assertEquals("a1175b70883560d2b89caee549c86157048f3859", hasher.hash().toString()); } @Test public void objectSha1Hash() throws IOException { JsonObjectHashing.hashJsonObject(hasher, ImmutableMap.of("k", ImmutableMap.of("k2", "v2"))); assertEquals("d19ac4d9a68ce5a15574fa3d2064084d93aac18c", hasher.hash().toString()); } @Test public void rawShortSha1Hash() throws IOException { // RawParser coerces integers to Long. Let's bypass it to test // this code path. Short val = 4096; JsonObjectHashing.hashJsonObject(hasher, val); assertEquals("d12109f0b8cb8b6e48f058f34766226b29d0b2bb", hasher.hash().toString()); } @Test public void rawIntSha1Hash() throws IOException { Integer val = 64738; JsonObjectHashing.hashJsonObject(hasher, val); assertEquals("7dbf423da794e905ef3107b7cc46bda8b7977eba", hasher.hash().toString()); } @Test public void rawFloatSha1Hash() throws IOException { Float val = 123.456f; JsonObjectHashing.hashJsonObject(hasher, val); assertEquals("fe1a9e33f321868d26f299524c79bf8753ab100b", hasher.hash().toString()); } @Test public void mapOrderDoesNotChangeHash() { Map<?, ?> map1 = ImmutableMap.of("firstKey", 24, "secondKey", new Float(13.5)); Map<?, ?> map2 = ImmutableMap.of("secondKey", new Float(13.5), "firstKey", 24); Hasher hasher1 = Hashing.sha1().newHasher(); Hasher hasher2 = Hashing.sha1().newHasher(); JsonObjectHashing.hashJsonObject(hasher1, map1); JsonObjectHashing.hashJsonObject(hasher2, map2); assertEquals(hasher1.hash().toString(), hasher2.hash().toString()); } /** * This helps keep the hashes more stable. While we include the buckversion in target hashes, when * making changes within Buck (such as adding or removing constructor arg fields) this means that * there are fewer places where tests fail, meaning less time being spent debugging "failed" * tests. * * <p>This means that two maps may generate the same hashcode, despite not being strictly equal. * We rely on the use case that this class was designed for (namely, generating target hashes) to * ensure that correct functionality is maintained. */ @Test public void nullFieldsAreIgnoredInTheHash() { HashMap<String, Object> map1 = new HashMap<>(); map1.put("firstKey", "value"); map1.put("secondKey", null); HashMap<String, Object> map2 = new HashMap<>(); map2.put("firstKey", "value"); map2.put("ignoredKey", null); Hasher hasher1 = Hashing.sha1().newHasher(); Hasher hasher2 = Hashing.sha1().newHasher(); JsonObjectHashing.hashJsonObject(hasher1, map1); JsonObjectHashing.hashJsonObject(hasher2, map2); assertEquals(hasher1.hash().toString(), hasher2.hash().toString()); } }