@Test
public void testConstructorJdkUUID() {
    Random random = new Random();
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        final long msb = random.nextLong();
        final long lsb = random.nextLong();
        GUID guid = new GUID(new UUID(msb, lsb));
        assertEquals(msb, guid.getMostSignificantBits());
        assertEquals(lsb, guid.getLeastSignificantBits());
    }
}

public long getMostSignificantBits() {
    return this.msb;
}

@Test
public void testV8() {
    GUID prev = GUID.v8(GUID.NIL, "");
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        GUID namespace = new GUID(UUID.randomUUID());
        String name = UUID.randomUUID().toString();
        GUID uuid = GUID.v8(namespace, name);
        assertNotNull(uuid);
        assertNotEquals(prev, uuid);
        assertEquals(8, uuid.version());
        assertNotEquals(GUID.NIL, uuid);
        assertNotEquals(GUID.MAX, uuid);
        assertNotEquals(GUID.v8(null, name), GUID.v8(namespace, name));
        assertNotEquals(GUID.v8(GUID.NIL, name), GUID.v8(namespace, name));
        assertNotEquals(GUID.v8(namespace, name), GUID.v3(namespace, name));
        assertNotEquals(GUID.v8(namespace, name), GUID.v5(namespace, name));
        assertEquals(GUID.v8(null, name), GUID.v8(null, name));
        assertEquals(GUID.v8(GUID.NIL, name), GUID.v8(GUID.NIL, name));
        assertEquals(GUID.v8(namespace, name), GUID.v8(namespace, name));
        prev = uuid;
    }
    {
        GUID guid = GUID.v8(GUID.NAMESPACE_DNS, "www.example.com");
        assertEquals("401835fd-a627-870a-873f-ed73f2bc5b2c", guid.toString());
    }
}

public static GUID v8(GUID namespace, String name) {
    return hash(8, "SHA-256", HASHSPACE_SHA2_256, namespace, name);
}

@Test
public void testHashCode() {
    Random random = new Random();
    byte[] bytes = new byte[GUID.GUID_BYTES];
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        random.nextBytes(bytes);
        GUID guid1 = new GUID(bytes);
        assertEquals(guid1.hashCode(), guid1.hashCode());
    }
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        random.nextBytes(bytes);
        GUID guid1 = new GUID(bytes);
        GUID guid2 = new GUID(bytes);
        assertEquals(guid1.hashCode(), guid2.hashCode());
    }
}

@Override
public int hashCode() {
    final long bits = msb ^ lsb;
    return (int) (bits ^ (bits >>> 32));
}

@Test
public void testConstructorString() {
    Random random = new Random();
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        final long msb = random.nextLong();
        final long lsb = random.nextLong();
        UUID uuid = new UUID(msb, lsb);
        String str = uuid.toString();
        GUID guid = new GUID(str);
        assertEquals(msb, guid.getMostSignificantBits());
        assertEquals(lsb, guid.getLeastSignificantBits());
    }
}

@Test
public void testConstructorGUID() {
    Random random = new Random();
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        final long msb = random.nextLong();
        final long lsb = random.nextLong();
        GUID guid = new GUID(new GUID(msb, lsb));
        assertEquals(msb, guid.getMostSignificantBits());
        assertEquals(lsb, guid.getLeastSignificantBits());
    }
}

@Test
public void testV7() {
    GUID prev = GUID.v7();
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        long t0 = System.currentTimeMillis();
        GUID uuid = GUID.v7();
        long t1 = uuid.getMostSignificantBits() >>> 16;
        long t2 = System.currentTimeMillis();
        assertNotNull(uuid);
        assertNotEquals(prev, uuid);
        assertNotEquals(GUID.NIL, uuid);
        assertNotEquals(GUID.MAX, uuid);
        assertEquals(7, uuid.version());
        assertTrue(t0 <= t1 && t1 <= t2);
        prev = uuid;
    }
}

public static GUID v7() {
    final long time = System.currentTimeMillis();
    ThreadLocalRandom random = ThreadLocalRandom.current();
    final long msb = (time << 16) | (random.nextLong() & MASK_16);
    final long lsb = random.nextLong();
    return version(msb, lsb, 7);
}

@Test
public void testToString() {
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        UUID uuid = UUID.randomUUID();
        GUID guid = new GUID(uuid);
        assertEquals(uuid.toString(), guid.toString());
    }
}

@Override
public String toString() {
    return get().toString();
}

@Test
public void testConstructorLongs() {
    Random random = new Random();
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        final long msb = random.nextLong();
        final long lsb = random.nextLong();
        GUID guid = new GUID(msb, lsb);
        assertEquals(msb, guid.getMostSignificantBits());
        assertEquals(lsb, guid.getLeastSignificantBits());
    }
}

@Test
public void testV6() {
    GUID prev = GUID.v6();
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        long t0 = System.currentTimeMillis();
        GUID uuid = GUID.v6();
        GUID temp = new GUID(TimeOrderedCodec.INSTANCE.decode(uuid.get()));
        long t1 = UuidTime.toUnixTimestamp(temp.get().timestamp()) / 10_000L;
        long t2 = System.currentTimeMillis();
        assertNotNull(uuid);
        assertNotEquals(prev, uuid);
        assertNotEquals(GUID.NIL, uuid);
        assertNotEquals(GUID.MAX, uuid);
        assertEquals(6, uuid.version());
        assertTrue(t0 <= t1 && t1 <= t2);
        prev = uuid;
    }
}

public static GUID v6() {
    final long time = gregorian();
    final long msb = ((time & ~MASK_12) << 4) | (time & MASK_12);
    final long lsb = ThreadLocalRandom.current().nextLong() | MULTICAST;
    return version(msb, lsb, 6);
}

@Test
public void testV2() {
    GUID prev = GUID.v2((byte) 0, (int) 0);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        byte ld = (byte) i;
        int li = (int) i * 31;
        GUID uuid = GUID.v2(ld, li);
        byte localDomain = UuidUtil.getLocalDomain(uuid.get());
        int localIdentifier = UuidUtil.getLocalIdentifier(uuid.get());
        assertNotNull(uuid);
        assertNotEquals(prev, uuid);
        assertNotEquals(GUID.NIL, uuid);
        assertNotEquals(GUID.MAX, uuid);
        assertEquals(2, uuid.version());
        assertEquals(ld, localDomain);
        assertEquals(li, localIdentifier);
        prev = uuid;
    }
}

public static GUID v2(byte localDomain, int localIdentifier) {
    GUID uuid = v1();
    final long hi = uuid.getMostSignificantBits();
    final long lo = uuid.getLeastSignificantBits();
    final long msb = (hi & MASK_32) | ((localIdentifier & MASK_32) << 32);
    final long lsb = (lo & 0x3f00_ffff_ffff_ffffL) | ((localDomain & MASK_08) << 48);
    return version(msb, lsb, 2);
}

@Test
public void testV5() {
    GUID prev = GUID.v5(GUID.NIL, "");
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        GUID namespace = new GUID(UUID.randomUUID());
        String name = UUID.randomUUID().toString();
        GUID uuid = GUID.v5(namespace, name);
        assertNotNull(uuid);
        assertNotEquals(prev, uuid);
        assertEquals(5, uuid.version());
        assertNotEquals(GUID.NIL, uuid);
        assertNotEquals(GUID.MAX, uuid);
        assertNotEquals(GUID.v5(null, name), GUID.v5(namespace, name));
        assertNotEquals(GUID.v5(GUID.NIL, name), GUID.v5(namespace, name));
        assertNotEquals(GUID.v5(namespace, name), GUID.v3(namespace, name));
        assertNotEquals(GUID.v5(namespace, name), GUID.v8(namespace, name));
        assertEquals(GUID.v5(null, name), GUID.v5(null, name));
        assertEquals(GUID.v5(GUID.NIL, name), GUID.v5(GUID.NIL, name));
        assertEquals(GUID.v5(namespace, name), GUID.v5(namespace, name));
        prev = uuid;
    }
    {
        GUID guid = GUID.v5(GUID.NAMESPACE_DNS, "www.example.com");
        assertEquals("2ed6657d-e927-568b-95e1-2665a8aea6a2", guid.toString());
    }
}

public static GUID v5(GUID namespace, String name) {
    return hash(5, "SHA-1", null, namespace, name);
}

@Test
public void testToUUID() {
    Random random = new Random();
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        final long msb = random.nextLong();
        final long lsb = random.nextLong();
        UUID uuid = (new GUID(msb, lsb)).get();
        assertEquals(msb, uuid.getMostSignificantBits());
        assertEquals(lsb, uuid.getLeastSignificantBits());
    }
}

public UUID get() {
    return toUUID();
}

@Test
public void testV3() {
    GUID prev = GUID.v3(GUID.NIL, "");
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        GUID namespace = new GUID(UUID.randomUUID());
        String name = UUID.randomUUID().toString();
        GUID uuid = GUID.v3(namespace, name);
        assertNotNull(uuid);
        assertNotEquals(prev, uuid);
        assertEquals(3, uuid.version());
        assertNotEquals(GUID.NIL, uuid);
        assertNotEquals(GUID.MAX, uuid);
        assertNotEquals(GUID.v3(null, name), GUID.v3(namespace, name));
        assertNotEquals(GUID.v3(GUID.NIL, name), GUID.v3(namespace, name));
        assertNotEquals(GUID.v3(namespace, name), GUID.v5(namespace, name));
        assertNotEquals(GUID.v3(namespace, name), GUID.v8(namespace, name));
        assertEquals(GUID.v3(null, name), GUID.v3(null, name));
        assertEquals(GUID.v3(GUID.NIL, name), GUID.v3(GUID.NIL, name));
        assertEquals(GUID.v3(namespace, name), GUID.v3(namespace, name));
        assertEquals(UUID.nameUUIDFromBytes(name.getBytes(StandardCharsets.UTF_8)), GUID.v3(null, name).get());
        prev = uuid;
    }
    {
        GUID guid = GUID.v3(GUID.NAMESPACE_DNS, "www.example.com");
        assertEquals("5df41881-3aed-3515-88a7-2f4a814cf09e", guid.toString());
    }
}

public static GUID v3(GUID namespace, String name) {
    return hash(3, "MD5", null, namespace, name);
}

@Test
public void testToBytes() {
    Random random = new Random();
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        byte[] bytes1 = new byte[16];
        random.nextBytes(bytes1);
        GUID guid0 = new GUID(bytes1);
        byte[] bytes2 = guid0.toBytes();
        for (int j = 0; j < bytes1.length; j++) {
            assertEquals(bytes1[j], bytes2[j]);
        }
    }
}

public byte[] toBytes() {
    return ByteBuffer.allocate(GUID_BYTES).putLong(msb).putLong(lsb).array();
}

@Test
public void testV1() {
    GUID prev = GUID.v1();
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        long t0 = System.currentTimeMillis();
        GUID uuid = GUID.v1();
        long t1 = UuidTime.toUnixTimestamp(uuid.get().timestamp()) / 10_000L;
        long t2 = System.currentTimeMillis();
        assertNotNull(uuid);
        assertNotEquals(prev, uuid);
        assertNotEquals(GUID.NIL, uuid);
        assertNotEquals(GUID.MAX, uuid);
        assertEquals(1, uuid.version());
        assertTrue(t0 <= t1 && t1 <= t2);
        prev = uuid;
    }
}

public static GUID v1() {
    final long time = gregorian();
    final long msb = (time << 32) | ((time >>> 16) & (MASK_16 << 16)) | ((time >>> 48) & MASK_12);
    final long lsb = ThreadLocalRandom.current().nextLong() | MULTICAST;
    return version(msb, lsb, 1);
}

@Test
public void testConstructorBytes() {
    Random random = new Random();
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        byte[] bytes = new byte[GUID.GUID_BYTES];
        random.nextBytes(bytes);
        GUID guid = new GUID(bytes);
        assertEquals(Arrays.toString(bytes), Arrays.toString(guid.toBytes()));
    }
    try {
        byte[] bytes = null;
        new GUID(bytes);
        fail("Should throw an exception");
    } catch (IllegalArgumentException e) {
    }
    try {
        byte[] bytes = new byte[GUID.GUID_BYTES + 1];
        new GUID(bytes);
        fail("Should throw an exception");
    } catch (IllegalArgumentException e) {
    }
}

@Test
public void testV4() {
    GUID prev = GUID.v4();
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        GUID uuid = GUID.v4();
        assertNotNull(uuid);
        assertNotEquals(prev, uuid);
        assertNotEquals(GUID.NIL, uuid);
        assertNotEquals(GUID.MAX, uuid);
        assertEquals(4, uuid.version());
        prev = uuid;
    }
}

