class Camflake {

    public long next() {

        // elapsedTime
        long elapsed = getElapsedTime();
        // sequence
        int sequence = getSequence(elapsed);
        log.debug("sequence id: {}", sequence);
        // If sequence ID exceeded maximum value, it retries once to generate unique ID 2 milliseconds later.
        if (sequence > SEQUENCE_MAX) {
            sleep(TimeUnit.MILLISECONDS.toMillis(2L));

            elapsed = getElapsedTime();
            sequence = getSequence(elapsed);
            if (sequence > SEQUENCE_MAX) {
                throw new CamflakeException("Failed to issue sequence id.");
            }
        }

        long id = (elapsed << 22) | (sequence << 16) | machineId;
        log.debug("unique id: {}", id);

        return id;
    }

}

class CamflakeTest {

    private static final int SEQUENCE_MAX;

    @Test
    public void testNextReturnsExpectedValue() {

        final Instant baseTime = ZonedDateTime
            .of(2017, 6, 1, 0, 0, 0, 0, ZoneId.of("UTC"))
            .toInstant();
        final Instant execTime = ZonedDateTime
            .of(2017, 6, 1, 0, 0, 1, 0, ZoneId.of("UTC"))
            .toInstant();
        new Expectations(Instant.class) {
            {
                Instant.now();
                returns(
                    baseTime, // at initialize
                    execTime  // at runtime
                );
            }
        };

        Camflake camflake = new Camflake(new TestMachineId());

        long id = camflake.next();
        assertThat(id, is(4194304001L));

    }

}