diff --git a/core/composer.json b/core/composer.json
index 459c00c..4a109a2 100644
--- a/core/composer.json
+++ b/core/composer.json
@@ -2,22 +2,28 @@
   "name": "drupal/drupal",
   "description": "Drupal is an open source content management platform powering millions of websites and applications.",
   "license": "GPL-2.0+",
+  "repositories": [{
+    "type": "vcs",
+    "url": "https://github.com/msonnabaum/phpunit.git"
+  }
+  ],
   "require": {
-    "symfony/class-loader": "<2.4",
-    "symfony/dependency-injection": "<2.4",
-    "symfony/event-dispatcher": "<2.4",
-    "symfony/http-foundation": "<2.4",
-    "symfony/http-kernel": "<2.4",
-    "symfony/routing": "<2.4",
+    "symfony/class-loader": "2.2.0-BETA2",
+    "symfony/dependency-injection": "2.2.0-BETA2",
+    "symfony/event-dispatcher": "2.2.0-BETA2",
+    "symfony/http-foundation": "2.2.0-BETA2",
+    "symfony/http-kernel": "2.2.0-BETA2",
+    "symfony/routing": "2.2.0-BETA2",
     "symfony/serializer": "2.2.0-BETA2",
-    "symfony/validator": "<2.4",
-    "symfony/yaml": "<2.4",
-    "twig/twig": "1.*@stable",
-    "doctrine/common": "2.3.*@stable",
-    "guzzle/http": "3.1.*@stable",
-    "kriswallsmith/assetic": "1.1.*@alpha",
+    "symfony/validator": "2.2.0-BETA2",
+    "symfony/yaml": "2.2.0-BETA2",
+    "twig/twig": "1.12.1",
+    "doctrine/common": "2.3.0",
+    "guzzle/http": "3.1.0",
+    "kriswallsmith/assetic": "1.1.0-alpha1",
     "symfony-cmf/routing": "dev-master#ea4a10",
-    "easyrdf/easyrdf": "dev-master"
+    "easyrdf/easyrdf": "0.8.0-beta.1",
+    "phpunit/phpunit": "3.7 as dev-3.7"
   },
   "minimum-stability": "dev"
 }
