From b4e7fb0364672f73b613b3d04eba9535e97d7c65 Mon Sep 17 00:00:00 2001 From: Jim Hahn Date: Fri, 15 Jun 2018 09:10:00 -0400 Subject: Add CurrentTime utilities Add some comments. Update license text. Change-Id: I4cee208378c44d9730274aee337feb95f9026add Issue-ID: POLICY-908 Signed-off-by: Jim Hahn --- utils-test/pom.xml | 5 + .../onap/policy/common/utils/time/TestTime.java | 59 ++++++ .../policy/common/utils/time/TestTimeMulti.java | 200 +++++++++++++++++++++ .../common/utils/time/TestTimeMultiTest.java | 116 ++++++++++++ .../policy/common/utils/time/TestTimeTest.java | 66 +++++++ .../onap/policy/common/utils/time/CurrentTime.java | 61 +++++++ .../policy/common/utils/time/CurrentTimeTest.java | 59 ++++++ 7 files changed, 566 insertions(+) create mode 100644 utils-test/src/main/java/org/onap/policy/common/utils/time/TestTime.java create mode 100644 utils-test/src/main/java/org/onap/policy/common/utils/time/TestTimeMulti.java create mode 100644 utils-test/src/test/java/org/onap/policy/common/utils/time/TestTimeMultiTest.java create mode 100644 utils-test/src/test/java/org/onap/policy/common/utils/time/TestTimeTest.java create mode 100644 utils/src/main/java/org/onap/policy/common/utils/time/CurrentTime.java create mode 100644 utils/src/test/java/org/onap/policy/common/utils/time/CurrentTimeTest.java diff --git a/utils-test/pom.xml b/utils-test/pom.xml index a7c2eaee..933104b0 100644 --- a/utils-test/pom.xml +++ b/utils-test/pom.xml @@ -42,6 +42,11 @@ ch.qos.logback logback-classic + + org.onap.policy.common + utils + ${project.version} + diff --git a/utils-test/src/main/java/org/onap/policy/common/utils/time/TestTime.java b/utils-test/src/main/java/org/onap/policy/common/utils/time/TestTime.java new file mode 100644 index 00000000..3dfed4bd --- /dev/null +++ b/utils-test/src/main/java/org/onap/policy/common/utils/time/TestTime.java @@ -0,0 +1,59 @@ +/* + * ============LICENSE_START======================================================= + * Common Utils-Test + * ================================================================================ + * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.common.utils.time; + +import java.util.Date; +import java.util.concurrent.atomic.AtomicLong; + +/** + * "Current" time, when running junit tests. This is intended to be injected into classes + * under test, to replace their {@link CurrentTime} objects. When {@link #sleep(long)} is + * invoked, it simply advances the notion of "current" time and returns immediately. + */ +public class TestTime extends CurrentTime { + + /** + * "Current" time, in milliseconds, used by tests. + */ + private AtomicLong tcur = new AtomicLong(System.currentTimeMillis()); + + /** + * + */ + public TestTime() { + super(); + } + + @Override + public long getMillis() { + return tcur.get(); + } + + @Override + public Date getDate() { + return new Date(tcur.get()); + } + + @Override + public void sleep(long sleepMs) throws InterruptedException { + tcur.addAndGet(sleepMs); + } +} diff --git a/utils-test/src/main/java/org/onap/policy/common/utils/time/TestTimeMulti.java b/utils-test/src/main/java/org/onap/policy/common/utils/time/TestTimeMulti.java new file mode 100644 index 00000000..7a8277c7 --- /dev/null +++ b/utils-test/src/main/java/org/onap/policy/common/utils/time/TestTimeMulti.java @@ -0,0 +1,200 @@ +/* + * ============LICENSE_START======================================================= + * Common Utils-Test + * ================================================================================ + * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.common.utils.time; + +import java.util.Date; +import java.util.PriorityQueue; +import java.util.concurrent.CountDownLatch; + +/** + * "Current" time, when running junit tests in multiple threads. This is intended to be + * injected into classes under test, to replace their {@link CurrentTime} objects. The + * {@link #sleep(long)} method blocks until all threads enter and then it moves the notion + * of "current" time forward, allowing threads to resume, as the end of their sleep time + * is reached. Additional threads do not resume until all threads have once again entered + * {@link #sleep(long)} or when {@link #threadCompleted()} is invoked to indicate that a + * thread will not re-enter {@link #sleep(long)}. + */ +public class TestTimeMulti extends CurrentTime { + + /** + * Number of threads that will be sleeping simultaneously. + */ + private int nthreads; + + /** + * "Current" time, in milliseconds, used by tests. + */ + private long tcur = System.currentTimeMillis(); + + /** + * Queue of sleeping threads waiting to be awakened. + */ + private final PriorityQueue queue = new PriorityQueue<>(); + + /** + * Used to synchronize updates. + */ + private final Object locker = new Object(); + + /** + * + * @param nthreads number of threads that will be sleeping simultaneously + */ + public TestTimeMulti(int nthreads) { + this.nthreads = nthreads; + } + + @Override + public long getMillis() { + return tcur; + } + + @Override + public Date getDate() { + return new Date(tcur); + } + + @Override + public void sleep(long sleepMs) throws InterruptedException { + if (sleepMs <= 0) { + return; + } + + Info info = new Info(tcur + sleepMs); + + synchronized (locker) { + queue.add(info); + + if (queue.size() == nthreads) { + // all threads are now sleeping - wake one up + wakeThreads(); + } + } + + // this MUST happen outside of the "synchronized" block + info.await(); + } + + /** + * Indicates that a thread has terminated or that it will no longer be invoking + * {@link #sleep(long)}. Awakens the next sleeping thread, if the queue is full after + * removing the terminated thread. + * + * @throws IllegalStateException if the queue is already full + */ + public void threadCompleted() { + synchronized (locker) { + int sz = queue.size(); + if (sz >= nthreads) { + throw new IllegalStateException("too many threads still sleeping"); + } + + --nthreads; + + if (sz == nthreads) { + // after removing terminated thread - queue is now full; awaken something + wakeThreads(); + } + } + } + + /** + * Advances the "current" time and awakens any threads sleeping until that time. + */ + private void wakeThreads() { + Info info = queue.poll(); + if(info == null) { + return; + } + + tcur = info.getAwakenAtMs(); + info.wake(); + + while ((info = queue.poll()) != null) { + if (tcur == info.getAwakenAtMs()) { + info.wake(); + + } else { + // not ready to wake this thread - put it back in the queue + queue.add(info); + break; + } + } + } + + /** + * Info about a sleeping thread. + */ + private static class Info implements Comparable { + + /** + * Time, in milliseconds, at which the associated thread should awaken. + */ + private final long awakenAtMs; + + /** + * This is triggered when the associated thread should awaken. + */ + private final CountDownLatch latch = new CountDownLatch(1); + + /** + * @param awakenAtMs time, in milliseconds, at which the associated thread should + * awaken + */ + public Info(long awakenAtMs) { + this.awakenAtMs = awakenAtMs; + } + + public long getAwakenAtMs() { + return awakenAtMs; + } + + /** + * Awakens the associated thread by decrementing its latch. + */ + public void wake() { + latch.countDown(); + } + + /** + * Blocks the current thread until awakened (i.e., until its latch is + * decremented). + * + * @throws InterruptedException + */ + public void await() throws InterruptedException { + latch.await(); + } + + @Override + public int compareTo(Info o) { + int diff = Long.compare(awakenAtMs, o.awakenAtMs); + + // this assumes that Object.toString() is unique for each Info object + if (diff == 0) + diff = this.toString().compareTo(o.toString()); + + return diff; + } + + } +} diff --git a/utils-test/src/test/java/org/onap/policy/common/utils/time/TestTimeMultiTest.java b/utils-test/src/test/java/org/onap/policy/common/utils/time/TestTimeMultiTest.java new file mode 100644 index 00000000..206ab5f1 --- /dev/null +++ b/utils-test/src/test/java/org/onap/policy/common/utils/time/TestTimeMultiTest.java @@ -0,0 +1,116 @@ +/* + * ============LICENSE_START======================================================= + * Common Utils-Test + * ================================================================================ + * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.common.utils.time; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import org.junit.Test; + +/** + * + */ +public class TestTimeMultiTest { + + private static final int NTHREADS = 10; + private static final int NTIMES = 100; + private static final long WAIT_SEC = 5L; + private static final long MIN_SLEEP_MS = 5L; + + private TestTimeMulti ttm; + private Semaphore done; + + @Test + public void test() throws Exception { + ttm = new TestTimeMulti(NTHREADS); + done = new Semaphore(0); + + long tbeg = ttm.getMillis(); + + // create threads + List threads = new ArrayList<>(NTHREADS); + for (int x = 0; x < NTHREADS; ++x) { + threads.add(new MyThread(x + MIN_SLEEP_MS)); + } + + // launch threads + for (MyThread thr : threads) { + thr.start(); + } + + // wait for each one to complete + for (MyThread thr : threads) { + assertTrue("complete " + thr.getSleepMs(), done.tryAcquire(WAIT_SEC, TimeUnit.SECONDS)); + ttm.threadCompleted(); + } + + // check results + for (MyThread thr : threads) { + assertEquals("time " + thr.getSleepMs(), thr.texpected, thr.tactual); + } + + assertTrue(ttm.getMillis() >= tbeg + NTIMES * MIN_SLEEP_MS); + } + + private class MyThread extends Thread { + + private final long sleepMs; + + private volatile long texpected; + private volatile long tactual; + + public MyThread(long sleepMs) { + this.sleepMs = sleepMs; + + this.setDaemon(true); + } + + public long getSleepMs() { + return sleepMs; + } + + @Override + public void run() { + try { + for (int x = 0; x < NTIMES; ++x) { + texpected = ttm.getMillis() + sleepMs; + ttm.sleep(sleepMs); + + if ((tactual = ttm.getMillis()) != texpected) { + break; + } + + if ((tactual = ttm.getDate().getTime()) != texpected) { + break; + } + } + + } catch (InterruptedException expected) { + Thread.currentThread().interrupt(); + } + + done.release(); + } + } +} diff --git a/utils-test/src/test/java/org/onap/policy/common/utils/time/TestTimeTest.java b/utils-test/src/test/java/org/onap/policy/common/utils/time/TestTimeTest.java new file mode 100644 index 00000000..c1e15b38 --- /dev/null +++ b/utils-test/src/test/java/org/onap/policy/common/utils/time/TestTimeTest.java @@ -0,0 +1,66 @@ +/* + * ============LICENSE_START======================================================= + * Common Utils-Test + * ================================================================================ + * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.common.utils.time; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import org.junit.Test; + +public class TestTimeTest { + + @Test + public void test() throws Exception { + TestTime tm = new TestTime(); + TestTime tm2 = new TestTime(); + + long treal = System.currentTimeMillis(); + + long tcur = tm.getMillis(); + assertEquals(tcur, tm.getDate().getTime()); + + long tsleep = 10000L; + long tcur2 = tcur; + + // sleep a bit and then check values + tcur2 += tsleep; + tm2.sleep(tsleep); + assertEquals(tcur2, tm2.getMillis()); + assertEquals(tcur2, tm2.getDate().getTime()); + + // sleep some more and then check values + tcur2 += tsleep; + tm2.sleep(tsleep); + assertEquals(tcur2, tm2.getMillis()); + assertEquals(tcur2, tm2.getDate().getTime()); + + // check again - to ensure unchanged + assertEquals(tcur2, tm2.getMillis()); + assertEquals(tcur2, tm2.getDate().getTime()); + + // original should also be unchanged + assertEquals(tcur, tm.getMillis()); + assertEquals(tcur, tm.getDate().getTime()); + + // ensure that no real time has elapsed + assertTrue(System.currentTimeMillis() < treal + tsleep / 2); + } + +} diff --git a/utils/src/main/java/org/onap/policy/common/utils/time/CurrentTime.java b/utils/src/main/java/org/onap/policy/common/utils/time/CurrentTime.java new file mode 100644 index 00000000..cab469e5 --- /dev/null +++ b/utils/src/main/java/org/onap/policy/common/utils/time/CurrentTime.java @@ -0,0 +1,61 @@ +/* + * ============LICENSE_START======================================================= + * Common Utils + * ================================================================================ + * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.common.utils.time; + +import java.util.Date; + +/** + * Methods to access the current time. Classes can use objects of this type to get current + * time information, while allowing the objects to be overridden by junit tests. + */ +public class CurrentTime { + + /** + * + */ + public CurrentTime() { + super(); + } + + /** + * @return the current time, in milliseconds + */ + public long getMillis() { + return System.currentTimeMillis(); + } + + /** + * @return the current Date + */ + public Date getDate() { + return new Date(); + } + + /** + * Sleeps for a period of time. + * + * @param sleepMs amount of time to sleep, in milliseconds + * @throws InterruptedException + */ + public void sleep(long sleepMs) throws InterruptedException { + Thread.sleep(sleepMs); + } +} diff --git a/utils/src/test/java/org/onap/policy/common/utils/time/CurrentTimeTest.java b/utils/src/test/java/org/onap/policy/common/utils/time/CurrentTimeTest.java new file mode 100644 index 00000000..694a3d21 --- /dev/null +++ b/utils/src/test/java/org/onap/policy/common/utils/time/CurrentTimeTest.java @@ -0,0 +1,59 @@ +/* + * ============LICENSE_START======================================================= + * Common Utils + * ================================================================================ + * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.common.utils.time; + +import static org.junit.Assert.assertTrue; +import org.junit.Test; + +public class CurrentTimeTest { + + @Test + public void testGetMillis() { + long tcur = System.currentTimeMillis(); + long tval = new CurrentTime().getMillis(); + long tval2 = new CurrentTime().getMillis(); + long tend = System.currentTimeMillis(); + + assertTrue(tval >= tcur && tval <= tend); + assertTrue(tval2 >= tcur && tval2 <= tend); + } + + @Test + public void testGetDate() { + long tcur = System.currentTimeMillis(); + long tval = new CurrentTime().getDate().getTime(); + long tval2 = new CurrentTime().getDate().getTime(); + long tend = System.currentTimeMillis(); + + assertTrue(tval >= tcur && tval <= tend); + assertTrue(tval2 >= tcur && tval2 <= tend); + } + + @Test + public void testSleep() throws Exception { + long tcur = System.currentTimeMillis(); + new CurrentTime().sleep(10); + long tend = System.currentTimeMillis(); + + assertTrue(tend >= tcur + 10 - 1); + } + +} -- cgit 1.2.3-korg