diff --git a/core/Plugin/Settings.php b/core/Plugin/Settings.php
index 0b0bb0953aa7eb015823535001411c2858e3b6d5..23d1472ca96b251f1f097a4d3702ccb364481c4a 100644
--- a/core/Plugin/Settings.php
+++ b/core/Plugin/Settings.php
@@ -8,11 +8,11 @@
  */
 namespace Piwik\Plugin;
 
-use Piwik\Option;
 use Piwik\Piwik;
 use Piwik\Settings\Setting;
+use Piwik\Settings\Storage;
 use Piwik\Settings\StorageInterface;
-use Piwik\SettingsServer;
+use Piwik\Tracker\SettingsStorage;
 
 /**
  * Base class of all plugin settings providers. Plugins that define their own configuration settings
@@ -25,7 +25,7 @@ use Piwik\SettingsServer;
  *
  * @api
  */
-abstract class Settings implements StorageInterface
+abstract class Settings
 {
     const TYPE_INT    = 'integer';
     const TYPE_FLOAT  = 'float';
@@ -48,18 +48,13 @@ abstract class Settings implements StorageInterface
      */
     private $settings = array();
 
-    /**
-     * Array containing all plugin settings values: Array( [setting-key] => [setting-value] ).
-     *
-     * @var array
-     */
-    private $settingsValues = array();
-
     private $introduction;
     private $pluginName;
 
-    // for lazy loading of setting values
-    private $settingValuesLoaded = false;
+    /**
+     * @var StorageInterface
+     */
+    private $storage;
 
     /**
      * Constructor.
@@ -70,14 +65,16 @@ abstract class Settings implements StorageInterface
             $this->pluginName = $pluginName;
         } else {
 
-            $classname    = get_class($this);
-            $parts        = explode('\\', $classname);
+            $classname = get_class($this);
+            $parts     = explode('\\', $classname);
 
             if (3 <= count($parts)) {
                 $this->pluginName = $parts[2];
             }
         }
 
+        $this->storage = Storage\Factory::make($this->pluginName);
+
         $this->init();
     }
 
@@ -89,6 +86,17 @@ abstract class Settings implements StorageInterface
         return $this->pluginName;
     }
 
+    /**
+     * @ignore
+     * @return Setting
+     */
+    public function getSetting($name)
+    {
+        if (array_key_exists($name, $this->settings)) {
+            return $this->settings[$name];
+        }
+    }
+
     /**
      * Implemented by descendants. This method should define plugin settings (via the
      * {@link addSetting()}) method and set the introduction text (via the
@@ -163,118 +171,6 @@ abstract class Settings implements StorageInterface
         return $this->settings;
     }
 
-    /**
-     * Saves (persists) the current setting values in the database.
-     */
-    public function save()
-    {
-        $this->loadSettingsIfNotDoneYet();
-
-        Option::set($this->getOptionKey(), serialize($this->settingsValues));
-
-        $pluginName = $this->getPluginName();
-
-        /**
-         * Triggered after a plugin settings have been updated.
-         *
-         * **Example**
-         *
-         *     Piwik::addAction('Settings.MyPlugin.settingsUpdated', function (Settings $settings) {
-         *         $value = $settings->someSetting->getValue();
-         *         // Do something with the new setting value
-         *     });
-         *
-         * @param Settings $settings The plugin settings object.
-         */
-        Piwik::postEvent(sprintf('Settings.%s.settingsUpdated', $pluginName), array($this));
-    }
-
-    /**
-     * Removes all settings for this plugin from the database. Useful when uninstalling
-     * a plugin.
-     */
-    public function removeAllPluginSettings()
-    {
-        Piwik::checkUserHasSuperUserAccess();
-
-        Option::delete($this->getOptionKey());
-        $this->settingsValues = array();
-        $this->settingValuesLoaded = false;
-    }
-
-    /**
-     * Returns the current value for a setting. If no value is stored, the default value
-     * is be returned.
-     *
-     * @param Setting $setting
-     * @return mixed
-     * @throws \Exception If the setting does not exist or if the current user is not allowed to change the value
-     *                    of this setting.
-     */
-    public function getSettingValue(Setting $setting)
-    {
-        $this->checkIsValidSetting($setting->getName());
-        $this->checkHasEnoughReadPermission($setting);
-        $this->loadSettingsIfNotDoneYet();
-
-        if (array_key_exists($setting->getKey(), $this->settingsValues)) {
-
-            return $this->settingsValues[$setting->getKey()];
-        }
-
-        return $setting->defaultValue;
-    }
-
-    /**
-     * Sets (overwrites) the value of a setting in memory. To persist the change, {@link save()} must be
-     * called afterwards, otherwise the change has no effect.
-     *
-     * Before the setting is changed, the {@link Piwik\Settings\Setting::$validate} and
-     * {@link Piwik\Settings\Setting::$transform} closures will be invoked (if defined). If there is no validation
-     * filter, the setting value will be casted to the appropriate data type.
-     *
-     * @param Setting $setting
-     * @param string $value
-     * @throws \Exception If the setting does not exist or if the current user is not allowed to change the value
-     *                    of this setting.
-     */
-    public function setSettingValue(Setting $setting, $value)
-    {
-        $this->checkIsValidSetting($setting->getName());
-        $this->checkHasEnoughWritePermission($setting);
-
-        if ($setting->validate && $setting->validate instanceof \Closure) {
-            call_user_func($setting->validate, $value, $setting);
-        }
-
-        if ($setting->transform && $setting->transform instanceof \Closure) {
-            $value = call_user_func($setting->transform, $value, $setting);
-        } elseif (isset($setting->type)) {
-            settype($value, $setting->type);
-        }
-
-        $this->loadSettingsIfNotDoneYet();
-        $this->settingsValues[$setting->getKey()] = $value;
-    }
-
-    /**
-     * Unsets a setting value in memory. To persist the change, {@link save()} must be
-     * called afterwards, otherwise the change has no effect.
-     *
-     * @param Setting $setting
-     */
-    public function removeSettingValue(Setting $setting)
-    {
-        $this->checkHasEnoughWritePermission($setting);
-        $this->loadSettingsIfNotDoneYet();
-
-        $key = $setting->getKey();
-
-        if (array_key_exists($key, $this->settingsValues)) {
-            unset($this->settingsValues[$key]);
-        }
-    }
-
     /**
      * Makes a new plugin setting available.
      *
@@ -296,53 +192,47 @@ abstract class Settings implements StorageInterface
         $this->setDefaultTypeAndFieldIfNeeded($setting);
         $this->addValidatorIfNeeded($setting);
 
-        $setting->setStorage($this);
+        $setting->setStorage($this->storage);
+        $setting->setPluginName($this->pluginName);
 
         $this->settings[$setting->getName()] = $setting;
     }
 
-    private function getOptionKey()
-    {
-        return 'Plugin_' . $this->pluginName . '_Settings';
-    }
-
-    private function loadSettingsIfNotDoneYet()
-    {
-        if ($this->settingValuesLoaded) {
-            return;
-        }
-
-        $this->settingValuesLoaded = true;
-        $this->loadSettings();
-    }
-
-    private function loadSettings()
+    /**
+     * Saves (persists) the current setting values in the database.
+     */
+    public function save()
     {
-        $values = Option::get($this->getOptionKey());
+        $this->storage->save();
 
-        if (!empty($values)) {
-            $this->settingsValues = unserialize($values);
-        }
-    }
+        SettingsStorage::clearCache();
 
-    private function checkIsValidSetting($name)
-    {
-        $setting = $this->getSetting($name);
-
-        if (empty($setting)) {
-            throw new \Exception(sprintf('The setting %s does not exist', $name));
-        }
+        /**
+         * Triggered after a plugin settings have been updated.
+         *
+         * **Example**
+         *
+         *     Piwik::addAction('Settings.MyPlugin.settingsUpdated', function (Settings $settings) {
+         *         $value = $settings->someSetting->getValue();
+         *         // Do something with the new setting value
+         *     });
+         *
+         * @param Settings $settings The plugin settings object.
+         */
+        Piwik::postEvent(sprintf('Settings.%s.settingsUpdated', $this->pluginName), array($this));
     }
 
     /**
-     * @param  $name
-     * @return Setting|null
+     * Removes all settings for this plugin from the database. Useful when uninstalling
+     * a plugin.
      */
-    private function getSetting($name)
+    public function removeAllPluginSettings()
     {
-        if (array_key_exists($name, $this->settings)) {
-            return $this->settings[$name];
-        }
+        Piwik::checkUserHasSuperUserAccess();
+
+        $this->storage->deleteAllValues();
+
+        SettingsStorage::clearCache();
     }
 
     private function getDefaultType($controlType)
@@ -373,40 +263,6 @@ abstract class Settings implements StorageInterface
         return $defaultControlTypes[$type];
     }
 
