<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>pkg/passenger-2.2.2/test/stub/rails_apps/foobar/log/.gitignore</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -26,11 +26,9 @@ test/CxxTests
 test/ApplicationPoolServerExecutable
 test/oxt/oxt_test_main
 test/stub/railsapp/app/controllers/bar_controller.rb
-test/stub/*/log/*
 test/stub/apache2/*.log
 test/stub/apache2/*.pid
 test/stub/apache2/httpd.conf
-test/stub/mycook/log
 test/stub/zsfa/mycook
 test/stub/zsfa/foo
 pkg</diff>
      <filename>.gitignore</filename>
    </modified>
    <modified>
      <diff>@@ -701,19 +701,10 @@ spec = Gem::Specification.new do |s|
 		'test/*.{rb,cpp,example}',
 		'test/support/*.{cpp,h,rb}',
 		'test/oxt/*.cpp',
-		'test/ruby/*',
-		'test/ruby/*/*',
-		'test/integration_tests/*',
-		'test/stub/*',
-		'test/stub/*/*',
-		'test/stub/*/*/*',
-		'test/stub/*/*/*/*',
-		'test/stub/*/*/*/*/*',
-		'test/stub/*/*/*/*/*/*',
-		'test/stub/*/*/*/*/*/*/*'
-	] - Dir['test/stub/*/log/*'] \
-	  - Dir['test/stub/*/tmp/*/*'] \
-	  - Dir['test/stub/apache2/*.{pid,lock,log}']
+		'test/ruby/**/*',
+		'test/integration_tests/**/*',
+		'test/stub/**/*'
+	]
 	s.executables = [
 		'passenger-spawn-server',
 		'passenger-install-apache2-module',</diff>
      <filename>Rakefile</filename>
    </modified>
    <modified>
      <diff>@@ -12,7 +12,8 @@
  * This file is used as a template to test the different ApplicationPool implementations.
  * It is #included in StandardApplicationPoolTest.cpp and ApplicationServer_ApplicationPoolTest.cpp
  */
-#ifdef USE_TEMPLATE	
+#ifdef USE_TEMPLATE
+	/** Create some stub request headers. */
 	static string createRequestHeaders(const char *uri = &quot;/foo/new&quot;) {
 		string headers;
 		#define ADD_HEADER(name, value) \
@@ -25,29 +26,11 @@
 		ADD_HEADER(&quot;REQUEST_URI&quot;, uri);
 		ADD_HEADER(&quot;REQUEST_METHOD&quot;, &quot;GET&quot;);
 		ADD_HEADER(&quot;REMOTE_ADDR&quot;, &quot;localhost&quot;);
+		ADD_HEADER(&quot;SCRIPT_NAME&quot;, &quot;&quot;);
 		ADD_HEADER(&quot;PATH_INFO&quot;, uri);
 		return headers;
 	}
 	
-	static string readAll(int fd) {
-		string result;
-		char buf[1024 * 32];
-		ssize_t ret;
-		while (true) {
-			do {
-				ret = read(fd, buf, sizeof(buf));
-			} while (ret == -1 &amp;&amp; errno == EINTR);
-			if (ret == 0) {
-				break;
-			} else if (ret == -1) {
-				throw SystemException(&quot;Cannot read from socket&quot;, errno);
-			} else {
-				result.append(buf, ret);
-			}
-		}
-		return result;
-	}
-	
 	static Application::SessionPtr spawnRackApp(ApplicationPoolPtr pool, const char *appRoot) {
 		PoolOptions options;
 		options.appRoot = appRoot;
@@ -61,17 +44,17 @@
 		options.appType = &quot;wsgi&quot;;
 		return pool-&gt;get(options);
 	}
-
+	
 	TEST_METHOD(1) {
 		// Calling ApplicationPool.get() once should return a valid Session.
-		Application::SessionPtr session(pool-&gt;get(&quot;stub/railsapp&quot;));
+		Application::SessionPtr session(spawnRackApp(pool, &quot;stub/rack&quot;));
 		session-&gt;sendHeaders(createRequestHeaders());
 		session-&gt;shutdownWriter();
 
 		int reader = session-&gt;getStream();
 		string result(readAll(reader));
 		session-&gt;closeStream();
-		ensure(result.find(&quot;hello world&quot;) != string::npos);
+		ensure(result.find(&quot;hello &lt;b&gt;world&lt;/b&gt;&quot;) != string::npos);
 	}
 	
 	TEST_METHOD(2) {
@@ -99,26 +82,29 @@
 		// If we call get() with an application root, then we call get() again before closing
 		// the session, then the pool should have spawned 2 apps in total.
 		Application::SessionPtr session(spawnRackApp(pool, &quot;stub/rack&quot;));
-		Application::SessionPtr session2(spawnRackApp(pool, &quot;stub/rack&quot;));
+		Application::SessionPtr session2(spawnRackApp(pool2, &quot;stub/rack&quot;));
 		ensure_equals(pool-&gt;getCount(), 2u);
 	}
 	
 	TEST_METHOD(5) {
 		// If we call get() twice with different application roots,
 		// then the pool should spawn two different apps.
-		Application::SessionPtr session(pool-&gt;get(&quot;stub/railsapp&quot;));
-		Application::SessionPtr session2(pool2-&gt;get(&quot;stub/railsapp2&quot;));
+		TempDirCopy c1(&quot;stub/rack&quot;, &quot;rackapp1.tmp&quot;);
+		TempDirCopy c2(&quot;stub/rack&quot;, &quot;rackapp2.tmp&quot;);
+		replaceStringInFile(&quot;rackapp2.tmp/config.ru&quot;, &quot;world&quot;, &quot;world 2&quot;);
+		Application::SessionPtr session(spawnRackApp(pool, &quot;rackapp1.tmp&quot;));
+		Application::SessionPtr session2(spawnRackApp(pool2, &quot;rackapp2.tmp&quot;));
 		ensure_equals(&quot;Before the sessions were closed, both apps were busy&quot;, pool-&gt;getActive(), 2u);
 		ensure_equals(&quot;Before the sessions were closed, both apps were in the pool&quot;, pool-&gt;getCount(), 2u);
 		
 		session-&gt;sendHeaders(createRequestHeaders());
 		string result(readAll(session-&gt;getStream()));
-		ensure(&quot;Session 1 belongs to the correct app&quot;, result.find(&quot;hello world&quot;) != string::npos);
+		ensure(&quot;Session 1 belongs to the correct app&quot;, result.find(&quot;hello &lt;b&gt;world&lt;/b&gt;&quot;) != string::npos);
 		session.reset();
 		
 		session2-&gt;sendHeaders(createRequestHeaders());
 		result = readAll(session2-&gt;getStream());
-		ensure(&quot;Session 2 belongs to the correct app&quot;, result.find(&quot;this is railsapp2&quot;) != string::npos);
+		ensure(&quot;Session 2 belongs to the correct app&quot;, result.find(&quot;hello &lt;b&gt;world 2&lt;/b&gt;&quot;) != string::npos);
 		session2.reset();
 	}
 	
@@ -126,12 +112,14 @@
 		// If we call get() twice with different application roots,
 		// and we close both sessions, then both 2 apps should still
 		// be in the pool.
-		Application::SessionPtr session(pool-&gt;get(&quot;stub/railsapp&quot;));
-		Application::SessionPtr session2(pool-&gt;get(&quot;stub/railsapp2&quot;));
+		TempDirCopy c1(&quot;stub/rack&quot;, &quot;rackapp1.tmp&quot;);
+		TempDirCopy c2(&quot;stub/rack&quot;, &quot;rackapp2.tmp&quot;);
+		Application::SessionPtr session(spawnRackApp(pool, &quot;rackapp1.tmp&quot;));
+		Application::SessionPtr session2(spawnRackApp(pool, &quot;rackapp2.tmp&quot;));
 		session.reset();
 		session2.reset();
-		ensure_equals(pool-&gt;getActive(), 0u);
-		ensure_equals(pool-&gt;getCount(), 2u);
+		ensure_equals(&quot;There are 0 active apps&quot;, pool-&gt;getActive(), 0u);
+		ensure_equals(&quot;There are 2 apps in total&quot;, pool-&gt;getCount(), 2u);
 	}
 	
 	TEST_METHOD(7) {
@@ -150,26 +138,26 @@
 		// But the get() thereafter should not:
 		// ApplicationPool should have spawned a new instance
 		// after detecting that the original one died.
-		Application::SessionPtr session(pool-&gt;get(&quot;stub/railsapp&quot;));
+		Application::SessionPtr session(spawnRackApp(pool, &quot;stub/rack&quot;));
 		kill(session-&gt;getPid(), SIGTERM);
 		session.reset();
 		try {
-			session = pool-&gt;get(&quot;stub/railsapp&quot;);
+			session = spawnRackApp(pool, &quot;stub/rack&quot;);
 			fail(&quot;ApplicationPool::get() is supposed to &quot;
 				&quot;throw an exception because we killed &quot;
 				&quot;the app instance.&quot;);
 		} catch (const exception &amp;e) {
-			session = pool-&gt;get(&quot;stub/railsapp&quot;);
+			session = spawnRackApp(pool, &quot;stub/rack&quot;);
 			// Should not throw.
 		}
 	}
 	