public static GUID v4() {
    ThreadLocalRandom random = ThreadLocalRandom.current();
    final long msb = random.nextLong();
    final long lsb = random.nextLong();
    return version(msb, lsb, 4);
}

@Test
public void testValid() {
    String uuid = null;
    assertFalse("Null UUID string should be invalid.", GUID.valid(uuid));
    uuid = "";
    assertFalse("UUID with empty string should be invalid.", GUID.valid(uuid));
    uuid = "01234567-89ab-4def-abcd-ef0123456789";
    assertTrue("UUID with length equals to 36 should be valid.", GUID.valid(uuid));
    uuid = "01234567-89ab-4def-abcdef01-23456789";
    assertFalse("UUID with length equals to 36 with hyphen in wrong position should be invalid.", GUID.valid(uuid));
    uuid = "01234567-89ab-4def-abcd-ef01-3456789";
    assertFalse("UUID with length equals to 36 with an extra hyphen should be invalid.", GUID.valid(uuid));
    uuid = "01234567-89ab-4def-abcddef0123456789";
    assertFalse("UUID with length equals to 36 with a missing hyphen should be invalid.", GUID.valid(uuid));
    uuid = "0123456789ab4defabcdef0123456789";
    assertFalse("UUID without hyphen should be invalid.", GUID.valid(uuid));
    uuid = "01234567-89ab-4def-abcd-ef0123456789";
    assertTrue("UUID in lower case should be valid.", GUID.valid(uuid));
    uuid = "01234567-89AB-4DEF-ABCD-EF0123456789";
    assertTrue("UUID in upper case should valid.", GUID.valid(uuid));
    uuid = "01234567-89ab-4DEF-abcd-EF0123456789";
    assertTrue("UUID in upper and lower case should valid.", GUID.valid(uuid));
    uuid = "01234567-89ab-4def-abcd-SOPQRSTUVXYZ";
    assertFalse("UUID string with non hexadecimal chars should be invalid.", GUID.valid(uuid));
    uuid = "01234567-89ab-4def-!@#$-ef0123456789";
    assertFalse("UUID string non alphanumeric chars should be invalid.", GUID.valid(uuid));
    try {
        uuid = null;
        UuidValidator.validate(uuid);
        fail();
    } catch (InvalidUuidException e) {
    }
}

public static boolean valid(String guid) {
    return guid != null && PATTERN.matcher(guid).matches();
}

@Test
public void testEquals() {
    Random random = new Random();
    byte[] bytes = new byte[GUID.GUID_BYTES];
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        random.nextBytes(bytes);
        GUID guid1 = new GUID(bytes);
        GUID guid2 = new GUID(bytes);
        assertEquals(guid1, guid2);
        assertEquals(guid1.toString(), guid2.toString());
        assertEquals(Arrays.toString(guid1.toBytes()), Arrays.toString(guid2.toBytes()));
        for (int j = 0; j < bytes.length; j++) {
            bytes[j]++;
        }
        GUID guid3 = new GUID(bytes);
        assertNotEquals(guid1, guid3);
        assertNotEquals(guid1.toString(), guid3.toString());
        assertNotEquals(Arrays.toString(guid1.toBytes()), Arrays.toString(guid3.toBytes()));
    }
}

@Override
public boolean equals(Object other) {
    if (other == null)
        return false;
    if (other.getClass() != GUID.class)
        return false;
    GUID that = (GUID) other;
    if (lsb != that.lsb)
        return false;
    else if (msb != that.msb)
        return false;
    return true;
}

@Test
public void testCompareTo() {
    final long zero = 0L;
    Random random = new Random();
    byte[] bytes = new byte[GUID.GUID_BYTES];
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        bytes = ByteBuffer.allocate(16).putLong(random.nextLong()).putLong(random.nextLong()).array();
        GUID guid1 = new GUID(bytes);
        BigInteger number1 = new BigInteger(1, bytes);
        bytes = ByteBuffer.allocate(16).putLong(random.nextLong()).putLong(random.nextLong()).array();
        GUID guid2 = new GUID(bytes);
        GUID guid3 = new GUID(bytes);
        BigInteger number2 = new BigInteger(1, bytes);
        BigInteger number3 = new BigInteger(1, bytes);
        assertEquals(number1.compareTo(number2) > 0, guid1.compareTo(guid2) > 0);
        assertEquals(number1.compareTo(number2) < 0, guid1.compareTo(guid2) < 0);
        assertEquals(number2.compareTo(number3) == 0, guid2.compareTo(guid3) == 0);
        assertEquals(number1.compareTo(number2) > 0, guid1.toString().compareTo(guid2.toString()) > 0);
        assertEquals(number1.compareTo(number2) < 0, guid1.toString().compareTo(guid2.toString()) < 0);
        assertEquals(number2.compareTo(number3) == 0, guid2.toString().compareTo(guid3.toString()) == 0);
    }
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        bytes = ByteBuffer.allocate(16).putLong(zero).putLong(random.nextLong()).array();
        GUID guid1 = new GUID(bytes);
        BigInteger number1 = new BigInteger(1, bytes);
        bytes = ByteBuffer.allocate(16).putLong(zero).putLong(random.nextLong()).array();
        GUID guid2 = new GUID(bytes);
        GUID guid3 = new GUID(bytes);
        BigInteger number2 = new BigInteger(1, bytes);
        BigInteger number3 = new BigInteger(1, bytes);
        assertEquals(number1.compareTo(number2) > 0, guid1.compareTo(guid2) > 0);
        assertEquals(number1.compareTo(number2) < 0, guid1.compareTo(guid2) < 0);
        assertEquals(number2.compareTo(number3) == 0, guid2.compareTo(guid3) == 0);
        assertEquals(number1.compareTo(number2) > 0, guid1.toString().compareTo(guid2.toString()) > 0);
        assertEquals(number1.compareTo(number2) < 0, guid1.toString().compareTo(guid2.toString()) < 0);
        assertEquals(number2.compareTo(number3) == 0, guid2.toString().compareTo(guid3.toString()) == 0);
    }
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        bytes = ByteBuffer.allocate(16).putLong(random.nextLong()).putLong(zero).array();
        GUID guid1 = new GUID(bytes);
        BigInteger number1 = new BigInteger(1, bytes);
        bytes = ByteBuffer.allocate(16).putLong(random.nextLong()).putLong(zero).array();
        GUID guid2 = new GUID(bytes);
        GUID guid3 = new GUID(bytes);
        BigInteger number2 = new BigInteger(1, bytes);
        BigInteger number3 = new BigInteger(1, bytes);
        assertEquals(number1.compareTo(number2) > 0, guid1.compareTo(guid2) > 0);
        assertEquals(number1.compareTo(number2) < 0, guid1.compareTo(guid2) < 0);
        assertEquals(number2.compareTo(number3) == 0, guid2.compareTo(guid3) == 0);
        assertEquals(number1.compareTo(number2) > 0, guid1.toString().compareTo(guid2.toString()) > 0);
        assertEquals(number1.compareTo(number2) < 0, guid1.toString().compareTo(guid2.toString()) < 0);
        assertEquals(number2.compareTo(number3) == 0, guid2.toString().compareTo(guid3.toString()) == 0);
    }
}

@Override
public int compareTo(GUID that) {
    final long min = 0x8000000000000000L;
    final long a = this.msb + min;
    final long b = that.msb + min;
    if (a > b)
        return 1;
    else if (a < b)
        return -1;
    final long c = this.lsb + min;
    final long d = that.lsb + min;
    if (c > d)
        return 1;
    else if (c < d)
        return -1;
    return 0;
}


@Test
public void testGetDCESecuritylLocalIdentifier() {
    DceSecurityFactory factory = new DceSecurityFactory();
    DceSecurityFactory factoryWithMac = DceSecurityFactory.builder().withMacNodeId().build();
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        byte localDomain = (byte) i;
        int localIdentifier = 1701 + i;
        UUID uuid = factory.create(localDomain, localIdentifier);
        int localIdentifier2 = UuidUtil.getLocalIdentifier(uuid);
        assertEquals(localIdentifier, localIdentifier2);
        uuid = factoryWithMac.create(localDomain, localIdentifier);
        localIdentifier2 = UuidUtil.getLocalIdentifier(uuid);
        assertEquals(localIdentifier, localIdentifier2);
    }
}

public UUID create(byte localDomain, int localIdentifier) {
    UUID uuid = super.create();
    final long lsb = embedLocalDomain(uuid.getLeastSignificantBits(), localDomain, this.counter.incrementAndGet());
    final long msb = emgedLocalIdentifier(uuid.getMostSignificantBits(), localIdentifier);
    return toUuid(msb, lsb);
}

@Test
public void testGetDCESecuritylLocalDomain() {
    DceSecurityFactory factory = new DceSecurityFactory();
    DceSecurityFactory factoryWithMac = DceSecurityFactory.builder().withMacNodeId().build();
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        byte localDomain = (byte) i;
        int localIdentifier = 1701 + i;
        UUID uuid = factory.create(localDomain, localIdentifier);
        byte localDomain2 = UuidUtil.getLocalDomain(uuid);
        assertEquals(localDomain, localDomain2);
        uuid = factoryWithMac.create(localDomain, localIdentifier);
        localDomain2 = UuidUtil.getLocalDomain(uuid);
        assertEquals(localDomain, localDomain2);
    }
}

@Override
public DceSecurityFactory build() {
    return new DceSecurityFactory(this);
}


@Test
public void testGetRandomBasedWithRandomFunction() {
    SplittableRandom random = new SplittableRandom(1);
    UUID[] list = new UUID[DEFAULT_LOOP_MAX];
    LongSupplier randomFunction = () -> random.nextLong();
    RandomBasedFactory factory = new RandomBasedFactory(randomFunction);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        list[i] = factory.create();
    }
    checkNotNull(list);
    checkUniqueness(list);
    checkVersion(list, UuidVersion.VERSION_RANDOM_BASED.getValue());
}

@Override
public UUID create() {
    lock.lock();
    try {
        if (this.random instanceof SafeRandom) {
            final byte[] bytes = this.random.nextBytes(16);
            final long msb = ByteUtil.toNumber(bytes, 0, 8);
            final long lsb = ByteUtil.toNumber(bytes, 8, 16);
            return toUuid(msb, lsb);
        } else {
            final long msb = this.random.nextLong();
            final long lsb = this.random.nextLong();
            return toUuid(msb, lsb);
        }
    } finally {
        lock.unlock();
    }
}

@Test
public void testGetRandomBasedFastInParallel() throws InterruptedException {
    Thread[] threads = new Thread[THREAD_TOTAL];
    TestThread.clearHashSet();
    for (int i = 0; i < THREAD_TOTAL; i++) {
        threads[i] = new TestThread(RandomBasedFactory.builder().withFastRandom().build(), DEFAULT_LOOP_MAX);
        threads[i].start();
    }
    for (Thread thread : threads) {
        thread.join();
    }
    assertEquals(DUPLICATE_UUID_MSG, TestThread.hashSet.size(), (DEFAULT_LOOP_MAX * THREAD_TOTAL));
}

@Test
public void testGetRandomBasedInParallel() throws InterruptedException {
    Thread[] threads = new Thread[THREAD_TOTAL];
    TestThread.clearHashSet();
    for (int i = 0; i < THREAD_TOTAL; i++) {
        threads[i] = new TestThread(new RandomBasedFactory(), DEFAULT_LOOP_MAX);
        threads[i].start();
    }
    for (Thread thread : threads) {
        thread.join();
    }
    assertEquals(DUPLICATE_UUID_MSG, TestThread.hashSet.size(), (DEFAULT_LOOP_MAX * THREAD_TOTAL));
}

@Test
public void testGetRandomBasedWithRandom() {
    UUID[] list = new UUID[DEFAULT_LOOP_MAX];
    SplittableRandom seeder = new SplittableRandom(1);
    RandomBasedFactory factory = new RandomBasedFactory(new Random(seeder.nextLong()));
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        list[i] = factory.create();
    }
    checkNotNull(list);
    checkUniqueness(list);
    checkVersion(list, UuidVersion.VERSION_RANDOM_BASED.getValue());
}


@Test
public void testGetTimeBasedWithClockSeqFunction() {
    long clockSeq = ClockSeqFunction.toExpectedRange((int) System.nanoTime());
    ClockSeqFunction clockSeqOperator = x -> clockSeq;
    long clockseq1 = clockSeqOperator.applyAsLong(0);
    UUID uuid = TimeBasedFactory.builder().withClockSeqFunction(clockSeqOperator).build().create();
    long clockseq2 = UuidUtil.getClockSequence(uuid);
    assertEquals(clockseq1, clockseq2);
}

