diff --git a/.gitignore b/.gitignore
index 09af6808d6471e4509f98c274bec7aadb1336029..40d6e6ca0fe0c3d38ee57cc4c4f7cff3116d4b1c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -56,6 +56,7 @@ nbproject
 
 # Cloud9IDE
 .settings.xml
+.c9revisions
 
 # vim ex mode
 .vimrc
diff --git a/apps/files/css/files.css b/apps/files/css/files.css
index 661a2e827a427661f5b2d56982de27f85bf5a8d0..f37ac4c262ac9c9096184de71878cc3a7ce6ebbb 100644
--- a/apps/files/css/files.css
+++ b/apps/files/css/files.css
@@ -3,7 +3,7 @@
  See the COPYING-README file. */
 
 /* FILE MENU */
-.actions { padding:.3em; float:left; height:2em; }
+.actions { padding:.3em; float:left; height:2em; width: 100%; }
 .actions input, .actions button, .actions .button { margin:0; float:left; }
 
 #new {
@@ -23,7 +23,7 @@
 #new>ul>li>p { cursor:pointer; }
 #new>ul>li>form>input { padding:0.3em; margin:-0.3em; }
 
-#trash { height:17px;  margin:0 0 0 1em; z-index:1010; position:absolute; right:13.5em; } 
+#trash { height:17px; margin: 0 1em; z-index:1010; float: right; }
 
 #upload { 
 	height:27px; padding:0; margin-left:0.2em; overflow:hidden;
@@ -44,7 +44,7 @@
 	z-index:20; position:relative; cursor:pointer; overflow:hidden;
 }
 
-#uploadprogresswrapper { position:absolute; right:13.5em; top:0em; }
+#uploadprogresswrapper { float: right; position: relative; }
 #uploadprogresswrapper #uploadprogressbar { position:relative; display:inline-block; width:10em; height:1.5em; top:.4em; }
 
 /* FILE TABLE */
@@ -55,7 +55,7 @@
 	font-size:1.5em; font-weight:bold;
 	color:#888; text-shadow:#fff 0 1px 0;
 }
