Adding tests for IntentCleaner

Also, two small bug fixes for SimpleIntentStore
and one for IntentCleanup

Change-Id: I19c8246dd669d894ba258e04f4f963a97b9a7626
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/IntentCleanup.java b/core/net/src/main/java/org/onosproject/net/intent/impl/IntentCleanup.java
index 46a65ac..7d5417c 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/IntentCleanup.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/IntentCleanup.java
@@ -128,7 +128,7 @@
         log.info("Settings: period={}", period);
     }
 
-    private void adjustRate() {
+    protected void adjustRate() {
         if (timerTask != null) {
             timerTask.cancel();
         }
@@ -192,15 +192,18 @@
      */
     private void cleanup() {
         int corruptCount = 0, stuckCount = 0, pendingCount = 0;
+        store.getIntentData(true, periodMs);
         for (IntentData intentData : store.getIntentData(true, periodMs)) {
             switch (intentData.state()) {
                 case CORRUPT:
                     resubmitCorrupt(intentData, false);
                     corruptCount++;
+                    break;
                 case INSTALLING: //FALLTHROUGH
                 case WITHDRAWING:
                     resubmitPendingRequest(intentData);
                     stuckCount++;
+                    break;
                 default:
                     //NOOP
                     break;
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/IntentCleanupTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/IntentCleanupTest.java
new file mode 100644
index 0000000..4953778
--- /dev/null
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/IntentCleanupTest.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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.
+ */
+package org.onosproject.net.intent.impl;
+
+import com.google.common.collect.Sets;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.cfg.ComponentConfigAdapter;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentEvent;
+import org.onosproject.net.intent.IntentServiceAdapter;
+import org.onosproject.net.intent.IntentStore;
+import org.onosproject.net.intent.IntentStoreDelegate;
+import org.onosproject.net.intent.MockIdGenerator;
+import org.onosproject.store.Timestamp;
+import org.onosproject.store.trivial.impl.SimpleIntentStore;
+import org.onosproject.store.trivial.impl.SystemClockTimestamp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.onosproject.net.intent.IntentState.*;
+import static org.onosproject.net.intent.IntentTestsMocks.MockIntent;
+
+/**
+ * Test intent cleanup.
+ */
+public class IntentCleanupTest {
+
+    private IntentCleanup cleanup;
+    private MockIntentService service;
+    private IntentStore store;
+    protected IdGenerator idGenerator; // global or one per test? per test for now.
+
+    private static class MockIntentService extends IntentServiceAdapter {
+
+        private int submitCounter = 0;
+
+        @Override
+        public void submit(Intent intent) {
+            submitCounter++;
+        }
+
+        public int submitCounter() {
+            return submitCounter;
+        }
+    }
+
+    @Before
+    public void setUp() {
+        service = new MockIntentService();
+        store = new SimpleIntentStore();
+        cleanup = new IntentCleanup();
+        idGenerator = new MockIdGenerator();
+
+        cleanup.cfgService = new ComponentConfigAdapter();
+        cleanup.service = service;
+        cleanup.store = store;
+        cleanup.period = 10;
+        cleanup.retryThreshold = 3;
+        cleanup.activate();
+
+        assertTrue("store should be empty",
+                   Sets.newHashSet(cleanup.store.getIntents()).isEmpty());
+
+        Intent.bindIdGenerator(idGenerator);
+    }
+
+    @After
+    public void tearDown() {
+        cleanup.deactivate();
+
+        Intent.unbindIdGenerator(idGenerator);
+    }
+
+    /**
+     * Trigger resubmit of intent in CORRUPT during periodic poll.
+     */
+    @Test
+    public void corruptPoll() {
+        IntentStoreDelegate mockDelegate = new IntentStoreDelegate() {
+            @Override
+            public void process(IntentData intentData) {
+                intentData.setState(CORRUPT);
+                store.write(intentData);
+            }
+
+            @Override
+            public void notify(IntentEvent event) {}
+        };
+        store.setDelegate(mockDelegate);
+
+        Intent intent = new MockIntent(1L);
+        Timestamp version = new SystemClockTimestamp(1L);
+        IntentData data = new IntentData(intent, INSTALL_REQ, version);
+        store.addPending(data);
+
+        cleanup.run(); //FIXME broken?
+        assertEquals("Expect number of submits incorrect",
+                     1, service.submitCounter());
+    }
+
+    /**
+     * Trigger resubmit of intent in INSTALL_REQ for too long.
+     */
+    @Test
+    public void pendingPoll() {
+        IntentStoreDelegate mockDelegate = new IntentStoreDelegate() {
+            @Override
+            public void process(IntentData intentData) {}
+
+            @Override
+            public void notify(IntentEvent event) {
+                cleanup.event(event);
+            }
+        };
+        store.setDelegate(mockDelegate);
+
+        Intent intent = new MockIntent(1L);
+        Timestamp version = new SystemClockTimestamp(1L);
+        IntentData data = new IntentData(intent, INSTALL_REQ, version);
+        store.addPending(data);
+
+        cleanup.run();
+        assertEquals("Expect number of submits incorrect",
+                     1, service.submitCounter());
+
+    }
+
+    /**
+     * Trigger resubmit of intent in INSTALLING for too long.
+     */
+    @Test
+    public void installingPoll() {
+        IntentStoreDelegate mockDelegate = new IntentStoreDelegate() {
+            @Override
+            public void process(IntentData intentData) {
+                intentData.setState(INSTALLING);
+                store.write(intentData);
+            }
+
+            @Override
+            public void notify(IntentEvent event) {
+                cleanup.event(event);
+            }
+        };
+        store.setDelegate(mockDelegate);
+
+        Intent intent = new MockIntent(1L);
+        Timestamp version = new SystemClockTimestamp(1L);
+        IntentData data = new IntentData(intent, INSTALL_REQ, version);
+        store.addPending(data);
+
+        cleanup.run();
+        assertEquals("Expect number of submits incorrect",
+                     1, service.submitCounter());
+
+    }
+
+    /**
+     * Only submit one of two intents because one is too new.
+     */
+    @Test
+    public void skipPoll() {
+        IntentStoreDelegate mockDelegate = new IntentStoreDelegate() {
+            @Override
+            public void process(IntentData intentData) {
+                intentData.setState(CORRUPT);
+                store.write(intentData);
+            }
+
+            @Override
+            public void notify(IntentEvent event) {}
+        };
+        store.setDelegate(mockDelegate);
+
+        Intent intent = new MockIntent(1L);
+        IntentData data = new IntentData(intent, INSTALL_REQ, null);
+        store.addPending(data);
+
+        Intent intent2 = new MockIntent(2L);
+        Timestamp version = new SystemClockTimestamp(1L);
+        data = new IntentData(intent2, INSTALL_REQ, version);
+        store.addPending(data);
+
+        cleanup.run();
+        assertEquals("Expect number of submits incorrect",
+                     1, service.submitCounter());
+    }
+
+    /**
+     * Verify resubmit in response to CORRUPT event.
+     */
+    @Test
+    public void corruptEvent() {
+        IntentStoreDelegate mockDelegate = new IntentStoreDelegate() {
+            @Override
+            public void process(IntentData intentData) {
+                intentData.setState(CORRUPT);
+                store.write(intentData);
+            }
+
+            @Override
+            public void notify(IntentEvent event) {
+                cleanup.event(event);
+            }
+        };
+        store.setDelegate(mockDelegate);
+
+
+        Intent intent = new MockIntent(1L);
+        IntentData data = new IntentData(intent, INSTALL_REQ, null);
+
+        store.addPending(data);
+        assertEquals("Expect number of submits incorrect",
+                     1, service.submitCounter());
+    }
+
+    /**
+     * Intent should not be retried because threshold is reached.
+     */
+    @Test
+    public void corruptEventThreshold() {
+        IntentStoreDelegate mockDelegate = new IntentStoreDelegate() {
+            @Override
+            public void process(IntentData intentData) {
+                intentData.setState(CORRUPT);
+                intentData.setErrorCount(cleanup.retryThreshold);
+                store.write(intentData);
+            }
+
+            @Override
+            public void notify(IntentEvent event) {
+                cleanup.event(event);
+            }
+        };
+        store.setDelegate(mockDelegate);
+
+        Intent intent = new MockIntent(1L);
+        IntentData data = new IntentData(intent, INSTALL_REQ, null);
+
+        store.addPending(data);
+        assertEquals("Expect number of submits incorrect",
+                     0, service.submitCounter());
+    }
+}
\ No newline at end of file
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/IntentCleanupTestMock.java b/core/net/src/test/java/org/onosproject/net/intent/impl/IntentCleanupTestMock.java
new file mode 100644
index 0000000..19c754c
--- /dev/null
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/IntentCleanupTestMock.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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.
+ */
+package org.onosproject.net.intent.impl;
+
+import com.google.common.collect.Sets;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.cfg.ComponentConfigAdapter;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentEvent;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.IntentStore;
+import org.onosproject.net.intent.IntentStoreDelegate;
+import org.onosproject.net.intent.MockIdGenerator;
+import org.onosproject.store.Timestamp;
+import org.onosproject.store.trivial.impl.SimpleIntentStore;
+import org.onosproject.store.trivial.impl.SystemClockTimestamp;
+
+import static org.easymock.EasyMock.*;
+import static org.junit.Assert.assertTrue;
+import static org.onosproject.net.intent.IntentState.*;
+import static org.onosproject.net.intent.IntentTestsMocks.MockIntent;
+
+/**
+ * Test intent cleanup using Mocks.
+ * FIXME remove this or IntentCleanupTest
+ */
+public class IntentCleanupTestMock {
+
+    private IntentCleanup cleanup;
+    private IntentService service;
+    private IntentStore store;
+    protected IdGenerator idGenerator; // global or one per test? per test for now.
+
+    @Before
+    public void setUp() {
+        service = createMock(IntentService.class);
+        store = new SimpleIntentStore();
+        cleanup = new IntentCleanup();
+        idGenerator = new MockIdGenerator();
+
+        service.addListener(cleanup);
+        expectLastCall().once();
+        replay(service);
+
+        cleanup.cfgService = new ComponentConfigAdapter();
+        cleanup.service = service;
+        cleanup.store = store;
+        cleanup.period = 1000;
+        cleanup.retryThreshold = 3;
+        cleanup.activate();
+
+        verify(service);
+        reset(service);
+
+        assertTrue("store should be empty",
+                   Sets.newHashSet(cleanup.store.getIntents()).isEmpty());
+
+        Intent.bindIdGenerator(idGenerator);
+    }
+
+    @After
+    public void tearDown() {
+        service.removeListener(cleanup);
+        expectLastCall().once();
+        replay(service);
+
+        cleanup.deactivate();
+
+        verify(service);
+        reset(service);
+
+        Intent.unbindIdGenerator(idGenerator);
+    }
+
+    /**
+     * Trigger resubmit of intent in CORRUPT during periodic poll.
+     */
+    @Test
+    public void corruptPoll() {
+        IntentStoreDelegate mockDelegate = new IntentStoreDelegate() {
+            @Override
+            public void process(IntentData intentData) {
+                intentData.setState(CORRUPT);
+                store.write(intentData);
+            }
+
+            @Override
+            public void notify(IntentEvent event) {}
+        };
+        store.setDelegate(mockDelegate);
+
+        Intent intent = new MockIntent(1L);
+        Timestamp version = new SystemClockTimestamp(1L);
+        IntentData data = new IntentData(intent, INSTALL_REQ, version);
+        store.addPending(data);
+
+        service.submit(intent);
+        expectLastCall().once();
+        replay(service);
+
+        cleanup.run(); //FIXME broken?
+        verify(service);
+        reset(service);
+    }
+
+    /**
+     * Trigger resubmit of intent in INSTALL_REQ for too long.
+     */
+    @Test
+    public void pendingPoll() {
+        IntentStoreDelegate mockDelegate = new IntentStoreDelegate() {
+            @Override
+            public void process(IntentData intentData) {}
+
+            @Override
+            public void notify(IntentEvent event) {
+                cleanup.event(event);
+            }
+        };
+        store.setDelegate(mockDelegate);
+
+        Intent intent = new MockIntent(1L);
+        Timestamp version = new SystemClockTimestamp(1L);
+        IntentData data = new IntentData(intent, INSTALL_REQ, version);
+        store.addPending(data);
+
+        service.submit(intent);
+        expectLastCall().once();
+        replay(service);
+
+        cleanup.run();
+        verify(service);
+        reset(service);
+    }
+
+    /**
+     * Trigger resubmit of intent in INSTALLING for too long.
+     */
+    @Test
+    public void installingPoll() {
+        IntentStoreDelegate mockDelegate = new IntentStoreDelegate() {
+            @Override
+            public void process(IntentData intentData) {
+                intentData.setState(INSTALLING);
+                store.write(intentData);
+            }
+
+            @Override
+            public void notify(IntentEvent event) {
+                cleanup.event(event);
+            }
+        };
+        store.setDelegate(mockDelegate);
+
+        Intent intent = new MockIntent(1L);
+        Timestamp version = new SystemClockTimestamp(1L);
+        IntentData data = new IntentData(intent, INSTALL_REQ, version);
+        store.addPending(data);
+
+        service.submit(intent);
+        expectLastCall().once();
+        replay(service);
+
+        cleanup.run();
+        verify(service);
+        reset(service);
+    }
+
+    /**
+     * Only submit one of two intents because one is too new.
+     */
+    @Test
+    public void skipPoll() {
+        IntentStoreDelegate mockDelegate = new IntentStoreDelegate() {
+            @Override
+            public void process(IntentData intentData) {
+                intentData.setState(CORRUPT);
+                store.write(intentData);
+            }
+
+            @Override
+            public void notify(IntentEvent event) {}
+        };
+        store.setDelegate(mockDelegate);
+
+        Intent intent = new MockIntent(1L);
+        IntentData data = new IntentData(intent, INSTALL_REQ, null);
+        store.addPending(data);
+
+        Intent intent2 = new MockIntent(2L);
+        Timestamp version = new SystemClockTimestamp(1L);
+        data = new IntentData(intent2, INSTALL_REQ, version);
+        store.addPending(data);
+
+        service.submit(intent2);
+        expectLastCall().once();
+        replay(service);
+
+        cleanup.run();
+        verify(service);
+        reset(service);
+    }
+
+    /**
+     * Verify resubmit in response to CORRUPT event.
+     */
+    @Test
+    public void corruptEvent() {
+        IntentStoreDelegate mockDelegate = new IntentStoreDelegate() {
+            @Override
+            public void process(IntentData intentData) {
+                intentData.setState(CORRUPT);
+                store.write(intentData);
+            }
+
+            @Override
+            public void notify(IntentEvent event) {
+                cleanup.event(event);
+            }
+        };
+        store.setDelegate(mockDelegate);
+
+
+        Intent intent = new MockIntent(1L);
+        IntentData data = new IntentData(intent, INSTALL_REQ, null);
+
+        service.submit(intent);
+        expectLastCall().once();
+        replay(service);
+
+        store.addPending(data);
+
+        verify(service);
+        reset(service);
+    }
+
+    /**
+     * Intent should not be retried because threshold is reached.
+     */
+    @Test
+    public void corruptEventThreshold() {
+        IntentStoreDelegate mockDelegate = new IntentStoreDelegate() {
+            @Override
+            public void process(IntentData intentData) {
+                intentData.setState(CORRUPT);
+                intentData.setErrorCount(cleanup.retryThreshold);
+                store.write(intentData);
+            }
+
+            @Override
+            public void notify(IntentEvent event) {
+                cleanup.event(event);
+            }
+        };
+        store.setDelegate(mockDelegate);
+
+
+        Intent intent = new MockIntent(1L);
+        IntentData data = new IntentData(intent, INSTALL_REQ, null);
+
+        replay(service);
+
+        store.addPending(data);
+
+        verify(service);
+        reset(service);
+    }
+}
\ No newline at end of file
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/IntentManagerTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/IntentManagerTest.java
index bdb3e8d..2512fee 100644
--- a/core/net/src/test/java/org/onosproject/net/intent/impl/IntentManagerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/IntentManagerTest.java
@@ -592,6 +592,48 @@
     }
 
     /**
+     * Test failure to install an intent, and verify retries.
+     */
+    @Test
+    public void testCorruptRetry() {
+        IntentCleanup cleanup = new IntentCleanup();
+        cleanup.service = manager;
+        cleanup.store = manager.store;
+        cleanup.cfgService = new ComponentConfigAdapter();
+        cleanup.period = 1_000_000;
+        cleanup.retryThreshold = 3;
+
+        try {
+            cleanup.activate();
+
+            final TestIntentCompilerMultipleFlows errorCompiler = new TestIntentCompilerMultipleFlows();
+            extensionService.registerCompiler(MockIntent.class, errorCompiler);
+            List<Intent> intents;
+
+            flowRuleService.setFuture(false);
+
+            intents = Lists.newArrayList(service.getIntents());
+            assertThat(intents, hasSize(0));
+
+            final MockIntent intent1 = new MockIntent(MockIntent.nextId());
+
+            listener.setLatch(1, Type.INSTALL_REQ);
+            listener.setLatch(cleanup.retryThreshold, Type.CORRUPT);
+            listener.setLatch(1, Type.INSTALLED);
+
+            service.submit(intent1);
+
+            listener.await(Type.INSTALL_REQ);
+            listener.await(Type.CORRUPT);
+            assertEquals(CORRUPT, manager.getIntentState(intent1.key()));
+            assertThat(listener.getCounts(Type.CORRUPT), is(cleanup.retryThreshold));
+
+        } finally {
+            cleanup.deactivate();
+        }
+    }
+
+    /**
      * Tests that an intent that fails installation results in no flows remaining.
      */
     @Test