@Override
public TimeBasedFactory build() {
    return new TimeBasedFactory(this);
}

@Test
public void testGetTimeBasedCheckGreatestTimestamp() {
    long maxTimestamp = 0x0fffffffffffffffL;
    Instant maxInstant = Instant.parse("5236-03-31T21:21:00.684697500Z");
    UUID uuid = TimeBasedFactory.builder().withInstant(maxInstant).build().create();
    long timestamp = UuidUtil.getTimestamp(uuid);
    assertEquals(maxTimestamp, timestamp);
    Instant instant = UuidUtil.getInstant(uuid);
    assertEquals(maxInstant, instant);
}

public static Builder builder() {
    return new Builder();
}

@Test
public void testGetTimeBasedWithNodeIdFunction() {
    long nodeid = NodeIdFunction.toExpectedRange(System.nanoTime());
    NodeIdFunction nodeIdSupplier = () -> nodeid;
    long nodeIdentifier1 = nodeIdSupplier.getAsLong();
    UUID uuid = TimeBasedFactory.builder().withNodeIdFunction(nodeIdSupplier).build().create();
    long nodeIdentifier2 = UuidUtil.getNodeIdentifier(uuid);
    assertEquals(nodeIdentifier1, nodeIdentifier2);
}

@Test
public void testGetTimeBasedWithInstant() {
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        Instant instant1 = Instant.now().truncatedTo(ChronoUnit.MICROS);
        UUID uuid = TimeBasedFactory.builder().withInstant(instant1).build().create();
        Instant instant2 = UuidUtil.getInstant(uuid).truncatedTo(ChronoUnit.MICROS);
        assertEquals(instant1, instant2);
    }
}

@Test
public void testGetTimeBasedCheckTimestamp() {
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        Instant instant1 = Instant.now();
        UUID uuid = TimeBasedFactory.builder().withInstant(instant1).build().create();
        Instant instant2 = UuidUtil.getInstant(uuid);
        long timestamp1 = UuidTime.toGregTimestamp(instant1);
        long timestamp2 = UuidTime.toGregTimestamp(instant2);
        assertEquals(timestamp1, timestamp2);
    }
}

@Test
public void testGetTimeBasedInParallel() throws InterruptedException {
    Thread[] threads = new Thread[THREAD_TOTAL];
    TestThread.clearHashSet();
    for (int i = 0; i < THREAD_TOTAL; i++) {
        TimeBasedFactory factory = TimeBasedFactory.builder().withHashNodeId().build();
        threads[i] = new TestThread(factory, DEFAULT_LOOP_MAX);
        threads[i].start();
    }
    for (Thread thread : threads) {
        thread.join();
    }
    assertEquals(DUPLICATE_UUID_MSG, (DEFAULT_LOOP_MAX * THREAD_TOTAL), TestThread.hashSet.size());
}

@Test
public void testCreateTimeBasedWithMac() {
    boolean multicast = false;
    testGetAbstractTimeBased(TimeBasedFactory.builder().withMacNodeId().build(), multicast);
}

@Test
public void testCreateTimeBasedWithHash() {
    boolean multicast = true;
    testGetAbstractTimeBased(TimeBasedFactory.builder().withHashNodeId().build(), multicast);
}


@Test
public void testWithRandomFunction() {
    SplittableRandom random = new SplittableRandom(1);
    {
        LongSupplier function = () -> random.nextLong();
        TimeOrderedEpochFactory factory = new TimeOrderedEpochFactory(function);
        assertNotNull(factory.create());
    }
    {
        LongSupplier function = () -> random.nextLong();
        TimeOrderedEpochFactory factory = new TimeOrderedEpochFactory(function);
        assertNotNull(factory.create());
    }
    {
        LongSupplier function = () -> random.nextLong();
        TimeOrderedEpochFactory factory = new TimeOrderedEpochFactory(function, Clock.systemDefaultZone());
        assertNotNull(factory.create());
    }
    {
        LongSupplier function = () -> random.nextLong();
        TimeOrderedEpochFactory factory = new TimeOrderedEpochFactory(function, Clock.systemDefaultZone());
        assertNotNull(factory.create());
    }
}

@Override
public UUID create() {
    UUID uuid = this.uuidFunction.get();
    return toUuid(uuid.getMostSignificantBits(), uuid.getLeastSignificantBits());
}

@Test
public void testWithRandomNull() {
    TimeOrderedEpochFactory factory = new TimeOrderedEpochFactory((Random) null);
    assertNotNull(factory.create());
}

@Test
public void testWithRandom() {
    SplittableRandom seeder = new SplittableRandom(1);
    {
        TimeOrderedEpochFactory factory = new TimeOrderedEpochFactory(new Random(seeder.nextLong()));
        assertNotNull(factory.create());
    }
    {
        TimeOrderedEpochFactory factory = new TimeOrderedEpochFactory(new Random(seeder.nextLong()));
        assertNotNull(factory.create());
    }
}

@Test
public void testGetTimeOrderedEpochCheckTimestamp() {
    SplittableRandom random = new SplittableRandom(1);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        long time = random.nextLong(1L << 48);
        Clock clock = Clock.fixed(Instant.ofEpochMilli(time), Clock.systemUTC().getZone());
        Instant instant1 = clock.instant();
        long timestamp1 = UuidTime.toGregTimestamp(instant1);
        UUID uuid = TimeOrderedEpochFactory.builder().withClock(clock).build().create();
        Instant instant2 = UuidUtil.getInstant(uuid);
        long timestamp2 = UuidUtil.getTimestamp(uuid);
        assertEquals(instant1, instant2);
        assertEquals(timestamp1, timestamp2);
    }
}

@Test
public void testGetTimeOrderedEpochInParallel() throws InterruptedException {
    Clock clock = Clock.systemDefaultZone();
    SplittableRandom seeder = new SplittableRandom(1);
    Thread[] threads = new Thread[THREAD_TOTAL];
    TestThread.clearHashSet();
    for (int i = 0; i < THREAD_TOTAL; i++) {
        Random random = new Random(seeder.nextLong());
        threads[i] = new TestThread(new TimeOrderedEpochFactory(random, clock), DEFAULT_LOOP_MAX);
        threads[i].start();
    }
    for (Thread thread : threads) {
        thread.join();
    }
    assertEquals(DUPLICATE_UUID_MSG, TestThread.hashSet.size(), (DEFAULT_LOOP_MAX * THREAD_TOTAL));
}

@Test
public void testDefault() {
    TimeOrderedEpochFactory factory = new TimeOrderedEpochFactory();
    assertNotNull(factory.create());
}


@Test
public void testGetTimeOrderedWithNodeIdFunction() {
    long nodeid = NodeIdFunction.toExpectedRange(System.nanoTime());
    NodeIdFunction nodeIdFunction = () -> nodeid;
    long nodeIdentifier1 = nodeIdFunction.getAsLong();
    UUID uuid = TimeOrderedFactory.builder().withNodeIdFunction(nodeIdFunction).build().create();
    long nodeIdentifier2 = UuidUtil.getNodeIdentifier(uuid);
    assertEquals(nodeIdentifier1, nodeIdentifier2);
}

@Test
public void testGetTimeOrderedWithClockSeqFunction() {
    long clockSeq = ClockSeqFunction.toExpectedRange((int) System.nanoTime());
    ClockSeqFunction clockSeqFunction = x -> clockSeq;
    long clockseq1 = clockSeqFunction.applyAsLong(0);
    UUID uuid = TimeOrderedFactory.builder().withClockSeqFunction(clockSeqFunction).build().create();
    long clockseq2 = UuidUtil.getClockSequence(uuid);
    assertEquals(clockseq1, clockseq2);
}

@Test
public void testTimeOrderedWithMac() {
    boolean multicast = false;
    testGetAbstractTimeBased(TimeOrderedFactory.builder().withMacNodeId().build(), multicast);
}

@Test
public void testGetTimeOrderedCheckGreatestTimestamp() {
    long maxTimestamp = 0x0fffffffffffffffL;
    Instant maxInstant = Instant.parse("5236-03-31T21:21:00.684697500Z");
    UUID uuid = TimeOrderedFactory.builder().withInstant(maxInstant).build().create();
    long timestamp = UuidUtil.getTimestamp(uuid);
    assertEquals(maxTimestamp, timestamp);
    Instant instant = UuidUtil.getInstant(uuid);
    assertEquals(maxInstant, instant);
}

@Test
public void testGetTimeOrderedWithInstant() {
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        Instant instant1 = Instant.now().truncatedTo(ChronoUnit.MICROS);
        UUID uuid = TimeOrderedFactory.builder().withInstant(instant1).build().create();
        Instant instant2 = UuidUtil.getInstant(uuid).truncatedTo(ChronoUnit.MICROS);
        assertEquals(instant1, instant2);
    }
}

@Test
public void testGetTimeOrderedCheckTimestamp() {
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        Instant instant1 = Instant.now();
        UUID uuid = TimeOrderedFactory.builder().withInstant(instant1).build().create();
        Instant instant2 = UuidUtil.getInstant(uuid);
        long timestamp1 = UuidTime.toGregTimestamp(instant1);
        long timestamp2 = UuidTime.toGregTimestamp(instant2);
        assertEquals(timestamp1, timestamp2);
    }
}

@Test
public void testTimeOrderedWithHash() {
    boolean multicast = true;
    testGetAbstractTimeBased(TimeOrderedFactory.builder().withHashNodeId().build(), multicast);
}

@Test
public void testGetTimeOrderedInParallel() throws InterruptedException {
    Thread[] threads = new Thread[THREAD_TOTAL];
    TestThread.clearHashSet();
    for (int i = 0; i < THREAD_TOTAL; i++) {
        TimeOrderedFactory factory = TimeOrderedFactory.builder().withHashNodeId().build();
        threads[i] = new TestThread(factory, DEFAULT_LOOP_MAX);
        threads[i].start();
    }
    for (Thread thread : threads) {
        thread.join();
    }
    assertEquals(DUPLICATE_UUID_MSG, (DEFAULT_LOOP_MAX * THREAD_TOTAL), TestThread.hashSet.size());
}


@Test
public void testGetTimeOrderedEpochInParallel() throws InterruptedException {
    Random random = new Random();
    Clock clock = Clock.systemDefaultZone();
    Thread[] threads = new Thread[THREAD_TOTAL];
    TestThread.clearHashSet();
    for (int i = 0; i < THREAD_TOTAL; i++) {
        threads[i] = new TestThread(new TimeOrderedEpochFactory(random, clock), DEFAULT_LOOP_MAX);
        threads[i].start();
    }
    for (Thread thread : threads) {
        thread.join();
    }
    assertEquals(DUPLICATE_UUID_MSG, TestThread.hashSet.size(), (DEFAULT_LOOP_MAX * THREAD_TOTAL));
}

@Override
public synchronized UUID create() {
    long time = clock.millis();
    if ((time > this.lastTime - CLOCK_DRIFT_TOLERANCE) && (time <= this.lastTime)) {
        this.lastUuid = increment(this.lastUuid);
    } else {
        this.lastTime = time;
        this.lastUuid = make(time, this.randomFunction.apply(RANDOM_BYTES));
    }
    return copy(this.lastUuid);
}

@Test
public void testGetTimeOrderedCheckTimestamp() {
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        long random = ThreadLocalRandom.current().nextLong(1L << 48);
        Clock clock = Clock.fixed(Instant.ofEpochMilli(random), Clock.systemUTC().getZone());
        Instant instant1 = clock.instant();
        long timestamp1 = UuidTime.toGregTimestamp(instant1);
        UUID uuid = TimeOrderedEpochFactory.builder().withClock(clock).build().create();
        Instant instant2 = UuidUtil.getInstant(uuid);
        long timestamp2 = UuidUtil.getTimestamp(uuid);
        assertEquals(instant1, instant2);
        assertEquals(timestamp1, timestamp2);
    }
}

@Override
public Builder withClock(Clock clock) {
    return (Builder) super.withClock(clock);
}


@Test
public void testIsMax() {
    UUID uuid = new UUID(-1L, -1L);
    assertTrue(isMax(uuid));
}

public static boolean isMax(UUID uuid) {
    Objects.requireNonNull(uuid, "Null UUID is not equal to Max UUID");
    return uuid.getMostSignificantBits() == -1L && uuid.getLeastSignificantBits() == -1L;
}


