| package com.google.enterprise.adaptor; |
| |
| import static org.junit.Assert.*; |
| |
| import org.junit.*; |
| import org.junit.rules.ExpectedException; |
| |
| import java.util.TimeZone; |
| import java.util.concurrent.*; |
| import java.util.concurrent.atomic.*; |
| |
| /** Test cases for {@link CronScheduler}. */ |
| public class CronSchedulerTest { |
| private ScheduledExecutorService executor |
| = Executors.newSingleThreadScheduledExecutor(); |
| private Runnable noopRunnable = new Runnable() { |
| @Override |
| public void run() {} |
| }; |
| |
| @Rule |
| public ExpectedException thrown = ExpectedException.none(); |
| |
| @After |
| public void tearDown() { |
| executor.shutdownNow(); |
| } |
| |
| @Test |
| public void testNullExecutor() { |
| thrown.expect(NullPointerException.class); |
| new CronScheduler(null); |
| } |
| |
| @Test |
| public void testWrongFutureReschedule() { |
| CronScheduler scheduler = new CronScheduler(executor); |
| Future<?> future = new FutureTask<Object>(noopRunnable, null); |
| thrown.expect(IllegalArgumentException.class); |
| scheduler.reschedule(future, "* * * * *"); |
| } |
| |
| @Test |
| public void testParsing() { |
| CronScheduler scheduler = new CronScheduler(executor); |
| assertEquals("CronFuture([{0}, {0}, {0}, {0}, {0}])", |
| scheduler.schedule("0 0 1 1 0", noopRunnable).toString()); |
| assertEquals("CronFuture([{59}, {23}, {30}, {11}, {6}])", |
| scheduler.schedule("59 23 31 12 6", noopRunnable).toString()); |
| assertEquals("CronFuture([{0}, {0}, {0}, {0}, {0, 1, 3, 5}])", |
| scheduler.schedule("0 0 1 1 1-7/2", noopRunnable).toString()); |
| assertEquals("CronFuture([{0}, {0}, {1, 3, 5, 7, 9, 11}, {0}, {0}])", |
| scheduler.schedule("0 0 2-13/2 1 0", noopRunnable).toString()); |
| assertEquals("CronFuture([{0}, {0}, {0}, {0}, {0, 1, 2, 3, 4, 5, 6}])", |
| scheduler.schedule("0 0 1 1 *", noopRunnable).toString()); |
| } |
| |
| @Test |
| public void testWrongNumberOfFields() { |
| CronScheduler scheduler = new CronScheduler(executor); |
| thrown.expect(IllegalArgumentException.class); |
| scheduler.schedule("0 0 1 1 0 0", noopRunnable); |
| } |
| |
| @Test |
| public void testValueNotInRange() { |
| CronScheduler scheduler = new CronScheduler(executor); |
| thrown.expect(IllegalArgumentException.class); |
| scheduler.schedule("0 0 1 0 0", noopRunnable); |
| } |
| |
| @Test |
| public void testValueNotInRange2() { |
| CronScheduler scheduler = new CronScheduler(executor); |
| thrown.expect(IllegalArgumentException.class); |
| scheduler.schedule("0 0 1 1000 0", noopRunnable); |
| } |
| |
| @Test |
| public void testNotANumber() { |
| CronScheduler scheduler = new CronScheduler(executor); |
| thrown.expect(IllegalArgumentException.class); |
| scheduler.schedule("0 0 1 wat 0", noopRunnable); |
| } |
| |
| @Test |
| public void testCatchesException() { |
| executor.shutdownNow(); |
| final AtomicReference<Runnable> atomicCommand |
| = new AtomicReference<Runnable>(); |
| executor = new ScheduledThreadPoolExecutor(1) { |
| @Override |
| public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, |
| long initialDelay, long period, TimeUnit unit) { |
| atomicCommand.set(command); |
| // Set high delay, because we don't want it to actually run. |
| return super.scheduleAtFixedRate(command, 100000, period, unit); |
| } |
| }; |
| MockTimeProvider timeProvider = new MockTimeProvider(); |
| timeProvider.autoIncrement = false; |
| TimeZone timeZone = TimeZone.getTimeZone("America/Los_Angeles"); |
| CronScheduler scheduler |
| = new CronScheduler(executor, timeProvider, timeZone); |
| Runnable runnable = new Runnable() { |
| @Override |
| public void run() { |
| throw new RuntimeException("This should be caught"); |
| } |
| }; |
| // Capture internally-used runnable. |
| Future<?> future = scheduler.schedule("* * * * *", runnable); |
| // Should not throw an exception. |
| atomicCommand.get().run(); |
| } |
| |
| @Test |
| public void testRunning() throws Exception { |
| executor.shutdownNow(); |
| final AtomicReference<Runnable> atomicCommand |
| = new AtomicReference<Runnable>(); |
| executor = new ScheduledThreadPoolExecutor(1) { |
| @Override |
| public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, |
| long initialDelay, long period, TimeUnit unit) { |
| atomicCommand.set(command); |
| assertEquals((60 * 1000) - (10 * 1000) + 1000, initialDelay); |
| assertEquals(60 * 1000, period); |
| assertEquals(TimeUnit.MILLISECONDS, unit); |
| // Set high delay, because we don't want it to actually run. |
| return super.scheduleAtFixedRate(command, 100000, period, unit); |
| } |
| }; |
| MockTimeProvider timeProvider = new MockTimeProvider(); |
| timeProvider.autoIncrement = false; |
| TimeZone timeZone = TimeZone.getTimeZone("America/Los_Angeles"); |
| CronScheduler scheduler |
| = new CronScheduler(executor, timeProvider, timeZone); |
| final AtomicLong counter = new AtomicLong(); |
| Runnable runnable = new Runnable() { |
| @Override |
| public void run() { |
| counter.incrementAndGet(); |
| } |
| }; |
| // 1970-01-01T00:00:10Z |
| timeProvider.time = 10 * 1000; |
| // Capture internally-used runnable. |
| Future<?> future = scheduler.schedule("2 2 2 2 1", runnable); |
| counter.set(0); |
| |
| // 1970-02-02T10:02:01Z, which is a Monday. That is 02:02:01 in PST. |
| timeProvider.time = 2800921L * 1000; |
| atomicCommand.get().run(); |
| Thread.sleep(10); |
| assertEquals(1, counter.get()); |
| |
| // Wrong minute. |
| // 1970-02-02T10:01:01Z, which is a Monday. That is 02:01:01 in PST. |
| timeProvider.time = 2800861L * 1000; |
| atomicCommand.get().run(); |
| Thread.sleep(10); |
| assertEquals(1, counter.get()); |
| |
| // Wrong hour. |
| // 1970-02-02T09:02:01Z, which is a Monday. That is 01:02:01 in PST. |
| timeProvider.time = 2797321L * 1000; |
| atomicCommand.get().run(); |
| Thread.sleep(10); |
| assertEquals(1, counter.get()); |
| |
| // Wrong day of month. |
| // 1970-02-02T10:02:01Z, which is a Monday. That is 02:02:01 in PST. |
| timeProvider.time = 2800921L * 1000; |
| scheduler.reschedule(future, "2 2 1 2 2"); |
| atomicCommand.get().run(); |
| Thread.sleep(10); |
| assertEquals(1, counter.get()); |
| |
| // Wrong month. |
| // 1970-02-02T10:02:01Z, which is a Monday. That is 02:02:01 in PST. |
| timeProvider.time = 2800921L * 1000; |
| scheduler.reschedule(future, "2 2 2 1 2"); |
| atomicCommand.get().run(); |
| Thread.sleep(10); |
| assertEquals(1, counter.get()); |
| |
| // Wrong day of week. |
| // 1970-02-02T10:02:01Z, which is a Monday. That is 02:02:01 in PST. |
| timeProvider.time = 2800921L * 1000; |
| scheduler.reschedule(future, "2 2 2 2 2"); |
| atomicCommand.get().run(); |
| Thread.sleep(10); |
| assertEquals(1, counter.get()); |
| } |
| |
| @Test |
| public void testRunsTwice() throws Exception { |
| executor.shutdownNow(); |
| executor = new ScheduledThreadPoolExecutor(1) { |
| @Override |
| public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, |
| long initialDelay, long period, TimeUnit unit) { |
| assertEquals((60 * 1000) - (10 * 1000) + 1000, initialDelay); |
| assertEquals(60 * 1000, period); |
| assertEquals(TimeUnit.MILLISECONDS, unit); |
| // Set low delay, because we want it to run soon. |
| return super.scheduleAtFixedRate(command, 1, 10, unit); |
| } |
| }; |
| final MockTimeProvider timeProvider = new MockTimeProvider(); |
| timeProvider.autoIncrement = false; |
| TimeZone timeZone = TimeZone.getTimeZone("America/Los_Angeles"); |
| CronScheduler scheduler |
| = new CronScheduler(executor, timeProvider, timeZone); |
| final LinkedBlockingQueue<Object> queue = new LinkedBlockingQueue<Object>(); |
| Runnable runnable = new Runnable() { |
| @Override |
| public void run() { |
| assertTrue(queue.offer(new Object())); |
| timeProvider.time += 60 * 1000; |
| } |
| }; |
| // 1970-01-01T00:00:10Z |
| timeProvider.time = 10 * 1000; |
| // Capture internally-used runnable. |
| Future<?> future = scheduler.schedule("* * * * *", runnable); |
| assertNotNull(queue.poll(100, TimeUnit.MILLISECONDS)); |
| assertNotNull(queue.poll(100, TimeUnit.MILLISECONDS)); |
| future.cancel(false); |
| } |
| } |