-	struct TestThread1 {
+	struct PoolWaitTestThread {
 		ApplicationPoolPtr pool;
 		Application::SessionPtr &amp;m_session;
 		bool &amp;m_done;
 		
-		TestThread1(const ApplicationPoolPtr &amp;pool,
+		PoolWaitTestThread(const ApplicationPoolPtr &amp;pool,
 			Application::SessionPtr &amp;session,
 			bool &amp;done)
 		: m_session(session), m_done(done) {
@@ -194,12 +182,13 @@
 		Application::SessionPtr session3;
 		bool done;
 		
-		thread *thr = new thread(TestThread1(pool2, session3, done));
+		shared_ptr&lt;thread&gt; thr = ptr(new thread(PoolWaitTestThread(pool2, session3, done)));
 		usleep(500000);
-		ensure(&quot;ApplicationPool is waiting&quot;, !done);
+		ensure(&quot;ApplicationPool is still waiting&quot;, !done);
 		ensure_equals(pool-&gt;getActive(), 2u);
 		ensure_equals(pool-&gt;getCount(), 2u);
 		
+		// Now release one slot from the pool.
 		session1.reset();
 		
 		// Wait at most 10 seconds.
@@ -213,7 +202,7 @@
 		ensure_equals(pool-&gt;getCount(), 2u);
 		
 		thr-&gt;join();
-		delete thr;
+		thr.reset();
 	}
 	
 	TEST_METHOD(10) {
@@ -221,25 +210,27 @@
 		// * the pool is already full, but there are inactive apps
 		//   (active &lt; count &amp;&amp; count == max)
 		// and
-		// * the application root is *not* already in the pool
+		// * the application root for this get() is *not* already in the pool
 		// then the an inactive app should be killed in order to
 		// satisfy this get() command.
+		TempDirCopy c1(&quot;stub/rack&quot;, &quot;rackapp1.tmp&quot;);
+		TempDirCopy c2(&quot;stub/rack&quot;, &quot;rackapp2.tmp&quot;);
 		pool-&gt;setMax(2);
-		Application::SessionPtr session1(pool-&gt;get(&quot;stub/railsapp&quot;));
-		Application::SessionPtr session2(pool-&gt;get(&quot;stub/railsapp&quot;));
+		Application::SessionPtr session1(spawnRackApp(pool, &quot;rackapp1.tmp&quot;));
+		Application::SessionPtr session2(spawnRackApp(pool, &quot;rackapp1.tmp&quot;));
 		session1.reset();
 		session2.reset();
 		
 		ensure_equals(pool-&gt;getActive(), 0u);
 		ensure_equals(pool-&gt;getCount(), 2u);
-		session1 = pool2-&gt;get(&quot;stub/railsapp2&quot;);
+		session1 = spawnRackApp(pool, &quot;rackapp2.tmp&quot;);
 		ensure_equals(pool-&gt;getActive(), 1u);
 		ensure_equals(pool-&gt;getCount(), 2u);
 	}
 	
 	TEST_METHOD(11) {
-		// Test whether Session is still usable after the Application has been destroyed.
-		Application::SessionPtr session(pool-&gt;get(&quot;stub/railsapp&quot;));
+		// A Session should still be usable after the pool has been destroyed.
+		Application::SessionPtr session(spawnRackApp(pool, &quot;stub/rack&quot;));
 		pool-&gt;clear();
 		pool.reset();
 		pool2.reset();
@@ -250,27 +241,27 @@
 		int reader = session-&gt;getStream();
 		string result(readAll(reader));
 		session-&gt;closeStream();
-		ensure(result.find(&quot;hello world&quot;) != string::npos);
+		ensure(result.find(&quot;hello &lt;b&gt;world&lt;/b&gt;&quot;) != string::npos);
 	}
 	
 	TEST_METHOD(12) {
 		// If tmp/restart.txt didn't exist but has now been created,
 		// then the applications under app_root should be restarted.
 		struct stat buf;
-		Application::SessionPtr session1 = pool-&gt;get(&quot;stub/railsapp&quot;);
-		Application::SessionPtr session2 = pool2-&gt;get(&quot;stub/railsapp&quot;);
+		TempDirCopy c(&quot;stub/rack&quot;, &quot;rackapp.tmp&quot;);
+		Application::SessionPtr session1 = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		Application::SessionPtr session2 = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
 		session1.reset();
 		session2.reset();
 		
-		DeleteFileEventually f(&quot;stub/railsapp/tmp/restart.txt&quot;);
-		touchFile(&quot;stub/railsapp/tmp/restart.txt&quot;);
-		pool-&gt;get(&quot;stub/railsapp&quot;);
+		touchFile(&quot;rackapp.tmp/tmp/restart.txt&quot;);
+		spawnRackApp(pool, &quot;rackapp.tmp&quot;);
 		
 		ensure_equals(&quot;No apps are active&quot;, pool-&gt;getActive(), 0u);
 		ensure_equals(&quot;Both apps are killed, and a new one was spawned&quot;,
 			pool-&gt;getCount(), 1u);
 		ensure(&quot;Restart file still exists&quot;,
-			stat(&quot;stub/railsapp/tmp/restart.txt&quot;, &amp;buf) == 0);
+			stat(&quot;rackapp.tmp/tmp/restart.txt&quot;, &amp;buf) == 0);
 	}
 	
 	TEST_METHOD(13) {
@@ -279,51 +270,213 @@
 		// should still be restarted. However, a subsequent get()
 		// should not result in a restart.
 		pid_t old_pid;
-		
-		TempDir d(&quot;stub/railsapp/tmp/restart.txt&quot;);
-		Application::SessionPtr session = pool-&gt;get(&quot;stub/railsapp&quot;);
+		TempDirCopy c(&quot;stub/rack&quot;, &quot;rackapp.tmp&quot;);
+		TempDir d(&quot;rackapp.tmp/tmp/restart.txt&quot;);
+		Application::SessionPtr session = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
 		old_pid = session-&gt;getPid();
 		session.reset();
 		
-		struct utimbuf buf;
-		buf.actime = time(NULL) - 10;
-		buf.modtime = time(NULL) - 10;
-		utime(&quot;stub/railsapp/tmp/restart.txt&quot;, &amp;buf);
+		touchFile(&quot;rackapp.tmp/tmp/restart.txt&quot;, 10);
 		
-		session = pool-&gt;get(&quot;stub/railsapp&quot;);
+		session = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
 		ensure(&quot;The app was restarted&quot;, session-&gt;getPid() != old_pid);
 		old_pid = session-&gt;getPid();
 		session.reset();
 		
-		session = pool-&gt;get(&quot;stub/railsapp&quot;);
+		session = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
 		ensure_equals(&quot;The app was not restarted&quot;,
 			old_pid, session-&gt;getPid());
 	}
 	
 	TEST_METHOD(15) {
-		// Test whether restarting really results in code reload.
-		DeleteFileEventually f1(&quot;stub/railsapp/app/controllers/bar_controller.rb&quot;);
-		DeleteFileEventually f2(&quot;stub/railsapp/tmp/restart.txt&quot;);
-		
-		system(&quot;cp -f stub/railsapp/app/controllers/bar_controller_1.txt &quot;
-			&quot;stub/railsapp/app/controllers/bar_controller.rb&quot;);
-		Application::SessionPtr session = pool-&gt;get(&quot;stub/railsapp&quot;);
-		session-&gt;sendHeaders(createRequestHeaders(&quot;/bar&quot;));
+		// Test whether restarting with restart.txt really results in code reload.
+		TempDirCopy c(&quot;stub/rack&quot;, &quot;rackapp.tmp&quot;);
+		Application::SessionPtr session = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		session-&gt;sendHeaders(createRequestHeaders());
 		string result = readAll(session-&gt;getStream());
-		ensure(result.find(&quot;bar 1!&quot;) != string::npos);
+		ensure(result.find(&quot;hello &lt;b&gt;world&lt;/b&gt;&quot;) != string::npos);
 		session.reset();
 		
-		system(&quot;cp -f stub/railsapp/app/controllers/bar_controller_2.txt &quot;
-			&quot;stub/railsapp/app/controllers/bar_controller.rb&quot;);
-		system(&quot;touch stub/railsapp/tmp/restart.txt&quot;);
+		touchFile(&quot;rackapp.tmp/tmp/restart.txt&quot;);
+		replaceStringInFile(&quot;rackapp.tmp/config.ru&quot;, &quot;world&quot;, &quot;world 2&quot;);
 		
-		session = pool-&gt;get(&quot;stub/railsapp&quot;);
-		session-&gt;sendHeaders(createRequestHeaders(&quot;/bar&quot;));
+		session = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		session-&gt;sendHeaders(createRequestHeaders());
 		result = readAll(session-&gt;getStream());
-		ensure(&quot;App code has been reloaded&quot;, result.find(&quot;bar 2!&quot;) != string::npos);
+		ensure(&quot;App code has been reloaded&quot;, result.find(&quot;hello &lt;b&gt;world 2&lt;/b&gt;&quot;) != string::npos);
 	}
 	
 	TEST_METHOD(16) {
+		// If tmp/always_restart.txt is present and is a file,
+		// then the application under app_root should be always restarted.
+		struct stat buf;
+		pid_t old_pid;
+		TempDirCopy c(&quot;stub/rack&quot;, &quot;rackapp.tmp&quot;);
+		Application::SessionPtr session1 = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		Application::SessionPtr session2 = spawnRackApp(pool2, &quot;rackapp.tmp&quot;);
+		session1.reset();
+		session2.reset();
+		
+		touchFile(&quot;rackapp.tmp/tmp/always_restart.txt&quot;);
+		
+		// This get() results in a restart.
+		session1 = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		old_pid = session1-&gt;getPid();
+		session1.reset();
+		ensure_equals(&quot;First restart: no apps are active&quot;, pool-&gt;getActive(), 0u);
+		ensure_equals(&quot;First restart: the first 2 apps were killed, and a new one was spawned&quot;,
+			pool-&gt;getCount(), 1u);
+		ensure(&quot;always_restart file has not been deleted&quot;,
+			stat(&quot;rackapp.tmp/tmp/always_restart.txt&quot;, &amp;buf) == 0);
+		
+		// This get() results in a restart as well.
+		session1 = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		ensure(old_pid != session1-&gt;getPid());
+		session1.reset();
+		ensure_equals(&quot;Second restart: no apps are active&quot;, pool-&gt;getActive(), 0u);
+		ensure_equals(&quot;Second restart: the last app was killed, and a new one was spawned&quot;,
+			pool-&gt;getCount(), 1u);
+		ensure(&quot;always_restart file has not been deleted&quot;,
+			stat(&quot;rackapp.tmp/tmp/always_restart.txt&quot;, &amp;buf) == 0);
+	}
+	
+	TEST_METHOD(17) {
+		// If tmp/always_restart.txt is present and is a directory,
+		// then the application under app_root should be always restarted.
+		struct stat buf;
+		pid_t old_pid;
+		TempDirCopy c(&quot;stub/rack&quot;, &quot;rackapp.tmp&quot;);
+		Application::SessionPtr session1 = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		Application::SessionPtr session2 = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		session1.reset();
+		session2.reset();
+		
+		TempDir d(&quot;rackapp.tmp/tmp/always_restart.txt&quot;);
+		
+		// This get() results in a restart.
+		session1 = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		old_pid = session1-&gt;getPid();
+		session1.reset();
+		ensure_equals(&quot;First restart: no apps are active&quot;, pool-&gt;getActive(), 0u);
+		ensure_equals(&quot;First restart: the first 2 apps were killed, and a new one was spawned&quot;,
+			pool-&gt;getCount(), 1u);
+		ensure(&quot;always_restart directory has not been deleted&quot;,
+			stat(&quot;rackapp.tmp/tmp/always_restart.txt&quot;, &amp;buf) == 0);
+		
+		// This get() results in a restart as well.
+		session1 = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		ensure(old_pid != session1-&gt;getPid());
+		session1.reset();
+		ensure_equals(&quot;Second restart: no apps are active&quot;, pool-&gt;getActive(), 0u);
+		ensure_equals(&quot;Second restart: the last app was killed, and a new one was spawned&quot;,
+			pool-&gt;getCount(), 1u);
+		ensure(&quot;always_restart directory has not been deleted&quot;,
+			stat(&quot;rackapp.tmp/tmp/always_restart.txt&quot;, &amp;buf) == 0);
+	}
+	
+	TEST_METHOD(18) {
+		// Test whether restarting with tmp/always_restart.txt really results in code reload.
+		TempDirCopy c(&quot;stub/rack&quot;, &quot;rackapp.tmp&quot;);
+		Application::SessionPtr session = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		session-&gt;sendHeaders(createRequestHeaders());
+		string result = readAll(session-&gt;getStream());
+		ensure(result.find(&quot;hello &lt;b&gt;world&lt;/b&gt;&quot;) != string::npos);
+		session.reset();
+
+		touchFile(&quot;rackapp.tmp/tmp/always_restart.txt&quot;);
+		replaceStringInFile(&quot;rackapp.tmp/config.ru&quot;, &quot;world&quot;, &quot;world 2&quot;);
+		
+		session = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		session-&gt;sendHeaders(createRequestHeaders());
+		result = readAll(session-&gt;getStream());
+		ensure(&quot;App code has been reloaded (1)&quot;, result.find(&quot;hello &lt;b&gt;world 2&lt;/b&gt;&quot;) != string::npos);
+		session.reset();
+		
+		replaceStringInFile(&quot;rackapp.tmp/config.ru&quot;, &quot;world 2&quot;, &quot;world 3&quot;);
+		session = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		session-&gt;sendHeaders(createRequestHeaders());
+		result = readAll(session-&gt;getStream());
+		ensure(&quot;App code has been reloaded (2)&quot;, result.find(&quot;hello &lt;b&gt;world 3&lt;/b&gt;&quot;) != string::npos);
+		session.reset();
+	}
+	
+	TEST_METHOD(19) {
+		// If tmp/restart.txt and tmp/always_restart.txt are present, 
+		// the application under app_root should still be restarted and
+		// both files must be kept.
+		pid_t old_pid, pid;
+		struct stat buf;
+		TempDirCopy c(&quot;stub/rack&quot;, &quot;rackapp.tmp&quot;);
+		Application::SessionPtr session1 = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		Application::SessionPtr session2 = spawnRackApp(pool2, &quot;rackapp.tmp&quot;);
+		session1.reset();
+		session2.reset();
+		
+		touchFile(&quot;rackapp.tmp/tmp/restart.txt&quot;);
+		touchFile(&quot;rackapp.tmp/tmp/always_restart.txt&quot;);
+		
+		old_pid = spawnRackApp(pool, &quot;rackapp.tmp&quot;)-&gt;getPid();
+		ensure(&quot;always_restart.txt file has not been deleted&quot;,
+			stat(&quot;rackapp.tmp/tmp/always_restart.txt&quot;, &amp;buf) == 0);
+		ensure(&quot;restart.txt file has not been deleted&quot;,
+			stat(&quot;rackapp.tmp/tmp/restart.txt&quot;, &amp;buf) == 0);
+		
+		pid = spawnRackApp(pool, &quot;rackapp.tmp&quot;)-&gt;getPid();
+		ensure(&quot;The app was restarted&quot;, pid != old_pid);
+	}
+	
+	TEST_METHOD(20) {
+		// It should look for restart.txt in the directory given by
+		// the restartDir option, if available.
+		struct stat buf;
+		char path[1024];
+		PoolOptions options(&quot;stub/rack&quot;);
+		options.appType = &quot;rack&quot;;
+		options.restartDir = string(getcwd(path, sizeof(path))) + &quot;/stub/rack&quot;;
+		
+		Application::SessionPtr session1 = pool-&gt;get(options);
+		Application::SessionPtr session2 = pool2-&gt;get(options);
+		session1.reset();
+		session2.reset();
+		
+		DeleteFileEventually f(&quot;stub/rack/restart.txt&quot;);
+		touchFile(&quot;stub/rack/restart.txt&quot;);
+		
+		pool-&gt;get(options);
+		
+		ensure_equals(&quot;No apps are active&quot;, pool-&gt;getActive(), 0u);
+		ensure_equals(&quot;Both apps are killed, and a new one was spawned&quot;,
+			pool-&gt;getCount(), 1u);
+		ensure(&quot;Restart file still exists&quot;,
+			stat(&quot;stub/rack/restart.txt&quot;, &amp;buf) == 0);
+	}
+	
+	TEST_METHOD(21) {
+		// restartDir may also be a directory relative to the
+		// application root.
+		struct stat buf;
+		PoolOptions options(&quot;stub/rack&quot;);
+		options.appType = &quot;rack&quot;;
+		options.restartDir = &quot;public&quot;;
+		
+		Application::SessionPtr session1 = pool-&gt;get(options);
+		Application::SessionPtr session2 = pool2-&gt;get(options);
+		session1.reset();
+		session2.reset();
+		
+		DeleteFileEventually f(&quot;stub/rack/public/restart.txt&quot;);
+		touchFile(&quot;stub/rack/public/restart.txt&quot;);
+		
+		pool-&gt;get(options);
+		
+		ensure_equals(&quot;No apps are active&quot;, pool-&gt;getActive(), 0u);
+		ensure_equals(&quot;Both apps are killed, and a new one was spawned&quot;,
+			pool-&gt;getCount(), 1u);
+		ensure(&quot;Restart file still exists&quot;,
+			stat(&quot;stub/rack/public/restart.txt&quot;, &amp;buf) == 0);
+	}
+	
+	TEST_METHOD(22) {
 		// The cleaner thread should clean idle applications without crashing.
 		pool-&gt;setMaxIdleTime(1);
 		spawnRackApp(pool, &quot;stub/rack&quot;);
@@ -335,7 +488,7 @@
 		ensure_equals(&quot;App should have been cleaned up&quot;, pool-&gt;getCount(), 0u);
 	}
 	
-	TEST_METHOD(17) {
+	TEST_METHOD(23) {
 		// MaxPerApp is respected.
 		pool-&gt;setMax(3);
 		pool-&gt;setMaxPerApp(1);
@@ -349,18 +502,20 @@
 		
 		// We connect to stub/wsgi. Assert that the pool spawns a new
 		// instance for this app.
+		TempDirCopy c(&quot;stub/wsgi&quot;, &quot;wsgiapp.tmp&quot;);
 		ApplicationPoolPtr pool3(newPoolConnection());
-		Application::SessionPtr session3 = spawnWsgiApp(pool3, &quot;stub/wsgi&quot;);
+		Application::SessionPtr session3 = spawnWsgiApp(pool3, &quot;wsgiapp.tmp&quot;);
 		ensure_equals(pool-&gt;getCount(), 2u);
 	}
 	
-	TEST_METHOD(18) {
+	TEST_METHOD(24) {
 		// Application instance is shutdown after 'maxRequests' requests.
-		PoolOptions options(&quot;stub/railsapp&quot;);
+		PoolOptions options(&quot;stub/rack&quot;);
 		int reader;
 		pid_t originalPid;
 		Application::SessionPtr session;
 		
+		options.appType = &quot;rack&quot;;
 		options.maxRequests = 4;
 		pool-&gt;setMax(1);
 		session = pool-&gt;get(options);
@@ -401,7 +556,7 @@
 		}
 	};
 	
-	TEST_METHOD(19) {
+	TEST_METHOD(25) {
 		// If global queueing mode is enabled, then get() waits until
 		// there's at least one idle backend process for this application
 		// domain.
@@ -429,160 +584,6 @@
 		thr.join();
 	}
 	
-	TEST_METHOD(20) {
-		// If tmp/always_restart.txt is present, then the application under app_root
-		// should be always restarted.
-		try {
-			struct stat buf;
-			Application::SessionPtr session1 = pool-&gt;get(&quot;stub/railsapp&quot;);
-			Application::SessionPtr session2 = pool2-&gt;get(&quot;stub/railsapp&quot;);
-			session1.reset();
-			session2.reset();
-		
-			system(&quot;touch stub/railsapp/tmp/always_restart.txt&quot;);
-			pool-&gt;get(&quot;stub/railsapp&quot;);
-		
-			ensure_equals(&quot;No apps are active&quot;, pool-&gt;getActive(), 0u);
-			ensure_equals(&quot;Both apps are killed, and a new one was spawned&quot;,
-				pool-&gt;getCount(), 1u);
-			ensure(&quot;always_restart file has not been deleted&quot;,
-				stat(&quot;stub/railsapp/tmp/always_restart.txt&quot;, &amp;buf) == 0);
-			unlink(&quot;stub/railsapp/tmp/always_restart.txt&quot;);
-		} catch (...) {
-			unlink(&quot;stub/railsapp/tmp/always_restart.txt&quot;);
-			throw;
-		}
-	}
-	
-	TEST_METHOD(21) {
-		// If tmp/always_restart.txt is present and is a directory, 
-		// then the application under app_root
-		// should be always restarted.
-		struct stat buf;
-		Application::SessionPtr session1 = pool-&gt;get(&quot;stub/railsapp&quot;);
-		Application::SessionPtr session2 = pool2-&gt;get(&quot;stub/railsapp&quot;);
-		session1.reset();
-		session2.reset();
-	
-		TempDir dir(&quot;stub/railsapp/tmp/always_restart.txt&quot;);
-		pool-&gt;get(&quot;stub/railsapp&quot;);
-	
-		ensure_equals(&quot;No apps are active&quot;, pool-&gt;getActive(), 0u);
-		ensure_equals(&quot;Both apps are killed, and a new one was spawned&quot;,
-			pool-&gt;getCount(), 1u);
-		ensure(&quot;always_restart file has not been deleted&quot;,
-			stat(&quot;stub/railsapp/tmp/always_restart.txt&quot;, &amp;buf) == 0);
-	}
-	
-	TEST_METHOD(22) {
-		// Test whether tmp/always_restart.txt really results in code reload.
-		DeleteFileEventually f1(&quot;stub/railsapp/app/controllers/bar_controller.rb&quot;);
-		DeleteFileEventually f2(&quot;stub/railsapp/tmp/always_restart.txt&quot;);
-		
-		system(&quot;cp -f stub/railsapp/app/controllers/bar_controller_1.txt &quot;
-			&quot;stub/railsapp/app/controllers/bar_controller.rb&quot;);
-		Application::SessionPtr session = pool-&gt;get(&quot;stub/railsapp&quot;);
-		session-&gt;sendHeaders(createRequestHeaders(&quot;/bar&quot;));
-		string result = readAll(session-&gt;getStream());
-		ensure(result.find(&quot;bar 1!&quot;) != string::npos);
-		session.reset();
-
-		system(&quot;cp -f stub/railsapp/app/controllers/bar_controller_2.txt &quot;
-			&quot;stub/railsapp/app/controllers/bar_controller.rb&quot;);
-		touchFile(&quot;stub/railsapp/tmp/always_restart.txt&quot;);
-		session = pool-&gt;get(&quot;stub/railsapp&quot;);
-		session-&gt;sendHeaders(createRequestHeaders(&quot;/bar&quot;));
-		result = readAll(session-&gt;getStream());
-		ensure(&quot;App code has been reloaded (1)&quot;, result.find(&quot;bar 2!&quot;) != string::npos);
-		session.reset();
-
-		system(&quot;cp -f stub/railsapp/app/controllers/bar_controller_1.txt &quot;
-			&quot;stub/railsapp/app/controllers/bar_controller.rb&quot;);
-		session = pool-&gt;get(&quot;stub/railsapp&quot;);
-		session-&gt;sendHeaders(createRequestHeaders(&quot;/bar&quot;));
-		result = readAll(session-&gt;getStream());
-		ensure(&quot;App code has been reloaded (2)&quot;, result.find(&quot;bar 1!&quot;) != string::npos);
-		session.reset();
-	}
-	
-	TEST_METHOD(23) {
-		// If tmp/restart.txt and tmp/always_restart.txt are present, 
-		// the application under app_root should still be restarted and
-		// both files must be kept
-		pid_t old_pid, pid;
-		struct stat buf;
-		Application::SessionPtr session1 = pool-&gt;get(&quot;stub/railsapp&quot;);
-		Application::SessionPtr session2 = pool2-&gt;get(&quot;stub/railsapp&quot;);
-		session1.reset();
-		session2.reset();
-		
-		DeleteFileEventually f1(&quot;stub/railsapp/tmp/restart.txt&quot;);
-		DeleteFileEventually f2(&quot;stub/railsapp/tmp/always_restart.txt&quot;);
-		touchFile(&quot;stub/railsapp/tmp/restart.txt&quot;);
-		touchFile(&quot;stub/railsapp/tmp/always_restart.txt&quot;);
-		
-		old_pid = pool-&gt;get(&quot;stub/railsapp&quot;)-&gt;getPid();
-		ensure(&quot;always_restart file has not been deleted&quot;,
-			stat(&quot;stub/railsapp/tmp/always_restart.txt&quot;, &amp;buf) == 0);
-
-		ensure(&quot;Restart file has not been deleted&quot;,
-			stat(&quot;stub/railsapp/tmp/restart.txt&quot;, &amp;buf) == 0);
-	
-		pid = pool-&gt;get(&quot;stub/railsapp&quot;)-&gt;getPid();
-		ensure(&quot;The app was restarted&quot;, pid != old_pid);
-	}
-	
-	TEST_METHOD(24) {
-		// It should look for restart.txt in the directory given by
-		// the restartDir option, if available.
-		struct stat buf;
-		char path[1024];
-		PoolOptions options(&quot;stub/rack&quot;);
-		options.appType = &quot;rack&quot;;
-		options.restartDir = string(getcwd(path, sizeof(path))) + &quot;/stub/rack&quot;;
-		
-		Application::SessionPtr session1 = pool-&gt;get(options);
-		Application::SessionPtr session2 = pool2-&gt;get(options);
-		session1.reset();
-		session2.reset();
-		
-		DeleteFileEventually f(&quot;stub/rack/restart.txt&quot;);
-		touchFile(&quot;stub/rack/restart.txt&quot;);
-		
-		pool-&gt;get(options);
-		
-		ensure_equals(&quot;No apps are active&quot;, pool-&gt;getActive(), 0u);
-		ensure_equals(&quot;Both apps are killed, and a new one was spawned&quot;,
-			pool-&gt;getCount(), 1u);
-		ensure(&quot;Restart file still exists&quot;,
-			stat(&quot;stub/rack/restart.txt&quot;, &amp;buf) == 0);
-	}
-	
-	TEST_METHOD(25) {
-		// restartDir may also be a directory relative to the
-		// application root.
-		struct stat buf;
-		PoolOptions options(&quot;stub/rack&quot;);
-		options.appType = &quot;rack&quot;;
-		options.restartDir = &quot;public&quot;;
-		
-		Application::SessionPtr session1 = pool-&gt;get(options);
-		Application::SessionPtr session2 = pool2-&gt;get(options);
-		session1.reset();
-		session2.reset();
-		
-		DeleteFileEventually f(&quot;stub/rack/public/restart.txt&quot;);
-		touchFile(&quot;stub/rack/public/restart.txt&quot;);
-		
-		pool-&gt;get(options);
-		
-		ensure_equals(&quot;No apps are active&quot;, pool-&gt;getActive(), 0u);
-		ensure_equals(&quot;Both apps are killed, and a new one was spawned&quot;,
-			pool-&gt;getCount(), 1u);
-		ensure(&quot;Restart file still exists&quot;,
-			stat(&quot;stub/rack/public/restart.txt&quot;, &amp;buf) == 0);
-	}
-	
 	/*************************************/
 	
 #endif /* USE_TEMPLATE */</diff>
      <filename>test/ApplicationPoolTest.cpp</filename>
    </modified>
    <modified>
      <diff>@@ -1,17 +1,76 @@
+#include &lt;unistd.h&gt;
 #include &quot;Support.h&quot;
 
 namespace Test {
 
+string
+readAll(int fd) {
+	string result;
+	char buf[1024 * 32];
+	ssize_t ret;
+	while (true) {
+		do {
+			ret = read(fd, buf, sizeof(buf));
+		} while (ret == -1 &amp;&amp; errno == EINTR);
+		if (ret == 0) {
+			break;
+		} else if (ret == -1) {
+			throw SystemException(&quot;Cannot read from socket&quot;, errno);
+		} else {
+			result.append(buf, ret);
+		}
+	}
+	return result;
+}
+
+string
+replaceString(const string &amp;str, const string &amp;toFind, const string &amp;replaceWith) {
+	string::size_type pos = str.find(toFind);
+	if (pos == string::npos) {
+		return str;
+	} else {
+		string result(str);
+		return result.replace(pos, toFind.size(), replaceWith);
+	}
+}
+
+void
+replaceStringInFile(const char *filename, const string &amp;toFind, const string &amp;replaceWith) {
+	FILE *f = fopen(filename, &quot;r&quot;);
+	if (f == NULL) {
+		int e = errno;
+		string message = &quot;Cannot open file '&quot;;
+		message.append(filename);
+		message.append(&quot;' for reading&quot;);
+		throw FileSystemException(message, e, filename);
+	}
+	string content(readAll(fileno(f)));
+	fclose(f);
+	
+	f = fopen(filename, &quot;w&quot;);
+	if (f == NULL) {
+		int e = errno;
+		string message = &quot;Cannot open file '&quot;;
+		message.append(filename);
+		message.append(&quot;' for writing&quot;);
+		throw FileSystemException(message, e, filename);
+	}
+	content = replaceString(content, toFind, replaceWith);
+	fwrite(content.data(), 1, content.size(), f);
+	fclose(f);
+}
+
 void
 touchFile(const char *filename, time_t timestamp) {
 	FILE *f = fopen(filename, &quot;a&quot;);
 	if (f != NULL) {
 		fclose(f);
-	} else {
+	} else if (errno != EISDIR) {
 		int e = errno;
-		cerr &lt;&lt; &quot;Cannot touch file '&quot; &lt;&lt; filename &lt;&lt; &quot;': &quot; &lt;&lt;
-			strerror(e) &lt;&lt;&quot; (&quot; &lt;&lt; e &lt;&lt; &quot;)&quot; &lt;&lt; endl;
-		throw exception();
+		string message = &quot;Cannot touch file '&quot;;
+		message.append(filename);
+		message.append(&quot;'&quot;);
+		throw FileSystemException(message, e, filename);
 	}
 	
 	if (timestamp != (time_t) -1) {</diff>
      <filename>test/support/Support.cpp</filename>
    </modified>
    <modified>
      <diff>@@ -11,6 +11,7 @@
 #include &lt;cstring&gt;
 #include &lt;utime.h&gt;
 
+#include &quot;Exceptions.h&quot;
 #include &quot;Utils.h&quot;
 
 namespace Test {
@@ -18,6 +19,35 @@ namespace Test {
 using namespace std;
 using namespace Passenger;
 
+/**
+ * Read all data from the given file descriptor until EOF.
+ *
+ * @throws SystemException
+ */
+string readAll(int fd);
+
+/**
+ * Look for 'toFind' inside 'str', replace it with 'replaceWith' and return the result.
+ * Only the first occurence of 'toFind' is replaced.
+ */
+string replaceString(const string &amp;str, const string &amp;toFind, const string &amp;replaceWith);
+
+/**
+ * Look for 'toFind' inside the given file, replace it with 'replaceWith' and write
+ * the result back to the file. Only the first occurence of 'toFind' is replaced.
+ *
+ * @throws FileSystemException
+ */
+void replaceStringInFile(const char *filename, const string &amp;toFind, const string &amp;replaceWith);
+
+/**
+ * Touch the given file: create the file if it doesn't exist, update its
+ * timestamp if it does. If the &lt;tt&gt;timestamp&lt;/tt&gt; argument is -1, then
+ * the current system time will be used, otherwise the given timestamp
+ * will be used.
+ *
+ * @throws FileSystemException
+ */
 void touchFile(const char *filename, time_t timestamp = (time_t) - 1);
 
 /**
@@ -32,9 +62,10 @@ public:
 		this-&gt;name = name;
 		if (mkdir(name.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0) {
 			int e = errno;
-			cerr &lt;&lt; &quot;Cannot create directory '&quot; &lt;&lt; name &lt;&lt; &quot;': &quot; &lt;&lt;
-				strerror(e) &lt;&lt;&quot; (&quot; &lt;&lt; e &lt;&lt; &quot;)&quot; &lt;&lt; endl;
-			throw exception();
+			string message = &quot;Cannot create directory '&quot;;
+			message.append(name);
+			message.append(&quot;'&quot;);
+			throw FileSystemException(message, e, name);
 		}
 	}
 	</diff>
      <filename>test/support/Support.h</filename>
    </modified>
  </modified>
  <removed type="array">
    <removed>
      <filename>test/stub/minimal-railsapp/README</filename>
    </removed>
    <removed>
      <filename>test/stub/minimal-railsapp/config/application.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/minimal-railsapp/config/environment.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/minimal-railsapp/vendor/rails/actionmailer/lib/action_mailer.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/minimal-railsapp/vendor/rails/actionpack/lib/action_controller.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/minimal-railsapp/vendor/rails/actionpack/lib/action_pack.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/minimal-railsapp/vendor/rails/actionpack/lib/action_view.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/minimal-railsapp/vendor/rails/activerecord/lib/active_record.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/minimal-railsapp/vendor/rails/activeresource/lib/active_resource.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/minimal-railsapp/vendor/rails/activesupport/lib/active_support.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/minimal-railsapp/vendor/rails/activesupport/lib/active_support/whiny_nil.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/minimal-railsapp/vendor/rails/railties/lib/dispatcher.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/minimal-railsapp/vendor/rails/railties/lib/initializer.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/minimal-railsapp/vendor/rails/railties/lib/ruby_version_check.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/app/controllers/application.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/app/controllers/bar_controller_1.txt</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/app/controllers/bar_controller_2.txt</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/app/controllers/foo_controller.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/app/helpers/application_helper.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/config/boot.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/config/database.yml</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/config/environment.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/config/environments/development.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/config/environments/production.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/config/initializers/inflections.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/config/initializers/mime_types.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/config/routes.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/log/useless.txt</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/public/useless.txt</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/tmp/cache/useless.txt</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/tmp/pids/useless.txt</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/tmp/sessions/useless.txt</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/tmp/sockets/useless.txt</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/app/controllers/application.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/app/controllers/foo_controller.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/app/helpers/application_helper.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/config/boot.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/config/database.yml</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/config/environment.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/config/environments/development.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/config/environments/production.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/config/initializers/inflections.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/config/initializers/mime_types.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/config/routes.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/log/useless.txt</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/public/useless.txt</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/tmp/cache/useless.txt</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/tmp/pids/useless.txt</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/tmp/sessions/useless.txt</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/tmp/sockets/useless.txt</filename>
    </removed>
  </removed>
  <parents type="array">
    <parent>
      <id>81ca3ade903218e37617fb279c99f60bb478a638</id>
    </parent>
  </parents>
  <author>
    <name>Hongli Lai (Phusion)</name>
    <email>hongli@phusion.nl</email>
  </author>
  <url>http://github.com/FooBarWidget/passenger/commit/b5093c289ce58bf4190fff1ca9b58e6de65d9de1</url>
  <id>b5093c289ce58bf4190fff1ca9b58e6de65d9de1</id>
  <committed-date>2009-05-26T08:35:02-07:00</committed-date>
  <authored-date>2009-05-26T01:41:44-07:00</authored-date>
  <message>Refactor ApplicationPoolTest: split some functionality to functions, and modify tests to use stub/rack instead of stub/railsapp because it's faster.</message>
  <tree>576395fd5b245c8cf519b1c155c7b99537633aa1</tree>
  <committer>
    <name>Hongli Lai (Phusion)</name>
    <email>hongli@phusion.nl</email>
  </committer>
</commit>