@Test
public void testGetSuffixWithClock() {
    final int bits = 48;
    final long bound = (long) Math.pow(2, bits);
    SplittableRandom random = new SplittableRandom(1);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        final long millis = Math.abs(random.nextLong(bound));
        Clock clock = Clock.fixed(Instant.ofEpochMilli(millis), ZoneOffset.UTC);
        AbstRandomBasedFactory factory = new SuffixCombFactory(clock);
        UUID comb = factory.create();
        long suffix = CombUtil.getSuffix(comb);
        assertEquals("Wrong suffix", millis, suffix);
    }
}

@Override
public UUID create() {
    lock.lock();
    try {
        final long time = instantFunction.get().toEpochMilli();
        final long long1 = this.random.nextLong(8);
        final long long2 = this.random.nextLong(2);
        return make(time, long1, long2);
    } finally {
        lock.unlock();
    }
}

@Override
public UUID create() {
    lock.lock();
    try {
        final long time = timeFunction.getAsLong();
        if (this.random instanceof SafeRandom) {
            final byte[] bytes = this.random.nextBytes(10);
            final long long1 = ByteUtil.toNumber(bytes, 0, 8);
            final long long2 = ByteUtil.toNumber(bytes, 8, 10);
            return make(time, long1, long2);
        } else {
            final long long1 = this.random.nextLong();
            final long long2 = this.random.nextLong();
            return make(time, long1, long2);
        }
    } finally {
        lock.unlock();
    }
}

@Test
public void testGetPrefixWithClock() {
    final int bits = 48;
    final long bound = (long) Math.pow(2, bits);
    SplittableRandom random = new SplittableRandom(1);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        final long millis = Math.abs(random.nextLong(bound));
        Clock clock = Clock.fixed(Instant.ofEpochMilli(millis), ZoneOffset.UTC);
        AbstRandomBasedFactory factory = new PrefixCombFactory(clock);
        UUID comb = factory.create();
        long prefix = CombUtil.getPrefix(comb);
        assertEquals("Wrong prefix", millis, prefix);
    }
}

@Override
public UUID create() {
    lock.lock();
    try {
        final long time = instantFunction.get().toEpochMilli();
        final long long1 = this.random.nextLong(2);
        final long long2 = this.random.nextLong(8);
        return make(time, long1, long2);
    } finally {
        lock.unlock();
    }
}

@Override
public UUID create() {
    lock.lock();
    try {
        final long time = timeFunction.getAsLong();
        if (this.random instanceof SafeRandom) {
            final byte[] bytes = this.random.nextBytes(10);
            final long long1 = ByteUtil.toNumber(bytes, 0, 2);
            final long long2 = ByteUtil.toNumber(bytes, 2, 10);
            return make(time, long1, long2);
        } else {
            final long long1 = this.random.nextLong();
            final long long2 = this.random.nextLong();
            return make(time, long1, long2);
        }
    } finally {
        lock.unlock();
    }
}


@Test
public void testRenderEdgeToConflictingDependency() throws Exception {
    DependencyNode fromNode = DependencyNodeUtil.createDependencyNode("org.springframework", "spring-context", "4.3.9-RELEASE");
    DependencyNode toNode = DependencyNodeUtil.createDependencyNodeWithConflict("commons-logging", "commons-logging", "1.1.3");
    String edgeInfo = this.renderer.render(fromNode, toNode);
    assertEquals("{\"begin\":\".[\",\"end\":\"].>\",\"color\":\"#FF0000\",\"label\":\"1.1.3\"}", edgeInfo);
}

@Override
public String render(DependencyNode from, DependencyNode to) {
    NodeResolution resolution = to.getResolution();
    PumlEdgeInfo edgeInfo = new PumlEdgeInfo();
    switch(resolution) {
        case INCLUDED:
            edgeInfo.withBegin("-[").withColor(INCLUDE_COLOR).withEnd("]->").withLabel("");
            break;
        case OMITTED_FOR_CONFLICT:
            edgeInfo.withBegin(".[").withColor(CONFLICT_COLOR).withEnd("].>").withLabel(to.getArtifact().getVersion());
            break;
        case OMITTED_FOR_DUPLICATE:
            edgeInfo.withBegin(".[").withColor(DUPLICATE_COLOR).withEnd("].>").withLabel(to.getArtifact().getVersion());
            break;
        case OMITTED_FOR_CYCLE:
        default:
    }
    return edgeInfo.toString();
}

@Test
public void testRenderEdgeToIncludeDependency() throws Exception {
    DependencyNode fromNode = DependencyNodeUtil.createDependencyNode("org.springframework", "spring-context", "4.3.9-RELEASE");
    DependencyNode toNode = DependencyNodeUtil.createDependencyNode("org.springframework", "spring-core", "4.3.9-RELEASE");
    String edgeInfo = this.renderer.render(fromNode, toNode);
    assertEquals("{\"begin\":\"-[\",\"end\":\"]->\",\"color\":\"#000000\",\"label\":\"\"}", edgeInfo);
}


@Test
public void mergeFullToFull() {
    Font other = new Font();
    other.color = "red";
    other.size = 12;
    other.name = "Sans";
    this.font.merge(other);
    assertEquals("red", this.font.color);
    assertEquals((Integer) 12, this.font.size);
    assertEquals("Sans", this.font.name);
}

void merge(Font other) {
    this.color = StringUtils.defaultIfBlank(other.color, this.color);
    this.size = other.size != null ? other.size : this.size;
    this.name = StringUtils.defaultIfBlank(other.name, this.name);
}

@Test
public void setAttributes() {
    DotAttributeBuilder builder = new DotAttributeBuilder();
    this.font.setAttributes(builder);
    assertEquals("[fontcolor=\"blue\",fontsize=\"10\",fontname=\"Courier\"]", builder.toString());
}

DotAttributeBuilder setAttributes(DotAttributeBuilder builder) {
    return builder.fontColor(this.color).fontSize(this.size).fontName(this.name);
}

@Test
public void mergeFullToEmpty() {
    this.emptyFont.merge(this.font);
    assertEquals("blue", this.font.color);
    assertEquals((Integer) 10, this.font.size);
    assertEquals("Courier", this.font.name);
}

@Test
public void mergeEmptyToFull() {
    this.font.merge(this.emptyFont);
    assertEquals("blue", this.font.color);
    assertEquals((Integer) 10, this.font.size);
    assertEquals("Courier", this.font.name);
}

@Test
public void setAttributesEmpty() {
    DotAttributeBuilder builder = new DotAttributeBuilder();
    this.emptyFont.setAttributes(builder);
    assertEquals("", builder.toString());
}


@Test
public void getIdFromValueAndType() {
    assertEquals("box", this.resolver.idFromValueAndType(null, Box.class));
    assertEquals("polygon", this.resolver.idFromValueAndType(null, Polygon.class));
    assertEquals("ellipse", this.resolver.idFromValueAndType(null, Ellipse.class));
}

@Override
public String idFromValueAndType(Object value, Class<?> suggestedType) {
    return suggestedType.getSimpleName().toLowerCase();
}

@Test
public void typeFromId() {
    assertEquals(Box.class, this.resolver.typeFromId(null, "box").getRawClass());
    assertEquals(Polygon.class, this.resolver.typeFromId(null, "polygon").getRawClass());
    assertEquals(Ellipse.class, this.resolver.typeFromId(null, "ellipse").getRawClass());
}

@Override
public JavaType typeFromId(DatabindContext context, String id) {
    try {
        return SimpleType.constructUnsafe(Class.forName(getClass().getPackage().getName() + "." + id.substring(0, 1).toUpperCase() + id.substring(1)));
    } catch (ClassNotFoundException e) {
        throw new RuntimeException(e);
    }
}

@Test(expected = RuntimeException.class)
public void typeFromIdForUnknownId() {
    assertEquals(Ellipse.class, this.resolver.typeFromId(null, "moebius").getRawClass());
}

@Test
public void getIdFromValue() {
    assertEquals("box", this.resolver.idFromValue(new Box()));
    assertEquals("polygon", this.resolver.idFromValue(new Polygon()));
    assertEquals("ellipse", this.resolver.idFromValue(new Ellipse()));
}

@Override
public String idFromValue(Object value) {
    return idFromValueAndType(value, value != null ? value.getClass() : Box.class);
}

@Test
public void getMechanism() {
    assertEquals(Id.CUSTOM, this.resolver.getMechanism());
}

@Override
public Id getMechanism() {
    return Id.CUSTOM;
}


@Test
public void nodeAttributesForEmptyConfiguration() {
    DotAttributeBuilder attributes = this.emptyConfig.nodeAttributes(COMPILE_STYLE_KEY, GROUP_ID, ARTIFACT_ID, VERSION, TYPE, CLASSIFIER_LINUX, SCOPE_COMPILE);
    assertEquals("[label=<groupId
artifactId
1.0.0
jar
linux
compile>]", attributes.toString());
}

public DotAttributeBuilder nodeAttributes(StyleKey artifactKey, String groupId, String artifactId, String version, String types, String classifiers, String scopes) {
    AbstractNode node = this.defaultNode;
    for (Entry<StyleKey, AbstractNode> entry : this.nodeStyles.entrySet()) {
        StyleKey styleKey = entry.getKey();
        if (styleKey.matches(artifactKey)) {
            node = entry.getValue();
            break;
        }
    }
    return node.createAttributes(groupId, artifactId, version, types, scopes, classifiers, node != this.defaultNode);
}

@Test
public void toJson() throws IOException {
    StyleConfiguration config = StyleConfiguration.load(this.testStyle, this.testOverride);
    String json = config.toJson();
    File configFile = this.tmp.newFile("config.json");
    Files.asCharSink(configFile, UTF_8).write(json);
    StyleConfiguration reloadedConfig = StyleConfiguration.load(new FileSystemStyleResource(configFile.toPath()));
    assertEquals("[fontname=\"Courier\"]", reloadedConfig.edgeAttributes(NodeResolution.OMITTED_FOR_CONFLICT, SCOPE_PROVIDED, null, null).toString());
}

public String toJson() {
    ObjectMapper mapper = createObjectMapper();
    try {
        StringWriter w = new StringWriter();
        mapper.writerWithDefaultPrettyPrinter().writeValue(w, this);
        return w.toString();
    } catch (IOException e) {
        throw new RuntimeException(e.getMessage(), e);
    }
}

@Test
public void edgeAttributesForEmptyConfiguration() {
    DotAttributeBuilder attributes = this.emptyConfig.edgeAttributes(NodeResolution.INCLUDED, SCOPE_COMPILE, null, null);
    assertEquals("", attributes.toString());
}

public DotAttributeBuilder edgeAttributes(NodeResolution resolution, String targetScope, Artifact from, Artifact to) {
    Edge edge = this.edgeResolutionStyles.get(resolution);
    if (resolution == INCLUDED && this.edgeScopeStyles.containsKey(targetScope)) {
        edge = this.edgeScopeStyles.get(targetScope);
    }
    if (from != null) {
        StyleKey artifactKeyFrom = StyleKey.create(from.getGroupId(), from.getArtifactId(), from.getScope(), from.getType(), from.getVersion());
        for (Entry<StyleKey, Edge> entry : this.edgeNodeStylesFrom.entrySet()) {
            StyleKey styleKey = entry.getKey();
            if (styleKey.matches(artifactKeyFrom)) {
                edge = entry.getValue();
                break;
            }
        }
    }
    if (to != null) {
        StyleKey artifactKeyTo = StyleKey.create(to.getGroupId(), to.getArtifactId(), to.getScope(), to.getType(), to.getVersion());
        for (Entry<StyleKey, Edge> entry : this.edgeNodeStylesTo.entrySet()) {
            StyleKey styleKey = entry.getKey();
            if (styleKey.matches(artifactKeyTo)) {
                edge = entry.getValue();
                break;
            }
        }
    }
    return edge != null ? edge.createAttributes() : new DotAttributeBuilder();
}

@Test
public void defaultNodeAttributesForEmptyConfiguration() {
    DotAttributeBuilder attributes = this.emptyConfig.defaultNodeAttributes();
    assertEquals("[shape=\"box\"]", attributes.toString());
}

public DotAttributeBuilder defaultNodeAttributes() {
    return this.defaultNode.createAttributes();
}

