/******************************************************************************* * Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2015-2019) * * contact.vitam@culture.gouv.fr * * This software is a computer program whose purpose is to implement a digital archiving back-office system managing * high volumetry securely and efficiently. * * This software is governed by the CeCILL 2.1 license under French law and abiding by the rules of distribution of free * software. You can use, modify and/ or redistribute the software under the terms of the CeCILL 2.1 license as * circulated by CEA, CNRS and INRIA at the following URL "http://www.cecill.info". * * As a counterpart to the access to the source code and rights to copy, modify and redistribute granted by the license, * users are provided only with a limited warranty and the software's author, the holder of the economic rights, and the * successive licensors have only limited liability. * * In this respect, the user's attention is drawn to the risks associated with loading, using, modifying and/or * developing or reproducing the software by the user in light of its specific status of free software, that may mean * that it is complicated to manipulate, and that also therefore means that it is reserved for developers and * experienced professionals having in-depth computer knowledge. Users are therefore encouraged to load and test the * software's suitability as regards their requirements in conditions enabling the security of their systems and/or data * to be ensured and, more generally, to use and operate it in the same conditions as regards security. * * The fact that you are presently reading this means that you have had knowledge of the CeCILL 2.1 license and that you * accept its terms. *******************************************************************************/ package fr.gouv.vitam.common.guid; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.Arrays; import java.util.Date; import java.util.HashSet; import java.util.Set; import java.util.TreeSet; import org.junit.Test; import fr.gouv.vitam.common.ResourcesPrivateUtilTest; import fr.gouv.vitam.common.ServerIdentity; import fr.gouv.vitam.common.exception.InvalidGuidOperationException; import fr.gouv.vitam.common.exception.InvalidParseOperationException; import fr.gouv.vitam.common.json.JsonHandler; import fr.gouv.vitam.common.logging.VitamLogger; import fr.gouv.vitam.common.logging.VitamLoggerFactory; @SuppressWarnings({"javadoc"}) public class GUIDImplPrivateTest { private static final VitamLogger LOGGER = VitamLoggerFactory.getInstance(GUIDImplPrivateTest.class); private static final int VERSION = 1 & 0x1F; private static int NB = 100000; private static final int HEX_LENGTH = GUIDImplPrivate.KEYSIZE * 2; @Test public void testStructure() { final GUIDImplPrivate id = new GUIDImplPrivate(); final String str = id.toHex(); assertEquals('0', str.charAt(0)); assertEquals('1', str.charAt(1)); assertEquals(HEX_LENGTH, str.length()); LOGGER.info(id.toArk() + " = " + id.toString()); } @Test public void testParsing() { for (int i = 0; i < 1000; i++) { final GUIDImplPrivate id1 = new GUIDImplPrivate(); GUIDImplPrivate id2; try { id2 = new GUIDImplPrivate(id1.toHex()); assertEquals(id1, id2); assertEquals(id1.hashCode(), id2.hashCode()); assertEquals(0, id1.compareTo(id2)); final GUIDImplPrivate id3 = new GUIDImplPrivate(id1.getBytes()); assertEquals(id1, id3); assertEquals(id1.hashCode(), id3.hashCode()); assertEquals(0, id1.compareTo(id3)); final GUIDImplPrivate id4 = new GUIDImplPrivate(id1.toBase32()); assertEquals(id1, id4); assertEquals(id1.hashCode(), id4.hashCode()); assertEquals(0, id1.compareTo(id4)); final GUIDImplPrivate id5 = new GUIDImplPrivate(id1.toArk()); assertEquals(id1, id5); assertEquals(id1.hashCode(), id5.hashCode()); assertEquals(0, id1.compareTo(id5)); } catch (final InvalidGuidOperationException e) { LOGGER.error(e); fail(e.getMessage()); } } } @Test public void testNonSequentialValue() { final int n = NB / 2; final String[] ids = new String[n]; final long start = System.currentTimeMillis(); for (int i = 0; i < n; i++) { ids[i] = new GUIDImplPrivate().toBase32(); } final long stop = System.currentTimeMillis(); for (int i = 1; i < n; i++) { assertTrue(!ids[i - 1].equals(ids[i])); } final long start2 = System.currentTimeMillis(); for (int i = 0; i < n; i++) { ids[i] = new GUIDImplPrivate().toHex(); } final long stop2 = System.currentTimeMillis(); for (int i = 1; i < n; i++) { assertTrue(!ids[i - 1].equals(ids[i])); } final long start4 = System.currentTimeMillis(); for (int i = 0; i < n; i++) { ids[i] = new GUIDImplPrivate().toBase32(); } final long stop4 = System.currentTimeMillis(); for (int i = 1; i < n; i++) { assertTrue(!ids[i - 1].equals(ids[i])); } final long start5 = System.currentTimeMillis(); for (int i = 0; i < n; i++) { ids[i] = new GUIDImplPrivate().toHex(); } final long stop5 = System.currentTimeMillis(); for (int i = 1; i < n; i++) { assertTrue(!ids[i - 1].equals(ids[i])); } LOGGER.info("B32: " + (stop - start) + " vsHex: " + (stop2 - start2) + " vsB32: " + (stop4 - start4) + " vxHex: " + (stop5 - start5)); } @Test public void testGetBytesImmutability() { final GUIDImplPrivate id = new GUIDImplPrivate(); final byte[] bytes = id.getBytes(); final byte[] original = Arrays.copyOf(bytes, bytes.length); bytes[0] = 0; bytes[1] = 0; bytes[2] = 0; assertTrue(Arrays.equals(id.getBytes(), original)); } @Test public void testConstructorImmutability() { final GUIDImplPrivate id = new GUIDImplPrivate(); final byte[] bytes = id.getBytes(); final byte[] original = Arrays.copyOf(bytes, bytes.length); try { final GUIDImplPrivate id2 = new GUIDImplPrivate(bytes); bytes[0] = 0; bytes[1] = 0; assertTrue(Arrays.equals(id2.getBytes(), original)); } catch (final InvalidGuidOperationException e) { LOGGER.error(e); fail(e.getMessage()); } } @Test public void testVersionField() { final GUIDImplPrivate generated = new GUIDImplPrivate(); assertEquals(VERSION, generated.getVersion()); try { final GUIDImplPrivate parsed1 = new GUIDImplPrivate("aeaqaaaaaitxll67abarqaktftcfyniaaaaq"); assertEquals(VERSION, parsed1.getVersion()); } catch (final InvalidGuidOperationException e) { LOGGER.error(e); fail(e.getMessage()); } } @Test public void testHexBase32() { try { final GUIDImplPrivate parsed1 = new GUIDImplPrivate("aeaqaaaaaitxll67abarqaktftcfyniaaaaq"); final GUIDImplPrivate parsed2 = new GUIDImplPrivate("0101000000022775afdf00411801532cc45c35000001"); final GUIDImplPrivate parsed0 = new GUIDImplPrivate("AQEAAAACJ3Wv3wBBGAFTLMRcNQAAAQ"); assertTrue(parsed1.equals(parsed2)); assertTrue(parsed1.equals(parsed0)); final GUIDImplPrivate generated = new GUIDImplPrivate(); final GUIDImplPrivate parsed3 = new GUIDImplPrivate(generated.getBytes()); final GUIDImplPrivate parsed4 = new GUIDImplPrivate(generated.toBase32()); final GUIDImplPrivate parsed5 = new GUIDImplPrivate(generated.toHex()); final GUIDImplPrivate parsed6 = new GUIDImplPrivate(generated.toString()); final GUIDImplPrivate parsed7 = new GUIDImplPrivate(generated.toBase64()); assertTrue(generated.equals(parsed3)); assertTrue(generated.equals(parsed4)); assertTrue(generated.equals(parsed5)); assertTrue(generated.equals(parsed6)); assertTrue(generated.equals(parsed7)); } catch (final InvalidGuidOperationException e) { LOGGER.error(e); fail(e.getMessage()); } } @Test public void testPIDField() { final GUIDImplPrivate id = new GUIDImplPrivate(); assertEquals(GUIDImplPrivate.jvmProcessId(), id.getProcessId()); } @Test public void testDateField() { final GUIDImplPrivate id = new GUIDImplPrivate(); assertTrue(id.getTimestamp() > new Date().getTime() - 100); assertTrue(id.getTimestamp() < new Date().getTime() + 100); } @Test public void testObjectIdField() { GUIDImplPrivate id = new GUIDImplPrivate(); assertEquals(0, id.getObjectId()); for (int i = 0; i < 32; i++) { id = new GUIDImplPrivate(i, 0); assertEquals(i, id.getObjectId()); } } @Test public void testDomainIdField() { GUIDImplPrivate id = new GUIDImplPrivate(); assertEquals(0, id.getTenantId()); for (int i = 0; i < 65535; i++) { id = new GUIDImplPrivate(0, i); assertEquals(i, id.getTenantId()); } for (int i = 0; i < 255; i++) { id = new GUIDImplPrivate(0, 0x3FFFFFFF - i); assertEquals(0x3FFFFFFF - i, id.getTenantId()); } } @Test public void testPlatformIdField() { GUIDImplPrivate id = new GUIDImplPrivate(); assertEquals(ServerIdentity.getInstance().getGlobalPlatformId(), id.getPlatformId()); for (int i = 0; i < 65535; i++) { id = new GUIDImplPrivate(0, 0, i); assertEquals(i, id.getPlatformId()); assertFalse(id.isWorm()); } for (int i = 0; i < 255; i++) { id = new GUIDImplPrivate(0, 0, 0x7FFFFFFF - i); assertEquals(0x7FFFFFFF - i, id.getPlatformId()); assertFalse(id.isWorm()); } // WORM for (int i = 0; i < 65535; i++) { id = new GUIDImplPrivate(0, 0, i, true); assertEquals(i, id.getPlatformId()); assertTrue(id.isWorm()); } for (int i = 0; i < 255; i++) { id = new GUIDImplPrivate(0, 0, 0x7FFFFFFF - i, true); assertEquals(0x7FFFFFFF - i, id.getPlatformId()); assertTrue(id.isWorm()); } } @Test public void testForDuplicates() { final int n = NB; final Set<GUIDImplPrivate> UUIDs = new HashSet<>(); final GUIDImplPrivate[] UUIDArray = new GUIDImplPrivate[n]; final long start = System.currentTimeMillis(); for (int i = 0; i < n; i++) { UUIDArray[i] = new GUIDImplPrivate(); } final long stop = System.currentTimeMillis(); LOGGER.info("TimeSequential = " + (stop - start) + " so " + n * 1000 / (stop - start) + " UUID22s/s"); for (int i = 0; i < n; i++) { UUIDs.add(UUIDArray[i]); } LOGGER.info("Create " + n + " and get: " + UUIDs.size()); assertEquals(n, UUIDs.size()); checkConsecutive(UUIDArray); } @Test public void checkShuffleInteger() { final byte[] uuid = new byte[4]; for (int src = 0; src < NB; src++) { uuid[1] = (byte) (src & 0xFF); uuid[2] = (byte) (src >> 8 & 0xFF); uuid[3] = (byte) (src >> 16 & 0xFF); int count = (uuid[3] & 0xFF) << 16; count |= (uuid[2] & 0xFF) << 8; count |= uuid[1] & 0xFF; assertEquals(src, count); } } private void checkConsecutive(final GUIDImplPrivate[] UUIDArray) { final int n = UUIDArray.length; int i = 1; int largest = 0; for (; i < n; i++) { if (UUIDArray[i].getTimestamp() > UUIDArray[i - 1].getTimestamp()) { int j = i + 1; final long time = UUIDArray[i].getTimestamp(); for (; j < n; j++) { assertEquals(-1, UUIDArray[i].compareTo(UUIDArray[j])); if (UUIDArray[j].getTimestamp() > time) { if (largest < j - i) { largest = j - i; } i = j; break; } } } else { assertEquals(-1, UUIDArray[i - 1].compareTo(UUIDArray[i])); int j = i + 1; final long time = UUIDArray[i].getTimestamp(); for (; j < n; j++) { assertEquals(-1, UUIDArray[i - 1].compareTo(UUIDArray[j])); if (UUIDArray[j].getTimestamp() > time) { if (largest < j - i + 1) { largest = j - i + 1; } i = j; break; } } } } LOGGER.info(largest + " different consecutive elements"); } private static class Generator extends Thread { private final GUIDImplPrivate[] UUIDs; int base; int n; public Generator(final int n, final GUIDImplPrivate[] UUIDs, final int base) { this.n = n; this.UUIDs = UUIDs; this.base = base; } @Override public void run() { for (int i = 0; i < n; i++) { UUIDs[base + i] = new GUIDImplPrivate(); } } } @Test public void testConcurrentGeneration() throws InterruptedException { final int numThreads = Runtime.getRuntime().availableProcessors() + 1; final Thread[] threads = new Thread[numThreads]; final int n = NB; final int step = n / numThreads; final GUIDImplPrivate[] UUIDs = new GUIDImplPrivate[step * numThreads]; final long start = System.currentTimeMillis(); for (int i = 0; i < numThreads; i++) { threads[i] = new Generator(step, UUIDs, i * step); threads[i].start(); } for (int i = 0; i < numThreads; i++) { threads[i].join(); } final long stop = System.currentTimeMillis(); final Set<GUIDImplPrivate> UUIDSet = new HashSet<>(); for (final GUIDImplPrivate uuid : UUIDs) { UUIDSet.add(uuid); } assertEquals(UUIDs.length, UUIDSet.size()); UUIDSet.clear(); LOGGER.info( "TimeConcurrent = " + (stop - start) + " so " + UUIDs.length * 1000 / (stop - start) + " UUID22s/s"); final TreeSet<GUIDImplPrivate> set = new TreeSet<>(); for (final GUIDImplPrivate uuid : UUIDs) { set.add(uuid); } checkConsecutive(set.toArray(new GUIDImplPrivate[0])); } @Test public void testJsonXml() { final GUIDImplPrivate uuid = new GUIDImplPrivate(1, 2); LOGGER.info("HEX:" + uuid.toHex()); LOGGER.info("BASE32: " + uuid.toBase32()); LOGGER.info("BASE64: " + uuid.toBase64()); try { final String json = JsonHandler.writeAsString(uuid); LOGGER.info(json); final GUIDImplPrivate uuid2 = JsonHandler.getFromString(json, GUIDImplPrivate.class); assertEquals("Json check", uuid, uuid2); } catch (final InvalidParseOperationException e) { LOGGER.error(e); fail("Exception occurs: " + e.getMessage()); return; } } @Test public void testConstruct() { assertTrue(new GUIDImplPrivate(1).getObjectId() == 1); assertTrue(new GUIDImplPrivate(true).isWorm()); assertTrue(new GUIDImplPrivate(1, false).getObjectId() == 1); assertTrue(new GUIDImplPrivate(1, 2, false).getTenantId() == 2); assertTrue(new GUIDImplPrivate(1, 2, 3, false).getPlatformId() == 3); try { new GUIDImplPrivate(-1, 2, 3, false); fail(ResourcesPrivateUtilTest.SHOULD_RAIZED_AN_EXCEPTION); } catch (final IllegalArgumentException e) {// NOSONAR // Ignore } try { new GUIDImplPrivate(0x1FF, 2, 3, false); fail(ResourcesPrivateUtilTest.SHOULD_RAIZED_AN_EXCEPTION); } catch (final IllegalArgumentException e) {// NOSONAR // Ignore } try { new GUIDImplPrivate(1, -2, 3, false); fail(ResourcesPrivateUtilTest.SHOULD_RAIZED_AN_EXCEPTION); } catch (final IllegalArgumentException e) {// NOSONAR // Ignore } try { new GUIDImplPrivate(1, 0x4FFFFFFF, 3, false); fail(ResourcesPrivateUtilTest.SHOULD_RAIZED_AN_EXCEPTION); } catch (final IllegalArgumentException e) {// NOSONAR // Ignore } try { new GUIDImplPrivate(1, 2, -3, false); fail(ResourcesPrivateUtilTest.SHOULD_RAIZED_AN_EXCEPTION); } catch (final IllegalArgumentException e) {// NOSONAR // Ignore } try { new GUIDImplPrivate(1, 2, 0x80000000, false); fail(ResourcesPrivateUtilTest.SHOULD_RAIZED_AN_EXCEPTION); } catch (final IllegalArgumentException e) {// NOSONAR // Ignore } final GUID guid = GUIDFactory.newEventGUID(2); assertNotNull(guid.toString()); assertNotNull(guid.toHex()); assertNotNull(guid.toBase32()); assertNotNull(guid.toBase64()); assertNotNull(guid.toArk()); assertTrue(guid.getCounter() >= 0); assertTrue(guid.getObjectId() == GUIDObjectType.EVENT_TYPE); assertTrue(guid.getPlatformId() >= 0); assertTrue(guid.getProcessId() >= 0); assertTrue(guid.getTenantId() == 2); assertTrue(guid.getTimestamp() > 0); assertTrue(guid.getVersion() == GUIDImpl.VERSION); assertTrue(guid.hashCode() != 0); assertNotNull(guid.toArkName()); assertNotNull(guid.getMacFragment()); assertNotNull(guid.getBytes()); } }