-    /**
-     * @param $setting
-     * @throws \Exception
-     */
-    private function checkHasEnoughWritePermission(Setting $setting)
-    {
-        // When the request is a Tracker request, allow plugins to write settings
-        if (SettingsServer::isTrackerApiRequest()) {
-            return;
-        }
-
-        if (!$setting->isWritableByCurrentUser()) {
-            $errorMsg = Piwik::translate('CoreAdminHome_PluginSettingChangeNotAllowed', array($setting->getName(), $this->pluginName));
-            throw new \Exception($errorMsg);
-        }
-    }
-
-    /**
-     * @param $setting
-     * @throws \Exception
-     */
-    private function checkHasEnoughReadPermission(Setting $setting)
-    {
-        // When the request is a Tracker request, allow plugins to read settings
-        if (SettingsServer::isTrackerApiRequest()) {
-            return;
-        }
-
-        if (!$setting->isReadableByCurrentUser()) {
-            $errorMsg = Piwik::translate('CoreAdminHome_PluginSettingReadNotAllowed', array($setting->getName(), $this->pluginName));
-            throw new \Exception($errorMsg);
-        }
-    }
-
     private function setDefaultTypeAndFieldIfNeeded(Setting $setting)
     {
         if (!is_null($setting->uiControlType) && is_null($setting->type)) {
diff --git a/core/Settings/Setting.php b/core/Settings/Setting.php
index be35345b2db522961eeacb51eff632555a5cc8d1..32883876715e9aaf8c44e4e87fc4bbfea1aad1a3 100644
--- a/core/Settings/Setting.php
+++ b/core/Settings/Setting.php
@@ -8,6 +8,8 @@
  */
 
 namespace Piwik\Settings;
+use Piwik\Piwik;
+use Piwik\SettingsServer;
 
 /**
  * Base setting type class.
@@ -152,6 +154,7 @@ abstract class Setting
      * @var StorageInterface
      */
     private $storage;
+    private $pluginName;
 
     /**
      * Constructor.
@@ -201,13 +204,33 @@ abstract class Setting
     /**
      * Sets the object used to persist settings.
      *
-     * @return StorageInterface
+     * @param StorageInterface $storage
      */
     public function setStorage(StorageInterface $storage)
     {
         $this->storage = $storage;
     }
 
+    /**
+     * @internal
+     * @ignore
+     * @return StorageInterface
+     */
+    public function getStorage()
+    {
+        return $this->storage;
+    }
+
+    /**
+     * Sets th name of the plugin the setting belongs to
+     *
+     * @param string $pluginName
+     */
+    public function setPluginName($pluginName)
+    {
+        $this->pluginName = $pluginName;
+    }
+
     /**
      * Returns the previously persisted setting value. If no value was set, the default value
      * is returned.
@@ -217,7 +240,23 @@ abstract class Setting
      */
     public function getValue()
     {
-        return $this->storage->getSettingValue($this);
+        $this->checkHasEnoughReadPermission();
+
+        return $this->storage->getValue($this);
+    }
+
+    /**
+     * Returns the previously persisted setting value. If no value was set, the default value
+     * is returned.
+     *
+     * @return mixed
+     * @throws \Exception If the current user is not allowed to change the value of this setting.
+     */
+    public function removeValue()
+    {
+        $this->checkHasEnoughWritePermission();
+
+        return $this->storage->deleteValue($this);
     }
 
     /**
@@ -228,7 +267,51 @@ abstract class Setting
      */
     public function setValue($value)
     {
-        return $this->storage->setSettingValue($this, $value);
+        $this->checkHasEnoughWritePermission();
+
+        if ($this->validate && $this->validate instanceof \Closure) {
+            call_user_func($this->validate, $value, $this);
+        }
+
+        if ($this->transform && $this->transform instanceof \Closure) {
+            $value = call_user_func($this->transform, $value, $this);
+        } elseif (isset($this->type)) {
+            settype($value, $this->type);
+        }
+
+        return $this->storage->setValue($this, $value);
+    }
+
+    /**
+     * @throws \Exception
+     */
+    private function checkHasEnoughWritePermission()
+    {
+        // When the request is a Tracker request, allow plugins to write settings
+        if (SettingsServer::isTrackerApiRequest()) {
+            return;
+        }
+
+        if (!$this->isWritableByCurrentUser()) {
+            $errorMsg = Piwik::translate('CoreAdminHome_PluginSettingChangeNotAllowed', array($this->getName(), $this->pluginName));
+            throw new \Exception($errorMsg);
+        }
+    }
+
+    /**
+     * @throws \Exception
+     */
+    private function checkHasEnoughReadPermission()
+    {
+        // When the request is a Tracker request, allow plugins to read settings
+        if (SettingsServer::isTrackerApiRequest()) {
+            return;
+        }
+
+        if (!$this->isReadableByCurrentUser()) {
+            $errorMsg = Piwik::translate('CoreAdminHome_PluginSettingReadNotAllowed', array($this->getName(), $this->pluginName));
+            throw new \Exception($errorMsg);
+        }
     }
 
     /**
diff --git a/core/Settings/Storage.php b/core/Settings/Storage.php
new file mode 100644
index 0000000000000000000000000000000000000000..09525cb6d3fb535a397486746d9c852e026b49d2
--- /dev/null
+++ b/core/Settings/Storage.php
@@ -0,0 +1,144 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+
+namespace Piwik\Settings;
+use Piwik\Option;
+
+/**
+ * Base setting type class.
+ *
+ * @api
+ */
+class Storage implements StorageInterface
+{
+
+    /**
+     * Array containing all plugin settings values: Array( [setting-key] => [setting-value] ).
+     *
+     * @var array
+     */
+    private $settingsValues = array();
+
+    // for lazy loading of setting values
+    private $settingValuesLoaded = false;
+
+    private $pluginName;
+
+    public function __construct($pluginName)
+    {
+        $this->pluginName = $pluginName;
+    }
+
+    /**
+     * Saves (persists) the current setting values in the database.
+     */
+    public function save()
+    {
+        $this->loadSettingsIfNotDoneYet();
+
+        Option::set($this->getOptionKey(), serialize($this->settingsValues));
+    }
+
+    /**
+     * Removes all settings for this plugin from the database. Useful when uninstalling
+     * a plugin.
+     */
+    public function deleteAllValues()
+    {
+        Option::delete($this->getOptionKey());
+
+        $this->settingsValues = array();
+        $this->settingValuesLoaded = false;
+    }
+
+    /**
+     * Returns the current value for a setting. If no value is stored, the default value
+     * is be returned.
+     *
+     * @param Setting $setting
+     * @return mixed
+     * @throws \Exception If the setting does not exist or if the current user is not allowed to change the value
+     *                    of this setting.
+     */
+    public function getValue(Setting $setting)
+    {
+        $this->loadSettingsIfNotDoneYet();
+
+        if (array_key_exists($setting->getKey(), $this->settingsValues)) {
+
+            return $this->settingsValues[$setting->getKey()];
+        }
+
+        return $setting->defaultValue;
+    }
+
+    /**
+     * Sets (overwrites) the value of a setting in memory. To persist the change, {@link save()} must be
+     * called afterwards, otherwise the change has no effect.
+     *
+     * Before the setting is changed, the {@link Piwik\Settings\Setting::$validate} and
+     * {@link Piwik\Settings\Setting::$transform} closures will be invoked (if defined). If there is no validation
+     * filter, the setting value will be casted to the appropriate data type.
+     *
+     * @param Setting $setting
+     * @param string $value
+     * @throws \Exception If the setting does not exist or if the current user is not allowed to change the value
+     *                    of this setting.
+     */
+    public function setValue(Setting $setting, $value)
+    {
+        $this->loadSettingsIfNotDoneYet();
+
+        $this->settingsValues[$setting->getKey()] = $value;
+    }
+
+    /**
+     * Unsets a setting value in memory. To persist the change, {@link save()} must be
+     * called afterwards, otherwise the change has no effect.
+     *
+     * @param Setting $setting
+     */
+    public function deleteValue(Setting $setting)
+    {
+        $this->loadSettingsIfNotDoneYet();
+
+        $key = $setting->getKey();
+
+        if (array_key_exists($key, $this->settingsValues)) {
+            unset($this->settingsValues[$key]);
+        }
+    }
+
+    public function getOptionKey()
+    {
+        return 'Plugin_' . $this->pluginName . '_Settings';
+    }
+
+    private function loadSettingsIfNotDoneYet()
+    {
+        if ($this->settingValuesLoaded) {
+            return;
+        }
+
+        $this->settingValuesLoaded = true;
+        $this->settingsValues = $this->loadSettings();
+    }
+
+    protected function loadSettings()
+    {
+        $values = Option::get($this->getOptionKey());
+
+        if (!empty($values)) {
+            return unserialize($values);
+        }
+
+        return array();
+    }
+
+}
diff --git a/core/Settings/Storage/Factory.php b/core/Settings/Storage/Factory.php
new file mode 100644
index 0000000000000000000000000000000000000000..c007e30e9f77e3474d10d44bc4d7ab61a100181c
--- /dev/null
+++ b/core/Settings/Storage/Factory.php
@@ -0,0 +1,29 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+
+namespace Piwik\Settings\Storage;
+
+use Piwik\Settings\Storage;
+use Piwik\SettingsServer;
+use Piwik\Tracker\SettingsStorage;
+
+class Factory
+{
+    public static function make($pluginName)
+    {
+        if (SettingsServer::isTrackerApiRequest()) {
+            $storage = new SettingsStorage($pluginName);
+        } else {
+            $storage = new Storage($pluginName);
+        }
+
+        return $storage;
+    }
+
+}
diff --git a/core/Settings/StorageInterface.php b/core/Settings/StorageInterface.php
index 74693f9ddb0b4f30641556431339aa6a5ba418dc..7ffcac0d607ac512b4c5acc9567b6cad243fd12a 100644
--- a/core/Settings/StorageInterface.php
+++ b/core/Settings/StorageInterface.php
@@ -22,7 +22,7 @@ interface StorageInterface
      * @throws \Exception In case the setting does not exist or if the current user is not allowed to change the value
      *                    of this setting.
      */
-    public function getSettingValue(Setting $setting);
+    public function getValue(Setting $setting);
 
     /**
      * Removes the value for the given setting. Make sure to call `save()` afterwards, otherwise the removal has no
@@ -30,7 +30,7 @@ interface StorageInterface
      *
      * @param Setting $setting
      */
-    public function removeSettingValue(Setting $setting);
+    public function deleteValue(Setting $setting);
 
     /**
      * Sets (overwrites) the value for the given setting. Make sure to call `save()` afterwards, otherwise the change
@@ -43,7 +43,13 @@ interface StorageInterface
      * @throws \Exception In case the setting does not exist or if the current user is not allowed to change the value
      *                    of this setting.
      */
-    public function setSettingValue(Setting $setting, $value);
+    public function setValue(Setting $setting, $value);
+
+    /**
+     * Removes all settings for this plugin from the database. Useful when uninstalling
+     * a plugin.
+     */
+    public function deleteAllValues();
 
     /**
      * Saves (persists) the current setting values in the database.
diff --git a/core/Settings/UserSetting.php b/core/Settings/UserSetting.php
index fa29269707b4e39193944a3c9af931fe7bf7d9f3..80cf66ebf21a63a7053149b1a1a58e8a0851e563 100644
--- a/core/Settings/UserSetting.php
+++ b/core/Settings/UserSetting.php
@@ -108,7 +108,7 @@ class UserSetting extends Setting
 
                 if ($setting instanceof UserSetting) {
                     $setting->setUserLogin($userLogin);
-                    $pluginSettings->removeSettingValue($setting);
+                    $setting->removeValue();
                 }
 
             }
diff --git a/core/Tracker/SettingsStorage.php b/core/Tracker/SettingsStorage.php
new file mode 100644
index 0000000000000000000000000000000000000000..6c54b1b993b6f6344bddbbe8bc8bf8de4a83bfca
--- /dev/null
+++ b/core/Tracker/SettingsStorage.php
@@ -0,0 +1,57 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+
+namespace Piwik\Tracker;
+
+use Piwik\Settings\Storage;
+use Piwik\Tracker;
+
+/**
+ * Loads settings from tracker cache instead of database. If not yet present in tracker cache will cache it.
+ */
+class SettingsStorage extends Storage
+{
+
+    protected function loadSettings()
+    {
+        $trackerCache = Cache::getCacheGeneral();
+        $settings = null;
+
+        if (array_key_exists('settingsStorage', $trackerCache)) {
+            $allSettings = $trackerCache['settingsStorage'];
+
+            if (is_array($allSettings) && array_key_exists($this->getOptionKey(), $allSettings)) {
+                $settings = $allSettings[$this->getOptionKey()];
+            }
+        } else {
+            $trackerCache['settingsStorage'] = array();
+        }
+
+        if (is_null($settings)) {
+            $settings = parent::loadSettings();
+
+            $trackerCache['settingsStorage'][$this->getOptionKey()] = $settings;
+            Cache::setCacheGeneral($trackerCache);
+        }
+
+        return $settings;
+    }
+
+    public function save()
+    {
+        parent::save();
+        self::clearCache();
+    }
+
+    public static function clearCache()
+    {
+        Cache::clearCacheGeneral();
+    }
+
+}
diff --git a/tests/PHPUnit/Integration/Plugin/SettingsTest.php b/tests/PHPUnit/Integration/Plugin/SettingsTest.php
index 3c146f5d31a6eb8476ff6aed17296e1ce07fe6f7..f8802674a4c0003d9732a8369b58a90ed8cc4ac5 100644
--- a/tests/PHPUnit/Integration/Plugin/SettingsTest.php
+++ b/tests/PHPUnit/Integration/Plugin/SettingsTest.php
@@ -8,55 +8,19 @@
 
 namespace Piwik\Tests\Integration\Plugin;
 
-use Piwik\Access;
 use Piwik\Db;
 use Piwik\Plugin\Settings as PluginSettings;
-use Piwik\Settings\Setting;
-use Piwik\Tests\Framework\Mock\FakeAccess;
-use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
-
-class CorePluginSettingsTest extends \Piwik\Plugins\ExampleSettingsPlugin\Settings {
-
-    public function init()
-    {
-
-    }
-
-    public function addSetting(Setting $setting)
-    {
-        parent::addSetting($setting);
-    }
-}
+use Piwik\Settings\Storage;
+use Piwik\Tests\Integration\Settings\CorePluginTestSettings;
+use Piwik\Tests\Integration\Settings\IntegrationTestCase;
+use Piwik\Tracker\Cache;
+use Piwik\Tracker\SettingsStorage;
 
 /**
- * @group Core
  * @group PluginSettings
  */
 class SettingsTest extends IntegrationTestCase
 {
-    /**
-     * @var CorePluginSettingsTest
-     */
-    private $settings;
-
-    public function setUp()
-    {
-        parent::setUp();
-        Access::setSingletonInstance(null);
-        Db::destroyDatabaseObject();
-
-        $this->settings = $this->createSettingsInstance();
-    }
-
-    public function tearDown()
-    {
-        $this->setSuperUser();
-        if ($this->settings) {
-            $this->settings->removeAllPluginSettings();
-        }
-
-        parent::tearDown();
-    }
 
     public function test_constructor_shouldNotEstablishADatabaseConnection()
     {
@@ -69,38 +33,6 @@ class SettingsTest extends IntegrationTestCase
         $this->assertNotDbConnectionCreated();
     }
 
-    public function test_constructor_shouldEstablishADatabaseConnection_AsSoonAsWeGetAValue()
-    {
-        $this->setSuperUser();
-        Db::destroyDatabaseObject();
-
-        $setting  = $this->buildUserSetting('testSetting', 'Test Setting');
-        $settings = $this->createSettingsInstance();
-        $settings->addSetting($setting);
-
-        $this->assertNotDbConnectionCreated();
-
-        $settings->getSettingValue($setting);
-
-        $this->assertDbConnectionCreated();
-    }
-
-    public function test_constructor_shouldEstablishADatabaseConnection_AsSoonAsWeSetAValue()
-    {
-        $this->setSuperUser();
-        Db::destroyDatabaseObject();
-
-        $setting  = $this->buildUserSetting('testSetting', 'Test Setting');
-        $settings = $this->createSettingsInstance();
-        $settings->addSetting($setting);
-
-        $this->assertNotDbConnectionCreated();
-
-        $settings->setSettingValue($setting, '5');
-
-        $this->assertDbConnectionCreated();
-    }
-
     /**
      * @expectedException \Exception
      * @expectedExceptionMessage A setting with name "myname" does already exist for plugin "ExampleSettingsPlugin"
@@ -126,21 +58,21 @@ class SettingsTest extends IntegrationTestCase
     public function test_addSetting_shouldAssignDefaultType_IfFieldIsGivenButNoType()
     {
         $setting = $this->buildUserSetting('myname', 'mytitle');
-        $setting->uiControlType = CorePluginSettingsTest::CONTROL_MULTI_SELECT;
+        $setting->uiControlType = CorePluginTestSettings::CONTROL_MULTI_SELECT;
 
         $this->settings->addSetting($setting);
 
-        $this->assertEquals(CorePluginSettingsTest::TYPE_ARRAY, $setting->type);
+        $this->assertEquals(CorePluginTestSettings::TYPE_ARRAY, $setting->type);
     }
 
     public function test_addSetting_shouldAssignDefaultField_IfTypeIsGivenButNoField()
     {
         $setting = $this->buildUserSetting('myname', 'mytitle');
-        $setting->type = CorePluginSettingsTest::TYPE_ARRAY;
+        $setting->type = CorePluginTestSettings::TYPE_ARRAY;
 
         $this->settings->addSetting($setting);
 
-        $this->assertEquals(CorePluginSettingsTest::CONTROL_MULTI_SELECT, $setting->uiControlType);
+        $this->assertEquals(CorePluginTestSettings::CONTROL_MULTI_SELECT, $setting->uiControlType);
     }
 
     public function test_addSetting_shouldAddAValidator_IfFieldOptionsAreGiven()
@@ -167,82 +99,29 @@ class SettingsTest extends IntegrationTestCase
         $setting = $this->buildUserSetting('myname', 'mytitle', 'myRandomName');
         $this->settings->addSetting($setting);
 
-        $this->settings->setSettingValue($setting, 5);
-        $this->assertSettingHasValue($setting, 5);
-
-        $this->assertEquals($setting->getValue(), 5);
-
-        $this->settings->setSettingValue($setting, 'test3434');
-        $this->assertEquals($setting->getValue(), 'test3434');
-    }
+        $storage = $setting->getStorage();
+        $this->assertTrue($storage instanceof Storage);
 
-    /**
-     * @expectedException \Exception
-     * @expectedExceptionMessage The setting myname2 does not exist
-     */
-    public function test_setSettingValue_shouldThrowException_IfTryingToSetAValueForNotAvailableSetting()
-    {
-        $this->addUserSetting('myname', 'mytitle');
-
-        $setting = $this->buildUserSetting('myname2', 'mytitle2');
-        $this->settings->setSettingValue($setting, 2);
-    }
-
-    /**
-     * @expectedException \Exception
-     * @expectedExceptionMessage CoreAdminHome_PluginSettingChangeNotAllowed
-     */
-    public function test_setSettingValue_shouldThrowException_IfAUserIsTryingToSetASettingWhichNeedsSuperUserPermission()
-    {
-        $this->setUser();
-        $setting = $this->addSystemSetting('mysystem', 'mytitle');
-
-        $this->settings->setSettingValue($setting, 2);
-    }
-
-    /**
-     * @expectedException \Exception
-     * @expectedExceptionMessage CoreAdminHome_PluginSettingChangeNotAllowed
-     */
-    public function test_setSettingValue_shouldThrowException_IfAnonymousIsTryingToSetASettingWhichNeedsUserPermission()
-    {
-        $setting = $this->addUserSetting('mysystem', 'mytitle');
-
-        $this->settings->setSettingValue($setting, 2);
-    }
-
-    public function test_setSettingValue_shouldSucceed_IfUserIsTryingToSetASettingWhichNeedsUserPermission()
-    {
-        $this->setUser();
-        $setting = $this->addUserSetting('mysystem', 'mytitle');
-
-        $this->settings->setSettingValue($setting, 2);
+        $setting->setValue(5);
+        $this->assertSettingHasValue($setting, 5);
 
-        $this->assertSettingHasValue($setting, 2);
+        $this->assertEquals($this->settings->getSetting('myname')->getValue(), 5);
     }
 
-    public function test_setSettingValue_shouldSucceed_IfSuperUserTriesToSaveASettingWhichRequiresSuperUserPermission()
+    public function test_addSetting_shouldPassTrackerStorage_IfInTrackerMode()
     {
         $this->setSuperUser();
 
-        $setting = $this->addSystemSetting('mysystem', 'mytitle');
-
-        $this->settings->setSettingValue($setting, 2);
-
-        $this->assertSettingHasValue($setting, 2);
-    }
+        $GLOBALS['PIWIK_TRACKER_MODE'] = true;
 
-    public function test_setSettingValue_shouldNotPersistValueInDatabase_OnSuccess()
-    {
-        $this->setSuperUser();
+        $settings = $this->createSettingsInstance();
+        $setting = $this->buildUserSetting('myname', 'mytitle', 'myRandomName');
+        $settings->addSetting($setting);
 
-        $setting = $this->buildSystemSetting('mysystem', 'mytitle');
-        $this->settings->addSetting($setting);
-        $this->settings->setSettingValue($setting, 2);
+        unset($GLOBALS['PIWIK_TRACKER_MODE']);
 
-        // make sure stored on the instance
-        $this->assertSettingHasValue($setting, 2);
-        $this->assertSettingIsNotSavedInTheDb('mysystem', null);
+        $storage = $setting->getStorage();
+        $this->assertTrue($storage instanceof SettingsStorage);
     }
 
     /**
@@ -254,10 +133,8 @@ class SettingsTest extends IntegrationTestCase
         $this->setUser();
         $setting = $this->buildUserSetting('mysystem', 'mytitle');
         $setting->availableValues = array('allowed' => 'text', 'allowed2' => 'text2');
-
         $this->settings->addSetting($setting);
-
-        $this->settings->setSettingValue($setting, 'notallowed');
+        $setting->setValue('notallowed');
     }
 
     /**
@@ -273,7 +150,7 @@ class SettingsTest extends IntegrationTestCase
 
         $this->settings->addSetting($setting);
 
-        $this->settings->setSettingValue($setting, array('allowed', 'notallowed'));
+        $setting->setValue(array('allowed', 'notallowed'));
     }
 
     public function test_setSettingValue_shouldApplyValidationAndSucceed_IfOptionsAreSet()
@@ -285,167 +162,14 @@ class SettingsTest extends IntegrationTestCase
 
         $this->settings->addSetting($setting);
 
-        $this->settings->setSettingValue($setting, array('allowed', 'allowed2'));
+        $setting->setValue(array('allowed', 'allowed2'));
         $this->assertSettingHasValue($setting, array('allowed', 'allowed2'));
 
         $setting->type = PluginSettings::TYPE_STRING;
-        $this->settings->setSettingValue($setting, 'allowed');
+        $setting->setValue('allowed');
         $this->assertSettingHasValue($setting, 'allowed');
     }
 
-    public function test_setSettingValue_shouldCastValue_IfTypeIsSetButNoFilter()
-    {
-        $this->setUser();
-
-        // cast to INT
-        $setting       = $this->addUserSetting('mysystem', 'mytitle');
-        $setting->type = PluginSettings::TYPE_INT;
-        $this->settings->setSettingValue($setting, '31xm42');
-        $this->assertSettingHasValue($setting, 31, 'integer');
-
-        // ARRAY
-        $setting->type = PluginSettings::TYPE_ARRAY;
-        $this->settings->setSettingValue($setting, '31xm42');
-        $this->assertSettingHasValue($setting, array('31xm42'), 'array');
-
-        // BOOL
-        $setting->type = PluginSettings::TYPE_BOOL;
-        $this->settings->setSettingValue($setting, '1');
-        $this->assertSettingHasValue($setting, true, 'boolean');
-
-        // FLOAT
-        $setting->type = PluginSettings::TYPE_FLOAT;
-        $this->settings->setSettingValue($setting, '1.21');
-        $this->assertSettingHasValue($setting, 1.21, 'float');
-
-        // STRING
-        $setting->type = PluginSettings::TYPE_STRING;
-        $this->settings->setSettingValue($setting, '31xm42');
-        $this->assertSettingHasValue($setting, '31xm42');
-    }
-
-    public function test_setSettingValue_shouldApplyFilterAndNotCast_IfAFilterIsSet()
-    {
-        $this->setUser();
-
-        $setting       = $this->buildUserSetting('mysystem', 'mytitle');
-        $setting->type = PluginSettings::TYPE_INT;
-
-        $self = $this;
-        $setting->transform = function ($value, $userSetting) use ($self, $setting) {
-            $self->assertEquals('31xm42', $value);
-            $self->assertEquals($setting, $userSetting);
-
-            return '43939kmf3m3';
-        };
-
-        $this->settings->addSetting($setting);
-        $this->settings->setSettingValue($setting, '31xm42');
-
-        // should not be casted to int
-        $this->assertSettingHasValue($setting, '43939kmf3m3', 'string');
-    }
-
-    public function test_getSettingValue_shouldReturnUncastedDefaultValue_IfNoValueIsSet()
-    {
-        $this->setUser();
-
-        $setting = $this->addUserSetting('mydefaultsystem', 'mytitle');
-        $setting->type = PluginSettings::TYPE_INT;
-        $setting->defaultValue ='mytestvalue';
-
-        // should not be casted to int
-        $this->assertSettingHasValue($setting, 'mytestvalue', 'string');
-    }
-
-    /**
-     * @expectedException \Exception
-     * @expectedExceptionMessage The setting myusersetting does not exist
-     */
-    public function test_getSettingValue_shouldThrowException_IfGivenSettingDoesNotExist()
-    {
-        $setting = $this->buildUserSetting('myusersetting', 'mytitle');
-
-        $this->settings->getSettingValue($setting);
-    }
-
-    /**
-     * @expectedException \Exception
-     * @expectedExceptionMessage CoreAdminHome_PluginSettingReadNotAllowed
-     */
-    public function test_getSettingValue_shouldThrowException_IfUserHasNotEnoughPermissionToReadValue()
-    {
-        $this->setUser();
-        $setting = $this->addSystemSetting('myusersetting', 'mytitle');
-        $this->settings->getSettingValue($setting);
-    }
-
-    public function test_getSettingValue_shouldReturnValue_IfReadbleByCurrentUserIsAllowed()
-    {
-        $this->setUser();
-        $setting = $this->addSystemSetting('myusersetting', 'mytitle');
-        $setting->readableByCurrentUser = true;
-
-        $this->assertEquals('', $this->settings->getSettingValue($setting));
-    }
-
-    public function test_getSettingValue_shouldReturnValue_IfValueExistsAndUserHasPermission()
-    {
-        $this->setUser();
-        $setting = $this->addUserSetting('myusersetting', 'mytitle');
-        $setting->type = PluginSettings::TYPE_ARRAY;
-        $this->settings->setSettingValue($setting, array(2,3,4));
-
-        $this->assertSettingHasValue($setting, array(2,3,4));
-    }
-
-    /**
-     * @expectedException \Exception
-     * @expectedExceptionMessage CoreAdminHome_PluginSettingChangeNotAllowed
-     */
-    public function test_removeSettingValue_shouldThrowException_IfUserHasNotEnoughUserPermissions()
-    {
-        $setting = $this->addUserSetting('myusersetting', 'mytitle');
-        $this->settings->removeSettingValue($setting);
-    }
-
-    /**
-     * @expectedException \Exception
-     * @expectedExceptionMessage CoreAdminHome_PluginSettingChangeNotAllowed
-     */
-    public function test_removeSettingValue_shouldThrowException_IfUserHasNotEnoughAdminPermissions()
-    {
-        $this->setUser();
-        $setting = $this->addSystemSetting('mysystemsetting', 'mytitle');
-        $this->settings->removeSettingValue($setting);
-    }
-
-    public function test_removeSettingValue_shouldRemoveValue_IfValueExistsAndHasEnoughPermissions()
-    {
-        $this->setUser();
-        $setting = $this->addUserSetting('myusersetting', 'mytitle');
-        $this->settings->setSettingValue($setting, '12345657');
-        $this->assertSettingHasValue($setting, '12345657');
-
-        $this->settings->removeSettingValue($setting);
-        $this->assertSettingHasValue($setting, null);
-    }
-
-    public function test_removeSettingValue_shouldRemoveValue_ShouldNotSaveValueInDb()
-    {
-        $this->setSuperUser();
-
-        $setting = $this->addSystemSetting('myusersetting', 'mytitle');
-        $this->settings->setSettingValue($setting, '12345657');
-        $this->settings->save();
-
-        $this->settings->removeSettingValue($setting);
-        $this->assertSettingHasValue($setting, null);
-
-        // should still have same value
-        $this->assertSettingIsNotSavedInTheDb('myusersetting', '12345657');
-    }
-
     public function test_getSettingsForCurrentUser_shouldOnlyReturnSettingsHavingEnoughAdminPermissions()
     {
         $this->setUser();
@@ -483,12 +207,14 @@ class SettingsTest extends IntegrationTestCase
         $this->setSuperUser();
 
         $this->addSystemSetting('mysystemsetting2', 'mytitle2');
-        $this->settings->setSettingValue($this->addSystemSetting('mysystemsetting1', 'mytitle1'), '111');
-        $this->settings->setSettingValue($this->addSystemSetting('mysystemsetting4', 'mytitle4'), '4444');
-        $this->settings->setSettingValue($this->addUserSetting('myusersetting1', 'mytitle5'), '55555');
+        $this->addSystemSetting('mysystemsetting1', 'mytitle1')->setValue('111');
+        $this->addSystemSetting('mysystemsetting4', 'mytitle4')->setValue('4444');
+        $this->addUserSetting('myusersetting1', 'mytitle5')->setValue('55555');
         $this->addSystemSetting('mysystemsetting3', 'mytitle3');
+
         $this->settings->save();
 
+        // verify actually saved
         $verifySettings = $this->createSettingsInstance();
 
         $setting1 = $this->buildSystemSetting('mysystemsetting1', 'mytitle1');
@@ -503,11 +229,26 @@ class SettingsTest extends IntegrationTestCase
         $verifySettings->addSetting($setting4);
         $verifySettings->addSetting($setting5);
 
-        $this->assertEquals('111', $verifySettings->getSettingValue($setting1));
-        $this->assertEquals(null, $verifySettings->getSettingValue($setting2));
-        $this->assertEquals(null, $verifySettings->getSettingValue($setting3));
-        $this->assertEquals('4444', $verifySettings->getSettingValue($setting4));
-        $this->assertEquals('55555', $verifySettings->getSettingValue($setting5));
+        $this->assertEquals('111', $setting1->getValue());
+        $this->assertEquals(null, $setting2->getValue());
+        $this->assertEquals(null, $setting3->getValue());
+        $this->assertEquals('4444', $setting4->getValue());
+        $this->assertEquals('55555', $setting5->getValue());
+    }
+
+
+    public function test_save_shouldClearTrackerCacheEntries()
+    {
+        $this->setSuperUser();
+
+        Cache::setCacheGeneral(array('testSetting' => 1));
+
+        $this->assertArrayHasKey('testSetting', Cache::getCacheGeneral());
+
+        $this->addSystemSetting('mysystemsetting2', 'mytitle2');
+        $this->settings->save();
+
+        $this->assertArrayNotHasKey('testSetting', Cache::getCacheGeneral());
     }
 
     public function test_removeAllPluginSettings_shouldRemoveAllSettings()
@@ -516,9 +257,9 @@ class SettingsTest extends IntegrationTestCase
 
         $this->addSystemSetting('mysystemsetting3', 'mytitle3');
         $this->addSystemSetting('mysystemsetting4', 'mytitle4');
-        $this->settings->setSettingValue($this->addSystemSetting('mysystemsetting1', 'mytitle1'), '111');
-        $this->settings->setSettingValue($this->addSystemSetting('mysystemsetting2', 'mytitle2'), '4444');
-        $this->settings->setSettingValue($this->addUserSetting('myusersetting1', 'mytitle5'), '55555');
+        $this->addSystemSetting('mysystemsetting1', 'mytitle1')->setValue('111');
+        $this->addSystemSetting('mysystemsetting2', 'mytitle2')->setValue('4444');
+        $this->addUserSetting('myusersetting1', 'mytitle5')->setValue('55555');
         $this->settings->save();
 
         $this->settings->removeAllPluginSettings();
@@ -537,11 +278,11 @@ class SettingsTest extends IntegrationTestCase
         $verifySettings->addSetting($setting4);
         $verifySettings->addSetting($setting5);
 
-        $this->assertEquals(null, $verifySettings->getSettingValue($setting1));
-        $this->assertEquals(null, $verifySettings->getSettingValue($setting2));
-        $this->assertEquals(null, $verifySettings->getSettingValue($setting3));
-        $this->assertEquals(null, $verifySettings->getSettingValue($setting4));
-        $this->assertEquals(null, $verifySettings->getSettingValue($setting5));
+        $this->assertEquals(null, $setting1->getValue());
+        $this->assertEquals(null, $setting2->getValue());
+        $this->assertEquals(null, $setting3->getValue());
+        $this->assertEquals(null, $setting4->getValue());
+        $this->assertEquals(null, $setting5->getValue());
     }
 
     /**
@@ -555,6 +296,15 @@ class SettingsTest extends IntegrationTestCase
         $this->settings->removeAllPluginSettings();
     }
 
+    /**
+     * @expectedException \Exception
+     * @expectedExceptionMessage General_ExceptionPrivilege
+     */
+    public function test_removeAllPluginSettings_shouldThrowException_InCaseAnonymousUser()
+    {
+        $this->settings->removeAllPluginSettings();
+    }
+
     public function test_userSetting_shouldGenerateDifferentKey_ThenSystemSetting()
     {
         $this->setSuperUser();
@@ -567,19 +317,6 @@ class SettingsTest extends IntegrationTestCase
         $this->assertEquals('myname#superUserLogin#', $user->getKey());
     }
 
-    public function test_userSetting_shouldGenerateDifferentKey_ForDifferentUsers()
-    {
-        $this->setSuperUser();
-
-        $user1 = $this->buildUserSetting('myname', 'mytitle', 'user1');
-        $user2 = $this->buildUserSetting('myname', 'mytitle', '_user2_');
-        $user3 = $this->buildUserSetting('myname', 'mytitle');
-
-        $this->assertEquals('myname#user1#', $user1->getKey());
-        $this->assertEquals('myname#_user2_#', $user2->getKey());
-        $this->assertEquals('myname#superUserLogin#', $user3->getKey());
-    }
-
     public function test_userSetting_shouldSaveValuesPerUser()
     {
         $this->setSuperUser();
@@ -591,11 +328,11 @@ class SettingsTest extends IntegrationTestCase
 
         $this->settings->addSetting($user);
 
-        $this->settings->setSettingValue($user, '111');
+        $user->setValue('111');
         $user->setUserLogin($user2Login);
-        $this->settings->setSettingValue($user, '222');
+        $user->setValue('222');
         $user->setUserLogin($user3Login);
-        $this->settings->setSettingValue($user, '333');
+        $user->setValue('333');
 
         $user->setUserLogin($user1Login);
         $this->assertSettingHasValue($user, '111');
@@ -605,7 +342,7 @@ class SettingsTest extends IntegrationTestCase
         $this->assertSettingHasValue($user, '333');
 
         $user->setUserLogin($user2Login);
-        $this->settings->removeSettingValue($user);
+        $user->removeValue();
 
         $user->setUserLogin($user1Login);
         $this->assertSettingHasValue($user, '111');
@@ -624,98 +361,10 @@ class SettingsTest extends IntegrationTestCase
         $this->assertSettingHasValue($user, null);
     }
 
-    /**
-     * @expectedException \Exception
-     * @expectedExceptionMessage You do not have the permission to read the settings of a different user
-     */
-    public function test_userSetting_shouldThrowException_IfSomeoneTriesToReadSettingsFromAnotherUserAndIsNotSuperuser()
-    {
-        $this->setUser();
-
-        $this->buildUserSetting('myname', 'mytitle', 'myRandomName');
-    }
-
-    public function test_userSetting_shouldBeAbleToSetLoginAndChangeValues_IfUserHasSuperUserAccess()
-    {
-        $this->setSuperUser();
-
-        $setting = $this->buildUserSetting('myname', 'mytitle', 'myRandomName');
-        $this->settings->addSetting($setting);
-
-        $this->settings->setSettingValue($setting, 5);
-        $this->assertSettingHasValue($setting, 5);
-
-        $this->settings->removeSettingValue($setting);
-        $this->assertSettingHasValue($setting, null);
-    }
-
     public function test_construct_shouldDetectTheNameOfThePluginAutomatically_IfPluginNameNotGiven()
     {
         $setting = new \Piwik\Plugins\ExampleSettingsPlugin\Settings();
 
         $this->assertEquals('ExampleSettingsPlugin', $setting->getPluginName());
     }
-
-    private function buildUserSetting($name, $title, $userLogin = null)
-    {
-        return new \Piwik\Settings\UserSetting($name, $title, $userLogin);
-    }
-
-    private function buildSystemSetting($name, $title)
-    {
-        return new \Piwik\Settings\SystemSetting($name, $title);
-    }
-
-    private function setSuperUser()
-    {
-        $pseudoMockAccess = new FakeAccess;
-        FakeAccess::$superUser = true;
-        Access::setSingletonInstance($pseudoMockAccess);
-    }
-
-    private function setUser()
-    {
-        $pseudoMockAccess = new FakeAccess;
-        FakeAccess::$idSitesView = array(1);
-        Access::setSingletonInstance($pseudoMockAccess);
-    }
-
-    private function addSystemSetting($name, $title)
-    {
-        $setting = $this->buildSystemSetting($name, $title);
-        $this->settings->addSetting($setting);
-        return $setting;
-    }
-
-    private function addUserSetting($name, $title)
-    {
-        $setting = $this->buildUserSetting($name, $title);
-        $this->settings->addSetting($setting);
-        return $setting;
-    }
-
-    private function assertSettingHasValue($setting, $expectedValue, $expectedType = null)
-    {
-        $value = $this->settings->getSettingValue($setting);
-        $this->assertEquals($expectedValue, $value);
-
-        if (!is_null($expectedType)) {
-            $this->assertInternalType($expectedType, $value);
-        }
-    }
-
-    private function assertSettingIsNotSavedInTheDb($settingName, $expectedValue)
-    {
-        // by creating a new instance...
-        $setting = $this->buildSystemSetting($settingName, 'mytitle');
-        $verifySettings = $this->createSettingsInstance();
-        $verifySettings->addSetting($setting);
-
-        $this->assertEquals($expectedValue, $verifySettings->getSettingValue($setting));
-    }
-
-    private function createSettingsInstance()
-    {
-        return new CorePluginSettingsTest('ExampleSettingsPlugin');
-    }
 }
diff --git a/tests/PHPUnit/Integration/Settings/CorePluginTestSettings.php b/tests/PHPUnit/Integration/Settings/CorePluginTestSettings.php
new file mode 100644
index 0000000000000000000000000000000000000000..7792f1f3534619355dbf776a96368814047583a7
--- /dev/null
+++ b/tests/PHPUnit/Integration/Settings/CorePluginTestSettings.php
@@ -0,0 +1,25 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Tests\Integration\Settings;
+
+use Piwik\Settings\Setting;
+
+class CorePluginTestSettings extends \Piwik\Plugins\ExampleSettingsPlugin\Settings {
+
+    public function init()
+    {
+
+    }
+
+    public function addSetting(Setting $setting)
+    {
+        parent::addSetting($setting);
+    }
+}
+
diff --git a/tests/PHPUnit/Integration/Settings/IntegrationTestCase.php b/tests/PHPUnit/Integration/Settings/IntegrationTestCase.php
new file mode 100644
index 0000000000000000000000000000000000000000..de16fc20f55c0f7aef8f46e49bbadbed7a722f5c
--- /dev/null
+++ b/tests/PHPUnit/Integration/Settings/IntegrationTestCase.php
@@ -0,0 +1,125 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Tests\Integration\Settings;
+
+use Piwik\Access;
+use Piwik\Db;
+use Piwik\Settings\Setting;
+use Piwik\Settings\Storage;
+use Piwik\Tests\Framework\Mock\FakeAccess;
+
+/**
+ * @group PluginSettings
+ * @group Settings
+ * @group Storage
+ */
+class IntegrationTestCase extends \Piwik\Tests\Framework\TestCase\IntegrationTestCase
+{
+    /**
+     * @var CorePluginTestSettings
+     */
+    protected $settings;
+
+    public function setUp()
+    {
+        parent::setUp();
+        Access::setSingletonInstance(null);
+        Db::destroyDatabaseObject();
+        $this->settings = $this->createSettingsInstance();
+    }
+
+    public function tearDown()
+    {
+        $this->setSuperUser();
+        if ($this->settings) {
+            $this->settings->removeAllPluginSettings();
+        }
+
+        parent::tearDown();
+    }
+
+    public function test_constructor_shouldNotEstablishADatabaseConnection()
+    {
+        $this->assertNotDbConnectionCreated();
+
+        new Storage('PluginName');
+
+        $this->assertNotDbConnectionCreated();
+    }
+
+    protected function assertSettingHasValue(Setting $setting, $expectedValue, $expectedType = null)
+    {
+        $value = $setting->getValue($setting);
+        $this->assertEquals($expectedValue, $value);
+
+        if (!is_null($expectedType)) {
+            $this->assertInternalType($expectedType, $value);
+        }
+    }
+
+    protected function buildUserSetting($name, $title, $userLogin = null)
+    {
+        $userSetting = new \Piwik\Settings\UserSetting($name, $title, $userLogin);
+        $userSetting->setStorage(new Storage('ExampleSettingsPlugin'));
+
+        return $userSetting;
+    }
+
+    protected function buildSystemSetting($name, $title)
+    {
+        $systemSetting = new \Piwik\Settings\SystemSetting($name, $title);
+        $systemSetting->setStorage(new Storage('ExampleSettingsPlugin'));
+
+        return $systemSetting;
+    }
+
+    protected function setSuperUser()
+    {
+        $pseudoMockAccess = new FakeAccess;
+        FakeAccess::$superUser = true;
+        Access::setSingletonInstance($pseudoMockAccess);
+    }
+
+    protected function setUser()
+    {
+        $pseudoMockAccess = new FakeAccess();
+        FakeAccess::$idSitesView = array(1);
+        Access::setSingletonInstance($pseudoMockAccess);
+    }
+
+    protected function createSettingsInstance()
+    {
+        return new CorePluginTestSettings('ExampleSettingsPlugin');
+    }
+
+    protected function addSystemSetting($name, $title)
+    {
+        $setting = $this->buildSystemSetting($name, $title);
+        $this->settings->addSetting($setting);
+        return $setting;
+    }
+
+    protected function addUserSetting($name, $title)
+    {
+        $setting = $this->buildUserSetting($name, $title);
+        $this->settings->addSetting($setting);
+        return $setting;
+    }
+
+
+    protected function assertSettingIsNotSavedInTheDb($settingName, $expectedValue)
+    {
+        // by creating a new instance...
+        $setting = $this->buildSystemSetting($settingName, 'mytitle');
+        $verifySettings = $this->createSettingsInstance();
+        $verifySettings->addSetting($setting);
+
+        $this->assertEquals($expectedValue, $setting->getValue());
+    }
+}
diff --git a/tests/PHPUnit/Integration/Settings/Storage/FactoryTest.php b/tests/PHPUnit/Integration/Settings/Storage/FactoryTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..0c63124dfe9b65d22a7af8bc5d5fd830c83b52a0
--- /dev/null
+++ b/tests/PHPUnit/Integration/Settings/Storage/FactoryTest.php
@@ -0,0 +1,62 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Tests\Integration\Settings\Storage;
+
+use Piwik\Settings\Storage;
+use Piwik\Settings\Storage\Factory;
+use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
+use Piwik\Tracker\SettingsStorage;
+
+/**
+ * @group Tracker
+ * @group Handler
+ * @group Visit
+ * @group Factory
+ * @group FactoryTest
+ */
+class FactoryTest extends IntegrationTestCase
+{
+
+    public function test_make_shouldCreateDefaultInstance()
+    {
+        $storage = Factory::make('PluginName');
+        $this->assertTrue($storage instanceof Storage);
+    }
+
+    public function test_make_shouldCreateTrackerInstance_IfInTrackerMode()
+    {
+        $storage = $this->makeTrackerInstance();
+
+        $this->assertTrue($storage instanceof SettingsStorage);
+    }
+
+    public function test_make_shouldPassThePluginNameToTheStorage()
+    {
+        $storage = Factory::make('PluginName');
+        $this->assertEquals('Plugin_PluginName_Settings', $storage->getOptionKey());
+    }
+
+    public function test_make_shouldPassThePluginNameToTheSettingsStorage()
+    {
+        $storage = $this->makeTrackerInstance();
+
+        $this->assertEquals('Plugin_PluginName_Settings', $storage->getOptionKey());
+    }
+
+    private function makeTrackerInstance()
+    {
+        $GLOBALS['PIWIK_TRACKER_MODE'] = true;
+
+        $storage = Factory::make('PluginName');
+
+        unset($GLOBALS['PIWIK_TRACKER_MODE']);
+
+        return $storage;
+    }
+}
diff --git a/tests/PHPUnit/Integration/Settings/StorageTest.php b/tests/PHPUnit/Integration/Settings/StorageTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..20a5652e676fd96bca37e103e722160b0d9404c8
--- /dev/null
+++ b/tests/PHPUnit/Integration/Settings/StorageTest.php
@@ -0,0 +1,162 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Tests\Integration\Settings;
+
+use Piwik\Option;
+use Piwik\Settings\Storage;
+use Piwik\Settings\Setting;
+
+/**
+ * @group PluginSettings
+ * @group Storage
+ */
+class StorageTest extends IntegrationTestCase
+{
+    /**
+     * @var Storage
+     */
+    protected $storage;
+
+    /**
+     * @var Setting
+     */
+    protected $setting;
+
+    public function setUp()
+    {
+        parent::setUp();
+
+        $this->setSuperUser();
+        $this->storage = $this->buildStorage();
+        $this->setting = $this->buildUserSetting('myname', 'My Name');
+    }
+
+    public function test_constructor_shouldNotEstablishADatabaseConnection()
+    {
+        $this->assertNotDbConnectionCreated();
+
+        $this->buildStorage();
+
+        $this->assertNotDbConnectionCreated();
+    }
+
+    public function test_getValue_shouldEstablishADatabaseConnection()
+    {
+        $this->assertNotDbConnectionCreated();
+
+        $this->storage->getValue($this->setting);
+
+        $this->assertDbConnectionCreated();
+    }
+
+    public function test_setValue_shouldEstablishADatabaseConnection()
+    {
+        $this->assertNotDbConnectionCreated();
+
+        $this->storage->setValue($this->setting, 5);
+
+        $this->assertDbConnectionCreated();
+    }
+
+    public function test_deleteValue_shouldEstablishADatabaseConnection()
+    {
+        $this->assertNotDbConnectionCreated();
+
+        $this->storage->deleteValue($this->setting, 5);
+
+        $this->assertDbConnectionCreated();
+    }
+
+    public function test_deleteAll_shouldEstablishADatabaseConnection()
+    {
+        $this->assertNotDbConnectionCreated();
+
+        $this->storage->deleteAllValues();
+
+        $this->assertDbConnectionCreated();
+    }
+
+    public function test_save_shouldEstablishADatabaseConnection()
+    {
+        $this->assertNotDbConnectionCreated();
+
+        $this->storage->save();
+
+        $this->assertDbConnectionCreated();
+    }
+
+    public function test_getValue_shouldReturnNullByDefault()
+    {
+        $value = $this->storage->getValue($this->setting);
+        $this->assertNull($value);
+    }
+
+    public function test_getValue_shouldReturnADefaultValueIfOneIsSet()
+    {
+        $this->setting->defaultValue = 194.34;
+        $value = $this->storage->getValue($this->setting);
+        $this->assertSame(194.34, $value);
+    }
+
+    public function test_setValue_getValue_shouldSetAndGetActualValue()
+    {
+        $this->storage->setValue($this->setting, 'myRandomVal');
+        $value = $this->storage->getValue($this->setting);
+        $this->assertEquals('myRandomVal', $value);
+    }
+
+    public function test_setValue_shouldNotSaveItInDatabase()
+    {
+        $this->storage->setValue($this->setting, 'myRandomVal');
+
+        $this->assertFalse($this->getValueFromOptionTable());
+    }
+
+    public function test_save_shouldPersistValueInDatabase()
+    {
+        $this->storage->setValue($this->setting, 'myRandomVal');
+        $this->storage->save();
+
+        $this->assertEquals('a:1:{s:22:"myname#superUserLogin#";s:11:"myRandomVal";}', $this->getValueFromOptionTable());
+    }
+
+    public function test_save_shouldPersistMultipleValues_ContainingInt()
+    {
+        $this->storage->setValue($this->setting, 'myRandomVal');
+        $this->storage->setValue($this->buildUserSetting('mySecondName', 'My Name'), 5);
+        $this->storage->save();
+
+        $this->assertEquals('a:2:{s:22:"myname#superUserLogin#";s:11:"myRandomVal";s:28:"mySecondName#superUserLogin#";i:5;}', $this->getValueFromOptionTable());
+    }
+
+    public function test_deleteAll_ShouldRemoveTheEntireEntry()
+    {
+        $this->storage->setValue($this->setting, 'myRandomVal');
+        $this->storage->save();
+        $this->storage->deleteAllValues();
+
+        $this->assertFalse($this->getValueFromOptionTable());
+    }
+
+    public function test_getOptionKey_shouldContainThePluginName()
+    {
+        $this->assertEquals('Plugin_PluginName_Settings', $this->storage->getOptionKey());
+    }
+
+    protected function buildStorage()
+    {
+        return new Storage('PluginName');
+    }
+
+    protected function getValueFromOptionTable()
+    {
+        return Option::get($this->storage->getOptionKey());
+    }
+
+}
diff --git a/tests/PHPUnit/Integration/Settings/SystemSettingTest.php b/tests/PHPUnit/Integration/Settings/SystemSettingTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..79eaf084ab856597e0d58d5cd72762062aa69b8b
--- /dev/null
+++ b/tests/PHPUnit/Integration/Settings/SystemSettingTest.php
@@ -0,0 +1,149 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Tests\Integration\Settings;
+
+use Piwik\Db;
+use Piwik\Settings\SystemSetting;
+
+/**
+ * @group PluginSettings
+ * @group Settings
+ * @group SystemSetting
+ */
+class SystemSettingTest extends IntegrationTestCase
+{
+
+    public function test_constructor_shouldNotEstablishADatabaseConnection()
+    {
+        $this->assertNotDbConnectionCreated();
+
+        new SystemSetting('name', 'title');
+
+        $this->assertNotDbConnectionCreated();
+    }
+
+    /**
+     * @expectedException \Exception
+     * @expectedExceptionMessage CoreAdminHome_PluginSettingChangeNotAllowed
+     */
+    public function test_setSettingValue_shouldThrowException_IfAUserIsTryingToSetASettingWhichNeedsSuperUserPermission()
+    {
+        $this->setUser();
+        $setting = $this->addSystemSetting('mysystem', 'mytitle');
+
+        $setting->setValue(2);
+    }
+
+    /**
+     * @expectedException \Exception
+     * @expectedExceptionMessage CoreAdminHome_PluginSettingChangeNotAllowed
+     */
+    public function test_setSettingValue_shouldThrowException_IfAnonymousIsTryingToSetASettingWhichNeedsSuperUserPermission()
+    {
+        $setting = $this->addSystemSetting('mysystem', 'mytitle');
+
+        $setting->setValue(2);
+    }
+
+    public function test_setSettingValue_shouldSucceed_IfSuperUserTriesToSaveASettingWhichRequiresSuperUserPermission()
+    {
+        $this->setSuperUser();
+
+        $setting = $this->addSystemSetting('mysystem', 'mytitle');
+        $setting->setValue(2);
+
+        $this->assertSettingHasValue($setting, 2);
+    }
+
+    public function test_setSettingValue_shouldNotPersistValueInDatabase_OnSuccess()
+    {
+        $this->setSuperUser();
+
+        $setting = $this->buildSystemSetting('mysystem', 'mytitle');
+        $this->settings->addSetting($setting);
+        $setting->setValue(2);
+
+        // make sure stored on the instance
+        $this->assertSettingHasValue($setting, 2);
+        $this->assertSettingIsNotSavedInTheDb('mysystem', null);
+    }
+
+    /**
+     * @expectedException \Exception
+     * @expectedExceptionMessage CoreAdminHome_PluginSettingReadNotAllowed
+     */
+    public function test_getSettingValue_shouldThrowException_IfUserHasNotEnoughPermissionToReadValue()
+    {
+        $this->setUser();
+        $setting = $this->addSystemSetting('myusersetting', 'mytitle');
+        $setting->getValue();
+    }
+
+    /**
+     * @expectedException \Exception
+     * @expectedExceptionMessage CoreAdminHome_PluginSettingReadNotAllowed
+     */
+    public function test_getSettingValue_shouldThrowException_IfAnonymousTriedToReadValue()
+    {
+        $setting = $this->addSystemSetting('myusersetting', 'mytitle');
+        $setting->getValue();
+    }
+
+    public function test_getSettingValue_shouldBeReadableBySuperUser()
+    {
+        $this->setSuperUser();
+        $setting = $this->addSystemSetting('myusersetting', 'mytitle');
+        $this->assertEquals('', $setting->getValue());
+    }
+
+    public function test_getSettingValue_shouldReturnValue_IfReadbleByCurrentUserIsAllowed()
+    {
+        $this->setUser();
+        $setting = $this->addSystemSetting('myusersetting', 'mytitle');
+        $setting->readableByCurrentUser = true;
+
+        $this->assertEquals('', $setting->getValue());
+    }
+
+    /**
+     * @expectedException \Exception
+     * @expectedExceptionMessage CoreAdminHome_PluginSettingChangeNotAllowed
+     */
+    public function test_removeSettingValue_shouldThrowException_IfUserHasNotEnoughAdminPermissions()
+    {
+        $this->setUser();
+        $setting = $this->addSystemSetting('mysystemsetting', 'mytitle');
+        $setting->removeValue();
+    }
+
+    /**
+     * @expectedException \Exception
+     * @expectedExceptionMessage CoreAdminHome_PluginSettingChangeNotAllowed
+     */
+    public function test_removeSettingValue_shouldThrowException_IfAnonymousTriesToRemoveValue()
+    {
+        $setting = $this->addSystemSetting('mysystemsetting', 'mytitle');
+        $setting->removeValue();
+    }
+
+    public function test_removeSettingValue_shouldRemoveValue_ShouldNotSaveValueInDb()
+    {
+        $this->setSuperUser();
+
+        $setting = $this->addSystemSetting('myusersetting', 'mytitle');
+        $setting->setValue('12345657');
+        $this->settings->save();
+
+        $setting->removeValue();
+        $this->assertSettingHasValue($setting, null);
+
+        // should still have same value
+        $this->assertSettingIsNotSavedInTheDb('myusersetting', '12345657');
+    }
+}
diff --git a/tests/PHPUnit/Integration/Settings/UserSettingTest.php b/tests/PHPUnit/Integration/Settings/UserSettingTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..5d9c089dc249eaa0c516b799351297cc2017a780
--- /dev/null
+++ b/tests/PHPUnit/Integration/Settings/UserSettingTest.php
@@ -0,0 +1,246 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Tests\Integration\Settings;
+
+use Piwik\Db;
+use Piwik\Settings\UserSetting;
+use Piwik\Plugin\Settings as PluginSettings;
+
+/**
+ * @group PluginSettings
+ * @group Settings
+ * @group UserSetting
+ */
+class UserSettingTest extends IntegrationTestCase
+{
+    public function test_constructor_shouldNotEstablishADatabaseConnection()
+    {
+        $this->assertNotDbConnectionCreated();
+
+        new UserSetting('name', 'title');
+
+        $this->assertNotDbConnectionCreated();
+    }
+
+    public function test_constructor_shouldEstablishADatabaseConnection_AsSoonAsWeGetAValue()
+    {
+        $this->setSuperUser();
+        Db::destroyDatabaseObject();
+
+        $setting = $this->buildUserSetting('testSetting', 'Test Setting');
+
+        $this->assertNotDbConnectionCreated();
+
+        $setting->getValue($setting);
+
+        $this->assertDbConnectionCreated();
+    }
+
+    public function test_constructor_shouldEstablishADatabaseConnection_AsSoonAsWeSetAValue()
+    {
+        $this->setSuperUser();
+        Db::destroyDatabaseObject();
+
+        $setting  = $this->buildUserSetting('testSetting', 'Test Setting');
+        $settings = $this->createSettingsInstance();
+        $settings->addSetting($setting);
+
+        $this->assertNotDbConnectionCreated();
+
+        $setting->setValue('5');
+
+        $this->assertDbConnectionCreated();
+    }
+
+    /**
+     * @expectedException \Exception
+     * @expectedExceptionMessage CoreAdminHome_PluginSettingChangeNotAllowed
+     */
+    public function test_setSettingValue_shouldThrowException_IfAnonymousIsTryingToSetASettingWhichNeedsUserPermission()
+    {
+        $setting = $this->addUserSetting('mysystem', 'mytitle');
+
+        $setting->setValue(2);
+    }
+
+    public function test_setSettingValue_shouldSucceed_IfUserIsTryingToSetASettingWhichNeedsUserPermission()
+    {
+        $this->setUser();
+        $setting = $this->addUserSetting('mysystem', 'mytitle');
+        $setting->setValue(2);
+
+        $this->assertSettingHasValue($setting, 2);
+    }
+
+    public function test_setSettingValue_shouldCastValue_IfTypeIsSetButNoFilter()
+    {
+        $this->setUser();
+
+        // cast to INT
+        $setting       = $this->addUserSetting('mysystem', 'mytitle');
+        $setting->type = PluginSettings::TYPE_INT;
+        $setting->setValue('31xm42');
+        $this->assertSettingHasValue($setting, 31, 'integer');
+
+        // ARRAY
+        $setting->type = PluginSettings::TYPE_ARRAY;
+        $setting->setValue('31xm42');
+        $this->assertSettingHasValue($setting, array('31xm42'), 'array');
+
+        // BOOL
+        $setting->type = PluginSettings::TYPE_BOOL;
+        $setting->setValue('1');
+        $this->assertSettingHasValue($setting, true, 'boolean');
+
+        // FLOAT
+        $setting->type = PluginSettings::TYPE_FLOAT;
+        $setting->setValue('1.21');
+        $this->assertSettingHasValue($setting, 1.21, 'float');
+
+        // STRING
+        $setting->type = PluginSettings::TYPE_STRING;
+        $setting->setValue('31xm42');
+        $this->assertSettingHasValue($setting, '31xm42');
+    }
+
+    public function test_setSettingValue_shouldApplyFilterAndNotCast_IfAFilterIsSet()
+    {
+        $this->setUser();
+
+        $setting       = $this->buildUserSetting('mysystem', 'mytitle');
+        $setting->type = PluginSettings::TYPE_INT;
+
+        $self = $this;
+        $setting->transform = function ($value, $userSetting) use ($self, $setting) {
+            $self->assertEquals('31xm42', $value);
+            $self->assertEquals($setting, $userSetting);
+
+            return '43939kmf3m3';
+        };
+
+        $setting->setValue('31xm42');
+
+        // should not be casted to int
+        $this->assertSettingHasValue($setting, '43939kmf3m3', 'string');
+    }
+
+    /**
+     * @expectedException \Exception
+     * @expectedExceptionMessage Validation Fail
+     */
+    public function test_setSettingValue_shouldValidateAValue_IfAFilterIsSet()
+    {
+        $this->setUser();
+
+        $setting       = $this->buildUserSetting('mysystem', 'mytitle');
+        $setting->type = PluginSettings::TYPE_INT;
+
+        $self = $this;
+        $setting->validate = function ($value, $userSetting) use ($self, $setting) {
+            $self->assertEquals('31xm42', $value);
+            $self->assertEquals($setting, $userSetting);
+
+            throw new \Exception('Validation Fail');
+        };
+
+        $setting->setValue('31xm42');
+    }
+
+    public function test_getSettingValue_shouldReturnUncastedDefaultValue_IfNoValueIsSet()
+    {
+        $this->setUser();
+
+        $setting = $this->addUserSetting('mydefaultsystem', 'mytitle');
+        $setting->type = PluginSettings::TYPE_INT;
+        $setting->defaultValue ='mytestvalue';
+
+        // should not be casted to int
+        $this->assertSettingHasValue($setting, 'mytestvalue', 'string');
+    }
+
+    /**
+     * @expectedException \Exception
+     * @expectedExceptionMessage CoreAdminHome_PluginSettingReadNotAllowed
+     */
+    public function test_getSettingValue_shouldThrowException_IfGivenSettingDoesNotExist()
+    {
+        $setting = $this->buildUserSetting('myusersetting', 'mytitle');
+        $setting->getValue();
+    }
+
+    public function test_getSettingValue_shouldReturnValue_IfValueExistsAndUserHasPermission()
+    {
+        $this->setUser();
+        $setting = $this->addUserSetting('myusersetting', 'mytitle');
+        $setting->type = PluginSettings::TYPE_ARRAY;
+        $setting->setValue(array(2,3,4));
+
+        $this->assertSettingHasValue($setting, array(2,3,4));
+    }
+
+    /**
+     * @expectedException \Exception
+     * @expectedExceptionMessage CoreAdminHome_PluginSettingChangeNotAllowed
+     */
+    public function test_removeSettingValue_shouldThrowException_IfUserHasNotEnoughUserPermissions()
+    {
+        $setting = $this->addUserSetting('myusersetting', 'mytitle');
+        $setting->removeValue();
+    }
+
+    public function test_removeSettingValue_shouldRemoveValue_IfValueExistsAndHasEnoughPermissions()
+    {
+        $this->setUser();
+        $setting = $this->addUserSetting('myusersetting', 'mytitle');
+        $setting->setValue('12345657');
+        $this->assertSettingHasValue($setting, '12345657');
+
+        $setting->removeValue();
+        $this->assertSettingHasValue($setting, null);
+    }
+
+    public function test_userSetting_shouldGenerateDifferentKey_ForDifferentUsers()
+    {
+        $this->setSuperUser();
+
+        $user1 = $this->buildUserSetting('myname', 'mytitle', 'user1');
+        $user2 = $this->buildUserSetting('myname', 'mytitle', '_user2_');
+        $user3 = $this->buildUserSetting('myname', 'mytitle');
+
+        $this->assertEquals('myname#user1#', $user1->getKey());
+        $this->assertEquals('myname#_user2_#', $user2->getKey());
+        $this->assertEquals('myname#superUserLogin#', $user3->getKey());
+    }
+
+    /**
+     * @expectedException \Exception
+     * @expectedExceptionMessage You do not have the permission to read the settings of a different user
+     */
+    public function test_userSetting_shouldThrowException_IfSomeoneTriesToReadSettingsFromAnotherUserAndIsNotSuperuser()
+    {
+        $this->setUser();
+
+        $this->buildUserSetting('myname', 'mytitle', 'myRandomName');
+    }
+
+    public function test_userSetting_shouldBeAbleToSetLoginAndChangeValues_IfUserHasSuperUserAccess()
+    {
+        $this->setSuperUser();
+
+        $setting = $this->buildUserSetting('myname', 'mytitle', 'myRandomName');
+        $this->settings->addSetting($setting);
+
+        $setting->setValue(5);
+        $this->assertSettingHasValue($setting, 5);
+
+        $setting->removeValue();
+        $this->assertSettingHasValue($setting, null);
+    }
+
+}
diff --git a/tests/PHPUnit/Integration/Tracker/SettingsStorageTest.php b/tests/PHPUnit/Integration/Tracker/SettingsStorageTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..1bda1105e69bfef5bbf2ee859e954ad9aa1103d6
--- /dev/null
+++ b/tests/PHPUnit/Integration/Tracker/SettingsStorageTest.php
@@ -0,0 +1,138 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Tests\Integration\Tracker;
+
+use Piwik\Option;
+use Piwik\Settings\Storage;
+use Piwik\Settings\Setting;
+use Piwik\Tests\Integration\Settings\IntegrationTestCase;
+use Piwik\Tests\Integration\Settings\StorageTest;
+use Piwik\Tracker\Cache;
+use Piwik\Tracker\SettingsStorage;
+
+/**
+ * @group PluginSettings
+ * @group Storage
+ * @group SettingStorage
+ */
+class SettingsStorageTest extends StorageTest
+{
+
+    public function test_storageShouldLoadSettingsFromCacheIfPossible()
+    {
+        $this->setSettingValueInCache('my0815RandomName');
+
+        $this->assertEquals('my0815RandomName', $this->storage->getValue($this->setting));
+    }
+
+    public function test_storageShouldLoadSettingsFromCache_AndNotCreateADatabaseInstance()
+    {
+        $this->setSettingValueInCache('my0815RandomName');
+
+        $this->storage->getValue($this->setting);
+
+        $this->assertNotDbConnectionCreated();
+    }
+
+    public function test_clearCache_shouldActuallyClearTheCacheEntry()
+    {
+        $this->setSettingValueInCache('my0815RandomName');
+
+        $this->assertArrayHasKey('settingsStorage', Cache::getCacheGeneral());
+
+        SettingsStorage::clearCache();
+
+        $this->assertArrayNotHasKey('settingsStorage', Cache::getCacheGeneral());
+    }
+
+    public function test_storageShouldNotCastAnyCachedValue()
+    {
+        $this->setSettingValueInCache(5);
+
+        $this->assertEquals(5, $this->storage->getValue($this->setting));
+    }
+
+    public function test_storageShouldFallbackToDatebaseInCaseNoCacheExists()
+    {
+        $this->storage->setValue($this->setting, 5);
+        $this->storage->save();
+
+        $this->assertNotFalse($this->getValueFromOptionTable()); // make sure saved in db
+
+        $storage = $this->buildStorage();
+        $this->assertEquals(5, $storage->getValue($this->setting));
+    }
+
+    public function test_storageCreateACacheEntryIfNoCacheExistsYet()
+    {
+        $cache = Cache::getCacheGeneral();
+        $this->assertArrayNotHasKey('settingsStorage', $cache); // make sure there is no cache entry yet
+
+        $this->setSettingValueAndMakeSureCacheGetsCreated('myVal');
+
+        $cache = Cache::getCacheGeneral();
+
+        $this->assertEquals(array(
+            $this->storage->getOptionKey() => array(
+                $this->setting->getKey() => 'myVal'
+            )
+        ), $cache['settingsStorage']);
+    }
+
+    public function test_shouldAddACacheEntryToAnotherCacheEntryAndNotOverwriteAll()
+    {
+        $dummyCacheEntry = array(
+            'Plugin_PluginNameOther_Settings' => array(
+                'anything' => 'anyval',
+                'any' => 'other'
+            )
+        );
+        Cache::setCacheGeneral(array(
+            'settingsStorage' => $dummyCacheEntry
+        ));
+
+        Option::set($this->storage->getOptionKey(), serialize(array('mykey' => 'myVal')));
+
+        $this->buildStorage()->getValue($this->setting); // force adding new cache entry
+
+        $cache = Cache::getCacheGeneral();
+
+        $dummyCacheEntry[$this->storage->getOptionKey()] = array(
+            'mykey' => 'myVal'
+        );
+
+        $this->assertEquals($dummyCacheEntry, $cache['settingsStorage']);
+    }
+
+    protected function buildStorage()
+    {
+        return new SettingsStorage('PluginName');
+    }
+
+    private function setSettingValueInCache($value)
+    {
+        Cache::setCacheGeneral(array(
+            'settingsStorage' => array(
+                $this->storage->getOptionKey() => array(
+                    $this->setting->getKey() => $value
+                )
+            )
+        ));
+    }
+
+    private function setSettingValueAndMakeSureCacheGetsCreated($value)
+    {
+        $this->storage->setValue($this->setting, $value);
+        $this->storage->save();
+
+        $storage = $this->buildStorage();
+        $storage->getValue($this->setting); // force creation of cache by loading settings
+    }
+
+}