@Test
public void load() {
    StyleConfiguration config = StyleConfiguration.load(this.testStyle);
    assertEquals("[shape=\"polygon\",color=\"black\",fontname=\"Courier\",fontsize=\"14\",fontcolor=\"green\",sides=\"8\"]", config.defaultNodeAttributes().toString());
    assertEquals("[style=\"dotted\",color=\"blue\"]", config.defaultEdgeAttributes().toString());
    assertEquals("", config.edgeAttributes(NodeResolution.INCLUDED, SCOPE_COMPILE, null, null).toString());
    assertEquals("[style=\"dotted\",color=\"gray\"]", config.edgeAttributes(NodeResolution.INCLUDED, SCOPE_TEST, null, null).toString());
    assertEquals("[style=\"dashed\"]", config.edgeAttributes(NodeResolution.OMITTED_FOR_DUPLICATE, SCOPE_COMPILE, null, null).toString());
    assertEquals("[label=<groupId
artifactId
1.0.0
jar
compile>]", config.nodeAttributes(COMPILE_STYLE_KEY, GROUP_ID, ARTIFACT_ID, VERSION, TYPE, CLASSIFIER_DEFAULT, SCOPE_COMPILE).toString());
    assertEquals("[shape=\"box\",style=\"filled\",fillcolor=\"orange\",label=<groupId
artifactId
1.0.0
jar
linux
test>]", config.nodeAttributes(TEST_STYLE_KEY, GROUP_ID, ARTIFACT_ID, VERSION, TYPE, CLASSIFIER_LINUX, SCOPE_TEST).toString());
}

public static StyleConfiguration load(StyleResource mainConfig, StyleResource... overrides) {
    ObjectMapper mapper = createObjectMapper();
    ObjectReader reader = mapper.readerFor(StyleConfiguration.class);
    StyleConfiguration styleConfiguration = readConfig(reader, mainConfig);
    for (StyleResource override : overrides) {
        StyleConfiguration overrideConfig = readConfig(reader, override);
        styleConfiguration.merge(overrideConfig);
    }
    return styleConfiguration;
}

@Test
public void defaultEdgeAttributesForEmptyConfiguration() {
    DotAttributeBuilder attributes = this.emptyConfig.defaultEdgeAttributes();
    assertEquals("", attributes.toString());
}

public DotAttributeBuilder defaultEdgeAttributes() {
    return this.defaultEdge.createAttributes();
}

@Test
public void loadWithOverride() {
    StyleConfiguration config = StyleConfiguration.load(this.testStyle, this.testOverride);
    assertEquals("[rankdir=\"LR\"]", config.graphAttributes().toString());
    assertEquals("[shape=\"ellipse\",color=\"black\",fontname=\"Courier\",fontsize=\"14\",fontcolor=\"green\"]", config.defaultNodeAttributes().toString());
    assertEquals("[style=\"dashed\",color=\"blue\"]", config.defaultEdgeAttributes().toString());
    assertEquals("[style=\"dotted\",color=\"blue\"]", config.edgeAttributes(NodeResolution.INCLUDED, SCOPE_TEST, null, null).toString());
    assertEquals("[style=\"dashed\",color=\"green\"]", config.edgeAttributes(NodeResolution.OMITTED_FOR_DUPLICATE, SCOPE_TEST, null, null).toString());
    assertEquals("[fontname=\"Courier\"]", config.edgeAttributes(NodeResolution.OMITTED_FOR_CONFLICT, StyleConfigurationTest.SCOPE_PROVIDED, null, null).toString());
    assertEquals("[shape=\"box\",style=\"filled\",color=\"red\",fillcolor=\"orange\",label=<groupId
artifactId
1.0.0
jar
test>]", config.nodeAttributes(TEST_STYLE_KEY, GROUP_ID, ARTIFACT_ID, VERSION, TYPE, CLASSIFIER_DEFAULT, SCOPE_TEST).toString());
    assertEquals("[shape=\"box\",color=\"blue\",label=<groupId
artifactId
1.0.0
jar
linux
provided>]", config.nodeAttributes(PROVIDED_STYLE_KEY, GROUP_ID, ARTIFACT_ID, VERSION, TYPE, CLASSIFIER_LINUX, SCOPE_PROVIDED).toString());
}


@Test
public void openStreamOnExistingResource() {
    try (InputStream is = this.existingResource.openStream()) {
        assertNotNull(is);
    } catch (IOException e) {
        throw new RuntimeException();
    }
}

@Override
public InputStream openStream() throws IOException {
    return Files.newInputStream(this.location, StandardOpenOption.READ);
}

@Test(expected = IOException.class)
public void openStreamOnNonExistingResource() throws IOException {
    try (InputStream is = this.nonExistingResource.openStream()) {
        fail("There should not be a stream on an inexistent resource.");
    }
}

@Test
public void existsOnExistingResource() {
    assertTrue(this.existingResource.exists());
}

@Override
public boolean exists() {
    return Files.exists(this.location);
}

@Test
public void existsOnNonExistingResource() {
    assertFalse(this.nonExistingResource.exists());
}


@Test
public void resourcesExistAndCanBeLoaded() {
    ClasspathStyleResource styleResource = this.resource.createStyleResource(getClass().getClassLoader());
    assertTrue("Style configuration " + this.resource + " does not exist.", styleResource.exists());
    StyleConfiguration.load(styleResource);
}

public ClasspathStyleResource createStyleResource(ClassLoader classLoader) {
    return new ClasspathStyleResource(this.styleConfig, classLoader);
}


@Test
public void toStringTest() {
    assertEquals("classpath:default-style.json", this.existingResource.toString());
}

@Override
public String toString() {
    return "classpath:" + this.name;
}


@Test
public void toStringTest() {
    Path path = Paths.get("src/main/resources/default-style.json");
    assertEquals("file:" + path.toString(), this.existingResource.toString());
}

@Override
public String toString() {
    return "file:" + this.location;
}


@Test
public void equalsAndHashCode() {
    StyleKey groupIdEqual = StyleKey.fromString("group.id");
    StyleKey groupIdDifferent = StyleKey.fromString("group.id2");
    assertEquals(this.groupId, groupIdEqual);
    assertEquals(groupIdEqual, this.groupId);
    assertNotEquals(this.groupId, groupIdDifferent);
    assertNotEquals(groupIdDifferent, this.groupId);
    assertEquals(this.artifactId, this.artifactId);
    assertNotEquals(this.version, "something else");
    assertEquals(this.groupId.hashCode(), groupIdEqual.hashCode());
    assertNotEquals(this.artifactId.hashCode(), this.scope.hashCode());
}

@Override
public int hashCode() {
    return Objects.hash(this.groupId, this.artifactId, this.scope, this.type, this.version);
}

@Test
public void create() {
    StyleKey key = StyleKey.create("groupId", "artifactId", "scope", "type", "version");
    assertEquals("groupId,artifactId,scope,type,version", key.toString());
}

public static StyleKey create(String groupId, String artifactId, String scope, String type, String version) {
    return new StyleKey(new String[] { groupId, artifactId, scope, type, version });
}

@Test
public void matchesForGroupId() {
    StyleKey wildcard = StyleKey.fromString("group.id*");
    assertTrue(this.groupId.matches(this.groupId));
    assertTrue(wildcard.matches(this.groupId));
}

public boolean matches(StyleKey other) {
    return (wildcardMatch(this.groupId, other.groupId)) && (wildcardMatch(this.artifactId, other.artifactId)) && (match(this.scope, other.scope)) && (match(this.type, other.type)) && (wildcardMatch(this.version, other.version));
}

@Test
public void fromStringWithTypeOnly() {
    assertEquals(",,,type,", this.type.toString());
}

@Override
public String toString() {
    return Joiner.on(",").join(this.groupId, this.artifactId, this.scope, this.type, this.version);
}

@Test
public void fromStringWithArtifactIdOnly() {
    assertEquals(",artifactId,,,", this.artifactId.toString());
}

@Test
public void matchesForVersion() {
    StyleKey wildcard = StyleKey.fromString(",,,,version*");
    assertTrue(this.version.matches(this.version));
    assertTrue(wildcard.matches(this.version));
}

@Test
public void fromStringWithGroupIdOnly() {
    assertEquals("group.id,,,,", this.groupId.toString());
}

@Test
public void fromStringEmpty() {
    StyleKey key = StyleKey.fromString("");
    assertEquals(",,,,", key.toString());
}

public static StyleKey fromString(String keyString) {
    String[] parts = keyString.split(",");
    return new StyleKey(parts);
}

@Test
public void createWithNullValues() {
    StyleKey key = StyleKey.create(null, null, null, null, null);
    assertEquals(",,,,", key.toString());
}

@Test
public void matchesForScope() {
    StyleKey unsupportedWildcard = StyleKey.fromString(",,scope*");
    assertTrue(this.scope.matches(this.scope));
    assertFalse(unsupportedWildcard.matches(this.scope));
}

@Test(expected = IllegalArgumentException.class)
public void fromStringTooManyParts() {
    StyleKey.fromString("groupId,artifactId,scope,type,version,something,else");
}

@Test
public void fromStringWithScopeOnly() {
    assertEquals(",,scope,,", this.scope.toString());
}

@Test
public void matchesForType() {
    StyleKey unsupportedWildcard = StyleKey.fromString(",,,type*");
    assertTrue(this.type.matches(this.type));
    assertFalse(unsupportedWildcard.matches(this.type));
}

@Test
public void fromStringWithVersionOnly() {
    assertEquals(",,,,version", this.version.toString());
}

@Test
public void matchesForArtifactId() {
    StyleKey wildcard = StyleKey.fromString(",artifactId*");
    assertTrue(this.artifactId.matches(this.artifactId));
    assertTrue(wildcard.matches(this.artifactId));
}


@Test
public void reduceEdgesWithCycle() {
    this.graphBuilder.addEdge("A", "B");
    this.graphBuilder.addEdge("B", "C");
    this.graphBuilder.addEdge("A", "C");
    this.graphBuilder.addEdge("C", "A");
    this.graphBuilder.reduceEdges();
    this.graphBuilder.toString();
    assertThat(this.formatter.edges, containsInAnyOrder(new Edge("A", "B", ""), new Edge("B", "C", ""), new Edge("C", "A", "")));
}

public void reduceEdges() {
    Iterator<Edge> edgeIterator = this.edges.iterator();
    while (edgeIterator.hasNext()) {
        Edge edge = edgeIterator.next();
        if (!edge.isPermanent() && this.reachabilityMap.hasOlderPath(edge.getToNodeId(), edge.getFromNodeId())) {
            edgeIterator.remove();
        }
    }
}

@Test
public void reduceEdges() {
    this.graphBuilder.addEdge("A", "B");
    this.graphBuilder.addEdge("B", "C");
    this.graphBuilder.addEdge("A", "C");
    this.graphBuilder.reduceEdges();
    this.graphBuilder.toString();
    assertThat(this.formatter.edges, containsInAnyOrder(new Edge("A", "B", ""), new Edge("B", "C", "")));
}

@Test
public void reduceEdgesWithPermanentEdge() {
    this.graphBuilder.addEdge("A", "B");
    this.graphBuilder.addEdge("B", "C");
    this.graphBuilder.addPermanentEdge("A", "C");
    this.graphBuilder.reduceEdges();
    this.graphBuilder.toString();
    assertThat(this.formatter.edges, containsInAnyOrder(new Edge("A", "B", ""), new Edge("B", "C", ""), new Edge("A", "C", "")));
}


@Test
public void testVerifyDefaultTransportReused() {
    HttpTransport t1 = ApiClientUtils.getDefaultTransport();
    HttpTransport t2 = ApiClientUtils.getDefaultTransport();
    assertEquals(t1, t2);
}

public static HttpTransport getDefaultTransport() {
    return new ApacheHttp2Transport();
}

public static HttpTransport getDefaultTransport() {
    return TransportInstanceHolder.INSTANCE;
}


@Test
public void testEncoding8859() throws UnsupportedEncodingException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    final AtomicReference<String> newLabel = new AtomicReference<String>();
    PrintStream ansi = new PrintStream(new AnsiNoSyncOutputStream(baos, new AnsiProcessor(baos) {

        @Override
        protected void processChangeWindowTitle(String label) {
            newLabel.set(label);
        }
    }, "ISO-8859-1"), true, "ISO-8859-1");
    ansi.print("\033]0;un bon café\007");
    ansi.flush();
    assertEquals("un bon café", newLabel.get());
}

