You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by bd...@apache.org on 2009/09/01 15:50:30 UTC
svn commit: r810055 - in /sling/trunk/installer/jcr/jcrinstall/src:
main/java/org/apache/sling/jcr/jcrinstall/impl/
test/java/org/apache/sling/jcr/jcrinstall/impl/
Author: bdelacretaz
Date: Tue Sep 1 13:50:30 2009
New Revision: 810055
URL: http://svn.apache.org/viewvc?rev=810055&view=rev
Log:
SLING-1078 - correctly handle deletion and moving of root folders
Added:
sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/FolderDetectionTest.java (with props)
sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/JcrInstallTestBase.java (with props)
Modified:
sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/JcrInstaller.java
sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/WatchedFolder.java
sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/ResourceDetectionTest.java
sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/ScanningLoopTest.java
Modified: sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/JcrInstaller.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/JcrInstaller.java?rev=810055&r1=810054&r2=810055&view=diff
==============================================================================
--- sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/JcrInstaller.java (original)
+++ sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/JcrInstaller.java Tue Sep 1 13:50:30 2009
@@ -20,6 +20,7 @@
import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
@@ -28,6 +29,9 @@
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
+import javax.jcr.observation.Event;
+import javax.jcr.observation.EventIterator;
+import javax.jcr.observation.EventListener;
import org.apache.sling.commons.osgi.OsgiUtil;
import org.apache.sling.jcr.api.SlingRepository;
@@ -55,7 +59,7 @@
* name="service.vendor"
* value="The Apache Software Foundation"
*/
-public class JcrInstaller implements Runnable {
+public class JcrInstaller implements Runnable, EventListener {
public static final long RUN_LOOP_DELAY_MSEC = 500L;
public static final String URL_SCHEME = "jcrinstall";
@@ -122,6 +126,12 @@
/** Used to stop background thread when deactivated */
private int deactivationCounter = 1;
+ /** The root folders that we watch */
+ private String [] roots;
+
+ /** Path of newly created root folders */
+ private Set<String> newRoots = new HashSet<String>();
+
/** Convert Nodes to InstallableResources */
static interface NodeConverter {
InstallableResource convertNode(String urlScheme, Node n) throws Exception;
@@ -141,13 +151,14 @@
log.info("activate()");
session = repository.loginAdministrative(repository.getDefaultWorkspace());
+ newRoots.clear();
// Setup converters
converters.add(new FileNodeConverter());
converters.add(new ConfigNodeConverter());
// Get search paths, and make sure each part starts and ends with a /
- String [] roots = OsgiUtil.toStringArray(context.getProperties().get(PROP_SEARCH_PATH));
+ roots = OsgiUtil.toStringArray(context.getProperties().get(PROP_SEARCH_PATH));
if (roots == null) {
roots = DEFAULT_SEARCH_PATH;
}
@@ -162,6 +173,15 @@
for(int i = 0; i < roots.length; i++) {
log.info("Configured root folder: {}", roots[i]);
}
+
+ // Watch for DELETE events on the root - that might be one of our root folders
+ int eventTypes = Event.NODE_ADDED | Event.NODE_REMOVED;
+ boolean isDeep = false;
+ boolean noLocal = true;
+ session.getWorkspace().getObservationManager().addEventListener(this, eventTypes, "/",
+ isDeep, null, null, noLocal);
+ log.info("Watching for NODE_REMOVED events on / to detect removal of our root folders");
+
// Configurable max depth, system property (via bundle context) overrides default value
Object obj = getPropertyValue(context, PROP_INSTALL_FOLDER_MAX_DEPTH);
@@ -213,7 +233,7 @@
protected void deactivate(ComponentContext context) {
log.info("deactivate()");
-
+
try {
deactivationCounter++;
folderNameFilter = null;
@@ -223,10 +243,12 @@
for(RootFolderListener wfc : listeners) {
wfc.cleanup(session);
}
+ session.getWorkspace().getObservationManager().removeEventListener(this);
session.logout();
session = null;
}
listeners.clear();
+ newRoots.clear();
} catch(Exception e) {
log.warn("Exception in deactivate()", e);
}
@@ -295,12 +317,45 @@
return path;
}
+ /** Add WatchedFolder to our list if it doesn't exist yet */
+ private void addWatchedFolder(WatchedFolder toAdd) {
+ WatchedFolder existing = null;
+ for(WatchedFolder wf : watchedFolders) {
+ if(wf.getPath().equals(toAdd.getPath())) {
+ existing = wf;
+ break;
+ }
+ }
+ if(existing == null) {
+ watchedFolders.add(toAdd);
+ toAdd.scheduleScan();
+ }
+ }
+
/** Add new folders to watch if any have been detected
* @return a list of InstallableResource that must be unregistered,
* for folders that have been removed
*/
private List<InstallableResource> updateFoldersList() throws Exception {
final List<InstallableResource> result = new LinkedList<InstallableResource>();
+
+ // If one of our root folders was just created, scan it for folders to watch
+ if(newRoots.size() > 0) {
+ final Set<String> toScan = new HashSet<String>();
+ synchronized (newRoots) {
+ toScan.addAll(newRoots);
+ newRoots.clear();
+ }
+ final List<WatchedFolder> newFolders = new ArrayList<WatchedFolder>();
+ for(String root : toScan) {
+ findPathsToWatch(root, newFolders);
+ }
+ for(WatchedFolder wf : newFolders) {
+ addWatchedFolder(wf);
+ }
+ }
+
+ // If changed occured in our watched paths, rescan
for(RootFolderListener wfc : listeners) {
final Set<String> changedPaths = wfc.getAndClearPaths();
if(changedPaths != null && changedPaths.size() > 0) {
@@ -308,22 +363,13 @@
for(String path : changedPaths) {
// Deletions are handled below
if(folderNameFilter.getPriority(path) > 0 && session.itemExists(path)) {
- WatchedFolder existing = null;
- for(WatchedFolder wf : watchedFolders) {
- if(wf.getPath().equals(path)) {
- existing = wf;
- break;
- }
- }
- if(existing == null) {
- watchedFolders.add(
- new WatchedFolder(session, path, folderNameFilter.getPriority(path), URL_SCHEME, converters));
- }
+ addWatchedFolder(new WatchedFolder(session, path, folderNameFilter.getPriority(path), URL_SCHEME, converters));
}
}
}
}
+ // Check all WatchedFolder, in case some were deleted
final List<WatchedFolder> toRemove = new ArrayList<WatchedFolder>();
for(WatchedFolder wf : watchedFolders) {
if(!session.itemExists(wf.getPath())) {
@@ -340,6 +386,31 @@
return result;
}
+ public void onEvent(EventIterator it) {
+ // Got a DELETE on root - schedule folders rescan if one
+ // of our root folders is impacted
+ try {
+ while(it.hasNext()) {
+ final Event e = it.nextEvent();
+ for(String root : roots) {
+ if(root.startsWith(e.getPath())) {
+ if(e.getType() == Event.NODE_ADDED) {
+ synchronized (newRoots) {
+ newRoots.add(e.getPath());
+ }
+ log.info("Got create event for root {}, scheduling scanning of new folders", root);
+ } else {
+ log.info("Got delete event for root {}, scheduling folders rescan", root);
+ }
+ updateFoldersListTimer.scheduleScan();
+ }
+ }
+ }
+ } catch(RepositoryException re) {
+ log.warn("RepositoryException in onEvent", re);
+ }
+ }
+
/** Run periodic scans of our watched folders, and watch for folders creations/deletions */
public void run() {
log.info("Background thread {} starting", Thread.currentThread().getName());
Modified: sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/WatchedFolder.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/WatchedFolder.java?rev=810055&r1=810054&r2=810055&view=diff
==============================================================================
--- sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/WatchedFolder.java (original)
+++ sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/WatchedFolder.java Tue Sep 1 13:50:30 2009
@@ -105,9 +105,14 @@
/** Set a static "timer" whenever an event occurs */
public void onEvent(EventIterator it) {
- rescanTimer.scheduleScan();
- needsScan = true;
- log.debug("Event received, scheduling scan of {}", path);
+ log.debug("JCR event received for path {}", path);
+ scheduleScan();
+ }
+
+ void scheduleScan() {
+ log.debug("Scheduling scan of {}", path);
+ rescanTimer.scheduleScan();
+ needsScan = true;
}
boolean needsScan() {
Added: sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/FolderDetectionTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/FolderDetectionTest.java?rev=810055&view=auto
==============================================================================
--- sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/FolderDetectionTest.java (added)
+++ sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/FolderDetectionTest.java Tue Sep 1 13:50:30 2009
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.sling.jcr.jcrinstall.impl;
+
+/** Test that changes in folders to watch are correctly detected,
+ * including when root folders are created or deleted
+ */
+public class FolderDetectionTest extends JcrInstallTestBase {
+
+ protected boolean needsTestContent() {
+ return false;
+ }
+
+ public void testCreateAndDeleteLibs() throws Exception {
+ final String res = "/libs/foo/install/somefile.jar";
+ assertRegistered("Before test", res, false);
+
+ assertFalse("/libs must not exist when test starts", session.itemExists("/libs"));
+ contentHelper.createFolder("/libs");
+ contentHelper.createFolder("/libs/foo");
+ contentHelper.createFolder("/libs/foo/install");
+ MiscUtil.waitAfterContentChanges(eventHelper, installer);
+
+ contentHelper.createOrUpdateFile(res);
+ MiscUtil.waitAfterContentChanges(eventHelper, installer);
+
+ assertRegistered("After creating libs and test file", res, true);
+
+ contentHelper.delete("/libs");
+ MiscUtil.waitAfterContentChanges(eventHelper, installer);
+ assertRegistered("After deleting libs", res, false);
+ }
+
+ public void testMoveLibsToFoo() throws Exception {
+ final String res = "/libs/foo/install/somefile.jar";
+ assertRegistered("Before test", res, false);
+
+ assertFalse("/libs must not exist when test starts", session.itemExists("/libs"));
+ contentHelper.createFolder("/libs");
+ contentHelper.createFolder("/libs/foo");
+ contentHelper.createFolder("/libs/foo/install");
+ MiscUtil.waitAfterContentChanges(eventHelper, installer);
+
+ contentHelper.createOrUpdateFile(res);
+ MiscUtil.waitAfterContentChanges(eventHelper, installer);
+
+ assertRegistered("After creating libs and test file", res, true);
+
+ session.move("/libs", "/foo");
+ MiscUtil.waitAfterContentChanges(eventHelper, installer);
+ assertRegistered("After moving /libs to /foo", res, false);
+ }
+
+ public void testMoveLibsToApps() throws Exception {
+ final String res = "/libs/foo/install/somefile.jar";
+ final String appsRes = "/apps/foo/install/somefile.jar";
+ assertRegistered("Before test", res, false);
+
+ assertFalse("/libs must not exist when test starts", session.itemExists("/libs"));
+ contentHelper.createFolder("/libs");
+ contentHelper.createFolder("/libs/foo");
+ contentHelper.createFolder("/libs/foo/install");
+ MiscUtil.waitAfterContentChanges(eventHelper, installer);
+
+ contentHelper.createOrUpdateFile(res);
+ MiscUtil.waitAfterContentChanges(eventHelper, installer);
+
+ assertRegistered("After creating libs and test file", res, true);
+
+ session.move("/libs", "/apps");
+ MiscUtil.waitAfterContentChanges(eventHelper, installer);
+ MiscUtil.waitAfterContentChanges(eventHelper, installer);
+ assertRegistered("/apps resource must be registered", appsRes, true);
+ assertRegistered("/libs resource must be gone", res, false);
+ }
+}
Propchange: sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/FolderDetectionTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/FolderDetectionTest.java
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision Rev URL
Added: sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/JcrInstallTestBase.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/JcrInstallTestBase.java?rev=810055&view=auto
==============================================================================
--- sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/JcrInstallTestBase.java (added)
+++ sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/JcrInstallTestBase.java Tue Sep 1 13:50:30 2009
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.sling.jcr.jcrinstall.impl;
+
+import javax.jcr.Session;
+
+import org.apache.sling.commons.testing.jcr.RepositoryTestBase;
+import org.apache.sling.jcr.api.SlingRepository;
+
+/** Base test class with common utilities */
+abstract class JcrInstallTestBase extends RepositoryTestBase {
+ public static final long TIMEOUT = 5000L;
+
+ SlingRepository repo;
+ Session session;
+ protected EventHelper eventHelper;
+ protected ContentHelper contentHelper;
+ protected JcrInstaller installer;
+ protected MockOsgiInstaller osgiInstaller;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ repo = getRepository();
+ session = repo.loginAdministrative(repo.getDefaultWorkspace());
+ eventHelper = new EventHelper(session);
+ contentHelper = new ContentHelper(session);
+ contentHelper.cleanupContent();
+ if(needsTestContent()) {
+ contentHelper.setupContent();
+ }
+ osgiInstaller = new MockOsgiInstaller();
+ installer = MiscUtil.getJcrInstaller(repo, osgiInstaller);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ contentHelper.cleanupContent();
+ session.logout();
+ eventHelper = null;
+ contentHelper = null;
+ installer.deactivate(MiscUtil.getMockComponentContext());
+ MiscUtil.waitForInstallerThread(installer, TIMEOUT);
+ }
+
+ protected abstract boolean needsTestContent();
+
+ protected void assertRegisteredPaths(String [] paths) {
+ for(String path : paths) {
+ assertRegistered(path, !path.contains("NOT"));
+ }
+ }
+
+ protected void assertRegistered(String path, boolean registered) {
+ assertRegistered(null, path, registered);
+ }
+
+ protected void assertRegistered(String info, String path, boolean registered) {
+ if(info == null) {
+ info = "";
+ } else {
+ info += ": ";
+ }
+
+ if(registered) {
+ assertTrue(info + "Expected " + path + " to be registered",
+ osgiInstaller.isRegistered(JcrInstaller.URL_SCHEME, path));
+ } else {
+ assertFalse(info + "Expected " + path + " to be unregistered",
+ osgiInstaller.isRegistered(JcrInstaller.URL_SCHEME, path));
+ }
+ }
+
+ protected void assertRecordedCall(String action, String path) {
+ final String callStr = action + ":" + JcrInstaller.URL_SCHEME + ":" + path;
+ boolean found = false;
+ for(String call : osgiInstaller.getRecordedCalls()) {
+ if(call.startsWith(callStr)) {
+ found = true;
+ break;
+ }
+ }
+ assertTrue("Expecting '" + callStr + "' in recorded calls (" + osgiInstaller.getRecordedCalls() + ")", found);
+ }
+}
Propchange: sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/JcrInstallTestBase.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/JcrInstallTestBase.java
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision Rev URL
Modified: sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/ResourceDetectionTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/ResourceDetectionTest.java?rev=810055&r1=810054&r2=810055&view=diff
==============================================================================
--- sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/ResourceDetectionTest.java (original)
+++ sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/ResourceDetectionTest.java Tue Sep 1 13:50:30 2009
@@ -19,82 +19,24 @@
package org.apache.sling.jcr.jcrinstall.impl;
import javax.jcr.Node;
-import javax.jcr.Session;
-import org.apache.sling.commons.testing.jcr.RepositoryTestBase;
-import org.apache.sling.jcr.api.SlingRepository;
import org.osgi.service.component.ComponentContext;
/** Test that added/updated/removed resources are
* correctly translated to OsgiInstaller registration
* calls.
*/
-public class ResourceDetectionTest extends RepositoryTestBase {
- public static final long TIMEOUT = 5000L;
- SlingRepository repo;
- Session session;
- private EventHelper eventHelper;
- private ContentHelper contentHelper;
- private JcrInstaller installer;
- private MockOsgiInstaller osgiInstaller;
+public class ResourceDetectionTest extends JcrInstallTestBase {
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- repo = getRepository();
- session = repo.loginAdministrative(repo.getDefaultWorkspace());
- eventHelper = new EventHelper(session);
- contentHelper = new ContentHelper(session);
- contentHelper.cleanupContent();
- contentHelper.setupContent();
- osgiInstaller = new MockOsgiInstaller();
- installer = MiscUtil.getJcrInstaller(repo, osgiInstaller);
- }
-
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- contentHelper.cleanupContent();
- session.logout();
- eventHelper = null;
- contentHelper = null;
- installer.deactivate(MiscUtil.getMockComponentContext());
- MiscUtil.waitForInstallerThread(installer, TIMEOUT);
- }
-
- private void assertRegisteredPaths(String [] paths) {
- for(String path : paths) {
- assertRegistered(path, !path.contains("NOT"));
- }
- }
-
- private void assertRegistered(String path, boolean registered) {
- if(registered) {
- assertTrue("Expected " + path + " to be registered",
- osgiInstaller.isRegistered(JcrInstaller.URL_SCHEME, path));
- } else {
- assertFalse("Expected " + path + " to be unregistered",
- osgiInstaller.isRegistered(JcrInstaller.URL_SCHEME, path));
- }
- }
-
- private void assertRecordedCall(String action, String path) {
- final String callStr = action + ":" + JcrInstaller.URL_SCHEME + ":" + path;
- boolean found = false;
- for(String call : osgiInstaller.getRecordedCalls()) {
- if(call.startsWith(callStr)) {
- found = true;
- break;
- }
- }
- assertTrue("Expecting '" + callStr + "' in recorded calls (" + osgiInstaller.getRecordedCalls() + ")", found);
- }
-
public void testInitialResourceDetection() throws Exception {
assertRegisteredPaths(contentHelper.FAKE_RESOURCES);
assertRegisteredPaths(contentHelper.FAKE_CONFIGS);
}
-
+
+ protected boolean needsTestContent() {
+ return true;
+ }
+
public void testAddFiles() throws Exception {
final String [] paths = {
"/libs/foo/bar/install/" + System.currentTimeMillis() + ".jar",
Modified: sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/ScanningLoopTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/ScanningLoopTest.java?rev=810055&r1=810054&r2=810055&view=diff
==============================================================================
--- sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/ScanningLoopTest.java (original)
+++ sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/ScanningLoopTest.java Tue Sep 1 13:50:30 2009
@@ -79,7 +79,7 @@
@Test
public void testIdleState() throws Exception {
- Thread.sleep(JcrInstaller.RUN_LOOP_DELAY_MSEC * 2);
+ Thread.sleep(JcrInstaller.RUN_LOOP_DELAY_MSEC * 4);
assertIdle();
}