diff --git a/core/composer.lock b/core/composer.lock
index 6eb40a1..6198c2d 100644
--- a/core/composer.lock
+++ b/core/composer.lock
@@ -1,5 +1,5 @@
 {
-    "hash": "c087560024f328e26e2ed57c9b2aa703",
+    "hash": "a8c56a3549ec70f775e4bf795e65a993",
     "packages": [
         {
             "name": "doctrine/common",
@@ -56,7 +56,7 @@
                 {
                     "name": "Johannes Schmitt",
                     "email": "schmittjoh@gmail.com",
-                    "homepage": "http://jmsyst.com",
+                    "homepage": "https://github.com/schmittjoh",
                     "role": "Developer of wrapped JMSSerializerBundle"
                 }
             ],
@@ -72,16 +72,16 @@
         },
         {
             "name": "easyrdf/easyrdf",
-            "version": "dev-master",
+            "version": "0.8.0-beta.1",
             "source": {
                 "type": "git",
                 "url": "git://github.com/njh/easyrdf.git",
-                "reference": "d1c363bd04cd55169fbefe52328e86031c6db3a2"
+                "reference": "0.8.0-beta.1"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://github.com/njh/easyrdf/archive/d1c363bd04cd55169fbefe52328e86031c6db3a2.zip",
-                "reference": "d1c363bd04cd55169fbefe52328e86031c6db3a2",
+                "url": "https://github.com/njh/easyrdf/archive/0.8.0-beta.1.zip",
+                "reference": "0.8.0-beta.1",
                 "shasum": ""
             },
             "require": {
@@ -95,7 +95,7 @@
                 "squizlabs/php_codesniffer": ">=1.4.3",
                 "sami/sami": "dev-master"
             },
-            "time": "2013-01-13 01:20:04",
+            "time": "2013-01-18 15:54:28",
             "type": "library",
             "autoload": {
                 "psr-0": {
@@ -379,6 +379,399 @@
             ]
         },
         {
+            "name": "phpunit/php-code-coverage",
+            "version": "1.2.x-dev",
+            "source": {
+                "type": "git",
+                "url": "git://github.com/sebastianbergmann/php-code-coverage.git",
+                "reference": "ec7f6f388ca315b6520f61be670ff82fa6ce68fb"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ec7f6f388ca315b6520f61be670ff82fa6ce68fb",
+                "reference": "ec7f6f388ca315b6520f61be670ff82fa6ce68fb",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3",
+                "phpunit/php-file-iterator": ">=1.3.0@stable",
+                "phpunit/php-token-stream": ">=1.1.3@stable",
+                "phpunit/php-text-template": ">=1.1.1@stable"
+            },
+            "suggest": {
+                "ext-dom": "*",
+                "ext-xdebug": ">=2.0.5"
+            },
+            "time": "2013-01-07 10:45:42",
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "PHP/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "include-path": [
+                ""
+            ],
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sb@sebastian-bergmann.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
+            "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+            "keywords": [
+                "testing",
+                "coverage",
+                "xunit"
+            ]
+        },
+        {
+            "name": "phpunit/php-file-iterator",
+            "version": "dev-master",
+            "source": {
+                "type": "git",
+                "url": "git://github.com/sebastianbergmann/php-file-iterator.git",
+                "reference": "2deb24c65ea78e126daa8d45b2089ddc29ec1d26"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/2deb24c65ea78e126daa8d45b2089ddc29ec1d26",
+                "reference": "2deb24c65ea78e126daa8d45b2089ddc29ec1d26",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "time": "2013-01-07 10:47:05",
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "File/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "include-path": [
+                ""
+            ],
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sb@sebastian-bergmann.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "FilterIterator implementation that filters files based on a list of suffixes.",
+            "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
+            "keywords": [
+                "filesystem",
+                "iterator"
+            ]
+        },
+        {
+            "name": "phpunit/php-text-template",
+            "version": "dev-master",
+            "source": {
+                "type": "git",
+                "url": "git://github.com/sebastianbergmann/php-text-template.git",
+                "reference": "1eeef106193d2f8c539728e566bb4793071a9e18"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/1eeef106193d2f8c539728e566bb4793071a9e18",
+                "reference": "1eeef106193d2f8c539728e566bb4793071a9e18",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "time": "2013-01-07 10:56:17",
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "Text/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "include-path": [
+                ""
+            ],
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sb@sebastian-bergmann.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Simple template engine.",
+            "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+            "keywords": [
+                "template"
+            ]
+        },
+        {
+            "name": "phpunit/php-timer",
+            "version": "1.0.x-dev",
+            "source": {
+                "type": "git",
+                "url": "git://github.com/sebastianbergmann/php-timer.git",
+                "reference": "ecf7920b27003a9412b07dad79dbb5ad1249e6c3"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/ecf7920b27003a9412b07dad79dbb5ad1249e6c3",
+                "reference": "ecf7920b27003a9412b07dad79dbb5ad1249e6c3",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "time": "2013-01-30 06:08:51",
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "PHP/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "include-path": [
+                ""
+            ],
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sb@sebastian-bergmann.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Utility class for timing",
+            "homepage": "https://github.com/sebastianbergmann/php-timer/",
+            "keywords": [
+                "timer"
+            ]
+        },
+        {
+            "name": "phpunit/php-token-stream",
+            "version": "dev-master",
+            "source": {
+                "type": "git",
+                "url": "git://github.com/sebastianbergmann/php-token-stream.git",
+                "reference": "c25dd88e1592e66dee2553c99ef244203d5a1b98"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/c25dd88e1592e66dee2553c99ef244203d5a1b98",
+                "reference": "c25dd88e1592e66dee2553c99ef244203d5a1b98",
+                "shasum": ""
+            },
+            "require": {
+                "ext-tokenizer": "*",
+                "php": ">=5.3.3"
+            },
+            "time": "2013-01-07 10:56:35",
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "PHP/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "include-path": [
+                ""
+            ],
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sb@sebastian-bergmann.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Wrapper around PHP's tokenizer extension.",
+            "homepage": "https://github.com/sebastianbergmann/php-token-stream/",
+            "keywords": [
+                "tokenizer"
+            ]
+        },
+        {
+            "name": "phpunit/phpunit",
+            "version": "3.7.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/msonnabaum/phpunit.git",
+                "reference": "3.7.0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/msonnabaum/phpunit/zipball/3.7.0",
+                "reference": "3.7.0",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3",
+                "phpunit/php-file-iterator": ">=1.3.1@stable",
+                "phpunit/php-text-template": ">=1.1.1@stable",
+                "phpunit/php-code-coverage": ">=1.2.0@stable",
+                "phpunit/php-timer": ">=1.0.2@stable",
+                "phpunit/phpunit-mock-objects": ">=1.2.0@stable",
+                "symfony/yaml": ">=2.1.0@stable",
+                "ext-dom": "*",
+                "ext-pcre": "*",
+                "ext-reflection": "*",
+                "ext-spl": "*"
+            },
+            "suggest": {
+                "phpunit/php-invoker": ">=1.1.0@stable",
+                "ext-json": "*",
+                "ext-simplexml": "*",
+                "ext-tokenizer": "*"
+            },
+            "time": "2012-09-19 05:07:48",
+            "bin": [
+                "composer/bin/phpunit"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.7.x-dev"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "PHPUnit/Autoload.php"
+                ]
+            },
+            "include-path": [
+                "",
+                "../../symfony/yaml/"
+            ],
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "The PHP Unit Testing framework.",
+            "homepage": "http://www.phpunit.de/",
+            "keywords": [
+                "phpunit",
+                "xunit",
+                "testing"
+            ],
+            "support": {
+                "issues": "https://github.com/sebastianbergmann/phpunit/issues",
+                "irc": "irc://irc.freenode.net/phpunit",
+                "source": "https://github.com/msonnabaum/phpunit/tree/3.7.0"
+            }
+        },
+        {
+            "name": "phpunit/phpunit-mock-objects",
+            "version": "1.2.x-dev",
+            "source": {
+                "type": "git",
+                "url": "git://github.com/sebastianbergmann/phpunit-mock-objects.git",
+                "reference": "d49b5683200b5db9b1c64cb06f52f50d147891c4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/d49b5683200b5db9b1c64cb06f52f50d147891c4",
+                "reference": "d49b5683200b5db9b1c64cb06f52f50d147891c4",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3",
+                "phpunit/php-text-template": ">=1.1.1@stable"
+            },
+            "suggest": {
+                "ext-soap": "*"
+            },
+            "time": "2013-02-05 07:46:41",
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "PHPUnit/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "include-path": [
+                ""
+            ],
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sb@sebastian-bergmann.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Mock Object library for PHPUnit",
+            "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/",
+            "keywords": [
+                "mock",
+                "xunit"
+            ]
+        },
+        {
+            "name": "psr/log",
+            "version": "1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/log",
+                "reference": "1.0.0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://github.com/php-fig/log/archive/1.0.0.zip",
+                "reference": "1.0.0",
+                "shasum": ""
+            },
+            "time": "2012-12-21 11:40:51",
+            "type": "library",
+            "autoload": {
+                "psr-0": {
+                    "Psr\\Log\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for logging libraries",
+            "keywords": [
+                "log",
+                "psr",
+                "psr-3"
+            ]
+        },
+        {
             "name": "symfony-cmf/routing",
             "version": "dev-master",
             "target-dir": "Symfony/Cmf/Component/Routing",
@@ -389,7 +782,7 @@
             },
             "dist": {
                 "type": "zip",
-                "url": "https://github.com/symfony-cmf/Routing/archive/ea4a10dd971580a9a57a69397f165e25233ba667.zip",
+                "url": "https://api.github.com/repos/symfony-cmf/Routing/zipball/72df1da07b3c4edf16df169fb7987f504070fe0d",
                 "reference": "ea4a10",
                 "shasum": ""
             },
@@ -429,17 +822,17 @@
         },
         {
             "name": "symfony/class-loader",
-            "version": "dev-master",
+            "version": "v2.2.0-BETA2",
             "target-dir": "Symfony/Component/ClassLoader",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/ClassLoader",
-                "reference": "0730d0f8f99cfeafbc23271f1e87f54da15f1e06"
+                "reference": "v2.2.0-BETA2"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://github.com/symfony/ClassLoader/archive/0730d0f8f99cfeafbc23271f1e87f54da15f1e06.zip",
-                "reference": "0730d0f8f99cfeafbc23271f1e87f54da15f1e06",
+                "url": "https://api.github.com/repos/symfony/ClassLoader/zipball/v2.2.0-BETA2",
+                "reference": "v2.2.0-BETA2",
                 "shasum": ""
             },
             "require": {
@@ -448,7 +841,7 @@
             "require-dev": {
                 "symfony/finder": "2.2.*"
             },
-            "time": "2013-01-04 16:58:00",
+            "time": "2013-01-23 20:21:00",
             "type": "library",
             "extra": {
                 "branch-alias": {
@@ -479,17 +872,17 @@
         },
         {
             "name": "symfony/dependency-injection",
-            "version": "dev-master",
+            "version": "v2.2.0-BETA2",
             "target-dir": "Symfony/Component/DependencyInjection",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/DependencyInjection",
-                "reference": "13b47742e1ef22aed78132e5e0930d28571142a4"
+                "reference": "v2.2.0-BETA2"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://github.com/symfony/DependencyInjection/archive/13b47742e1ef22aed78132e5e0930d28571142a4.zip",
-                "reference": "13b47742e1ef22aed78132e5e0930d28571142a4",
+                "url": "https://github.com/symfony/DependencyInjection/archive/v2.2.0-BETA2.zip",
+                "reference": "v2.2.0-BETA2",
                 "shasum": ""
             },
             "require": {
@@ -503,7 +896,7 @@
                 "symfony/yaml": "2.2.*",
                 "symfony/config": "2.2.*"
             },
-            "time": "2013-01-04 16:58:00",
+            "time": "2013-01-23 20:21:00",
             "type": "library",
             "extra": {
                 "branch-alias": {
@@ -534,17 +927,17 @@
         },
         {
             "name": "symfony/event-dispatcher",
-            "version": "dev-master",
+            "version": "v2.2.0-BETA2",
             "target-dir": "Symfony/Component/EventDispatcher",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/EventDispatcher",
-                "reference": "0fcc9fa82fa3ea7ec2605af2e8137698fbaca3dc"
+                "reference": "v2.2.0-BETA2"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://github.com/symfony/EventDispatcher/archive/0fcc9fa82fa3ea7ec2605af2e8137698fbaca3dc.zip",
-                "reference": "0fcc9fa82fa3ea7ec2605af2e8137698fbaca3dc",
+                "url": "https://github.com/symfony/EventDispatcher/archive/v2.2.0-BETA2.zip",
+                "reference": "v2.2.0-BETA2",
                 "shasum": ""
             },
             "require": {
@@ -557,7 +950,7 @@
                 "symfony/dependency-injection": "2.2.*",
                 "symfony/http-kernel": "2.2.*"
             },
-            "time": "2013-01-04 17:02:19",
+            "time": "2013-01-17 15:25:59",
             "type": "library",
             "extra": {
                 "branch-alias": {
@@ -588,23 +981,23 @@
         },
         {
             "name": "symfony/http-foundation",
-            "version": "dev-master",
+            "version": "v2.2.0-BETA2",
             "target-dir": "Symfony/Component/HttpFoundation",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/HttpFoundation",
-                "reference": "5aa0119417f1fc8de7513d9432dd2f43f9d9e97b"
+                "reference": "v2.2.0-BETA2"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://github.com/symfony/HttpFoundation/archive/5aa0119417f1fc8de7513d9432dd2f43f9d9e97b.zip",
-                "reference": "5aa0119417f1fc8de7513d9432dd2f43f9d9e97b",
+                "url": "https://github.com/symfony/HttpFoundation/archive/v2.2.0-BETA2.zip",
+                "reference": "v2.2.0-BETA2",
                 "shasum": ""
             },
             "require": {
                 "php": ">=5.3.3"
             },
-            "time": "2013-01-04 17:02:19",
+            "time": "2013-01-21 16:56:55",
             "type": "library",
             "extra": {
                 "branch-alias": {
@@ -638,23 +1031,24 @@
         },
         {
             "name": "symfony/http-kernel",
-            "version": "dev-master",
+            "version": "v2.2.0-BETA2",
             "target-dir": "Symfony/Component/HttpKernel",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/HttpKernel",
-                "reference": "ce164c088666b80ffc116173065d504055414137"
+                "reference": "v2.2.0-BETA2"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://github.com/symfony/HttpKernel/archive/ce164c088666b80ffc116173065d504055414137.zip",
-                "reference": "ce164c088666b80ffc116173065d504055414137",
+                "url": "https://api.github.com/repos/symfony/HttpKernel/zipball/v2.2.0-BETA2",
+                "reference": "v2.2.0-BETA2",
                 "shasum": ""
             },
             "require": {
                 "php": ">=5.3.3",
                 "symfony/event-dispatcher": "2.2.*",
-                "symfony/http-foundation": "2.2.*"
+                "symfony/http-foundation": "2.2.*",
+                "psr/log": ">=1.0,<2.0"
             },
             "require-dev": {
                 "symfony/browser-kit": "2.2.*",
@@ -675,7 +1069,7 @@
                 "symfony/dependency-injection": "2.2.*",
                 "symfony/finder": "2.2.*"
             },
-            "time": "2013-01-04 16:58:00",
+            "time": "2013-01-24 07:15:19",
             "type": "library",
             "extra": {
                 "branch-alias": {
@@ -711,18 +1105,18 @@
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/Process",
-                "reference": "058ae5038a9623fa64b120a2d10ba81f1ade05c7"
+                "reference": "v2.1.7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://github.com/symfony/Process/archive/058ae5038a9623fa64b120a2d10ba81f1ade05c7.zip",
-                "reference": "058ae5038a9623fa64b120a2d10ba81f1ade05c7",
+                "url": "https://api.github.com/repos/symfony/Process/zipball/v2.1.7",
+                "reference": "v2.1.7",
                 "shasum": ""
             },
             "require": {
                 "php": ">=5.3.3"
             },
-            "time": "2012-12-06 10:00:55",
+            "time": "2013-01-16 09:27:54",
             "type": "library",
             "autoload": {
                 "psr-0": {
@@ -748,34 +1142,35 @@
         },
         {
             "name": "symfony/routing",
-            "version": "dev-master",
+            "version": "v2.2.0-BETA2",
             "target-dir": "Symfony/Component/Routing",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/Routing",
-                "reference": "87a66890450658ccac99702ac5e4f002ece9e23e"
+                "reference": "v2.2.0-BETA2"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://github.com/symfony/Routing/archive/87a66890450658ccac99702ac5e4f002ece9e23e.zip",
-                "reference": "87a66890450658ccac99702ac5e4f002ece9e23e",
+                "url": "https://api.github.com/repos/symfony/Routing/zipball/v2.2.0-BETA2",
+                "reference": "v2.2.0-BETA2",
                 "shasum": ""
             },
             "require": {
                 "php": ">=5.3.3"
             },
             "require-dev": {
-                "doctrine/common": ">=2.2,<2.4-dev",
                 "symfony/config": "2.2.*",
                 "symfony/yaml": "2.2.*",
-                "symfony/http-kernel": "2.2.*"
+                "symfony/http-kernel": "2.2.*",
+                "doctrine/common": ">=2.2,<2.4-dev",
+                "psr/log": ">=1.0,<2.0"
             },
             "suggest": {
-                "doctrine/common": ">=2.2,<2.4-dev",
                 "symfony/config": "2.2.*",
-                "symfony/yaml": "2.2.*"
+                "symfony/yaml": "2.2.*",
+                "doctrine/common": ">=2.2,<2.4-dev"
             },
-            "time": "2012-12-28 13:21:48",
+            "time": "2013-01-21 16:57:32",
             "type": "library",
             "extra": {
                 "branch-alias": {
@@ -853,7 +1248,7 @@
         },
         {
             "name": "symfony/translation",
-            "version": "dev-master",
+            "version": "2.2.x-dev",
             "target-dir": "Symfony/Component/Translation",
             "source": {
                 "type": "git",
@@ -862,7 +1257,7 @@
             },
             "dist": {
                 "type": "zip",
-                "url": "https://github.com/symfony/Translation/archive/v2.2.0-BETA2.zip",
+                "url": "https://api.github.com/repos/symfony/Translation/zipball/v2.2.0-BETA2",
                 "reference": "v2.2.0-BETA2",
                 "shasum": ""
             },
@@ -908,17 +1303,17 @@
         },
         {
             "name": "symfony/validator",
-            "version": "dev-master",
+            "version": "v2.2.0-BETA2",
             "target-dir": "Symfony/Component/Validator",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/Validator",
-                "reference": "f764b2e61c51c45f93bd4c9fe933e6a66928d2d3"
+                "reference": "v2.2.0-BETA2"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://github.com/symfony/Validator/archive/f764b2e61c51c45f93bd4c9fe933e6a66928d2d3.zip",
-                "reference": "f764b2e61c51c45f93bd4c9fe933e6a66928d2d3",
+                "url": "https://github.com/symfony/Validator/archive/v2.2.0-BETA2.zip",
+                "reference": "v2.2.0-BETA2",
                 "shasum": ""
             },
             "require": {
@@ -937,7 +1332,7 @@
                 "symfony/yaml": "2.2.*",
                 "symfony/config": "2.2.*"
             },
-            "time": "2013-01-24 10:00:40",
+            "time": "2013-01-21 16:57:12",
             "type": "library",
             "extra": {
                 "branch-alias": {
@@ -968,23 +1363,23 @@
         },
         {
             "name": "symfony/yaml",
-            "version": "dev-master",
+            "version": "v2.2.0-BETA2",
             "target-dir": "Symfony/Component/Yaml",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/Yaml",
-                "reference": "365b4df7802b9bb93084d2cc9a51fe88b488f956"
+                "reference": "v2.2.0-BETA2"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://github.com/symfony/Yaml/archive/365b4df7802b9bb93084d2cc9a51fe88b488f956.zip",
-                "reference": "365b4df7802b9bb93084d2cc9a51fe88b488f956",
+                "url": "https://api.github.com/repos/symfony/Yaml/zipball/v2.2.0-BETA2",
+                "reference": "v2.2.0-BETA2",
                 "shasum": ""
             },
             "require": {
                 "php": ">=5.3.3"
             },
-            "time": "2012-12-19 07:09:49",
+            "time": "2013-01-23 20:21:00",
             "type": "library",
             "extra": {
                 "branch-alias": {
@@ -1015,26 +1410,26 @@
         },
         {
             "name": "twig/twig",
-            "version": "v1.11.1",
+            "version": "v1.12.1",
             "source": {
                 "type": "git",
                 "url": "git://github.com/fabpot/Twig.git",
-                "reference": "v1.11.1"
+                "reference": "v1.12.1"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://github.com/fabpot/Twig/archive/v1.11.1.zip",
-                "reference": "v1.11.1",
+                "url": "https://github.com/fabpot/Twig/archive/v1.12.1.zip",
+                "reference": "v1.12.1",
                 "shasum": ""
             },
             "require": {
                 "php": ">=5.2.4"
             },
-            "time": "2012-11-11 17:17:59",
+            "time": "2013-01-15 20:03:52",
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.11-dev"
+                    "dev-master": "1.12-dev"
                 }
             },
             "autoload": {
@@ -1065,16 +1460,26 @@
     ],
     "packages-dev": null,
     "aliases": [
-
+        {
+            "alias": "dev-3.7",
+            "alias_normalized": "dev-3.7",
+            "version": "3.7.0.0",
+            "package": "phpunit/phpunit"
+        }
     ],
     "minimum-stability": "dev",
     "stability-flags": {
+        "symfony/class-loader": 10,
+        "symfony/dependency-injection": 10,
+        "symfony/event-dispatcher": 10,
+        "symfony/http-foundation": 10,
+        "symfony/http-kernel": 10,
+        "symfony/routing": 10,
         "symfony/serializer": 10,
-        "twig/twig": 0,
-        "doctrine/common": 0,
-        "guzzle/http": 0,
+        "symfony/validator": 10,
+        "symfony/yaml": 10,
         "kriswallsmith/assetic": 15,
         "symfony-cmf/routing": 20,
-        "easyrdf/easyrdf": 20
+        "easyrdf/easyrdf": 10
     }
 }
diff --git a/core/modules/breakpoint/tests/Drupal/breakpoint/Tests/BreakpointMediaQueryTest.php b/core/modules/breakpoint/tests/Drupal/breakpoint/Tests/BreakpointMediaQueryTest.php
new file mode 100644
index 0000000..1e2524f
--- /dev/null
+++ b/core/modules/breakpoint/tests/Drupal/breakpoint/Tests/BreakpointMediaQueryTest.php
@@ -0,0 +1,125 @@
+<?php
+/**
+ * @file
+ * Definition of Drupal\breakpoint\Tests\BreakpointMediaQueryTest.
+ */
+
+namespace Drupal\breakpoint\Tests;
+
+use Drupal\Tests\UnitTestCase;
+use Drupal\breakpoint\Plugin\Core\Entity\Breakpoint;
+use Drupal\breakpoint\InvalidBreakpointMediaQueryException;
+
+/**
+ * Tests for media queries in a breakpoint.
+ *
+ * @group Breakpoint
+ */
+class BreakpointMediaQueryTest extends UnitTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Breakpoint media query tests',
+      'description' => 'Test validation of media queries.',
+      'group' => 'Breakpoint',
+    );
+  }
+
+  /**
+   * Test valid media queries.
+   */
+  public function testValidMediaQueries() {
+    $media_queries = array(
+      // Bartik breakpoints.
+      '(min-width: 0px)',
+      'all and (min-width: 560px) and (max-width:850px)',
+      'all and (min-width: 851px)',
+      // Seven breakpoints.
+      '(min-width: 0em)',
+      'screen and (min-width: 40em)',
+      // Stark breakpoints.
+      '(min-width: 0px)',
+      'all and (min-width: 480px) and (max-width: 959px)',
+      'all and (min-width: 960px)',
+      // Other media queries.
+      '(orientation)',
+      'all and (orientation)',
+      'not all and (orientation)',
+      'only all and (orientation)',
+      'screen and (width)',
+      'screen and (width: 0)',
+      'screen and (width: 0px)',
+      'screen and (width: 0em)',
+      'screen and (min-width: -0)',
+      'screen and (max-width: 0)',
+      'screen and (max-width: 0.3)',
+      'screen and (min-width)',
+      // Multiline and comments.
+      'screen and /* this is a comment */ (min-width)',
+      "screen\nand /* this is a comment */ (min-width)",
+      "screen\n\nand /* this is\n a comment */ (min-width)",
+      // Unrecognized features are allowed.
+      'screen and (-webkit-min-device-pixel-ratio: 7)',
+      'screen and (min-orientation: landscape)',
+      'screen and (max-orientation: landscape)',
+    );
+
+    foreach ($media_queries as $media_query) {
+      $this->assertTrue(Breakpoint::isValidMediaQuery($media_query), $media_query . ' is valid.');
+    }
+  }
+
+  /**
+   * Test invalid media queries.
+   */
+  public function testInvalidMediaQueries() {
+    $media_queries = array(
+      '',
+      'not (orientation)',
+      'only (orientation)',
+      'all and not all',
+      'screen and (width: 0xx)',
+      'screen and (width: -8xx)',
+      'screen and (width: -xx)',
+      'screen and (width: xx)',
+      'screen and (width: px)',
+      'screen and (width: -8px)',
+      'screen and (width: -0.8px)',
+      'screen and (height: 0xx)',
+      'screen and (height: -8xx)',
+      'screen and (height: -xx)',
+      'screen and (height: xx)',
+      'screen and (height: px)',
+      'screen and (height: -8px)',
+      'screen and (height: -0.8px)',
+      'screen and (device-width: 0xx)',
+      'screen and (device-width: -8xx)',
+      'screen and (device-width: -xx)',
+      'screen and (device-width: xx)',
+      'screen and (device-width: px)',
+      'screen and (device-width: -8px)',
+      'screen and (device-width: -0.8px)',
+      'screen and (device-height: 0xx)',
+      'screen and (device-height: -8xx)',
+      'screen and (device-height: -xx)',
+      'screen and (device-height: xx)',
+      'screen and (device-height: px)',
+      'screen and (device-height: -8px)',
+      'screen and (device-height: -0.8px)',
+      'screen and (min-orientation)',
+      'screen and (max-orientation)',
+      'screen and (orientation: bogus)',
+      '(orientation: bogus)',
+      'screen and (ori"entation: bogus)',
+    );
+
+    foreach ($media_queries as $media_query) {
+      try {
+        $this->assertFalse(Breakpoint::isValidMediaQuery($media_query), $media_query . ' is not valid.');
+      }
+      catch (InvalidBreakpointMediaQueryException $e) {
+        $this->assertTrue(TRUE, sprintf('%s is not valid.', $media_query));
+      }
+    }
+  }
+}
diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module
index d7adb65..4eb114d 100644
--- a/core/modules/simpletest/simpletest.module
+++ b/core/modules/simpletest/simpletest.module
@@ -130,6 +130,23 @@ function simpletest_run_tests($test_list, $reporter = 'drupal') {
     ->useDefaults(array('test_id'))
     ->execute();
 
+  $phpunit_results = simpletest_run_phpunit_tests($test_id, $test_list['UnitTest']);
+  // Insert the results of the phpunit test run into the db so the results are
+  // displayed along with simpletest's results.
+  $query = db_insert('simpletest')->fields(array_keys($phpunit_results[0]));
+  foreach ($phpunit_results as $result) {
+    $query->values($result);
+  }
+  $query->execute();
+
+  if (!array_key_exists('WebTest', $test_list) || empty($test_list['WebTest'])) {
+    // Early return if there are no WebTests to run.
+    return $test_id;
+  }
+
+  // Contine with SimpleTests only.
+  $test_list = $test_list['WebTest'];
+
   // Clear out the previous verbose files.
   file_unmanaged_delete_recursive('public://simpletest/verbose');
 
@@ -157,6 +174,84 @@ function simpletest_run_tests($test_list, $reporter = 'drupal') {
 }
 
 /**
+ * Executes phpunit tests and returns the results of the run.
+ *
+ * @param $test_id
+ *   The current test ID.
+ * @param $unescaped_test_classnames
+ *   An array of test class names, including full namespaces, to be passed as
+ *   a regular expression to phpunit's --filter option.
+ *
+ * @return array
+ *   The parsed results of phpunit's junit xml output, in the format of the 
+ *   simpletest table's schema.
+ */
+function simpletest_run_phpunit_tests($test_id, array $unescaped_test_classnames) {
+  $phpunit_file = simpletest_phpunit_xml_filepath($test_id);
+  simpletest_phpunit_run_command($unescaped_test_classnames, $phpunit_file);
+  return simpletest_phpunit_xml_to_rows($test_id, $phpunit_file);
+}
+
+/**
+ * Returns the path to use for phpunit's --log-junit option.
+ *
+ * @param $test_id
+ *   The current test ID.
+ * @return string
+ *   Path to the phpunit xml file to use for the current test_id.
+ */
+function simpletest_phpunit_xml_filepath($test_id) {
+  return drupal_realpath('public://simpletest') . '/phpunit-' . $test_id . '.xml';
+}
+
+/**
+ * Returns the path to core's phpunit.xml.dist configuration file.
+ *
+ * @return string
+ *   Path to core's phpunit.xml.dist configuration file.
+ */
+function simpletest_phpunit_configuration_filepath() {
+  return DRUPAL_ROOT . '/core/phpunit.xml.dist';
+}
+
+/**
+ * Executes the phpunit command. We do this via exec in a subshell so that the
+ * environment is isolated when running tests via the simpletest UI.
+ *
+ * @param array $unescaped_test_classnames
+ *   An array of test class names, including full namespaces, to be passed as
+ *   a regular expression to phpunit's --filter option.
+ * @param string $phpunit_file
+ *   A filepath to use for phpunit's --log-junit option.
+ */
+function simpletest_phpunit_run_command(array $unescaped_test_classnames, $phpunit_file) {
+  $phpunit_bin = DRUPAL_ROOT . "/core/vendor/bin/phpunit";
+
+  // Double escape namespaces so they'll work in a regexp.
+  $escaped_test_classnames = array_map(function($class) {
+    return addslashes($class);
+  }, $unescaped_test_classnames);
+
+  $filter_string = implode("|", $escaped_test_classnames);
+
+  $command = array(
+    $phpunit_bin,
+    '--filter',
+    escapeshellarg($filter_string),
+    '--log-junit',
+    escapeshellarg($phpunit_file),
+  );
+
+  // Need to change directories before running the command so that we can use
+  // relative paths in the configuration file's exclusions.
+  $old_cwd = getcwd();
+  chdir(DRUPAL_ROOT . "/core");
+  $ret = exec(join($command, " "));
+  chdir($old_cwd);
+  return $ret;
+}
+
+/**
  * Batch operation callback.
  */
 function _simpletest_batch_operation($test_list_init, $test_id, &$context) {
@@ -397,10 +492,22 @@ function simpletest_classloader_register() {
     $matches = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.' . $info['extension'] . '$/', $info['dir']);
     foreach ($matches as $name => $file) {
       drupal_classloader_register($name, dirname($file->uri));
+      drupal_classloader()->registerNamespace('Drupal\\' . $name . '\\Tests', DRUPAL_ROOT . '/' . dirname($file->uri) . '/tests');
       // While being there, prime drupal_get_filename().
       drupal_get_filename($type, $name, $file->uri);
     }
   }
+
+  // Register the core test directory so we can find Drupal\UnitTestCase.
+  drupal_classloader()->registerNamespace('Drupal\\Tests', DRUPAL_ROOT . '/core/tests');
+
+  // Manually register phpunit prefixes because they use a classmap instead of a
+  // prefix. This can be safely removed if we move to using composer's
+  // autoloader with a classmap.
+  drupal_classloader()->registerPrefixes(array(
+    'PHPUnit' => DRUPAL_ROOT . '/core/vendor/phpunit/phpunit',
+    'File_Iterator' => DRUPAL_ROOT . '/core/vendor/phpunit/php-file-iterator/',
+  ));
 }
 
 /**
@@ -568,3 +675,64 @@ function simpletest_library_info() {
 
   return $libraries;
 }
+
+/**
+ * Get PHPUnit Classes
+ *
+ * @param bool $name_only
+ *  If TRUE, returns a flat array of class names only.
+ */
+function simpletest_phpunit_get_available_tests() {
+  // Load the PHPUnit configuration file, which tells us where to find the tests.
+  $phpunit_config = simpletest_phpunit_configuration_filepath();
+  $configuration = PHPUnit_Util_Configuration::getInstance($phpunit_config);
+  // Find all the tests and get a list of unique class names.
+  $test_suite = $configuration->getTestSuiteConfiguration(NULL);
+  $test_classes = array();
+  foreach ($test_suite AS $test) {
+    $name = get_class($test);
+    if (!array_key_exists($name, $test_classes)) {
+      $test_classes[$name] =  $test->getInfo();
+    }
+  }
+
+  return $test_classes;
+}
+
+/**
+ * Converts phpunit's junit xml output to an array of rows that can be inserted
+ * into the simpletest results table.
+ *
+ * @param $test_id
+ *   The current test ID.
+ * @param $phpunit_xml_file
+ *   Path to the phpunit xml file.
+ */
+function simpletest_phpunit_xml_to_rows($test_id, $phpunit_xml_file) {
+  $contents = file_get_contents($phpunit_xml_file);
+  $xml = new SimpleXMLElement($contents);
+  $records = array();
+  foreach ($xml->testsuite as $testsuite) {
+    foreach ($testsuite as $suite) {
+      foreach ($suite as $testcase) {
+        $message = '';
+        if ($testcase->failure) {
+          $lines = explode("\n", $testcase->failure);
+          $message = $lines[2];
+        }
+        $attributes = $testcase->attributes();
+        $records[] = array(
+          'test_id' => $test_id,
+          'test_class' => (string)$attributes->class,
+          'status' => empty($testcase->failure) ? 'pass' : 'fail',
+          'message' => $message,
+          'message_group' => 'Other', // TODO: Check on the proper values for this.
+          'function' => $attributes->class . '->' . $attributes->name . '()',
+          'line' => (string)$attributes->line,
+          'file' => (string)$attributes->file,
+        );
+      }
+    }
+  }
+  return $records;
+}
diff --git a/core/modules/simpletest/simpletest.pages.inc b/core/modules/simpletest/simpletest.pages.inc
index f1d55d8..d228357 100644
--- a/core/modules/simpletest/simpletest.pages.inc
+++ b/core/modules/simpletest/simpletest.pages.inc
@@ -8,7 +8,7 @@
 /**
  * List tests arranged in groups that can be selected and run.
  */
-function simpletest_test_form($form) {
+function simpletest_test_form($form, &$form_state) {
   $form['tests'] = array(
     '#type' => 'details',
     '#title' => t('Tests'),
@@ -21,6 +21,9 @@ function simpletest_test_form($form) {
 
   // Generate the list of tests arranged by group.
   $groups = simpletest_test_get_all();
+  $groups['PHPUnit'] = simpletest_phpunit_get_available_tests();
+  $form_state['storage']['PHPUnit'] = $groups['PHPUnit'];
+
   foreach ($groups as $group => $tests) {
     $form['tests']['table'][$group] = array(
       '#collapsed' => TRUE,
@@ -180,11 +183,15 @@ function simpletest_test_form_submit($form, &$form_state) {
   // Get list of tests.
   $tests_list = array();
   simpletest_classloader_register();
+
+  $phpunit_all = array_keys($form_state['storage']['PHPUnit']);
+
   foreach ($form_state['values'] as $class_name => $value) {
     // Since class_exists() will likely trigger an autoload lookup,
     // we do the fast check first.
     if ($value === 1 && class_exists($class_name)) {
-      $tests_list[] = $class_name;
+      $test_type = in_array($class_name, $phpunit_all) ? 'UnitTest' : 'WebTest';
+      $tests_list[$test_type][] = $class_name;
     }
   }
   if (count($tests_list) > 0 ) {
@@ -242,7 +249,6 @@ function simpletest_result_form($form, &$form_state, $test_id) {
       '#type' => 'details',
       '#title' => $info['name'],
       '#description' => $info['description'],
-      '#collapsible' => TRUE,
     );
     $form['result']['results'][$group]['summary'] = $summary;
     $group_summary = &$form['result']['results'][$group]['summary'];
@@ -455,7 +461,6 @@ function simpletest_settings_form($form, &$form_state) {
     '#type' => 'details',
     '#title' => t('HTTP authentication'),
     '#description' => t('HTTP auth settings to be used by the SimpleTest browser during testing. Useful when the site requires basic HTTP authentication.'),
-    '#collapsible' => TRUE,
     '#collapsed' => TRUE,
   );
   $form['httpauth']['simpletest_httpauth_method'] = array(
@@ -521,4 +526,3 @@ function simpletest_settings_form_validate($form, &$form_state) {
     form_set_error('simpletest_httpauth_username', t('HTTP authentication credentials must include a username in addition to a password.'));
   }
 }
-
diff --git a/core/phpunit.xml.dist b/core/phpunit.xml.dist
new file mode 100644
index 0000000..6ca7e89
--- /dev/null
+++ b/core/phpunit.xml.dist
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit bootstrap="tests/bootstrap.php" colors="true">
+  <testsuites>
+    <testsuite name="Drupal Unit Test Suite">
+      <directory>./tests/*</directory>
+      <directory>./modules/*/tests/*</directory>
+      <directory>../modules/*/tests/*</directory>
+      <directory>../sites/*/modules/*/tests/*</directory>
+      <!-- Exclude files that end in Test.php that aren't actually phpunit tests. -->
+      <exclude>./modules/config/tests/config_test/lib/Drupal/config_test</exclude>
+      <exclude>./modules/views/tests/views_test_data/lib/Drupal/views_test_data</exclude>
+    </testsuite>
+  </testsuites>
+</phpunit>
+
diff --git a/core/tests/Drupal/Tests/Core/Cache/BackendChainImplementationUnitTest.php b/core/tests/Drupal/Tests/Core/Cache/BackendChainImplementationUnitTest.php
new file mode 100644
index 0000000..2a3db3d
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Cache/BackendChainImplementationUnitTest.php
@@ -0,0 +1,308 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Tests\Core\Cache\BackendChainImplementationUnitTest.
+ */
+
+namespace Drupal\Tests\Core\Cache;
+
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Cache\BackendChain;
+use Drupal\Core\Cache\MemoryBackend;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * Tests implementation-specific functionality of the BackendChain backend.
+ */
+class BackendChainImplementationUnitTest extends UnitTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Backend chain implementation',
+      'description' => 'Unit test of backend chain implementation specifics.',
+      'group' => 'Cache'
+    );
+  }
+
+  /**
+   * Chain that will be heavily tested.
+   *
+   * @var Drupal\Core\Cache\BackendChain
+   */
+  protected $chain;
+
+  /**
+   * First backend in the chain.
+   *
+   * @var Drupal\Core\Cache\CacheBackendInterface
+   */
+  protected $firstBackend;
+
+  /**
+   * Second backend in the chain.
+   *
+   * @var Drupal\Core\Cache\CacheBackendInterface
+   */
+  protected $secondBackend;
+
+  /**
+   * Third backend in the chain.
+   *
+   * @var Drupal\Core\Cache\CacheBackendInterface
+   */
+  protected $thirdBackend;
+
+  public function setUp() {
+    parent::setUp();
+
+    // Set up three memory backends to be used in the chain.
+    $this->firstBackend = new MemoryBackend('foo');
+    $this->secondBackend = new MemoryBackend('bar');
+    $this->thirdBackend = new MemoryBackend('baz');
+
+    // Set an initial fixed dataset for all testing. The next three data
+    // collections will test two edge cases (last backend has the data, and
+    // first backend has the data) and will test a normal use case (middle
+    // backend has the data). We should have a complete unit test with those.
+    // Note that in all cases, when the same key is set on more than one
+    // backend, the values are voluntarily different, this ensures in which
+    // backend we actually fetched the key when doing get calls.
+
+    // Set a key present on all backends (for delete).
+    $this->firstBackend->set('t123', 1231);
+    $this->secondBackend->set('t123', 1232);
+    $this->thirdBackend->set('t123', 1233);
+
+    // Set a key present on the second and the third (for get), those two will
+    // be different, this will ensure from where we get the key.
+    $this->secondBackend->set('t23', 232);
+    $this->thirdBackend->set('t23', 233);
+
+    // Set a key on only the third, we will ensure propagation using this one.
+    $this->thirdBackend->set('t3', 33);
+
+    // Create the chain.
+    $this->chain = new BackendChain('foobarbaz');
+    $this->chain
+      ->appendBackend($this->firstBackend)
+      ->appendBackend($this->secondBackend)
+      ->appendBackend($this->thirdBackend);
+  }
+
+  /**
+   * Test the get feature.
+   */
+  public function testGet() {
+    $cached = $this->chain->get('t123');
+    $this->assertNotEquals(FALSE, $cached, 'Got key that is on all backends');
+    $this->assertEquals(1231, $cached->data, 'Got the key from the backend 1');
+
+    $cached = $this->chain->get('t23');
+    $this->assertNotEquals(FALSE, $cached, 'Got key that is on backends 2 and 3');
+    $this->assertEquals(232, $cached->data, 'Got the key from the backend 2');
+
+    $cached = $this->chain->get('t3');
+    $this->assertNotEquals(FALSE, $cached, 'Got key that is on the backend 3');
+    $this->assertEquals(33, $cached->data, 'Got the key from the backend 3');
+  }
+
+  /**
+   * Test the get multiple feature.
+   */
+  public function testGetMultiple() {
+    $cids = array('t123', 't23', 't3', 't4');
+
+    $ret = $this->chain->getMultiple($cids);
+    $this->assertEquals($ret['t123']->data, 1231, 'Got key 123 and value is from the first backend');
+    $this->assertEquals($ret['t23']->data, 232, 'Got key 23 and value is from the second backend');
+    $this->assertEquals($ret['t3']->data, 33, 'Got key 3 and value is from the third backend');
+    $this->assertFalse(array_key_exists('t4', $ret), "Didn't get the nonexistent key");
+
+    $this->assertFalse(in_array('t123', $cids), "Existing key 123 has been removed from &\$cids");
+    $this->assertFalse(in_array('t23', $cids), "Existing key 23 has been removed from &\$cids");
+    $this->assertFalse(in_array('t3', $cids), "Existing key 3 has been removed from &\$cids");
+    $this->assertTrue(in_array('t4', $cids), "Non existing key 4 is still in &\$cids");
+  }
+
+  /**
+   * Test that set will propagate.
+   */
+  public function testSet() {
+    $this->chain->set('test', 123);
+
+    $cached = $this->firstBackend->get('test');
+    $this->assertNotEquals(FALSE, $cached, 'Test key is in the first backend');
+    $this->assertEquals(123, $cached->data, 'Test key has the right value');
+
+    $cached = $this->secondBackend->get('test');
+    $this->assertNotEquals(FALSE, $cached, 'Test key is in the second backend');
+    $this->assertEquals(123, $cached->data, 'Test key has the right value');
+
+    $cached = $this->thirdBackend->get('test');
+    $this->assertNotEquals(FALSE, $cached, 'Test key is in the third backend');
+    $this->assertEquals(123, $cached->data, 'Test key has the right value');
+  }
+
+  /**
+   * Test that delete will propagate.
+   */
+  public function testDelete() {
+    $this->chain->set('test', 5);
+
+    $cached = $this->firstBackend->get('test');
+    $this->assertNotEquals(FALSE, $cached, 'Test key has been added to the first backend');
+    $cached = $this->secondBackend->get('test');
+    $this->assertNotEquals(FALSE, $cached, 'Test key has been added to the first backend');
+    $cached = $this->thirdBackend->get('test');
+    $this->assertNotEquals(FALSE, $cached, 'Test key has been added to the first backend');
+
+    $this->chain->delete('test');
+
+    $cached = $this->firstBackend->get('test');
+    $this->assertEquals(FALSE, $cached, 'Test key is removed from the first backend');
+    $cached = $this->secondBackend->get('test');
+    $this->assertEquals(FALSE, $cached, 'Test key is removed from the second backend');
+    $cached = $this->thirdBackend->get('test');
+    $this->assertEquals(FALSE, $cached, 'Test key is removed from the third backend');
+  }
+
+  /**
+   * Ensure get values propagation to previous backends.
+   */
+  public function testGetHasPropagated() {
+    $this->chain->get('t23');
+    $cached = $this->firstBackend->get('t23');
+    $this->assertNotEquals(FALSE, $cached, 'Test 2 has been propagated to the first backend');
+
+    $this->chain->get('t3');
+    $cached = $this->firstBackend->get('t3');
+    $this->assertNotEquals(FALSE, $cached, 'Test 3 has been propagated to the first backend');
+    $cached = $this->secondBackend->get('t3');
+    $this->assertNotEquals(FALSE, $cached, 'Test 3 has been propagated to the second backend');
+  }
+
+  /**
+   * Ensure get multiple values propagation to previous backends.
+   */
+  public function testGetMultipleHasPropagated() {
+    $cids = array('t3', 't23');
+    $this->chain->getMultiple($cids);
+
+    $cached = $this->firstBackend->get('t3');
+    $this->assertNotEquals(FALSE, $cached, 'Test 3 has been propagated to the first backend');
+    $this->assertEquals(33, $cached->data, 'And value has been kept');
+    $cached = $this->secondBackend->get('t3');
+    $this->assertNotEquals(FALSE, $cached, 'Test 3 has been propagated to the second backend');
+    $this->assertEquals(33, $cached->data, 'And value has been kept');
+
+    $cached = $this->firstBackend->get('t23');
+    $this->assertNotEquals(FALSE, $cached, 'Test 2 has been propagated to the first backend');
+    $this->assertEquals(232, $cached->data, 'And value has been kept');
+  }
+
+  /**
+   * Test that the chain is not empty when at least one backend has data.
+   */
+  public function testNotEmptyIfOneBackendHasTheKey() {
+    $this->assertFalse($this->chain->isEmpty(), 'Chain is not empty');
+
+    // This is the only test that needs to start with an empty chain.
+    $this->chain->deleteAll();
+    $this->assertTrue($this->chain->isEmpty(), 'Chain have been emptied by the deleteAll() call');
+
+    $this->secondBackend->set('test', 5);
+    $this->assertFalse($this->chain->isEmpty(), 'Chain is not empty anymore now that the second backend has something');
+  }
+
+  /**
+   * Test that the delete all operation is propagated to all backends in the chain.
+   */
+  public function testDeleteAllPropagation() {
+    // Set both expiring and permanent keys.
+    $this->chain->set('test1', 1, CacheBackendInterface::CACHE_PERMANENT);
+    $this->chain->set('test2', 3, time() + 1000);
+    $this->chain->deleteAll();
+
+    $this->assertTrue($this->firstBackend->isEmpty(), 'First backend is empty after delete all.');
+    $this->assertTrue($this->secondBackend->isEmpty(), 'Second backend is empty after delete all.');
+    $this->assertTrue($this->thirdBackend->isEmpty(), 'Third backend is empty after delete all.');
+  }
+
+  /**
+   * Test that the delete tags operation is propagated to all backends
+   * in the chain.
+   */
+  public function testDeleteTagsPropagation() {
+    // Create two cache entries with the same tag and tag value.
+    $this->chain->set('test_cid_clear1', 'foo', CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => 2));
+    $this->chain->set('test_cid_clear2', 'foo', CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => 2));
+    $this->assertNotEquals(FALSE, $this->firstBackend->get('test_cid_clear1')
+      && $this->firstBackend->get('test_cid_clear2')
+      && $this->secondBackend->get('test_cid_clear1')
+      && $this->secondBackend->get('test_cid_clear2')
+      && $this->thirdBackend->get('test_cid_clear1')
+      && $this->thirdBackend->get('test_cid_clear2'),
+      'Two cache items were created in all backends.');
+
+    // Invalidate test_tag of value 1. This should invalidate both entries.
+    $this->chain->deleteTags(array('test_tag' => 2));
+    $this->assertEquals(FALSE, $this->firstBackend->get('test_cid_clear1')
+      && $this->firstBackend->get('test_cid_clear2')
+      && $this->secondBackend->get('test_cid_clear1')
+      && $this->secondBackend->get('test_cid_clear2')
+      && $this->thirdBackend->get('test_cid_clear1')
+      && $this->thirdBackend->get('test_cid_clear2'),
+      'Two caches removed from all backends after clearing a cache tag.');
+
+    // Create two cache entries with the same tag and an array tag value.
+    $this->chain->set('test_cid_clear1', 'foo', CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => array(1)));
+    $this->chain->set('test_cid_clear2', 'foo', CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => array(1)));
+    $this->assertNotEquals(FALSE, $this->firstBackend->get('test_cid_clear1')
+      && $this->firstBackend->get('test_cid_clear2')
+      && $this->secondBackend->get('test_cid_clear1')
+      && $this->secondBackend->get('test_cid_clear2')
+      && $this->thirdBackend->get('test_cid_clear1')
+      && $this->thirdBackend->get('test_cid_clear2'),
+      'Two cache items were created in all backends.');
+
+    // Invalidate test_tag of value 1. This should invalidate both entries.
+    $this->chain->deleteTags(array('test_tag' => array(1)));
+    $this->assertEquals(FALSE, $this->firstBackend->get('test_cid_clear1')
+      && $this->firstBackend->get('test_cid_clear2')
+      && $this->secondBackend->get('test_cid_clear1')
+      && $this->secondBackend->get('test_cid_clear2')
+      && $this->thirdBackend->get('test_cid_clear1')
+      && $this->thirdBackend->get('test_cid_clear2'),
+      'Two caches removed from all backends after clearing a cache tag.');
+
+    // Create three cache entries with a mix of tags and tag values.
+    $this->chain->set('test_cid_clear1', 'foo', CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => array(1)));
+    $this->chain->set('test_cid_clear2', 'foo', CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => array(2)));
+    $this->chain->set('test_cid_clear3', 'foo', CacheBackendInterface::CACHE_PERMANENT, array('test_tag_foo' => array(3)));
+    $this->assertNotEquals(FALSE, $this->firstBackend->get('test_cid_clear1')
+      && $this->firstBackend->get('test_cid_clear2')
+      && $this->firstBackend->get('test_cid_clear3')
+      && $this->secondBackend->get('test_cid_clear1')
+      && $this->secondBackend->get('test_cid_clear2')
+      && $this->secondBackend->get('test_cid_clear3')
+      && $this->thirdBackend->get('test_cid_clear1')
+      && $this->thirdBackend->get('test_cid_clear2')
+      && $this->thirdBackend->get('test_cid_clear3'),
+      'Three cached items were created in all backends.');
+
+    $this->chain->deleteTags(array('test_tag_foo' => array(3)));
+    $this->assertNotEquals(FALSE, $this->firstBackend->get('test_cid_clear1')
+      && $this->firstBackend->get('test_cid_clear2')
+      && $this->secondBackend->get('test_cid_clear1')
+      && $this->secondBackend->get('test_cid_clear2')
+      && $this->thirdBackend->get('test_cid_clear1')
+      && $this->thirdBackend->get('test_cid_clear2'),
+      'Cached items not matching the tag were not cleared from any of the backends.');
+
+    $this->assertEquals(FALSE, $this->firstBackend->get('test_cid_clear3')
+      && $this->secondBackend->get('test_cid_clear3')
+      && $this->thirdBackend->get('test_cid_clear3'),
+      'Cached item matching the tag was removed from all backends.');
+  }
+}
diff --git a/core/tests/Drupal/Tests/Core/Cache/GenericCacheBackendUnitTestBase.php b/core/tests/Drupal/Tests/Core/Cache/GenericCacheBackendUnitTestBase.php
new file mode 100644
index 0000000..9ca9bb7
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Cache/GenericCacheBackendUnitTestBase.php
@@ -0,0 +1,541 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Tests\Core\Cache\GenericCacheBackendUnitTestCase.
+ */
+
+namespace Drupal\Tests\Core\Cache;
+
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * Tests any cache backend.
+ *
+ * Full generic unit test suite for any cache backend. In order to use it for a
+ * cache backend implementation, extend this class and override the
+ * createBackendInstace() method to return an object.
+ *
+ * @see DatabaseBackendUnitTestCase
+ *   For a full working implementation.
+ */
+abstract class GenericCacheBackendUnitTestCase extends UnitTestCase {
+
+  /**
+   * Array of objects implementing Drupal\Core\Cache\CacheBackendInterface.
+   *
+   * @var array
+   */
+  protected $cachebackends;
+
+  /**
+   * Cache bin to use for testing.
+   *
+   * @var string
+   */
+  protected $testBin;
+
+  /**
+   * Random value to use in tests.
+   *
+   * @var string
+   */
+  protected $defaultValue;
+
+  /**
+   * Gets the testing bin.
+   *
+   * Override this method if you want to work on a different bin than the
+   * default one.
+   *
+   * @return string
+   *   Bin name.
+   */
+  protected function getTestBin() {
+    if (!isset($this->testBin)) {
+      $this->testBin = 'page';
+    }
+    return $this->testBin;
+  }
+
+  /**
+   * Creates a cache backend to test.
+   *
+   * Override this method to test a CacheBackend.
+   *
+   * @param string $bin
+   *   Bin name to use for this backend instance.
+   *
+   * @return Drupal\Core\Cache\CacheBackendInterface
+   *   Cache backend to test.
+   */
+  protected abstract function createCacheBackend($bin);
+
+  /**
+   * Allows specific implementation to change the environment before a test run.
+   */
+  public function setUpCacheBackend() {
+  }
+
+  /**
+   * Allows alteration of environment after a test run but before tear down.
+   *
+   * Used before the real tear down because the tear down will change things
+   * such as the database prefix.
+   */
+  public function tearDownCacheBackend() {
+  }
+
+  /**
+   * Gets a backend to test; this will get a shared instance set in the object.
+   *
+   * @return Drupal\Core\Cache\CacheBackendInterface
+   *   Cache backend to test.
+   */
+  final function getCacheBackend($bin = null) {
+    if (!isset($bin)) {
+      $bin = $this->getTestBin();
+    }
+    if (!isset($this->cachebackends[$bin])) {
+      $this->cachebackends[$bin] = $this->createCacheBackend($bin);
+      // Ensure the backend is empty.
+      $this->cachebackends[$bin]->deleteAll();
+    }
+    return $this->cachebackends[$bin];
+  }
+
+  public function setUp() {
+    $this->cachebackends = array();
+    $this->defaultValue = $this->randomName(10);
+
+    parent::setUp();
+
+    $this->setUpCacheBackend();
+  }
+
+  public function tearDown() {
+    // Destruct the registered backend, each test will get a fresh instance,
+    // properly emptying it here ensure that on persistant data backends they
+    // will come up empty the next test.
+    foreach ($this->cachebackends as $bin => $cachebackend) {
+      $this->cachebackends[$bin]->deleteAll();
+    }
+    unset($this->cachebackends);
+
+    $this->tearDownCacheBackend();
+
+    parent::tearDown();
+  }
+
+  /**
+   * Tests the get and set methods of Drupal\Core\Cache\CacheBackendInterface.
+   */
+  public function testSetGet() {
+    $backend = $this->getCacheBackend();
+
+    $this->assertEquals(FALSE, $backend->get('test1'), "Backend does not contain data for cache id test1.");
+    $backend->set('test1', 7);
+    $cached = $backend->get('test1');
+    $this->assert(is_object($cached), "Backend returned an object for cache id test1.");
+    $this->assertEquals(7, $cached->data);
+    $this->assertTrue($cached->valid, 'Item is marked as valid.');
+    $this->assertEqual($cached->created, REQUEST_TIME, 'Created time is correct.');
+    $this->assertEqual($cached->expire, CacheBackendInterface::CACHE_PERMANENT, 'Expire time is correct.');
+
+    $this->assertEquals(FALSE, $backend->get('test2'), "Backend does not contain data for cache id test2.");
+    $backend->set('test2', array('value' => 3), REQUEST_TIME + 3);
+    $cached = $backend->get('test2');
+    $this->assert(is_object($cached), "Backend returned an object for cache id test2.");
+    $this->assertEquals(array('value' => 3), $cached->data);
+    $this->assertTrue($cached->valid, 'Item is marked as valid.');
+    $this->assertEqual($cached->created, REQUEST_TIME, 'Created time is correct.');
+    $this->assertEqual($cached->expire, REQUEST_TIME + 3, 'Expire time is correct.');
+
+    $backend->set('test3', 'foobar', REQUEST_TIME - 3);
+    $this->assertFalse($backend->get('test3'), 'Invalid item not returned.');
+    $cached = $backend->get('test3', TRUE);
+    $this->assert(is_object($cached), 'Backend returned an object for cache id test3.');
+    $this->assertFalse($cached->valid, 'Item is marked as valid.');
+    $this->assertEqual($cached->created, REQUEST_TIME, 'Created time is correct.');
+    $this->assertEqual($cached->expire, REQUEST_TIME - 3, 'Expire time is correct.');
+  }
+
+  /**
+   * Tests Drupal\Core\Cache\CacheBackendInterface::delete().
+   */
+  public function testDelete() {
+    $backend = $this->getCacheBackend();
+
+    $this->assertEquals(FALSE, $backend->get('test1'), "Backend does not contain data for cache id test1.");
+    $backend->set('test1', 7);
+    $this->assert(is_object($backend->get('test1')), "Backend returned an object for cache id test1.");
+
+    $this->assertEquals(FALSE, $backend->get('test2'), "Backend does not contain data for cache id test2.");
+    $backend->set('test2', 3);
+    $this->assert(is_object($backend->get('test2')), "Backend returned an object for cache id %cid.");
+
+    $backend->delete('test1');
+    $this->assertEquals(FALSE, $backend->get('test1'), "Backend does not contain data for cache id test1 after deletion.");
+
+    $cached = $backend->get('test2');
+    $this->assert(is_object($backend->get('test2')), "Backend still has an object for cache id test2.");
+
+    $backend->delete('test2');
+    $this->assertEquals(FALSE, $backend->get('test2'), "Backend does not contain data for cache id test2 after deletion.");
+  }
+
+  /**
+   * Tests data type preservation.
+   */
+  public function testValueTypeIsKept() {
+    $backend = $this->getCacheBackend();
+
+    $variables = array(
+      'test1' => 1,
+      'test2' => '0',
+      'test3' => '',
+      'test4' => 12.64,
+      'test5' => FALSE,
+      'test6' => array(1,2,3),
+    );
+
+    // Create cache entries.
+    foreach ($variables as $cid => $data) {
+      $backend->set($cid, $data);
+    }
+
+    // Retrieve and test cache objects.
+    foreach ($variables as $cid => $value) {
+      $object = $backend->get($cid);
+      $this->assert(is_object($object), sprintf("Backend returned an object for cache id %s.", $cid));
+      $this->assertEquals($value, $object->data, sprintf("Data of cached id %s kept is identical in type and value", $cid));
+    }
+  }
+
+  /**
+   * Tests Drupal\Core\Cache\CacheBackendInterface::getMultiple().
+   */
+  public function testGetMultiple() {
+    $backend = $this->getCacheBackend();
+
+    // Set numerous testing keys.
+    $backend->set('test1', 1);
+    $backend->set('test2', 3);
+    $backend->set('test3', 5);
+    $backend->set('test4', 7);
+    $backend->set('test5', 11);
+    $backend->set('test6', 13);
+    $backend->set('test7', 17);
+
+    // Mismatch order for harder testing.
+    $reference = array(
+      'test3',
+      'test7',
+      'test21', // Cid does not exist.
+      'test6',
+      'test19', // Cid does not exist until added before second getMultiple().
+      'test2',
+    );
+
+    $cids = $reference;
+    $ret = $backend->getMultiple($cids);
+    // Test return - ensure it contains existing cache ids.
+    $this->assert(isset($ret['test2']), "Existing cache id test2 is set.");
+    $this->assert(isset($ret['test3']), "Existing cache id test3 is set.");
+    $this->assert(isset($ret['test6']), "Existing cache id test6 is set.");
+    $this->assert(isset($ret['test7']), "Existing cache id test7 is set.");
+    // Test return - ensure that objects has expected properties.
+    $this->assertTrue($ret['test2']->valid, 'Item is marked as valid.');
+    $this->assertEqual($ret['test2']->created, REQUEST_TIME, 'Created time is correct.');
+    $this->assertEqual($ret['test2']->expire, CacheBackendInterface::CACHE_PERMANENT, 'Expire time is correct.');
+    // Test return - ensure it does not contain nonexistent cache ids.
+    $this->assertFalse(isset($ret['test19']), "Nonexistent cache id test19 is not set.");
+    $this->assertFalse(isset($ret['test21']), "Nonexistent cache id test21 is not set.");
+    // Test values.
+    $this->assertEquals($ret['test2']->data, 3, "Existing cache id test2 has the correct value.");
+    $this->assertEquals($ret['test3']->data, 5, "Existing cache id test3 has the correct value.");
+    $this->assertEquals($ret['test6']->data, 13, "Existing cache id test6 has the correct value.");
+    $this->assertEquals($ret['test7']->data, 17, "Existing cache id test7 has the correct value.");
+    // Test $cids array - ensure it contains cache id's that do not exist.
+    $this->assert(in_array('test19', $cids), "Nonexistent cache id test19 is in cids array.");
+    $this->assert(in_array('test21', $cids), "Nonexistent cache id test21 is in cids array.");
+    // Test $cids array - ensure it does not contain cache id's that exist.
+    $this->assertFalse(in_array('test2', $cids), "Existing cache id test2 is not in cids array.");
+    $this->assertFalse(in_array('test3', $cids), "Existing cache id test3 is not in cids array.");
+    $this->assertFalse(in_array('test6', $cids), "Existing cache id test6 is not in cids array.");
+    $this->assertFalse(in_array('test7', $cids), "Existing cache id test7 is not in cids array.");
+
+    // Test a second time after deleting and setting new keys which ensures that
+    // if the backend uses statics it does not cause unexpected results.
+    $backend->delete('test3');
+    $backend->delete('test6');
+    $backend->set('test19', 57);
+
+    $cids = $reference;
+    $ret = $backend->getMultiple($cids);
+    // Test return - ensure it contains existing cache ids.
+    $this->assert(isset($ret['test2']), "Existing cache id test2 is set");
+    $this->assert(isset($ret['test7']), "Existing cache id test7 is set");
+    $this->assert(isset($ret['test19']), "Added cache id test19 is set");
+    // Test return - ensure it does not contain nonexistent cache ids.
+    $this->assertFalse(isset($ret['test3']), "Deleted cache id test3 is not set");
+    $this->assertFalse(isset($ret['test6']), "Deleted cache id test6 is not set");
+    $this->assertFalse(isset($ret['test21']), "Nonexistent cache id test21 is not set");
+    // Test values.
+    $this->assertEquals($ret['test2']->data, 3, "Existing cache id test2 has the correct value.");
+    $this->assertEquals($ret['test7']->data, 17, "Existing cache id test7 has the correct value.");
+    $this->assertEquals($ret['test19']->data, 57, "Added cache id test19 has the correct value.");
+    // Test $cids array - ensure it contains cache id's that do not exist.
+    $this->assert(in_array('test3', $cids), "Deleted cache id test3 is in cids array.");
+    $this->assert(in_array('test6', $cids), "Deleted cache id test6 is in cids array.");
+    $this->assert(in_array('test21', $cids), "Nonexistent cache id test21 is in cids array.");
+    // Test $cids array - ensure it does not contain cache id's that exist.
+    $this->assertFalse(in_array('test2', $cids), "Existing cache id test2 is not in cids array.");
+    $this->assertFalse(in_array('test7', $cids), "Existing cache id test7 is not in cids array.");
+    $this->assertFalse(in_array('test19', $cids), "Added cache id test19 is not in cids array.");
+  }
+
+  /**
+   * Tests Drupal\Core\Cache\CacheBackendInterface::isEmpty().
+   */
+  public function testIsEmpty() {
+    $backend = $this->getCacheBackend();
+
+    $this->assertTrue($backend->isEmpty(), "Backend is empty.");
+
+    $backend->set('pony', "Shetland");
+    $this->assertFalse($backend->isEmpty(), "Backend is not empty.");
+
+    $backend->delete('pony');
+    $this->assertTrue($backend->isEmpty(), "Backend is empty.");
+  }
+
+  /**
+   * Test Drupal\Core\Cache\CacheBackendInterface::delete() and
+   * Drupal\Core\Cache\CacheBackendInterface::deleteMultiple().
+   */
+  public function testDeleteMultiple() {
+    $backend = $this->getCacheBackend();
+
+    // Set numerous testing keys.
+    $backend->set('test1', 1);
+    $backend->set('test2', 3);
+    $backend->set('test3', 5);
+    $backend->set('test4', 7);
+    $backend->set('test5', 11);
+    $backend->set('test6', 13);
+    $backend->set('test7', 17);
+
+    $backend->delete('test1');
+    $backend->delete('test23'); // Nonexistent key should not cause an error.
+    $backend->deleteMultiple(array(
+      'test3',
+      'test5',
+      'test7',
+      'test19', // Nonexistent key should not cause an error.
+      'test21', // Nonexistent key should not cause an error.
+    ));
+
+    // Test if expected keys have been deleted.
+    $this->assertEquals(FALSE, $backend->get('test1'), "Cache id test1 deleted.");
+    $this->assertEquals(FALSE, $backend->get('test3'), "Cache id test3 deleted.");
+    $this->assertEquals(FALSE, $backend->get('test5'), "Cache id test5 deleted.");
+    $this->assertEquals(FALSE, $backend->get('test7'), "Cache id test7 deleted.");
+
+    // Test if expected keys exist.
+    $this->assertNotIdentical(FALSE, $backend->get('test2'), "Cache id test2 exists.");
+    $this->assertNotIdentical(FALSE, $backend->get('test4'), "Cache id test4 exists.");
+    $this->assertNotIdentical(FALSE, $backend->get('test6'), "Cache id test6 exists.");
+
+    // Test if that expected keys do not exist.
+    $this->assertEquals(FALSE, $backend->get('test19'), "Cache id test19 does not exist.");
+    $this->assertEquals(FALSE, $backend->get('test21'), "Cache id test21 does not exist.");
+  }
+
+  /**
+   * Tests Drupal\Core\Cache\CacheBackendInterface::deleteTags().
+   */
+  function testDeleteTags() {
+    $backend = $this->getCacheBackend();
+
+    // Create two cache entries with the same tag and tag value.
+    $backend->set('test_cid_invalidate1', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => 2));
+    $backend->set('test_cid_invalidate2', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => 2));
+    $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Two cache items were created.');
+
+    // Delete test_tag of value 1. This should delete both entries.
+    $backend->deleteTags(array('test_tag' => 2));
+    $this->assertFalse($backend->get('test_cid_invalidate1') || $backend->get('test_cid_invalidate2'), 'Two cache items invalidated after deleting a cache tag.');
+    $this->assertFalse($backend->get('test_cid_invalidate1', TRUE) || $backend->get('test_cid_invalidate2', TRUE), 'Two cache items deleted after deleting a cache tag.');
+
+    // Create two cache entries with the same tag and an array tag value.
+    $backend->set('test_cid_invalidate1', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => array(1)));
+    $backend->set('test_cid_invalidate2', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => array(1)));
+    $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Two cache items were created.');
+
+    // Delete test_tag of value 1. This should delete both entries.
+    $backend->deleteTags(array('test_tag' => array(1)));
+    $this->assertFalse($backend->get('test_cid_invalidate1') || $backend->get('test_cid_invalidate2'), 'Two cache items invalidated after deleted a cache tag.');
+    $this->assertFalse($backend->get('test_cid_invalidate1', TRUE) || $backend->get('test_cid_invalidate2', TRUE), 'Two cache items deleted after deleting a cache tag.');
+
+    // Create three cache entries with a mix of tags and tag values.
+    $backend->set('test_cid_invalidate1', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => array(1)));
+    $backend->set('test_cid_invalidate2', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => array(2)));
+    $backend->set('test_cid_invalidate3', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag_foo' => array(3)));
+    $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2') && $backend->get('test_cid_invalidate3'), 'Three cached items were created.');
+    $backend->deleteTags(array('test_tag_foo' => array(3)));
+    $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Cached items not matching the tag were not deleted.');
+    $this->assertFalse($backend->get('test_cid_invalidated3', TRUE), 'Cache item matching the tag was deleted.');
+
+    // Create cache entry in multiple bins. Two cache entries
+    // (test_cid_invalidate1 and test_cid_invalidate2) still exist from previous
+    // tests.
+    $tags = array('test_tag' => array(1, 2, 3));
+    $bins = array('path', 'bootstrap', 'page');
+    foreach ($bins as $bin) {
+      $this->getCacheBackend($bin)->set('test', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, $tags);
+      $this->assertTrue($this->getCacheBackend($bin)->get('test'), 'Cache item was set in bin.');
+    }
+
+    // Delete tag in mulitple bins.
+    foreach ($bins as $bin) {
+      $this->getCacheBackend($bin)->deleteTags(array('test_tag' => array(2)));
+    }
+
+    // Test that cache entry has been deleted in multple bins.
+    foreach ($bins as $bin) {
+      $this->assertFalse($this->getCacheBackend($bin)->get('test', TRUE), 'Tag deletion affected item in bin.');
+    }
+    // Test that the cache entry with a matching tag has been invalidated.
+    $this->assertFalse($this->getCacheBackend($bin)->get('test_cid_invalidate2', TRUE), 'Cache items matching tag were deleted.');
+    // Test that the cache entry with without a matching tag still exists.
+    $this->assertTrue($this->getCacheBackend($bin)->get('test_cid_invalidate1'), 'Cache items not matching tag were not invalidated.');
+  }
+
+  /**
+   * Test Drupal\Core\Cache\CacheBackendInterface::deleteAll().
+   */
+  public function testDeleteAll() {
+    $backend = $this->getCacheBackend();
+
+    // Set both expiring and permanent keys.
+    $backend->set('test1', 1, CacheBackendInterface::CACHE_PERMANENT);
+    $backend->set('test2', 3, time() + 1000);
+
+    $backend->deleteAll();
+
+    $this->assertTrue($backend->isEmpty(), "Backend is empty after deleteAll().");
+
+    $this->assertFalse($backend->get('test1'), 'First key has been deleted.');
+    $this->assertFalse($backend->get('test2'), 'Second key has been deleted.');
+  }
+
+  /**
+   * Test Drupal\Core\Cache\CacheBackendInterface::invalidate() and
+   * Drupal\Core\Cache\CacheBackendInterface::invalidateMultiple().
+   */
+  function testInvalidate() {
+    $backend = $this->getCacheBackend();
+    $backend->set('test1', 1);
+    $backend->set('test2', 2);
+    $backend->set('test3', 2);
+    $backend->set('test4', 2);
+
+    $reference = array('test1', 'test2', 'test3', 'test4');
+
+    $cids = $reference;
+    $ret = $backend->getMultiple($cids);
+    $this->assertEqual(count($ret), 4, 'Four items returned.');
+
+    $backend->invalidate('test1');
+    $backend->invalidateMultiple(array('test2', 'test3'));
+
+    $cids = $reference;
+    $ret = $backend->getMultiple($cids);
+    $this->assertEqual(count($ret), 1, 'Only one item element returned.');
+
+    $cids = $reference;
+    $ret = $backend->getMultiple($cids, TRUE);
+    $this->assertEqual(count($ret), 4, 'Four items returned.');
+  }
+
+  /**
+   * Tests Drupal\Core\Cache\CacheBackendInterface::invalidateTags().
+   */
+  function testInvalidateTags() {
+    $backend = $this->getCacheBackend();
+
+    // Create two cache entries with the same tag and tag value.
+    $backend->set('test_cid_invalidate1', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => 2));
+    $backend->set('test_cid_invalidate2', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => 2));
+    $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Two cache items were created.');
+
+    // Invalidate test_tag of value 1. This should invalidate both entries.
+    $backend->invalidateTags(array('test_tag' => 2));
+    $this->assertFalse($backend->get('test_cid_invalidate1') || $backend->get('test_cid_invalidate2'), 'Two cache items invalidated after invalidating a cache tag.');
+    $this->assertTrue($backend->get('test_cid_invalidate1', TRUE) && $backend->get('test_cid_invalidate2', TRUE), 'Cache items not deleted after invalidating a cache tag.');
+
+    // Create two cache entries with the same tag and an array tag value.
+    $backend->set('test_cid_invalidate1', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => array(1)));
+    $backend->set('test_cid_invalidate2', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => array(1)));
+    $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Two cache items were created.');
+
+    // Invalidate test_tag of value 1. This should invalidate both entries.
+    $backend->invalidateTags(array('test_tag' => array(1)));
+    $this->assertFalse($backend->get('test_cid_invalidate1') || $backend->get('test_cid_invalidate2'), 'Two caches removed after invalidating a cache tag.');
+    $this->assertTrue($backend->get('test_cid_invalidate1', TRUE) && $backend->get('test_cid_invalidate2', TRUE), 'Cache items not deleted after invalidating a cache tag.');
+
+    // Create three cache entries with a mix of tags and tag values.
+    $backend->set('test_cid_invalidate1', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => array(1)));
+    $backend->set('test_cid_invalidate2', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag' => array(2)));
+    $backend->set('test_cid_invalidate3', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, array('test_tag_foo' => array(3)));
+    $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2') && $backend->get('test_cid_invalidate3'), 'Three cached items were created.');
+    $backend->invalidateTags(array('test_tag_foo' => array(3)));
+    $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Cache items not matching the tag were not invalidated.');
+    $this->assertFalse($backend->get('test_cid_invalidated3'), 'Cached item matching the tag was removed.');
+
+    // Create cache entry in multiple bins. Two cache entries
+    // (test_cid_invalidate1 and test_cid_invalidate2) still exist from previous
+    // tests.
+    $tags = array('test_tag' => array(1, 2, 3));
+    $bins = array('path', 'bootstrap', 'page');
+    foreach ($bins as $bin) {
+      $this->getCacheBackend($bin)->set('test', $this->defaultValue, CacheBackendInterface::CACHE_PERMANENT, $tags);
+      $this->assertTrue($this->getCacheBackend($bin)->get('test'), 'Cache item was set in bin.');
+    }
+
+    // Invalidate tag in mulitple bins.
+    foreach ($bins as $bin) {
+      $this->getCacheBackend($bin)->invalidateTags(array('test_tag' => array(2)));
+    }
+
+    // Test that cache entry has been invalidated in multple bins.
+    foreach ($bins as $bin) {
+      $this->assertFalse($this->getCacheBackend($bin)->get('test'), 'Tag invalidation affected item in bin.');
+    }
+    // Test that the cache entry with a matching tag has been invalidated.
+    $this->assertFalse($this->getCacheBackend($bin)->get('test_cid_invalidate2'), 'Cache items matching tag were invalidated.');
+    // Test that the cache entry with without a matching tag still exists.
+    $this->assertTrue($this->getCacheBackend($bin)->get('test_cid_invalidate1'), 'Cache items not matching tag were not invalidated.');
+  }
+
+  /**
+   * Test Drupal\Core\Cache\CacheBackendInterface::invalidateAll().
+   */
+  public function testInvalidateAll() {
+    $backend = $this->getCacheBackend();
+
+    // Set both expiring and permanent keys.
+    $backend->set('test1', 1, CacheBackendInterface::CACHE_PERMANENT);
+    $backend->set('test2', 3, time() + 1000);
+
+    $backend->invalidateAll();
+
+    $this->assertFalse($backend->get('test1'), 'First key has been invalidated.');
+    $this->assertFalse($backend->get('test2'), 'Second key has been invalidated.');
+    $this->assertTrue($backend->get('test1', TRUE), 'First key has not been deleted.');
+    $this->assertTrue($backend->get('test2', TRUE), 'Second key has not been deleted.');
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Cache/NullBackendTest.php b/core/tests/Drupal/Tests/Core/Cache/NullBackendTest.php
new file mode 100644
index 0000000..b304116
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Cache/NullBackendTest.php
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Tests\Core\Cache\NullBackendTest.
+ */
+
+namespace Drupal\Tests\Core\Cache;
+
+use Drupal\Core\Cache\NullBackend;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * Tests the cache NullBackend.
+ */
+class NullBackendTest extends UnitTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Cache NullBackend test',
+      'description' => 'Tests the cache NullBackend.',
+      'group' => 'Cache',
+    );
+  }
+
+  /**
+   * Tests that the NullBackend does not actually store variables.
+   */
+  function testNullBackend() {
+    $null_cache = new NullBackend('test');
+
+    $key = $this->randomName();
+    $value = $this->randomName();
+
+    $null_cache->set($key, $value);
+    $this->assertTrue($null_cache->isEmpty());
+    $this->assertFalse($null_cache->get($key));
+  }
+}
diff --git a/core/tests/Drupal/Tests/Core/NestedArrayUnitTest.php b/core/tests/Drupal/Tests/Core/NestedArrayUnitTest.php
new file mode 100644
index 0000000..8473517
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/NestedArrayUnitTest.php
@@ -0,0 +1,148 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\NestedArrayUnitTest.
+ */
+
+namespace Drupal\Tests\Core;
+
+use Drupal\Component\Utility\NestedArray;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * Tests the NestedArray helper class.
+ *
+ * @group System
+ */
+class NestedArrayUnitTest extends UnitTestCase {
+
+  /**
+   * Form array to check.
+   */
+  protected $form;
+
+  /**
+   * Array of parents for the nested element.
+   */
+  protected $parents;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'NestedArray functionality',
+      'description' => 'Tests the NestedArray helper class.',
+      'group' => 'System',
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+
+    // Create a form structure with a nested element.
+    $this->form['details']['element'] = array(
+     '#value' => 'Nested element',
+    );
+
+    // Set up parent array.
+    $this->parents = array('details', 'element');
+  }
+
+  /**
+   * Tests getting nested array values.
+   */
+  function testGetValue() {
+    // Verify getting a value of a nested element.
+    $value = NestedArray::getValue($this->form, $this->parents);
+    $this->assertEquals($value['#value'], 'Nested element', 'Nested element value found.');
+
+    // Verify changing a value of a nested element by reference.
+    $value = &NestedArray::getValue($this->form, $this->parents);
+    $value['#value'] = 'New value';
+    $value = NestedArray::getValue($this->form, $this->parents);
+    $this->assertEquals($value['#value'], 'New value', 'Nested element value was changed by reference.');
+    $this->assertEquals($this->form['details']['element']['#value'], 'New value', 'Nested element value was changed by reference.');
+
+    // Verify that an existing key is reported back.
+    $key_exists = NULL;
+    NestedArray::getValue($this->form, $this->parents, $key_exists);
+    $this->assertEquals($key_exists, TRUE, 'Existing key found.');
+
+    // Verify that a non-existing key is reported back and throws no errors.
+    $key_exists = NULL;
+    $parents = $this->parents;
+    $parents[] = 'foo';
+    NestedArray::getValue($this->form, $parents, $key_exists);
+    $this->assertEquals($key_exists, FALSE, 'Non-existing key not found.');
+  }
+
+  /**
+   * Tests setting nested array values.
+   */
+  function testSetValue() {
+    $new_value = array(
+      '#value' => 'New value',
+      '#required' => TRUE,
+    );
+
+    // Verify setting the value of a nested element.
+    NestedArray::setValue($this->form, $this->parents, $new_value);
+    $this->assertEquals($this->form['details']['element']['#value'], 'New value', 'Changed nested element value found.');
+    $this->assertEquals($this->form['details']['element']['#required'], TRUE, 'New nested element value found.');
+  }
+
+  /**
+   * Tests unsetting nested array values.
+   */
+  function testUnsetValue() {
+    // Verify unsetting a non-existing nested element throws no errors and the
+    // non-existing key is properly reported.
+    $key_existed = NULL;
+    $parents = $this->parents;
+    $parents[] = 'foo';
+    NestedArray::unsetValue($this->form, $parents, $key_existed);
+    $this->assertTrue(isset($this->form['details']['element']['#value']), 'Outermost nested element key still exists.');
+    $this->assertEquals($key_existed, FALSE, 'Non-existing key not found.');
+
+    // Verify unsetting a nested element.
+    $key_existed = NULL;
+    NestedArray::unsetValue($this->form, $this->parents, $key_existed);
+    $this->assertFalse(isset($this->form['details']['element']), 'Removed nested element not found.');
+    $this->assertEquals($key_existed, TRUE, 'Existing key was found.');
+  }
+
+  /**
+   * Tests existence of array key.
+   */
+  function testKeyExists() {
+    // Verify that existing key is found.
+    $this->assertEquals(NestedArray::keyExists($this->form, $this->parents), TRUE, 'Nested key found.');
+
+    // Verify that non-existing keys are not found.
+    $parents = $this->parents;
+    $parents[] = 'foo';
+    $this->assertEquals(NestedArray::keyExists($this->form, $parents), FALSE, 'Non-existing nested key not found.');
+  }
+
+  /**
+   * Tests NestedArray::mergeDeepArray().
+   */
+  function testMergeDeepArray() {
+    $link_options_1 = array(
+      'fragment' => 'x',
+      'attributes' => array('title' => 'X', 'class' => array('a', 'b')),
+      'language' => 'en',
+    );
+    $link_options_2 = array(
+      'fragment' => 'y',
+      'attributes' => array('title' => 'Y', 'class' => array('c', 'd')),
+      'html' => TRUE,
+    );
+    $expected = array(
+      'fragment' => 'y',
+      'attributes' => array('title' => 'Y', 'class' => array('a', 'b', 'c', 'd')),
+      'language' => 'en',
+      'html' => TRUE,
+    );
+    $this->assertEquals(NestedArray::mergeDeepArray(array($link_options_1, $link_options_2)), $expected, 'NestedArray::mergeDeepArray() returned a properly merged array.');
+  }
+}
diff --git a/core/tests/Drupal/Tests/UnitTestCase.php b/core/tests/Drupal/Tests/UnitTestCase.php
new file mode 100644
index 0000000..e89d21b
--- /dev/null
+++ b/core/tests/Drupal/Tests/UnitTestCase.php
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\UnitTestCase.
+ */
+namespace Drupal\Tests;
+
+class UnitTestCase extends \PHPUnit_Framework_TestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'test name',
+      'description' => 'test description',
+      'group' => 'somegroup',
+    );
+  }
+
+  /**
+   * Generates a random string containing letters and numbers.
+   *
+   * The string will always start with a letter. The letters may be upper or
+   * lower case. This method is better for restricted inputs that do not
+   * accept certain characters. For example, when testing input fields that
+   * require machine readable values (i.e. without spaces and non-standard
+   * characters) this method is best.
+   *
+   * Do not use this method when testing unvalidated user input. Instead, use
+   * Drupal\simpletest\TestBase::randomString().
+   *
+   * @param $length
+   *   Length of random string to generate.
+   *
+   * @return
+   *   Randomly generated string.
+   *
+   * @see Drupal\simpletest\TestBase::randomString()
+   */
+  public static function randomName($length = 8) {
+    $values = array_merge(range(65, 90), range(97, 122), range(48, 57));
+    $max = count($values) - 1;
+    $str = chr(mt_rand(97, 122));
+    for ($i = 1; $i < $length; $i++) {
+      $str .= chr($values[mt_rand(0, $max)]);
+    }
+    return $str;
+  }
+
+  /**
+   * Fire an assertion that is always positive.
+   *
+   * @param $message
+   *   (optional) A message to display with the assertion. Do not translate
+   *   messages: use format_string() to embed variables in the message text, not
+   *   t(). If left blank, a default message will be displayed.
+   * @param $group
+   *   (optional) The group this message is in, which is displayed in a column
+   *   in test output. Use 'Debug' to indicate this is debugging output. Do not
+   *   translate this string. Defaults to 'Other'; most tests do not override
+   *   this default.
+   * @return
+   *   TRUE.
+   */
+  protected function pass($message = NULL, $group = 'Other') {
+    return $this->assertTrue($message, $group);
+  }
+}
diff --git a/core/tests/bootstrap.php b/core/tests/bootstrap.php
new file mode 100644
index 0000000..6c947df
--- /dev/null
+++ b/core/tests/bootstrap.php
@@ -0,0 +1,13 @@
+<?php
+
+// Register the namespaces we'll need to autoload from.
+$loader = require __DIR__ . "/../vendor/autoload.php";
+$loader->add('Drupal\\', __DIR__);
+$loader->add('Drupal\Core', __DIR__ . "/../../core/lib");
+$loader->add('Drupal\Component', __DIR__ . "/../../core/lib");
+
+foreach (scandir(__DIR__ . "/../modules") as $module) {
+  $loader->add('Drupal\\' . $module, __DIR__ . "/../modules/" . $module . "/lib");
+}
+// Look into removing this later.
+define('REQUEST_TIME', (int) $_SERVER['REQUEST_TIME']);