@Override
public void write(int data) throws IOException {
    switch(state) {
        case LOOKING_FOR_FIRST_ESC_CHAR:
            if (data == FIRST_ESC_CHAR) {
                buffer[pos++] = (byte) data;
                state = LOOKING_FOR_SECOND_ESC_CHAR;
            } else {
                out.write(data);
            }
            break;
        case LOOKING_FOR_SECOND_ESC_CHAR:
            buffer[pos++] = (byte) data;
            if (data == SECOND_ESC_CHAR) {
                state = LOOKING_FOR_NEXT_ARG;
            } else if (data == SECOND_OSC_CHAR) {
                state = LOOKING_FOR_OSC_COMMAND;
            } else if (data == SECOND_CHARSET0_CHAR) {
                options.add(0);
                state = LOOKING_FOR_CHARSET;
            } else if (data == SECOND_CHARSET1_CHAR) {
                options.add(1);
                state = LOOKING_FOR_CHARSET;
            } else {
                reset(false);
            }
            break;
        case LOOKING_FOR_NEXT_ARG:
            buffer[pos++] = (byte) data;
            if ('"' == data) {
                startOfValue = pos - 1;
                state = LOOKING_FOR_STR_ARG_END;
            } else if ('0' <= data && data <= '9') {
                startOfValue = pos - 1;
                state = LOOKING_FOR_INT_ARG_END;
            } else if (';' == data) {
                options.add(null);
            } else if ('?' == data) {
                options.add('?');
            } else if ('=' == data) {
                options.add('=');
            } else {
                reset(ap.processEscapeCommand(options, data));
            }
            break;
        default:
            break;
        case LOOKING_FOR_INT_ARG_END:
            buffer[pos++] = (byte) data;
            if (!('0' <= data && data <= '9')) {
                String strValue = new String(buffer, startOfValue, (pos - 1) - startOfValue);
                Integer value = Integer.valueOf(strValue);
                options.add(value);
                if (data == ';') {
                    state = LOOKING_FOR_NEXT_ARG;
                } else {
                    reset(ap.processEscapeCommand(options, data));
                }
            }
            break;
        case LOOKING_FOR_STR_ARG_END:
            buffer[pos++] = (byte) data;
            if ('"' != data) {
                String value = new String(buffer, startOfValue, (pos - 1) - startOfValue);
                options.add(value);
                if (data == ';') {
                    state = LOOKING_FOR_NEXT_ARG;
                } else {
                    reset(ap.processEscapeCommand(options, data));
                }
            }
            break;
        case LOOKING_FOR_OSC_COMMAND:
            buffer[pos++] = (byte) data;
            if ('0' <= data && data <= '9') {
                startOfValue = pos - 1;
                state = LOOKING_FOR_OSC_COMMAND_END;
            } else {
                reset(false);
            }
            break;
        case LOOKING_FOR_OSC_COMMAND_END:
            buffer[pos++] = (byte) data;
            if (';' == data) {
                String strValue = new String(buffer, startOfValue, (pos - 1) - startOfValue);
                Integer value = Integer.valueOf(strValue);
                options.add(value);
                startOfValue = pos;
                state = LOOKING_FOR_OSC_PARAM;
            } else if ('0' <= data && data <= '9') {
            } else {
                reset(false);
            }
            break;
        case LOOKING_FOR_OSC_PARAM:
            buffer[pos++] = (byte) data;
            if (BEL == data) {
                String value = new String(buffer, startOfValue, (pos - 1) - startOfValue);
                options.add(value);
                reset(ap.processOperatingSystemCommand(options));
            } else if (FIRST_ESC_CHAR == data) {
                state = LOOKING_FOR_ST;
            } else {
            }
            break;
        case LOOKING_FOR_ST:
            buffer[pos++] = (byte) data;
            if (SECOND_ST_CHAR == data) {
                String value = new String(buffer, startOfValue, (pos - 2) - startOfValue);
                options.add(value);
                reset(ap.processOperatingSystemCommand(options));
            } else {
                state = LOOKING_FOR_OSC_PARAM;
            }
            break;
        case LOOKING_FOR_CHARSET:
            options.add((char) data);
            reset(ap.processCharsetSelect(options));
            break;
    }
    if (pos >= buffer.length) {
        reset(false);
    }
}

@Override
public void write(int data) throws IOException {
    switch(state) {
        case LOOKING_FOR_FIRST_ESC_CHAR:
            if (data == FIRST_ESC_CHAR) {
                buffer[pos++] = (byte) data;
                state = LOOKING_FOR_SECOND_ESC_CHAR;
            } else {
                out.write(data);
            }
            break;
        case LOOKING_FOR_SECOND_ESC_CHAR:
            buffer[pos++] = (byte) data;
            if (data == SECOND_ESC_CHAR) {
                state = LOOKING_FOR_NEXT_ARG;
            } else if (data == SECOND_OSC_CHAR) {
                state = LOOKING_FOR_OSC_COMMAND;
            } else if (data == SECOND_CHARSET0_CHAR) {
                options.add(0);
                state = LOOKING_FOR_CHARSET;
            } else if (data == SECOND_CHARSET1_CHAR) {
                options.add(1);
                state = LOOKING_FOR_CHARSET;
            } else {
                reset(false);
            }
            break;
        case LOOKING_FOR_NEXT_ARG:
            buffer[pos++] = (byte) data;
            if ('"' == data) {
                startOfValue = pos - 1;
                state = LOOKING_FOR_STR_ARG_END;
            } else if ('0' <= data && data <= '9') {
                startOfValue = pos - 1;
                state = LOOKING_FOR_INT_ARG_END;
            } else if (';' == data) {
                options.add(null);
            } else if ('?' == data) {
                options.add('?');
            } else if ('=' == data) {
                options.add('=');
            } else {
                reset(ap.processEscapeCommand(options, data));
            }
            break;
        default:
            break;
        case LOOKING_FOR_INT_ARG_END:
            buffer[pos++] = (byte) data;
            if (!('0' <= data && data <= '9')) {
                String strValue = new String(buffer, startOfValue, (pos - 1) - startOfValue);
                Integer value = Integer.valueOf(strValue);
                options.add(value);
                if (data == ';') {
                    state = LOOKING_FOR_NEXT_ARG;
                } else {
                    reset(ap.processEscapeCommand(options, data));
                }
            }
            break;
        case LOOKING_FOR_STR_ARG_END:
            buffer[pos++] = (byte) data;
            if ('"' != data) {
                String value = new String(buffer, startOfValue, (pos - 1) - startOfValue, cs);
                options.add(value);
                if (data == ';') {
                    state = LOOKING_FOR_NEXT_ARG;
                } else {
                    reset(ap.processEscapeCommand(options, data));
                }
            }
            break;
        case LOOKING_FOR_OSC_COMMAND:
            buffer[pos++] = (byte) data;
            if ('0' <= data && data <= '9') {
                startOfValue = pos - 1;
                state = LOOKING_FOR_OSC_COMMAND_END;
            } else {
                reset(false);
            }
            break;
        case LOOKING_FOR_OSC_COMMAND_END:
            buffer[pos++] = (byte) data;
            if (';' == data) {
                String strValue = new String(buffer, startOfValue, (pos - 1) - startOfValue);
                Integer value = Integer.valueOf(strValue);
                options.add(value);
                startOfValue = pos;
                state = LOOKING_FOR_OSC_PARAM;
            } else if ('0' <= data && data <= '9') {
            } else {
                reset(false);
            }
            break;
        case LOOKING_FOR_OSC_PARAM:
            buffer[pos++] = (byte) data;
            if (BEL == data) {
                String value = new String(buffer, startOfValue, (pos - 1) - startOfValue, cs);
                options.add(value);
                reset(ap.processOperatingSystemCommand(options));
            } else if (FIRST_ESC_CHAR == data) {
                state = LOOKING_FOR_ST;
            } else {
            }
            break;
        case LOOKING_FOR_ST:
            buffer[pos++] = (byte) data;
            if (SECOND_ST_CHAR == data) {
                String value = new String(buffer, startOfValue, (pos - 2) - startOfValue, cs);
                options.add(value);
                reset(ap.processOperatingSystemCommand(options));
            } else {
                state = LOOKING_FOR_OSC_PARAM;
            }
            break;
        case LOOKING_FOR_CHARSET:
            options.add((char) data);
            reset(ap.processCharsetSelect(options));
            break;
    }
    if (pos >= buffer.length) {
        reset(false);
    }
}

@Test
public void testEncodingUtf8() throws UnsupportedEncodingException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    final AtomicReference<String> newLabel = new AtomicReference<String>();
    PrintStream ansi = new PrintStream(new AnsiNoSyncOutputStream(baos, new AnsiProcessor(baos) {

        @Override
        protected void processChangeWindowTitle(String label) {
            newLabel.set(label);
        }
    }, "UTF-8"), true, "UTF-8");
    ansi.print("\033]0;ひらがな\007");
    ansi.flush();
    assertEquals("ひらがな", newLabel.get());
}


@Test
public void testRender3() {
    String str = render("@|bold,red foo bar baz|@");
    System.out.println(str);
    assertEquals(ansi().a(INTENSITY_BOLD).fg(RED).a("foo bar baz").reset().toString(), str);
    assertEquals(ansi().bold().fgRed().a("foo bar baz").reset().toString(), str);
}

public static String render(final String input) throws IllegalArgumentException {
    try {
        return render(input, new StringBuilder()).toString();
    } catch (IOException e) {
        throw new IllegalArgumentException(e);
    }
}

@Test
public void testRender4() {
    String str = render("@|bold,red foo bar baz|@ ick @|bold,red foo bar baz|@");
    System.out.println(str);
    assertEquals(ansi().a(INTENSITY_BOLD).fg(RED).a("foo bar baz").reset().a(" ick ").a(INTENSITY_BOLD).fg(RED).a("foo bar baz").reset().toString(), str);
}

@Test
public void testRenderCodes() {
    String str = renderCodes("bold red");
    System.out.println(str);
    assertEquals(ansi().bold().fg(Color.RED).toString(), str);
}

public static String renderCodes(final String codes) {
    return renderCodes(codes.split("\\s"));
}

@Test
public void testRender2() {
    String str = render("@|bold,red foo|@");
    System.out.println(str);
    assertEquals(Ansi.ansi().a(INTENSITY_BOLD).fg(RED).a("foo").reset().toString(), str);
    assertEquals(Ansi.ansi().bold().fgRed().a("foo").reset().toString(), str);
}

@Test
public void testRender5() {
    String str = ansi().render("@|bold Hello|@").toString();
    System.out.println(str);
    assertEquals(ansi().a(INTENSITY_BOLD).a("Hello").reset().toString(), str);
}

public Ansi render(final String text) {
    a(AnsiRenderer.render(text));
    return this;
}

@Test
public void testRenderInvalidMissingEnd() {
    String str = render("@|bold foo");
    assertEquals("@|bold foo", str);
}

@Test
public void testRenderNothing() {
    assertEquals("foo", render("foo"));
}

@Test
public void testTest() throws Exception {
    assertFalse(test("foo"));
    assertTrue(test("@|foo|"));
    assertTrue(test("@|foo"));
}

public static boolean test(final String text) {
    return text != null && text.contains(BEGIN_TOKEN);
}

@Test
public void testRender() {
    String str = render("@|bold foo|@");
    System.out.println(str);
    assertEquals(ansi().a(INTENSITY_BOLD).a("foo").reset().toString(), str);
    assertEquals(ansi().bold().a("foo").reset().toString(), str);
}

@Test
public void testRenderInvalidMissingText() {
    String str = render("@|bold|@");
    assertEquals("@|bold|@", str);
}


@Test
public void testPrintMethods() throws Exception {
    PrintStream ps = new FilterPrintStream(System.out);
    ps.println("String");
    ps.println('€');
    ps.flush();
}

@Override
public void println(String x) {
    synchronized (this) {
        print(x);
        newLine();
    }
}

@Test
public void testWrite() throws Exception {
    OutputStream os = new FilterPrintStream(System.out);
    os.write('A');
    os.write('B');
    os.write("€".getBytes());
    os.flush();
}

@Override
public void write(int data) {
    if (filter(data)) {
        ps.write(data);
    }
}


@Test
public void testColorDisabled() {
    Ansi.setEnabled(false);
    try {
        assertEquals("test", Ansi.ansi().fg(32).a("t").fgRgb(0).a("e").bg(24).a("s").bgRgb(100).a("t").toString());
    } finally {
        Ansi.setEnabled(true);
    }
}

@Override
public Ansi bg(int color) {
    return this;
}