-table { position:relative; top:37px; width:100%; }
+table { position:relative; width:100%; }
 tbody tr { background-color:#fff; height:2.5em; }
 tbody tr:hover, tbody tr:active, tbody tr.selected { background-color:#f8f8f8; }
 tbody tr.selected { background-color:#eee; }
diff --git a/apps/files/templates/index.php b/apps/files/templates/index.php
index 2d4ed9ab2d9b33012db04917c041885caa3ab7e6..0b4aa21eac32fbe75ece09dc781dbc0da528d846 100644
--- a/apps/files/templates/index.php
+++ b/apps/files/templates/index.php
@@ -59,7 +59,7 @@
 	<div id="emptyfolder"><?php echo $l->t('Nothing in here. Upload something!')?></div>
 <?php endif; ?>
 
-<table>
+<table class="hascontrols">
 	<thead>
 		<tr>
 			<th id='headerName'>
diff --git a/apps/files_versions/ajax/getVersions.php b/apps/files_versions/ajax/getVersions.php
index 600e69cf798054bca6f9fe63a140c0e2c0cb0eca..53fc04625c652ebaf27289b8a5c73ddbfa53399c 100644
--- a/apps/files_versions/ajax/getVersions.php
+++ b/apps/files_versions/ajax/getVersions.php
@@ -5,7 +5,7 @@ $userDirectory = "/".OCP\USER::getUser()."/files";
 $source = $_GET['source'];
 
 $count = 5; //show the newest revisions
-if( ($versions = OCA_Versions\Storage::getVersions( $source, $count)) ) {
+if( ($versions = OCA\Files_Versions\Storage::getVersions( $source, $count)) ) {
 
 	$versionsFormatted = array();
 
diff --git a/apps/files_versions/ajax/rollbackVersion.php b/apps/files_versions/ajax/rollbackVersion.php
index f2c211d9c1ec944247c0469c5d1ca654e8331f13..dc5a59cb4af626961652496c85f22b1bb16d6a78 100644
--- a/apps/files_versions/ajax/rollbackVersion.php
+++ b/apps/files_versions/ajax/rollbackVersion.php
@@ -8,7 +8,7 @@ $userDirectory = "/".OCP\USER::getUser()."/files";
 $file = $_GET['file'];
 $revision=(int)$_GET['revision'];
 
-if(OCA_Versions\Storage::rollback( $file, $revision )) {
+if(OCA\Files_Versions\Storage::rollback( $file, $revision )) {
 	OCP\JSON::success(array("data" => array( "revision" => $revision, "file" => $file )));
 }else{
 	OCP\JSON::error(array("data" => array( "message" => "Could not revert:" . $file )));
diff --git a/apps/files_versions/appinfo/app.php b/apps/files_versions/appinfo/app.php
index edd0a2f70226a5c5cfb1c0a05362342830d2f2be..f7c6989ce2dc3bca084e3c693fe4962850f865ef 100644
--- a/apps/files_versions/appinfo/app.php
+++ b/apps/files_versions/appinfo/app.php
@@ -1,8 +1,8 @@
 <?php
 
 //require_once 'files_versions/versions.php';
-OC::$CLASSPATH['OCA_Versions\Storage'] = 'apps/files_versions/lib/versions.php';
-OC::$CLASSPATH['OCA_Versions\Hooks'] = 'apps/files_versions/lib/hooks.php';
+OC::$CLASSPATH['OCA\Files_Versions\Storage'] = 'apps/files_versions/lib/versions.php';
+OC::$CLASSPATH['OCA\Files_Versions\Hooks'] = 'apps/files_versions/lib/hooks.php';
 
 OCP\App::registerAdmin('files_versions', 'settings');
 OCP\App::registerPersonal('files_versions', 'settings-personal');
@@ -10,7 +10,7 @@ OCP\App::registerPersonal('files_versions', 'settings-personal');
 OCP\Util::addscript('files_versions', 'versions');
 
 // Listen to write signals
-OCP\Util::connectHook('OC_Filesystem', 'write', "OCA_Versions\Hooks", "write_hook");
+OCP\Util::connectHook('OC_Filesystem', 'write', "OCA\Files_Versions\Hooks", "write_hook");
 // Listen to delete and rename signals
-OCP\Util::connectHook('OC_Filesystem', 'post-delete', "OCA_Versions\Hooks", "remove_hook");
-OCP\Util::connectHook('OC_Filesystem', 'rename', "OCA_Versions\Hooks", "rename_hook");
\ No newline at end of file
+OCP\Util::connectHook('OC_Filesystem', 'post-delete', "OCA\Files_Versions\Hooks", "remove_hook");
+OCP\Util::connectHook('OC_Filesystem', 'rename', "OCA\Files_Versions\Hooks", "rename_hook");
diff --git a/apps/files_versions/history.php b/apps/files_versions/history.php
index 6071240e58320c9572742a6185efc1f224885d52..1bd5cde44bec45d9a1616af143f9385c6a3ef737 100644
--- a/apps/files_versions/history.php
+++ b/apps/files_versions/history.php
@@ -29,7 +29,7 @@ if ( isset( $_GET['path'] ) ) {
 
 	$path = $_GET['path'];
 	$tmpl->assign( 'path', $path );
-	$versions = new OCA_Versions\Storage();
+	$versions = new OCA\Files_Versions\Storage();
 
 	// roll back to old version if button clicked
 	if( isset( $_GET['revert'] ) ) {
@@ -52,7 +52,7 @@ if ( isset( $_GET['path'] ) ) {
 
 	// show the history only if there is something to show
 	$count = 999; //show the newest revisions
-	if( ($versions = OCA_Versions\Storage::getVersions( $path, $count)) ) {
+	if( ($versions = OCA\Files_Versions\Storage::getVersions( $path, $count)) ) {
 
 		$tmpl->assign( 'versions', array_reverse( $versions ) );
 
diff --git a/apps/files_versions/lib/hooks.php b/apps/files_versions/lib/hooks.php
index 5cefc532895e4db4fdc6e6f804e506195ebbe502..dc02c605c44ce51877a807dd7ffa4572c47550a6 100644
--- a/apps/files_versions/lib/hooks.php
+++ b/apps/files_versions/lib/hooks.php
@@ -10,7 +10,7 @@
  * This class contains all hooks.
  */
 
-namespace OCA_Versions;
+namespace OCA\Files_Versions;
 
 class Hooks {
 
diff --git a/apps/files_versions/lib/versions.php b/apps/files_versions/lib/versions.php
index 5063ef3e21ccdc77d00aaab27402c9fe425e7a67..b54bc4a4422969f8f8d74cee253c03faa60b6cb8 100644
--- a/apps/files_versions/lib/versions.php
+++ b/apps/files_versions/lib/versions.php
@@ -13,7 +13,7 @@
  * A class to handle the versioning of files.
  */
 
-namespace OCA_Versions;
+namespace OCA\Files_Versions;
 
 class Storage {
 
@@ -195,6 +195,7 @@ class Storage {
 
 			$files_view = new \OC_FilesystemView('/'.$uid.'/files');
 			$local_file = $files_view->getLocalFile($filename);
+			$local_file_md5 = \md5_file( $local_file );
 
 			foreach( $matches as $ma ) {
 				$parts = explode( '.v', $ma );
@@ -206,7 +207,7 @@ class Storage {
 				$versions[$key]['size'] = $versions_fileview->filesize($filename.'.v'.$version);
 
 				// if file with modified date exists, flag it in array as currently enabled version
-				( \md5_file( $ma ) == \md5_file( $local_file ) ? $versions[$key]['fileMatch'] = 1 : $versions[$key]['fileMatch'] = 0 );
+				( \md5_file( $ma ) == $local_file_md5 ? $versions[$key]['fileMatch'] = 1 : $versions[$key]['fileMatch'] = 0 );
 
 			}
 
diff --git a/autotest.cmd b/autotest.cmd
new file mode 100644
index 0000000000000000000000000000000000000000..053860db5473f73d9ad36fab0c5126ecb9058827
--- /dev/null
+++ b/autotest.cmd
@@ -0,0 +1,117 @@
+::
+:: ownCloud
+::
+:: @author Thomas Müller
+:: @author Tobias Ramforth (translated into Windows batch file)
+::
+:: @copyright 2012 Thomas Müller thomas.mueller@tmit.eu
+::
+@echo off
+
+set DATADIR=data-autotest
+set BASEDIR=%~dp0
+
+:: create autoconfig for sqlite, mysql and postgresql
+echo ^<?php                                      > .\tests\autoconfig-sqlite.php
+echo $AUTOCONFIG ^= array ^(                     >> .\tests\autoconfig-sqlite.php
+echo  'installed' ^=^> false^,                   >> .\tests\autoconfig-sqlite.php
+echo  'dbtype' ^=^> 'sqlite'^,                   >> .\tests\autoconfig-sqlite.php
+echo  'dbtableprefix' ^=^> 'oc_'^,               >> .\tests\autoconfig-sqlite.php
+echo  'adminlogin' ^=^> 'admin'^,                >> .\tests\autoconfig-sqlite.php
+echo  'adminpass' ^=^> 'admin'^,                 >> .\tests\autoconfig-sqlite.php
+echo  'directory' ^=^> '%BASEDIR%%DATADIR%'^,    >> .\tests\autoconfig-sqlite.php
+echo ^)^;                                        >> .\tests\autoconfig-sqlite.php
+
+echo ^<?php                                      > .\tests\autoconfig-mysql.php
+echo $AUTOCONFIG ^= array ^(                     >> .\tests\autoconfig-mysql.php
+echo   'installed' ^=^> false^,                  >> .\tests\autoconfig-mysql.php
+echo   'dbtype' ^=^> 'mysql'^,                   >> .\tests\autoconfig-mysql.php
+echo   'dbtableprefix' ^=^> 'oc_'^,              >> .\tests\autoconfig-mysql.php
+echo   'adminlogin' ^=^> 'admin'^,               >> .\tests\autoconfig-mysql.php
+echo   'adminpass' ^=^> 'admin'^,                >> .\tests\autoconfig-mysql.php
+echo   'directory' ^=^> '%BASEDIR%%DATADIR%'^,   >> .\tests\autoconfig-mysql.php
+echo   'dbuser' ^=^> 'oc_autotest'^,             >> .\tests\autoconfig-mysql.php
+echo   'dbname' ^=^> 'oc_autotest'^,             >> .\tests\autoconfig-mysql.php
+echo   'dbhost' ^=^> 'localhost'^,               >> .\tests\autoconfig-mysql.php
+echo   'dbpass' ^=^> 'owncloud'^,                >> .\tests\autoconfig-mysql.php
+echo ^)^;                                        >> .\tests\autoconfig-mysql.php
+
+echo ^<?php                                      > .\tests\autoconfig-pgsql.php
+echo $AUTOCONFIG ^= array ^(                     >> .\tests\autoconfig-pgsql.php
+echo   'installed' ^=^> false^,                  >> .\tests\autoconfig-pgsql.php
+echo   'dbtype' ^=^> 'pgsql'^,                   >> .\tests\autoconfig-pgsql.php
+echo   'dbtableprefix' ^=^> 'oc_'^,              >> .\tests\autoconfig-pgsql.php
+echo   'adminlogin' ^=^> 'admin'^,               >> .\tests\autoconfig-pgsql.php
+echo   'adminpass' ^=^> 'admin'^,                >> .\tests\autoconfig-pgsql.php
+echo   'directory' ^=^> '%BASEDIR%%DATADIR%'^,   >> .\tests\autoconfig-pgsql.php
+echo   'dbuser' ^=^> 'oc_autotest'^,             >> .\tests\autoconfig-pgsql.php
+echo   'dbname' ^=^> 'oc_autotest'^,             >> .\tests\autoconfig-pgsql.php
+echo   'dbhost' ^=^> 'localhost'^,               >> .\tests\autoconfig-pgsql.php
+echo   'dbpass' ^=^> 'owncloud'^,                >> .\tests\autoconfig-pgsql.php
+echo ^)^;                                        >> .\tests\autoconfig-pgsql.php
+
+echo localhost:5432:*:oc_autotest:owncloud > %APPDATA%\postgresql\pgpass.conf
+
+::
+:: start test execution
+::
+::call:execute_tests "sqlite"
+call:execute_tests "mysql"
+::call:execute_tests "mssql"
+::call:execute_tests "ora"
+::call:execute_tests "pgsql"
+
+goto:eof
+
+:execute_tests
+	echo "Setup environment for %~1 testing ..."
+	:: back to root folder
+	cd %BASEDIR%
+
+	:: revert changes to tests\data
+	git checkout tests\data\*
+
+	:: reset data directory
+	rmdir /s /q %DATADIR%
+	md %DATADIR%
+
+	:: remove the old config file
+	:: del /q /f config\config.php
+	copy /y tests\preseed-config.php config\config.php
+
+	:: drop database
+	if "%~1" == "mysql" mysql -u oc_autotest -powncloud -e "DROP DATABASE oc_autotest"
+	
+	if "%~1" == "pgsql" dropdb -h localhost -p 5432 -U oc_autotest -w oc_autotest
+	
+	:: copy autoconfig
+	copy /y %BASEDIR%\tests\autoconfig-%~1.php %BASEDIR%\config\autoconfig.php
+
+	:: trigger installation
+	php -f index.php
+
+	::test execution
+	echo "Testing with %~1 ..."
+	cd tests
+	rmdir /s /q coverage-html-%~1
+	md coverage-html-%~1
+	php -f enable_all.php
+	::phpunit --log-junit autotest-results-%~1.xml --coverage-clover autotest-clover-%~1.xml --coverage-html coverage-html-%~1
+	::phpunit --bootstrap bootstrap.php --configuration phpunit.xml
+	php win32-phpunit.php --bootstrap bootstrap.php --configuration phpunit.xml --log-junit autotest-results-%~1.xml --coverage-clover autotest-clover-%~1.xml --coverage-html coverage-html-%~1
+	echo "Done with testing %~1 ..."
+	cd %BASEDIR%
+goto:eof
+
+::
+:: NOTES on mysql:
+::  - CREATE USER 'oc_autotest'@'localhost' IDENTIFIED BY 'owncloud';
+::  - grant access permissions: grant all on oc_autotest.* to 'oc_autotest'@'localhost';
+::
+:: NOTES on pgsql:
+::  - su - postgres
+::  - createuser -P (enter username and password and enable superuser)
+::  - to enable dropdb I decided to add following line to pg_hba.conf (this is not the safest way but I don't care for the testing machine):
+:: local	all	all	trust
+::
+
diff --git a/core/css/styles.css b/core/css/styles.css
index 6a2773c2d9dc22eeea47ce4fc7b4418ab103053c..268040525bf3219f768e60f78e18b93e04835639 100644
--- a/core/css/styles.css
+++ b/core/css/styles.css
@@ -119,18 +119,26 @@ input[type="submit"].enabled { background:#66f866; border:1px solid #5e5; -moz-b
 #select_all{ margin-top:.4em !important;}
 
 /* CONTENT ------------------------------------------------------------------ */
-#controls { padding:0 0.5em; width:100%; top:3.5em; height:2.8em; margin:0; background:#f7f7f7; border-bottom:1px solid #eee; position:fixed; z-index:50; -moz-box-shadow:0 -3px 7px #000; -webkit-box-shadow:0 -3px 7px #000; box-shadow:0 -3px 7px #000; }
+#controls {
+	position:fixed;
+	height:2.8em; width:100%;
+	padding:0 70px 0 0.5em; margin:0;
+	-moz-box-sizing:border-box; box-sizing:border-box;
+	-moz-box-shadow:0 -3px 7px #000; -webkit-box-shadow:0 -3px 7px #000; box-shadow:0 -3px 7px #000;
+	background:#f7f7f7; border-bottom:1px solid #eee;  z-index:50;
+}
 #controls .button { display:inline-block; }
 
 #content { position:relative; height:100%; width:100%; }
+#content .hascontrols { position: relative; top: 2.9em; }
 #content-wrapper {
 	position:absolute; height:100%; width:100%; padding-top:3.5em; padding-left:64px;
 	-moz-box-sizing:border-box; box-sizing:border-box;
 }
 #leftcontent, .leftcontent {
-	position:fixed; overflow:auto; top:0; width:20em; height:100%;
+	position:relative; overflow:auto; width:20em; height:100%;
 	background:#f8f8f8; border-right:1px solid #ddd;
-	-moz-box-sizing:border-box; box-sizing:border-box; padding-top:6.4em;
+	-moz-box-sizing:border-box; box-sizing:border-box;
 }
 #leftcontent li, .leftcontent li { background:#f8f8f8; padding:.5em .8em; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; -webkit-transition:background-color 200ms; -moz-transition:background-color 200ms; -o-transition:background-color 200ms; transition:background-color 200ms; }
 #leftcontent li:hover, #leftcontent li:active, #leftcontent li.active, .leftcontent li:hover, .leftcontent li:active, .leftcontent li.active { background:#eee; }
@@ -191,7 +199,7 @@ input[name="password-clone"] { padding-left:1.8em; width:11.7em !important; }
 /* NEEDED FOR INFIELD LABELS */
 p.infield { position:relative; }
 label.infield { cursor:text !important; top:1.05em; left:.85em; }
-#login form label.infield { position:absolute; font-size:19px; color:#aaa; white-space:nowrap; padding-left:1.2em; }
+#login form label.infield { position:absolute; font-size:19px; color:#aaa; white-space:nowrap; padding-left:1.4em; }
 #login form input[type="checkbox"]+label { position:relative; margin:0; font-size:1em; text-shadow:#fff 0 1px 0; }
 #login form .errors { background:#fed7d7; border:1px solid #f00; list-style-indent:inside; margin:0 0 2em; padding:1em; }
 
diff --git a/settings/css/settings.css b/settings/css/settings.css
index e722cca91eed37ef016dce7219dbb44f320bb96a..9dd17daaeb765c60ff0c01cb0cf7552823240586 100644
--- a/settings/css/settings.css
+++ b/settings/css/settings.css
@@ -33,7 +33,6 @@ tr:hover>td.password>span, tr:hover>td.displayName>span { margin:0; cursor:point
 tr:hover>td.remove>a, tr:hover>td.password>img,tr:hover>td.displayName>img, tr:hover>td.quota>img { visibility:visible; cursor:pointer; }
 tr:hover>td.remove>a { float:right; }
 li.selected { background-color:#ddd; }
-#content>table:not(.nostyle) { margin-top:3em; }
 table:not(.nostyle) { width:100%; }
 #rightcontent { padding-left: 1em; }
 div.quota { float:right; display:block; position:absolute; right:25em; top:0; }
diff --git a/settings/templates/apps.php b/settings/templates/apps.php
index 3f0d2a9d1c6462941e48cbcbf5cc0e68093f7dc0..ed1232ac3227204c4719d9b9421581cd52cad6ed 100644
--- a/settings/templates/apps.php
+++ b/settings/templates/apps.php
@@ -10,7 +10,7 @@
 	<a class="button" target="_blank" href="http://owncloud.org/dev"><?php echo $l->t('Add your App');?></a>
 	<a class="button" target="_blank" href="http://apps.owncloud.com"><?php echo $l->t('More Apps');?></a>
 </div>
-<ul id="leftcontent" class="applist">
+<ul id="leftcontent" class="applist hascontrols">
 	<?php foreach($_['apps'] as $app):?>
 	<li <?php if($app['active']) echo 'class="active"'?> data-id="<?php echo $app['id'] ?>" <?php if ( isset( $app['ocs_id'] ) ) { echo "data-id-ocs=\"{$app['ocs_id']}\""; } ?>
 		data-type="<?php echo $app['internal'] ? 'internal' : 'external' ?>" data-installed="1">
diff --git a/settings/templates/users.php b/settings/templates/users.php
index 4d7c29678ce1559cd23d10152e39cb871a005325..b3cab526947d58cd260ad13738143b5887e6a593 100644
--- a/settings/templates/users.php
+++ b/settings/templates/users.php
@@ -73,7 +73,7 @@ $_['subadmingroups'] = array_flip($items);
 	</div>
 </div>
 
-<table data-groups="<?php echo implode(', ', $allGroups);?>">
+<table class="hascontrols" data-groups="<?php echo implode(', ', $allGroups);?>">
 	<thead>
 		<tr>
 			<th id='headerName'><?php echo $l->t('Login Name')?></th>
diff --git a/tests/win32-phpunit.php b/tests/win32-phpunit.php
new file mode 100644
index 0000000000000000000000000000000000000000..ac8f95efcbfb17af7a508bcd9401eb461fbca8f7
--- /dev/null
+++ b/tests/win32-phpunit.php
@@ -0,0 +1,347 @@
+<?php
+OC_PHPUnit_Loader::checkIncludePath();
+OC_PHPUnit_Loader::detectPHPUnitVersionId();
+
+//load PHPUnit
+switch (OC_PHPUnit_Loader::$PHPUnitVersionId) {
+    case "36": {
+        OC_PHPUnit_Loader::load36();
+        break;
+    }
+    case "37": {
+        OC_PHPUnit_Loader::load37();
+        break;
+    }
+}
+
+//load custom implementation of the PHPUnit_TextUI_ResultPrinter
+switch (OC_PHPUnit_Loader::$PHPUnitVersionId) {
+    case "36":
+    case "37": {
+        class OC_PHPUnit_TextUI_ResultPrinter extends PHPUnit_TextUI_ResultPrinter
+        {
+            function __construct()
+            {
+                parent::__construct('php://stderr');
+            }
+
+            public function printResult(PHPUnit_Framework_TestResult $result)
+            {
+                $this->printHeader();
+                $this->printFooter($result);
+            }
+
+            protected function writeProgress($progress)
+            {
+                //ignore
+            }
+        }
+        break;
+    }
+}
+
+//loading of OC_PHPUnit_TextUI_Command
+switch (OC_PHPUnit_Loader::$PHPUnitVersionId) {
+    case "36":
+    case "37": {
+        class OC_PHPUnit_TextUI_Command extends PHPUnit_TextUI_Command
+        {
+
+            public static function main($exit = TRUE)
+            {
+                $command = new OC_PHPUnit_TextUI_Command();
+                $command->run($_SERVER['argv'], $exit);
+            }
+
+            protected function handleArguments(array $argv)
+            {
+                parent::handleArguments($argv);
+                $this->arguments['listeners'][] = new OC_PHPUnit_Framework_TestListener();
+                $this->arguments['printer'] = new OC_PHPUnit_TextUI_ResultPrinter();
+            }
+
+            protected function createRunner()
+            {
+                $coverage_Filter = new PHP_CodeCoverage_Filter();
+                $coverage_Filter->addFileToBlacklist(__FILE__);
+                $runner = new PHPUnit_TextUI_TestRunner($this->arguments['loader'], $coverage_Filter);
+                return $runner;
+            }
+        }
+        break;
+    }
+}
+
+class OC_PHPUnit_Loader
+{
+
+    const SUCCESS_EXIT = 0;
+    const FAILURE_EXIT = 1;
+    const EXCEPTION_EXIT = 2;
+
+    public static $PHPUnitVersionId;
+
+    /**
+     * @return void
+     */
+    public static function checkIncludePath()
+    {
+        //check include path
+        $PHPUnitParentDirectory = self::getPHPUnitParentDirectory();
+        if (is_null($PHPUnitParentDirectory)) {
+            echo "Cannot find PHPUnit in include path (" . ini_get('include_path') . ")";
+            exit(OC_PHPUnit_Loader::FAILURE_EXIT);
+        }
+    }
+
+    /**
+     * @return null | string
+     */
+    private static function getPHPUnitParentDirectory()
+    {
+        $pathArray = explode(PATH_SEPARATOR, ini_get('include_path'));
+        foreach ($pathArray as $path)
+        {
+            if (file_exists($path . DIRECTORY_SEPARATOR . 'PHPUnit/')) {
+                return $path;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * @return void
+     */
+    public static function detectPHPUnitVersionId()
+    {
+        require_once 'PHPUnit/Runner/Version.php';
+
+        $PHPUnitVersion = PHPUnit_Runner_Version::id();
+
+        if ($PHPUnitVersion === "@package_version@") {
+
+            self::$PHPUnitVersionId = "37";
+        }
+        else if (version_compare($PHPUnitVersion, '3.7.0') >= 0) {
+
+            self::$PHPUnitVersionId = "37";
+        }
+        else if (version_compare($PHPUnitVersion, '3.6.0') >= 0) {
+
+            self::$PHPUnitVersionId = "36";
+        }
+        else if (version_compare($PHPUnitVersion, '3.6.0') >= 0) {
+
+            echo "unsupported PHPUnit version:  $PHPUnitVersion";
+            exit(OC_PHPUnit_Loader::FAILURE_EXIT);
+        }
+    }
+
+    /**
+     * @return void
+     */
+    public static function load37()
+    {
+
+        require 'PHPUnit/Autoload.php';
+
+    }
+
+
+    /**
+     * @return void
+     */
+    public static function load36()
+    {
+        define('PHPUnit_MAIN_METHOD', 'OC_PHPUnit_TextUI_Command::main');
+
+        require 'PHPUnit/Autoload.php';
+
+    }
+}
+
+class OC_PHPUnit_Framework_TestListener implements PHPUnit_Framework_TestListener
+{
+
+    private $isSummaryTestCountPrinted = false;
+
+    public static function printEvent($eventName, $params = array())
+    {
+        self::printText("\n[$eventName");
+        foreach ($params as $key => $value) {
+            self::printText(" $key='$value'");
+        }
+        self::printText("]\n");
+    }
+
+    public static function printText($text)
+    {
+        file_put_contents('php://stderr', $text);
+    }
+
+    private static function getMessage(Exception $e)
+    {
+        $message = "";
+        if (strlen(get_class($e)) != 0) {
+            $message = $message . get_class($e);
+        }
+        if (strlen($message) != 0 && strlen($e->getMessage()) != 0) {
+            $message = $message . " : ";
+        }
+        $message = $message . $e->getMessage();
+        return self::escapeValue($message);
+    }
+
+    private static function getDetails(Exception $e)
+    {
+        return self::escapeValue($e->getTraceAsString());
+    }
+
+    public static function getValueAsString($value)
+    {
+        if (is_null($value)) {
+            return "null";
+        }
+        else if (is_bool($value)) {
+            return $value == true ? "true" : "false";
+        }
+        else if (is_array($value) || is_string($value)) {
+            $valueAsString = print_r($value, true);
+            if (strlen($valueAsString) > 10000) {
+                return null;
+            }
+            return $valueAsString;
+        }
+        else if (is_scalar($value)){
+            return print_r($value, true);
+        }
+        return null;
+    }
+
+    private static function escapeValue($text) {
+        $text = str_replace("|", "||", $text);
+        $text = str_replace("'", "|'", $text);
+        $text = str_replace("\n", "|n", $text);
+        $text = str_replace("\r", "|r", $text);
+        $text = str_replace("]", "|]", $text);
+        return $text;
+    }
+
+    public static function getFileName($className)
+    {
+        $reflectionClass = new ReflectionClass($className);
+        $fileName = $reflectionClass->getFileName();
+        return $fileName;
+    }
+
+    public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
+    {
+        self::printEvent("testFailed", array(
+            "name" => $test->getName(),
+            "message" => self::getMessage($e),
+            "details" => self::getDetails($e)
+        ));
+    }
+
+    public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
+    {
+        $params = array(
+            "name" => $test->getName(),
+            "message" => self::getMessage($e),
+            "details" => self::getDetails($e)
+        );
+        if ($e instanceof PHPUnit_Framework_ExpectationFailedException) {
+            $comparisonFailure = $e->getComparisonFailure();
+            if ($comparisonFailure instanceof PHPUnit_Framework_ComparisonFailure) {
+                $actualResult = $comparisonFailure->getActual();
+                $expectedResult = $comparisonFailure->getExpected();
+                $actualString = self::getValueAsString($actualResult);
+                $expectedString = self::getValueAsString($expectedResult);
+                if (!is_null($actualString) && !is_null($expectedString)) {
+                    $params['actual'] = self::escapeValue($actualString);
+                    $params['expected'] = self::escapeValue($expectedString);
+                }
+            }
+        }
+        self::printEvent("testFailed", $params);
+    }
+
+    public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+    {
+        self::printEvent("testIgnored", array(
+            "name" => $test->getName(),
+            "message" => self::getMessage($e),
+            "details" => self::getDetails($e)
+        ));
+    }
+
+    public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+    {
+        self::printEvent("testIgnored", array(
+            "name" => $test->getName(),
+            "message" => self::getMessage($e),
+            "details" => self::getDetails($e)
+        ));
+    }
+
+    public function startTest(PHPUnit_Framework_Test $test)
+    {
+        $testName = $test->getName();
+        $params = array(
+            "name" => $testName
+        );
+        if ($test instanceof PHPUnit_Framework_TestCase) {
+            $className = get_class($test);
+            $fileName = self::getFileName($className);
+            $params['locationHint'] = "php_qn://$fileName::\\$className::$testName";
+        }
+        self::printEvent("testStarted", $params);
+    }
+
+    public function endTest(PHPUnit_Framework_Test $test, $time)
+    {
+        self::printEvent("testFinished", array(
+            "name" => $test->getName(),
+            "duration" => (int)(round($time, 2) * 1000)
+        ));
+    }
+
+    public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
+    {
+        if (!$this->isSummaryTestCountPrinted) {
+            $this->isSummaryTestCountPrinted = true;
+            //print tests count
+            self::printEvent("testCount", array(
+                "count" => count($suite)
+            ));
+        }
+
+        $suiteName = $suite->getName();
+        if (empty($suiteName)) {
+            return;
+        }
+        $params = array(
+            "name" => $suiteName,
+        );
+        if (class_exists($suiteName, false)) {
+            $fileName = self::getFileName($suiteName);
+            $params['locationHint'] = "php_qn://$fileName::\\$suiteName";
+        }
+        self::printEvent("testSuiteStarted", $params);
+    }
+
+    public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
+    {
+        $suiteName = $suite->getName();
+        if (empty($suiteName)) {
+            return;
+        }
+        self::printEvent("testSuiteFinished",
+            array(
+                "name" => $suite->getName()
+            ));
+    }
+
+}
+
+OC_PHPUnit_TextUI_Command::main();