@Test
public void testGetUsernameForRevisionExist() throws Exception {
    gitlab.enqueue(new MockResponse().setResponseCode(200).setBody("{\n" + "  \"id\": \"6104942438c14ec7bd21c6cd5bd995272b3faff6\",\n" + "  \"short_id\": \"6104942438c\",\n" + "  \"title\": \"Sanitize for network graph\",\n" + "  \"author_name\": \"randx\",\n" + "  \"author_email\": \"john@example.com\",\n" + "  \"committer_name\": \"Dmitriy\",\n" + "  \"committer_email\": \"dmitriy.zaporozhets@gmail.com\",\n" + "  \"created_at\": \"2012-09-20T09:06:12+03:00\",\n" + "  \"message\": \"Sanitize for network graph\",\n" + "  \"committed_date\": \"2012-09-20T09:06:12+03:00\",\n" + "  \"authored_date\": \"2012-09-20T09:06:12+03:00\",\n" + "  \"parent_ids\": [\n" + "    \"ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba\"\n" + "  ],\n" + "  \"stats\": {\n" + "    \"additions\": 15,\n" + "    \"deletions\": 10,\n" + "    \"total\": 25\n" + "  },\n" + "  \"status\": \"running\"\n" + "}"));
    gitlab.enqueue(new MockResponse().setResponseCode(200).setBody("[\n" + "  {\n" + "    \"id\": 1,\n" + "    \"username\": \"john_smith\",\n" + "    \"email\": \"john@example.com\",\n" + "    \"name\": \"John Smith\",\n" + "    \"state\": \"active\",\n" + "    \"avatar_url\": \"http://localhost:3000/uploads/user/avatar/1/cd8.jpeg\",\n" + "    \"web_url\": \"http://localhost:3000/john_smith\"\n" + "  },\n" + "  {\n" + "    \"id\": 2,\n" + "    \"username\": \"jack_smith\",\n" + "    \"email\": \"jack@example.com\",\n" + "    \"name\": \"Jack Smith\",\n" + "    \"state\": \"blocked\",\n" + "    \"avatar_url\": \"http://gravatar.com/../e32131cd8.jpeg\",\n" + "    \"web_url\": \"http://localhost:3000/jack_smith\"\n" + "  }\n" + "]"));
    CommitFacade facade = new CommitFacade(gitLabPluginConfiguration);
    facade.setGitLabAPI(GitLabAPI.connect(gitLabPluginConfiguration.url(), gitLabPluginConfiguration.userToken()));
    GitLabProject gitLabProject = Mockito.mock(GitLabProject.class);
    Mockito.when(gitLabProject.getId()).thenReturn(1);
    facade.setGitLabProject(gitLabProject);
    Assertions.assertThat(facade.getUsernameForRevision("6104942438c14ec7bd21c6cd5bd995272b3faff6")).isEqualTo("john_smith");
}

public String getUsernameForRevision(String revision) {
    try {
        GitLabCommit commit = gitLabAPI.getGitLabAPICommits().getCommit(gitLabProject.getId(), revision);
        Paged<GitLabUser> paged = gitLabAPI.getGitLabAPIUsers().getUsers(commit.getAuthorEmail(), null);
        List<GitLabUser> users = new ArrayList<>();
        do {
            if (paged.getResults() != null) {
                users.addAll(paged.getResults());
            }
        } while ((paged = paged.nextPage()) != null);
        if (users.size() == 1) {
            return users.get(0).getUsername();
        }
        return users.stream().filter(x -> commit.getAuthorEmail().equals(x.getEmail())).map(GitLabUser::getUsername).findFirst().orElse(null);
    } catch (IOException e) {
        throw new IllegalStateException("Unable to create retrive author for commit " + revision, e);
    }
}

@Test
public void testGetUsernameForRevisionNull2() throws Exception {
    gitlab.enqueue(new MockResponse().setResponseCode(200).setBody("{\n" + "  \"id\": \"6104942438c14ec7bd21c6cd5bd995272b3faff6\",\n" + "  \"short_id\": \"6104942438c\",\n" + "  \"title\": \"Sanitize for network graph\",\n" + "  \"author_name\": \"randx\",\n" + "  \"author_email\": \"dmitriy.zaporozhets@gmail.com\",\n" + "  \"committer_name\": \"Dmitriy\",\n" + "  \"committer_email\": \"dmitriy.zaporozhets@gmail.com\",\n" + "  \"created_at\": \"2012-09-20T09:06:12+03:00\",\n" + "  \"message\": \"Sanitize for network graph\",\n" + "  \"committed_date\": \"2012-09-20T09:06:12+03:00\",\n" + "  \"authored_date\": \"2012-09-20T09:06:12+03:00\",\n" + "  \"parent_ids\": [\n" + "    \"ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba\"\n" + "  ],\n" + "  \"stats\": {\n" + "    \"additions\": 15,\n" + "    \"deletions\": 10,\n" + "    \"total\": 25\n" + "  },\n" + "  \"status\": \"running\"\n" + "}"));
    gitlab.enqueue(new MockResponse().setResponseCode(200).setBody("[\n" + "  {\n" + "    \"id\": 1,\n" + "    \"username\": \"john_smith\",\n" + "    \"name\": \"John Smith\",\n" + "    \"state\": \"active\",\n" + "    \"avatar_url\": \"http://localhost:3000/uploads/user/avatar/1/cd8.jpeg\",\n" + "    \"web_url\": \"http://localhost:3000/john_smith\"\n" + "  },\n" + "  {\n" + "    \"id\": 2,\n" + "    \"username\": \"jack_smith\",\n" + "    \"name\": \"Jack Smith\",\n" + "    \"state\": \"blocked\",\n" + "    \"avatar_url\": \"http://gravatar.com/../e32131cd8.jpeg\",\n" + "    \"web_url\": \"http://localhost:3000/jack_smith\"\n" + "  }\n" + "]"));
    CommitFacade facade = new CommitFacade(gitLabPluginConfiguration);
    facade.setGitLabAPI(GitLabAPI.connect(gitLabPluginConfiguration.url(), gitLabPluginConfiguration.userToken()));
    GitLabProject gitLabProject = Mockito.mock(GitLabProject.class);
    Mockito.when(gitLabProject.getId()).thenReturn(1);
    facade.setGitLabProject(gitLabProject);
    Assertions.assertThat(facade.getUsernameForRevision("6104942438c14ec7bd21c6cd5bd995272b3faff6")).isNull();
}

@Test
public void testGetUsernameForRevisionNull1() throws Exception {
    gitlab.enqueue(new MockResponse().setResponseCode(200).setBody("{\n" + "  \"id\": \"6104942438c14ec7bd21c6cd5bd995272b3faff6\",\n" + "  \"short_id\": \"6104942438c\",\n" + "  \"title\": \"Sanitize for network graph\",\n" + "  \"author_name\": \"randx\",\n" + "  \"author_email\": \"dmitriy.zaporozhets@gmail.com\",\n" + "  \"committer_name\": \"Dmitriy\",\n" + "  \"committer_email\": \"dmitriy.zaporozhets@gmail.com\",\n" + "  \"created_at\": \"2012-09-20T09:06:12+03:00\",\n" + "  \"message\": \"Sanitize for network graph\",\n" + "  \"committed_date\": \"2012-09-20T09:06:12+03:00\",\n" + "  \"authored_date\": \"2012-09-20T09:06:12+03:00\",\n" + "  \"parent_ids\": [\n" + "    \"ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba\"\n" + "  ],\n" + "  \"stats\": {\n" + "    \"additions\": 15,\n" + "    \"deletions\": 10,\n" + "    \"total\": 25\n" + "  },\n" + "  \"status\": \"running\"\n" + "}"));
    gitlab.enqueue(new MockResponse().setResponseCode(200).setBody("[\n" + "  {\n" + "    \"id\": 1,\n" + "    \"username\": \"john_smith\",\n" + "    \"name\": \"John Smith\",\n" + "    \"state\": \"active\",\n" + "    \"avatar_url\": \"http://localhost:3000/uploads/user/avatar/1/cd8.jpeg\",\n" + "    \"web_url\": \"http://localhost:3000/john_smith\"\n" + "  },\n" + "  {\n" + "    \"id\": 2,\n" + "    \"username\": \"jack_smith\",\n" + "    \"name\": \"Jack Smith\",\n" + "    \"state\": \"blocked\",\n" + "    \"avatar_url\": \"http://gravatar.com/../e32131cd8.jpeg\",\n" + "    \"web_url\": \"http://localhost:3000/jack_smith\"\n" + "  }\n" + "]"));
    CommitFacade facade = new CommitFacade(gitLabPluginConfiguration);
    facade.setGitLabAPI(GitLabAPI.connect(gitLabPluginConfiguration.url(), gitLabPluginConfiguration.userToken()));
    GitLabProject gitLabProject = Mockito.mock(GitLabProject.class);
    Mockito.when(gitLabProject.getId()).thenReturn(1);
    facade.setGitLabProject(gitLabProject);
    Assertions.assertThat(facade.getUsernameForRevision("123")).isNull();
}


@Test
public void testTemplateConfig() {
    settings.setProperty(GitLabPlugin.GITLAB_PROJECT_ID, "123");
    settings.setProperty(GitLabPlugin.GITLAB_COMMIT_SHA, "123456789");
    settings.setProperty(GitLabPlugin.GITLAB_REF_NAME, "master");
    settings.setProperty(GitLabPlugin.GITLAB_GLOBAL_TEMPLATE, "${projectId}\n${commitSHA[0]}\n${refName}\n${url}\n" + "${maxGlobalIssues}\n${maxBlockerIssuesGate}\n${maxCriticalIssuesGate}\n${maxMajorIssuesGate}\n${maxMinorIssuesGate}\n${maxInfoIssuesGate}\n" + "${disableIssuesInline?c}\n${onlyIssueFromCommitFile?c}\n${commentNoIssue?c}\n${revision}");
    Reporter reporter = new Reporter(config);
    Assertions.assertThat(new GlobalCommentBuilder(config, null, reporter, new MarkDownUtils(settings)).buildForMarkdown()).isEqualTo("123\n" + "123456789\n" + "master\n" + "https://gitlab.com\n" + "10\n" + "0\n" + "0\n" + "-1\n" + "-1\n" + "-1\n" + "false\n" + "false\n" + "false\n" + "123456789");
}

public String buildForMarkdown() {
    if (template != null && !template.isEmpty()) {
        return buildFreemarkerComment();
    }
    return buildDefaultComment();
}

@Test
public void testNoIssues() {
    Assertions.assertThat(new GlobalCommentBuilder(config, null, new Reporter(config), new MarkDownUtils(settings)).buildForMarkdown()).isEqualTo("SonarQube analysis reported no issues.");
}


@Test
public void testNoIssues() {
    Assertions.assertThat(new GlobalCommentBuilder(config, null, new Reporter(config), new MarkDownUtils(settings)).buildForMarkdown()).isEqualTo("SonarQube analysis reported no issues.\n");
}


@Test
public void testTemplateIssue() {
    settings.setProperty(GitLabPlugin.GITLAB_INLINE_TEMPLATE, "${issueCount()}\n<#list issues() as issue>\n${issue.componentKey}\n</#list>");
    List<Reporter.ReportIssue> ris = Stream.iterate(0, i -> i++).limit(17).map(i -> new Reporter.ReportIssue(Utils.newMockedIssue("component", null, null, Severity.MAJOR, true, "Issue number:" + i, "rule" + i), null, "lalal", true)).collect(Collectors.toList());
    Assertions.assertThat(new InlineCommentBuilder(config, "123", null, 1, ris, new MarkDownUtils(settings)).buildForMarkdown()).isEqualTo("17\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n");
}

@Test
public void testTemplateIssueReported() {
    settings.setProperty(GitLabPlugin.GITLAB_INLINE_TEMPLATE, "${issueCount(true)}\n<#list issues(true) as issue>\n${issue.componentKey}\n</#list>\n${issueCount(false)}\n<#list issues(false) as issue>\n${issue.componentKey}\n</#list>");
    List<Reporter.ReportIssue> ris = Stream.iterate(0, i -> i++).limit(17).map(i -> new Reporter.ReportIssue(Utils.newMockedIssue("component", null, null, Severity.MAJOR, true, "Issue number:" + i, "rule" + i), null, GITLAB_URL + "/File.java#L" + i, false)).collect(Collectors.toList());
    Assertions.assertThat(new InlineCommentBuilder(config, "123", null, 1, ris, new MarkDownUtils(settings)).buildForMarkdown()).isEqualTo("0\n" + "17\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n");
}

@Test
public void testTemplateIssueOthers() {
    settings.setProperty(GitLabPlugin.GITLAB_INLINE_TEMPLATE, "${emojiSeverity(BLOCKER)}\n${imageSeverity(BLOCKER)}\n${ruleLink(\"repo%3Arule0\")}\n<#list issues() as issue>${print(issue)}\n</#list>");
    List<Reporter.ReportIssue> ris = Stream.iterate(0, i -> i++).limit(10).map(i -> new Reporter.ReportIssue(Utils.newMockedIssue("component", null, null, Severity.MAJOR, true, "Issue number:" + i, "rule" + i), null, GITLAB_URL + "/File.java#L" + i, false)).collect(Collectors.toList());
    Assertions.assertThat(new InlineCommentBuilder(config, "123", null, 1, ris, new MarkDownUtils(settings)).buildForMarkdown()).isEqualTo(":no_entry:\n" + "![BLOCKER](https://github.com/gabrie-allaigre/sonar-gitlab-plugin/raw/master/images/severity-blocker.png)\n" + "http://myserver/coding_rules#rule_key=repo%253Arule0\n" + ":warning: [Issue number:0](https://gitlab.com/test/test/File.java#L0) [:blue_book:](http://myserver/coding_rules#rule_key=repo%3Arule0)\n" + ":warning: [Issue number:0](https://gitlab.com/test/test/File.java#L0) [:blue_book:](http://myserver/coding_rules#rule_key=repo%3Arule0)\n" + ":warning: [Issue number:0](https://gitlab.com/test/test/File.java#L0) [:blue_book:](http://myserver/coding_rules#rule_key=repo%3Arule0)\n" + ":warning: [Issue number:0](https://gitlab.com/test/test/File.java#L0) [:blue_book:](http://myserver/coding_rules#rule_key=repo%3Arule0)\n" + ":warning: [Issue number:0](https://gitlab.com/test/test/File.java#L0) [:blue_book:](http://myserver/coding_rules#rule_key=repo%3Arule0)\n" + ":warning: [Issue number:0](https://gitlab.com/test/test/File.java#L0) [:blue_book:](http://myserver/coding_rules#rule_key=repo%3Arule0)\n" + ":warning: [Issue number:0](https://gitlab.com/test/test/File.java#L0) [:blue_book:](http://myserver/coding_rules#rule_key=repo%3Arule0)\n" + ":warning: [Issue number:0](https://gitlab.com/test/test/File.java#L0) [:blue_book:](http://myserver/coding_rules#rule_key=repo%3Arule0)\n" + ":warning: [Issue number:0](https://gitlab.com/test/test/File.java#L0) [:blue_book:](http://myserver/coding_rules#rule_key=repo%3Arule0)\n" + ":warning: [Issue number:0](https://gitlab.com/test/test/File.java#L0) [:blue_book:](http://myserver/coding_rules#rule_key=repo%3Arule0)\n");
}

@Test
public void testTemplateIssueForSeverity() {
    settings.setProperty(GitLabPlugin.GITLAB_INLINE_TEMPLATE, "${issueCount(MAJOR)}\n<#list issues(MAJOR) as issue>\n${issue.componentKey}\n</#list>" + "${issueCount(\"MAJOR\")}\n<#list issues(\"MAJOR\") as issue>\n${issue.componentKey}\n</#list>");
    List<Reporter.ReportIssue> ris = Stream.iterate(0, i -> i++).limit(9).map(i -> new Reporter.ReportIssue(Utils.newMockedIssue("component", null, null, i % 2 == 0 ? Severity.MAJOR : Severity.BLOCKER, true, "Issue number:" + i, "rule" + i), null, "lalal", true)).collect(Collectors.toList());
    Assertions.assertThat(new InlineCommentBuilder(config, "123", null, 1, ris, new MarkDownUtils(settings)).buildForMarkdown()).isEqualTo("9\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "9\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n");
}

@Test
public void testNoIssues() {
    Assertions.assertThat(new InlineCommentBuilder(config, "123", null, 1, Collections.emptyList(), new MarkDownUtils(settings)).buildForMarkdown()).isEqualTo("");
}

@Test
public void testMultiIssue() {
    settings.setProperty(GitLabPlugin.GITLAB_DISABLE_INLINE_COMMENTS, false);
    List<Reporter.ReportIssue> ris = Stream.iterate(0, i -> i++).limit(10).map(i -> new Reporter.ReportIssue(Utils.newMockedIssue("component", null, 1, Severity.INFO, true, "Issue", "rule"), null, "lalal", true)).collect(Collectors.toList());
    Assertions.assertThat(new InlineCommentBuilder(config, "123", null, 1, ris, new MarkDownUtils(settings)).buildForMarkdown()).isEqualTo(":information_source: Issue [:blue_book:](http://myserver/coding_rules#rule_key=repo%3Arule)\n" + ":information_source: Issue [:blue_book:](http://myserver/coding_rules#rule_key=repo%3Arule)\n" + ":information_source: Issue [:blue_book:](http://myserver/coding_rules#rule_key=repo%3Arule)\n" + ":information_source: Issue [:blue_book:](http://myserver/coding_rules#rule_key=repo%3Arule)\n" + ":information_source: Issue [:blue_book:](http://myserver/coding_rules#rule_key=repo%3Arule)\n" + ":information_source: Issue [:blue_book:](http://myserver/coding_rules#rule_key=repo%3Arule)\n" + ":information_source: Issue [:blue_book:](http://myserver/coding_rules#rule_key=repo%3Arule)\n" + ":information_source: Issue [:blue_book:](http://myserver/coding_rules#rule_key=repo%3Arule)\n" + ":information_source: Issue [:blue_book:](http://myserver/coding_rules#rule_key=repo%3Arule)\n" + ":information_source: Issue [:blue_book:](http://myserver/coding_rules#rule_key=repo%3Arule)");
}

@Test
public void testTemplateIssueReportedForSeverity() {
    settings.setProperty(GitLabPlugin.GITLAB_INLINE_TEMPLATE, "${issueCount(MAJOR,true)}\n<#list issues(MAJOR,true) as issue>\n${issue.componentKey}\n</#list>" + "${issueCount(\"MAJOR\",false)}\n<#list issues(\"MAJOR\",false) as issue>\n${issue.componentKey}\n</#list>");
    List<Reporter.ReportIssue> ris = Stream.iterate(0, i -> i++).limit(10).map(i -> new Reporter.ReportIssue(Utils.newMockedIssue("component", null, null, i % 2 == 0 ? Severity.MAJOR : Severity.BLOCKER, true, "Issue number:" + i, "rule" + i), null, GITLAB_URL + "/File.java#L" + i, false)).collect(Collectors.toList());
    Assertions.assertThat(new InlineCommentBuilder(config, "123", null, 1, ris, new MarkDownUtils(settings)).buildForMarkdown()).isEqualTo("0\n" + "10\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n" + "component\n");
}

@Test
public void testTemplateConfig() {
    settings.setProperty(GitLabPlugin.GITLAB_PROJECT_ID, "123");
    settings.setProperty(GitLabPlugin.GITLAB_COMMIT_SHA, "123456789");
    settings.setProperty(GitLabPlugin.GITLAB_REF_NAME, "master");
    settings.setProperty(GitLabPlugin.GITLAB_INLINE_TEMPLATE, "${projectId}\n${commitSHA[0]}\n${refName}\n${url}\n" + "${maxGlobalIssues}\n${maxBlockerIssuesGate}\n${maxCriticalIssuesGate}\n${maxMajorIssuesGate}\n${maxMinorIssuesGate}\n${maxInfoIssuesGate}\n" + "${disableIssuesInline?c}\n${onlyIssueFromCommitFile?c}\n${commentNoIssue?c}\n${revision}\n${lineNumber}");
    Assertions.assertThat(new InlineCommentBuilder(config, "123", null, 1, Collections.emptyList(), new MarkDownUtils(settings)).buildForMarkdown()).isEqualTo("123\n" + "123456789\n" + "master\n" + "https://gitlab.com\n" + "10\n" + "0\n" + "0\n" + "-1\n" + "-1\n" + "-1\n" + "false\n" + "false\n" + "false\n" + "123\n" + "1");
}


@Test
public void testTwoIssue() {
    List<Reporter.ReportIssue> ris = Stream.iterate(0, i -> i++).limit(2).map(i -> new Reporter.ReportIssue(Utils.newMockedIssue("component", null, 1, Severity.INFO, true, "Issue", "rule"), null, "lalal", true)).collect(Collectors.toList());
    Assertions.assertThat(new InlineCommentBuilder(config, "123", null, 1, ris, new MarkDownUtils(settings)).buildForMarkdown()).isEqualTo(":information_source: Issue [:blue_book:](http://myserver/coding_rules#rule_key=repo%3Arule)\n" + ":information_source: Issue [:blue_book:](http://myserver/coding_rules#rule_key=repo%3Arule)\n");
}


@Test
public void testInitGitBaseDirNotFound() throws Exception {
    CommitFacade facade = new CommitFacade(mock(GitLabPluginConfiguration.class));
    File projectBaseDir = temp.newFolder();
    facade.initGitBaseDir(projectBaseDir);
    assertThat(facade.getPath(new DefaultInputFile("foo", "src/main/java/Foo.java").setModuleBaseDir(projectBaseDir.toPath()))).isEqualTo("src/main/java/Foo.java");
}

void initGitBaseDir(File projectBaseDir) {
    File detectedGitBaseDir = findGitBaseDir(projectBaseDir);
    if (detectedGitBaseDir == null) {
        LOG.debug("Unable to find Git root directory. Is " + projectBaseDir + " part of a Git repository?");
        setGitBaseDir(projectBaseDir);
    } else {
        setGitBaseDir(detectedGitBaseDir);
    }
}

@Test
public void testGetGitLabUrl() throws Exception {
    File gitBasedir = temp.newFolder();
    GitLabPluginConfiguration gitLabPluginConfiguration = mock(GitLabPluginConfiguration.class);
    when(gitLabPluginConfiguration.commitSHA()).thenReturn("abc123");
    CommitFacade facade = new CommitFacade(gitLabPluginConfiguration);
    facade.setGitBaseDir(gitBasedir);
    GitLabProject gitLabProject = mock(GitLabProject.class);
    when(gitLabProject.getWebUrl()).thenReturn("https://gitLab.com/gaby/test");
    facade.setGitLabProject(gitLabProject);
    InputPath inputPath = mock(InputPath.class);
    when(inputPath.file()).thenReturn(new File(gitBasedir, "src/main/Foo.java"));
    assertThat(facade.getGitLabUrl(inputPath, 10)).isEqualTo("https://gitLab.com/gaby/test/blob/abc123/src/main/Foo.java#L10");
}

void setGitLabProject(GitLabProject gitLabProject) {
    this.gitLabProject = gitLabProject;
}

@Test
public void testInitGitBaseDir() throws Exception {
    CommitFacade facade = new CommitFacade(mock(GitLabPluginConfiguration.class));
    File gitBaseDir = temp.newFolder();
    Files.createDirectory(gitBaseDir.toPath().resolve(".git"));
    File projectBaseDir = new File(gitBaseDir, "myProject");
    facade.initGitBaseDir(projectBaseDir);
    assertThat(facade.getPath(new DefaultInputFile("foo", "src/main/java/Foo.java").setModuleBaseDir(projectBaseDir.toPath()))).isEqualTo("myProject/src/main/java/Foo.java");
}


@Test
public void shouldFailIfNotPreview() {
    settings.setProperty(GitLabPlugin.GITLAB_COMMIT_SHA, "1");
    thrown.expect(MessageException.class);
    thrown.expectMessage("The GitLab plugin is only intended to be used in preview or issues mode. Please set 'sonar.analysis.mode'.");
    commitProjectBuilder.build(null);
}

@Override
public void build(Context context) {
    if (!gitLabPluginConfiguration.isEnabled()) {
        return;
    }
    commitFacade.init(context.projectReactor().getRoot().getBaseDir());
    commitFacade.createOrUpdateSonarQubeStatus("pending", "SonarQube analysis in progress");
}

@Override
public void build(Context context) {
    if (!gitLabPluginConfiguration.isEnabled()) {
        return;
    }
    checkMode();
    commitFacade.init(context.projectReactor().getRoot().getBaseDir());
    commitFacade.createOrUpdateSonarQubeStatus("pending", "SonarQube analysis in progress");
}

@Test
public void shouldNotFailIfIssues() {
    settings.setProperty(GitLabPlugin.GITLAB_COMMIT_SHA, "1");
    when(mode.isIssues()).thenReturn(true);
    commitProjectBuilder.build(mock(ProjectBuilder.Context.class, withSettings().defaultAnswer(RETURNS_DEEP_STUBS)));
    verify(facade).init(any(File.class));
}

@Test
public void testShouldDoNothing() {
    commitProjectBuilder.build(null);
    verifyZeroInteractions(facade);
}