Merge pull request #1744 from dchill42/load_config_units

Loader and Config Unit Test Improvements
diff --git a/tests/Bootstrap.php b/tests/Bootstrap.php
index 1c666d5..8ce80b3 100644
--- a/tests/Bootstrap.php
+++ b/tests/Bootstrap.php
@@ -8,10 +8,7 @@
 
 // Path constants
 defined('PROJECT_BASE') OR define('PROJECT_BASE', realpath($dir.'/../').'/');
-defined('BASEPATH') OR define('BASEPATH', PROJECT_BASE.'system/');
-defined('APPPATH') OR define('APPPATH', PROJECT_BASE.'application/');
-defined('VIEWPATH') OR define('VIEWPATH', PROJECT_BASE.'');
-isset($_SERVER['REMOTE_ADDR']) OR $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
+defined('SYSTEM_PATH') OR define('SYSTEM_PATH', PROJECT_BASE.'system/');
 
 // Get vfsStream either via PEAR or composer
 foreach (explode(PATH_SEPARATOR, get_include_path()) as $path)
@@ -31,8 +28,17 @@
 	class_alias('org\bovigo\vfs\vfsStreamWrapper', 'vfsStreamWrapper');
 }
 
+// Define CI path constants to VFS (filesystem setup in CI_TestCase::setUp)
+defined('BASEPATH') OR define('BASEPATH', vfsStream::url('system/'));
+defined('APPPATH') OR define('APPPATH', vfsStream::url('application/'));
+defined('VIEWPATH') OR define('VIEWPATH', APPPATH.'views/');
+
+// Set localhost "remote" IP
+isset($_SERVER['REMOTE_ADDR']) OR $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
+
 // Prep our test environment
 include_once $dir.'/mocks/core/common.php';
+include_once SYSTEM_PATH.'core/Common.php';
 include_once $dir.'/mocks/autoloader.php';
 spl_autoload_register('autoload');
 
diff --git a/tests/README.md b/tests/README.md
index d600951..a5f89a2 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -64,6 +64,30 @@
 3. Application Test	- bootstrapping for application/tests [not started]
 4. Package Test		- bootstrapping for <package>/tests [not started]
 
+### Test Environment:
+
+The test/Bootstrap.php file establishes global constants such as BASEPATH,
+APPPATH, and VIEWPATH, initializing them to point to VFS locations. The
+test case class employs vfsStream to make a clean virtual filesystem with
+the necessary paths for every individual test.
+
+Within each test case, VFS directory objects are available to use as arguments
+to the VFS convenience functions (see below):
+
+- ci_vfs_root: VFS filesystem root
+- ci_app_root: Application directory
+- ci_base_root: System directory
+- ci_view_root: Views directory
+
+Classes being instantiated for testing are read from the actual filesystem
+by the unit test autoloader, as are mockups created in tests/mocks. If you
+need access to the real system directory, the SYSTEM_PATH constant always
+points to it.
+
+Any other resources which need to be read from the path constants must be
+created or cloned within your test. Functions for doing so are outlined
+below.
+
 ### CI_TestCase Documentation
 
 Test cases should extend CI_TestCase. This internally extends
@@ -78,8 +102,14 @@
 
     $this->ci_set_config($key, $val)
 
-Set the global config variables. If key is an array, it will
-replace the entire config array. They are _not_ merged.
+Set the global config variables in a mock Config object. If key is an array,
+it will replace the entire config array. They are _not_ merged. If called
+without any parameters, it will create the mock object but not set any values.
+The mock Config object also provides rudimentary item() and load() stubs for
+delivering configured values to classes being tested and handling config load
+calls, respectively. The load() stub does _not_ actually load any files, it
+only records the filename provided. Check the config->loaded array to verify
+calls made.
 
     $this->ci_instance($obj)
 
@@ -103,11 +133,48 @@
     $cfg = new $cfg; // instantiates config and overwrites the CFG global
 
 	$this->ci_set_core_class($name, $obj)
-	
+
 An alternative way to set one of the core globals.
 
+	$this->ci_vfs_mkdir($name, $root)
+
+Creates a new directory in the test VFS. Pass a directory object to be the
+parent directory or none to create a root-level directory. Returns the new
+directory object.
+
+	$this->ci_vfs_create($file, $content, $root, $path)
+
+Creates a new VFS file. '.php' is automatically appended to the filename if
+it has no extension. Pass a directory object as the root, and an optional path
+to recurse and/or create for containing the file. Path may be a string (such
+as 'models/subdir') or an array (e.g. - array('models', 'subdir') ). Existing
+directories in the VFS root will be recursed until a new directory is
+identified - all others in the path will be created, so you can mix-and-match
+old and new directories. If $file is an array (key = name, value = content),
+multiple files will be created in the same path.
+
+	$this->ci_vfs_clone($path)
+
+Clones an existing file from the real filesystem to exist in the same path of
+the VFS. Path must be relative to the project root (i.e. - starting with
+'system' or 'application').
+
+	$this->ci_vfs_path($path, $base)
+
+Creates a VFS file path string suitable for use with PHP file operations. Path
+may be absolute from the VFS root, or relative to a base path. It is often
+useful to use APPPATH or BASEPATH as the base.
+
+	$this->helper($name)
+
+Loads a helper from the real filesystem.
+
+	$this->lang($name)
+
+Loads a language file from the real filesystem and returns the $lang array.
+
 	$this->ci_get_config()  __internal__
-	
+
 Returns the global config array. Internal as you shouldn't need to
 call this (you're setting it, after all). Used internally to make
 CI's get_config() work.
@@ -155,4 +222,4 @@
 
 Needs to be able to handle packages
 that are used multiple times within the application (i.e. EE/Pyro modules)
-as well as packages that are used by multiple applications (library distributions)
+as well as packages that are used by multiple applications (library distributions)
\ No newline at end of file
diff --git a/tests/codeigniter/core/Config_test.php b/tests/codeigniter/core/Config_test.php
index 30cb90a..d652a62 100644
--- a/tests/codeigniter/core/Config_test.php
+++ b/tests/codeigniter/core/Config_test.php
@@ -7,11 +7,12 @@
 		$cls =& $this->ci_core_class('cfg');
 
 		// set predictable config values
-		$this->ci_set_config(array(
+		$this->cfg = array(
 			'index_page'		=> 'index.php',
 			'base_url'			=> 'http://example.com/',
 			'subclass_prefix'	=> 'MY_'
-		));
+		);
+		$this->ci_set_config($this->cfg);
 
 		$this->config = new $cls;
 	}
@@ -20,7 +21,7 @@
 
 	public function test_item()
 	{
-		$this->assertEquals('http://example.com/', $this->config->item('base_url'));
+		$this->assertEquals($this->cfg['base_url'], $this->config->item('base_url'));
 
 		// Bad Config value
 		$this->assertFalse($this->config->item('no_good_item'));
@@ -48,36 +49,103 @@
 		// Bad Config value
 		$this->assertFalse($this->config->slash_item('no_good_item'));
 
-		$this->assertEquals('http://example.com/', $this->config->slash_item('base_url'));
+		$this->assertEquals($this->cfg['base_url'], $this->config->slash_item('base_url'));
 
-		$this->assertEquals('MY_/', $this->config->slash_item('subclass_prefix'));
+		$this->assertEquals($this->cfg['subclass_prefix'].'/', $this->config->slash_item('subclass_prefix'));
+	}
+
+	// --------------------------------------------------------------------
+
+	public function test_base_url()
+	{
+		// Test regular base URL
+		$base_url = $this->cfg['base_url'];
+		$this->assertEquals($base_url, $this->config->base_url());
+
+		// Test with URI
+		$uri = 'test';
+		$this->assertEquals($base_url.$uri, $this->config->base_url($uri));
+
+		// Clear base_url
+		$this->ci_set_config('base_url', '');
+
+		// Rerun constructor
+		$cls =& $this->ci_core_class('cfg');
+		$this->config = new $cls;
+
+		// Test default base
+		$this->assertEquals('http://localhost/', $this->config->base_url());
+
+		// Capture server vars
+		$old_host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : NULL;
+		$old_script = isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : NULL;
+		$old_https = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : NULL;
+
+		// Setup server vars for detection
+		$host = 'test.com';
+		$path = '/path/';
+		$script = 'base_test.php';
+		$_SERVER['HTTP_HOST'] = $host;
+		$_SERVER['SCRIPT_NAME'] = $path.$script;
+
+		// Rerun constructor
+		$this->config = new $cls;
+
+		// Test plain detected
+		$this->assertEquals('http://'.$host.$path, $this->config->base_url());
+
+		// Rerun constructor
+		$_SERVER['HTTPS'] = 'on';
+		$this->config = new $cls;
+
+		// Test secure detected
+		$this->assertEquals('https://'.$host.$path, $this->config->base_url());
+
+		// Restore server vars
+		if ($old_host === NULL) unset($_SERVER['HTTP_HOST']);
+		else $_SERVER['HTTP_HOST'] = $old_host;
+		if ($old_script === NULL) unset($_SERVER['SCRIPT_NAME']);
+		else $_SERVER['SCRIPT_NAME'] = $old_script;
+		if ($old_https === NULL) unset($_SERVER['HTTPS']);
+		else $_SERVER['HTTPS'] = $old_https;
 	}
 
 	// --------------------------------------------------------------------
 
 	public function test_site_url()
 	{
-		$this->assertEquals('http://example.com/index.php', $this->config->site_url());
+		$base_url = $this->cfg['base_url'];
+		$index_page = $this->cfg['index_page'];
+		$this->assertEquals($base_url.$index_page, $this->config->site_url());
 
-		$base_url = $this->config->item('base_url');
-
+		$old_base = $this->config->item('base_url');
 		$this->config->set_item('base_url', '');
 
 		$q_string = $this->config->item('enable_query_strings');
-
 		$this->config->set_item('enable_query_strings', FALSE);
 
-		$this->assertEquals('index.php/test', $this->config->site_url('test'));
-		$this->assertEquals('index.php/test/1', $this->config->site_url(array('test', '1')));
+		$uri= 'test';
+		$uri2 = '1';
+		$this->assertEquals($index_page.'/'.$uri, $this->config->site_url($uri));
+		$this->assertEquals($index_page.'/'.$uri.'/'.$uri2, $this->config->site_url(array($uri, $uri2)));
+
+		$suffix = 'ing';
+		$this->config->set_item('url_suffix', $suffix);
+
+		$arg = 'pass';
+		$this->assertEquals($index_page.'/'.$uri.$suffix, $this->config->site_url($uri));
+		$this->assertEquals($index_page.'/'.$uri.$suffix.'?'.$arg, $this->config->site_url($uri.'?'.$arg));
+
+		$this->config->set_item('url_suffix', FALSE);
 
 		$this->config->set_item('enable_query_strings', TRUE);
 
-		$this->assertEquals('index.php?test', $this->config->site_url('test'));
-		$this->assertEquals('index.php?0=test&1=1', $this->config->site_url(array('test', '1')));
+		$this->assertEquals($index_page.'?'.$uri, $this->config->site_url($uri));
+		$this->assertEquals($index_page.'?0='.$uri.'&1='.$uri2, $this->config->site_url(array($uri, $uri2)));
 
-		$this->config->set_item('base_url', $base_url);
+		$this->config->set_item('base_url', $old_base);
 
-		$this->assertEquals('http://example.com/index.php?test', $this->config->site_url('test'));
+		$this->assertEquals($base_url.$index_page.'?'.$uri, $this->config->site_url($uri));
 
 		// back to home base
 		$this->config->set_item('enable_query_strings', $q_string);
@@ -87,7 +155,100 @@
 
 	public function test_system_url()
 	{
-		$this->assertEquals('http://example.com/system/', $this->config->system_url());
+		$this->assertEquals($this->cfg['base_url'].'system/', $this->config->system_url());
+	}
+
+	// --------------------------------------------------------------------
+
+	public function test_load()
+	{
+		// Test regular load
+		$file = 'test.php';
+		$key = 'testconfig';
+		$val = 'my_value';
+		$cfg = array($key => $val);
+		$this->ci_vfs_create($file, '<?php $config = '.var_export($cfg, TRUE).';', $this->ci_app_root, 'config');
+		$this->assertTrue($this->config->load($file));
+		$this->assertEquals($val, $this->config->item($key));
+
+		// Test reload - value should not change
+		$val2 = 'new_value';
+		$cfg = array($key => $val2);
+		$this->ci_vfs_create($file, '<?php $config = '.var_export($cfg, TRUE).';', $this->ci_app_root, 'config');
+		$this->assertTrue($this->config->load($file));
+		$this->assertEquals($val, $this->config->item($key));
+
+		// Test section load
+		$file = 'secttest';
+		$cfg = array(
+			'one' => 'prime',
+			'two' => 2,
+			'three' => true
+		);
+		$this->ci_vfs_create($file.'.php', '<?php $config = '.var_export($cfg, TRUE).';', $this->ci_app_root, 'config');
+		$this->assertTrue($this->config->load($file, TRUE));
+		$this->assertEquals($cfg, $this->config->item($file));
+
+		// Test section merge
+		$cfg2 = array(
+			'three' => 'tres',
+			'number' => 42,
+			'letter' => 'Z'
+		);
+		$pkg_dir = 'package';
+		$this->ci_vfs_create($file.'.php', '<?php $config = '.var_export($cfg2, TRUE).';', $this->ci_app_root,
+			array($pkg_dir, 'config'));
+		array_push($this->config->_config_paths, $this->ci_vfs_path($pkg_dir.'/', APPPATH));
+		$this->assertTrue($this->config->load($file, TRUE));
+		$this->assertEquals(array_merge($cfg, $cfg2), $this->config->item($file));
+		array_pop($this->config->_config_paths);
+
+		// Test graceful fail of invalid file
+		$file = 'badfile';
+		$this->ci_vfs_create($file, '', $this->ci_app_root, 'config');
+		$this->assertFalse($this->config->load($file, FALSE, TRUE));
+
+		// Test regular fail of invalid file
+		$this->setExpectedException(
+			'RuntimeException',
+			'CI Error: Your '.$this->ci_vfs_path('config/'.$file.'.php', APPPATH).
+				' file does not appear to contain a valid configuration array.'
+		);
+		$this->assertNull($this->config->load($file));
+	}
+
+	// --------------------------------------------------------------------
+
+	public function test_load_nonexistent()
+	{
+		// Test graceful fail of nonexistent file
+		$this->assertFalse($this->config->load('not_config_file', FALSE, TRUE));
+
+		// Test regular fail
+		$file = 'absentia';
+		$this->setExpectedException(
+			'RuntimeException',
+			'CI Error: The configuration file '.$file.'.php does not exist.'
+		);
+		$this->assertNull($this->config->load($file));
+	}
+
+	// --------------------------------------------------------------------
+
+	public function test_assign_to_config()
+	{
+		$key1 = 'test';
+		$key2 = '1';
+		$val1 = 'foo';
+		$val2 = 'bar';
+		$cfg = array(
+			$key1 => $val1,
+			$key2 => $val2
+		);
+
+		$this->assertNull($this->config->_assign_to_config($cfg));
+		$this->assertEquals($val1, $this->config->item($key1));
+		$this->assertEquals($val2, $this->config->item($key2));
 	}
 
 }
\ No newline at end of file
diff --git a/tests/codeigniter/core/Lang_test.php b/tests/codeigniter/core/Lang_test.php
index a410dab..3364362 100644
--- a/tests/codeigniter/core/Lang_test.php
+++ b/tests/codeigniter/core/Lang_test.php
@@ -17,6 +17,7 @@
 
 	public function test_load()
 	{
+		$this->ci_vfs_clone('system/language/english/profiler_lang.php');
 		$this->assertTrue($this->lang->load('profiler', 'english'));
 		$this->assertEquals('URI STRING', $this->lang->line('profiler_uri_string'));
 	}
@@ -25,6 +26,7 @@
 
 	public function test_load_with_unspecified_language()
 	{
+		$this->ci_vfs_clone('system/language/english/profiler_lang.php');
 		$this->assertTrue($this->lang->load('profiler'));
 		$this->assertEquals('URI STRING', $this->lang->line('profiler_uri_string'));
 	}
diff --git a/tests/codeigniter/core/Loader_test.php b/tests/codeigniter/core/Loader_test.php
index fdea962..f7c338a 100644
--- a/tests/codeigniter/core/Loader_test.php
+++ b/tests/codeigniter/core/Loader_test.php
@@ -7,61 +7,225 @@
 	public function set_up()
 	{
 		// Instantiate a new loader
-		$this->load = new Mock_Core_Loader();
+		$loader = $this->ci_core_class('loader');
+		$this->load = new $loader();
 
-		// mock up a ci instance
-		$this->ci_obj = new stdClass;
+		// Get CI instance
+		$this->ci_obj = $this->ci_instance();
 
-		// Fix get_instance()
-		$this->ci_instance($this->ci_obj);
+		// Set subclass prefix
+		$this->prefix = 'MY_';
+		$this->ci_set_config('subclass_prefix', $this->prefix);
 	}
 
 	// --------------------------------------------------------------------
 
 	public function test_library()
 	{
-		$this->_setup_config_mock();
+		// Create library in VFS
+		$lib = 'unit_test_lib';
+		$class = 'CI_'.ucfirst($lib);
+		$this->ci_vfs_create($lib, '<?php class '.$class.' { }', $this->ci_base_root, 'libraries');
+
+		// Test is_loaded fail
+		$this->assertFalse($this->load->is_loaded($lib));
 
 		// Test loading as an array.
-		$this->assertNull($this->load->library(array('table')));
-		$this->assertTrue(class_exists('CI_Table'), 'Table class exists');
-		$this->assertAttributeInstanceOf('CI_Table', 'table', $this->ci_obj);
+		$this->assertNull($this->load->library(array($lib)));
+		$this->assertTrue(class_exists($class), $class.' does not exist');
+		$this->assertAttributeInstanceOf($class, $lib, $this->ci_obj);
 
 		// Test no lib given
-		$this->assertEquals(FALSE, $this->load->library());
+		$this->assertFalse($this->load->library());
 
 		// Test a string given to params
-		$this->assertEquals(NULL, $this->load->library('table', ' '));
+		$this->assertNull($this->load->library($lib, ' '));
+
+		// Create library w/o class
+		$lib = 'bad_test_lib';
+		$this->ci_vfs_create($lib, '', $this->ci_base_root, 'libraries');
+
+		// Test non-existent class
+		$this->setExpectedException(
+			'RuntimeException',
+			'CI Error: Non-existent class: '.$lib
+		);
+		$this->assertNull($this->load->library($lib));
+	}
+
+	// --------------------------------------------------------------------
+
+	public function test_library_extension()
+	{
+		// Create library and extension in VFS
+		$name = 'ext_test_lib';
+		$lib = ucfirst($name);
+		$class = 'CI_'.$lib;
+		$ext = $this->prefix.$lib;
+		$this->ci_vfs_create($lib, '<?php class '.$class.' { }', $this->ci_base_root, 'libraries');
+		$this->ci_vfs_create($ext, '<?php class '.$ext.' extends '.$class.' { }', $this->ci_app_root, 'libraries');
+
+		// Test loading with extension
+		$this->assertNull($this->load->library($lib));
+		$this->assertTrue(class_exists($class), $class.' does not exist');
+		$this->assertTrue(class_exists($ext), $ext.' does not exist');
+		$this->assertAttributeInstanceOf($class, $name, $this->ci_obj);
+		$this->assertAttributeInstanceOf($ext, $name, $this->ci_obj);
+
+		// Test reloading with object name
+		$obj = 'exttest';
+		$this->assertNull($this->load->library($lib, NULL, $obj));
+		$this->assertAttributeInstanceOf($class, $obj, $this->ci_obj);
+		$this->assertAttributeInstanceOf($ext, $obj, $this->ci_obj);
+
+		// Test reloading
+		unset($this->ci_obj->$name);
+		$this->assertNull($this->load->library($lib));
+		$this->assertObjectNotHasAttribute($name, $this->ci_obj);
+
+		// Create baseless library
+		$name = 'ext_baseless_lib';
+		$lib = ucfirst($name);
+		$class = $this->prefix.$lib;
+		$this->ci_vfs_create($class, '<?php class '.$class.' { }', $this->ci_app_root, 'libraries');
+
+		// Test missing base class
+		$this->setExpectedException(
+			'RuntimeException',
+			'CI Error: Unable to load the requested class: '.$lib
+		);
+		$this->assertNull($this->load->library($lib));
+	}
+
+	// --------------------------------------------------------------------
+
+	public function test_library_config()
+	{
+		// Create library in VFS
+		$lib = 'unit_test_config_lib';
+		$class = 'CI_'.ucfirst($lib);
+		$content = '<?php class '.$class.' { public function __construct($params) { $this->config = $params; } }';
+		$this->ci_vfs_create($lib, $content, $this->ci_base_root, 'libraries');
+
+		// Create config file
+		$cfg = array(
+			'foo' => 'bar',
+			'bar' => 'baz',
+			'baz' => false
+		);
+		$this->ci_vfs_create($lib, '<?php $config = '.var_export($cfg, TRUE).';', $this->ci_app_root, 'config');
+
+		// Test object name and config
+		$obj = 'testy';
+		$this->assertNull($this->load->library($lib, NULL, $obj));
+		$this->assertTrue(class_exists($class), $class.' does not exist');
+		$this->assertAttributeInstanceOf($class, $obj, $this->ci_obj);
+		$this->assertEquals($cfg, $this->ci_obj->$obj->config);
+
+		// Test is_loaded
+		$this->assertEquals($obj, $this->load->is_loaded($lib));
 	}
 
 	// --------------------------------------------------------------------
 
 	public function test_load_library_in_application_dir()
 	{
-		$this->_setup_config_mock();
+		// Create library in VFS
+		$lib = 'super_test_library';
+		$class = ucfirst($lib);
+		$this->ci_vfs_create($lib, '<?php class '.$class.' { }', $this->ci_app_root, 'libraries');
 
-		$content = '<?php class Super_test_library {} ';
-
-		$model = vfsStream::newFile('Super_test_library.php')->withContent($content)->at($this->load->libs_dir);
-		$this->assertNull($this->load->library('super_test_library'));
+		// Load library
+		$this->assertNull($this->load->library($lib));
 
 		// Was the model class instantiated.
-		$this->assertTrue(class_exists('Super_test_library'));
+		$this->assertTrue(class_exists($class), $class.' does not exist');
+		$this->assertAttributeInstanceOf($class, $lib, $this->ci_obj);
 	}
 
 	// --------------------------------------------------------------------
 
-	private function _setup_config_mock()
+	public function test_driver()
 	{
-		// Mock up a config object until we
-		// figure out how to test the library configs
-		$config = $this->getMock('CI_Config', NULL, array(), '', FALSE);
-		$config->expects($this->any())
-			   ->method('load')
-			   ->will($this->returnValue(TRUE));
+		// Create driver in VFS
+		$driver = 'unit_test_driver';
+		$dir = ucfirst($driver);
+		$class = 'CI_'.$dir;
+		$content = '<?php class '.$class.' { } ';
+		$this->ci_vfs_create($driver, $content, $this->ci_base_root, 'libraries/'.$dir);
 
-		// Add the mock to our stdClass
-		$this->ci_instance_var('config', $config);
+		// Test loading as an array.
+		$this->assertNull($this->load->driver(array($driver)));
+		$this->assertTrue(class_exists($class), $class.' does not exist');
+		$this->assertAttributeInstanceOf($class, $driver, $this->ci_obj);
+
+		// Test loading as a library with a name
+		$obj = 'testdrive';
+		$this->assertNull($this->load->library($driver, NULL, $obj));
+		$this->assertAttributeInstanceOf($class, $obj, $this->ci_obj);
+
+		// Test no driver given
+		$this->assertFalse($this->load->driver());
+
+		// Test a string given to params
+		$this->assertNull($this->load->driver($driver, ' '));
+	}
+
+	// --------------------------------------------------------------------
+
+	public function test_models()
+	{
+		$this->ci_set_core_class('model', 'CI_Model');
+
+		// Create model in VFS
+		$model = 'unit_test_model';
+		$class = ucfirst($model);
+		$content = '<?php class '.$class.' extends CI_Model {} ';
+		$this->ci_vfs_create($model, $content, $this->ci_app_root, 'models');
+
+		// Load model
+		$this->assertNull($this->load->model($model));
+
+		// Was the model class instantiated.
+		$this->assertTrue(class_exists($class));
+
+		// Test no model given
+		$this->assertNull($this->load->model(''));
+	}
+
+	// --------------------------------------------------------------------
+
+	public function test_model_subdir()
+	{
+		// Make sure base class is loaded - we'll test _ci_include later
+		$this->ci_core_class('model');
+
+		// Create modelin VFS
+		$model = 'test_sub_model';
+		$base = 'CI_Model';
+		$class = ucfirst($model);
+		$subdir = 'cars';
+		$this->ci_vfs_create($model, '<?php class '.$class.' extends '.$base.' { }', $this->ci_app_root,
+			array('models', $subdir));
+
+		// Load model
+		$name = 'testors';
+		$this->assertNull($this->load->model($subdir.'/'.$model, $name));
+
+		// Was the model class instantiated?
+		$this->assertTrue(class_exists($class));
+		$this->assertObjectHasAttribute($name, $this->ci_obj);
+		$this->assertAttributeInstanceOf($base, $name, $this->ci_obj);
+		$this->assertAttributeInstanceOf($class, $name, $this->ci_obj);
+
+		// Test name conflict
+		$obj = 'conflict';
+		$this->ci_obj->$obj = new StdClass();
+		$this->setExpectedException(
+			'RuntimeException',
+			'CI Error: The model name you are loading is the name of a resource that is already being used: '.$obj
+		);
+		$this->load->model('not_real', $obj);
 	}
 
 	// --------------------------------------------------------------------
@@ -78,58 +242,38 @@
 
 	// --------------------------------------------------------------------
 
-	/**
-	 * @coverts CI_Loader::model
-	 */
-	public function test_models()
-	{
-		$this->ci_set_core_class('model', 'CI_Model');
-
-		$content = '<?php class Unit_test_model extends CI_Model {} ';
-
-		$model = vfsStream::newFile('unit_test_model.php')->withContent($content)->at($this->load->models_dir);
-
-		$this->assertNull($this->load->model('unit_test_model'));
-
-		// Was the model class instantiated.
-		$this->assertTrue(class_exists('Unit_test_model'));
-
-		// Test no model given
-		$this->assertNull($this->load->model(''));
-	}
-
-	// --------------------------------------------------------------------
-
 	// public function testDatabase()
 	// {
-	// 	$this->assertEquals(NULL, $this->load->database());
-	// 	$this->assertEquals(NULL, $this->load->dbutil());
+	// 	$this->assertNull($this->load->database());
+	// 	$this->assertNull($this->load->dbutil());
 	// }
 
 	// --------------------------------------------------------------------
 
-	/**
-	 * @coverts CI_Loader::view
-	 */
 	public function test_load_view()
 	{
-		$this->ci_set_core_class('output', 'CI_Output');
+		// Create view in VFS
+		$view = 'unit_test_view';
+		$var = 'hello';
+		$value = 'World!';
+		$content = 'This is my test page.  ';
+		$this->ci_vfs_create($view, $content.'<?php echo $'.$var.';', $this->ci_app_root, 'views');
 
-		$content = 'This is my test page.  <?php echo $hello; ?>';
-		$view = vfsStream::newFile('unit_test_view.php')->withContent($content)->at($this->load->views_dir);
+		// Test returning view
+		$out = $this->load->view($view, array($var => $value), TRUE);
+		$this->assertEquals($content.$value, $out);
 
-		// Use the optional return parameter in this test, so the view is not
-		// run through the output class.
-		$this->assertEquals('This is my test page.  World!',
-		$this->load->view('unit_test_view', array('hello' => "World!"), TRUE));
+		// Mock output class
+		$output = $this->getMock('CI_Output', array('append_output'));
+		$output->expects($this->once())->method('append_output')->with($content.$value);
+		$this->ci_instance_var('output', $output);
 
+		// Test view output
+		$this->assertNull($this->load->view($view, array($var => $value)));
 	}
 
 	// --------------------------------------------------------------------
 
-	/**
-	 * @coverts CI_Loader::view
-	 */
 	public function test_non_existent_view()
 	{
 		$this->setExpectedException(
@@ -144,14 +288,17 @@
 
 	public function test_file()
 	{
+		// Create view in VFS
+		$dir = 'views';
+		$file = 'ci_test_mock_file';
 		$content = 'Here is a test file, which we will load now.';
-		$file = vfsStream::newFile('ci_test_mock_file.php')->withContent($content)->at($this->load->views_dir);
+		$this->ci_vfs_create($file, $content, $this->ci_app_root, $dir);
 
 		// Just like load->view(), take the output class out of the mix here.
-		$load = $this->load->file(vfsStream::url('application').'/views/ci_test_mock_file.php', TRUE);
+		$out = $this->load->file(APPPATH.$dir.'/'.$file.'.php', TRUE);
+		$this->assertEquals($content, $out);
 
-		$this->assertEquals($content, $load);
-
+		// Test non-existent file
 		$this->setExpectedException(
 			'RuntimeException',
 			'CI Error: Unable to load the requested file: ci_test_file_not_exists'
@@ -164,21 +311,56 @@
 
 	public function test_vars()
 	{
-		$this->assertNull($this->load->vars(array('foo' => 'bar')));
-		$this->assertNull($this->load->vars('foo', 'bar'));
+		$key1 = 'foo';
+		$val1 = 'bar';
+		$key2 = 'boo';
+		$val2 = 'hoo';
+		$this->assertNull($this->load->vars(array($key1 => $val1)));
+		$this->assertNull($this->load->vars($key2, $val2));
+		$this->assertEquals($val1, $this->load->get_var($key1));
+		$this->assertEquals(array($key1 => $val1, $key2 => $val2), $this->load->get_vars());
 	}
 
 	// --------------------------------------------------------------------
 
 	public function test_helper()
 	{
-		$this->assertEquals(NULL, $this->load->helper('array'));
+		// Create helper in VFS
+		$helper = 'test';
+		$func = '_my_helper_test_func';
+		$content = '<?php function '.$func.'() { return true; } ';
+		$this->ci_vfs_create($helper.'_helper', $content, $this->ci_base_root, 'helpers');
 
+		// Create helper extension
+		$exfunc = '_my_extension_func';
+		$content = '<?php function '.$exfunc.'() { return true; } ';
+		$this->ci_vfs_create($this->prefix.$helper.'_helper', $content, $this->ci_app_root, 'helpers');
+
+		// Load helper
+		$this->assertNull($this->load->helper($helper));
+		$this->assertTrue(function_exists($func), $func.' does not exist');
+		$this->assertTrue(function_exists($exfunc), $exfunc.' does not exist');
+
+		// Create baseless extension
+		$ext = 'bad_ext';
+		$this->ci_vfs_create($this->prefix.$ext.'_helper', '', $this->ci_app_root, 'helpers');
+
+		// Test bad extension
+		$this->setExpectedException(
+			'RuntimeException',
+			'CI Error: Unable to load the requested file: helpers/'.$ext.'_helper.php'
+		);
+		$this->load->helper($ext);
+	}
+
+	// --------------------------------------------------------------------
+
+	public function test_non_existent_helper()
+	{
 		$this->setExpectedException(
 			'RuntimeException',
 			'CI Error: Unable to load the requested file: helpers/bad_helper.php'
 		);
-
 		$this->load->helper('bad');
 	}
 
@@ -186,36 +368,154 @@
 
 	public function test_loading_multiple_helpers()
 	{
-		$this->assertEquals(NULL, $this->load->helpers(array('file', 'array', 'string')));
+		// Create helpers in VFS
+		$helpers = array();
+		$funcs = array();
+		$files = array();
+		for ($i = 1; $i <= 3; ++$i) {
+			$helper = 'test'.$i;
+			$helpers[] = $helper;
+			$func = '_my_helper_test_func'.$i;
+			$funcs[] = $func;
+			$files[$helper.'_helper'] = '<?php function '.$func.'() { return true; } ';
+		}
+		$this->ci_vfs_create($files, NULL, $this->ci_base_root, 'helpers');
+
+		// Load helpers
+		$this->assertNull($this->load->helpers($helpers));
+
+		// Verify helper existence
+		foreach ($funcs as $func) {
+			$this->assertTrue(function_exists($func), $func.' does not exist');
+		}
 	}
 
 	// --------------------------------------------------------------------
 
-	// public function testLanguage()
-	// {
-	// 	$this->assertEquals(NULL, $this->load->language('test'));
-	// }
+	public function test_language()
+	{
+		// Mock lang class and test load call
+		$file = 'test';
+		$lang = $this->getMock('CI_Lang', array('load'));
+		$lang->expects($this->once())->method('load')->with($file);
+		$this->ci_instance_var('lang', $lang);
+		$this->assertNull($this->load->language($file));
+	}
+
+	// --------------------------------------------------------------------
+
+	public function test_packages()
+	{
+		// Create model in VFS package path
+		$dir = 'third-party';
+		$lib = 'unit_test_package';
+		$class = 'CI_'.ucfirst($lib);
+		$this->ci_vfs_create($lib, '<?php class '.$class.' { }', $this->ci_app_root, array($dir, 'libraries'));
+
+		// Get paths
+		$paths = $this->load->get_package_paths(TRUE);
+
+		// Add path and verify
+		$path = APPPATH.$dir.'/';
+		$this->assertNull($this->load->add_package_path($path));
+		$this->assertContains($path, $this->load->get_package_paths(TRUE));
+
+		// Test successful load
+		$this->assertNull($this->load->library($lib));
+		$this->assertTrue(class_exists($class), $class.' does not exist');
+
+		// Add another path
+		$path2 = APPPATH.'another/';
+		$this->assertNull($this->load->add_package_path($path2));
+		$this->assertContains($path2, $this->load->get_package_paths(TRUE));
+
+		// Remove last path
+		$this->assertNull($this->load->remove_package_path());
+		$this->assertNotContains($path2, $this->load->get_package_paths(TRUE));
+
+		// Remove path and verify restored paths
+		$this->assertNull($this->load->remove_package_path($path));
+		$this->assertEquals($paths, $this->load->get_package_paths(TRUE));
+
+		// Test failed load without path
+		$this->setExpectedException(
+			'RuntimeException',
+			'CI Error: Unable to load the requested class: '.$lib
+		);
+		$this->load->library($lib);
+	}
 
 	// --------------------------------------------------------------------
 
 	public function test_load_config()
 	{
-		$this->_setup_config_mock();
-		$this->assertNull($this->load->config('config', FALSE));
+		$cfg = 'someconfig';
+		$this->assertNull($this->load->config($cfg, FALSE));
+		$this->assertContains($cfg, $this->ci_obj->config->loaded);
 	}
 
 	// --------------------------------------------------------------------
 
-	public function test_load_bad_config()
+	public function test_initialize()
 	{
-		$this->_setup_config_mock();
+		// Create helper in VFS
+		$helper = 'autohelp';
+		$hlp_func = '_autohelp_test_func';
+		$content = '<?php function '.$hlp_func.'() { return true; }';
+		$this->ci_vfs_create($helper.'_helper', $content, $this->ci_app_root, 'helpers');
 
-		$this->setExpectedException(
-			'RuntimeException',
-			'CI Error: The configuration file foobar.php does not exist.'
+		// Create library in VFS
+		$lib = 'autolib';
+		$lib_class = 'CI_'.ucfirst($lib);
+		$this->ci_vfs_create($lib, '<?php class '.$lib_class.' { }', $this->ci_base_root, 'libraries');
+
+		// Create driver in VFS
+		$drv = 'autodrv';
+		$subdir = ucfirst($drv);
+		$drv_class = 'CI_'.$subdir;
+		$this->ci_vfs_create($drv, '<?php class '.$drv_class.' { }', $this->ci_base_root, array('libraries', $subdir));
+
+		// Create model in VFS package path
+		$dir = 'testdir';
+		$path = APPPATH.$dir.'/';
+		$model = 'automod';
+		$mod_class = ucfirst($model);
+		$this->ci_vfs_create($model, '<?php class '.$mod_class.' { }', $this->ci_app_root, array($dir, 'models'));
+
+		// Create autoloader config
+		$cfg = array(
+			'packages' => array($path),
+			'helper' => array($helper),
+			'libraries' => array($lib),
+			'drivers' => array($drv),
+			'model' => array($model),
+			'config' => array('config1', 'config2')
 		);
+		$this->ci_vfs_create('autoload', '<?php $autoload = '.var_export($cfg, TRUE).';', $this->ci_app_root, 'config');
 
-		$this->load->config('foobar', FALSE);
+		// Run initialize and autoloader
+		$this->load->initialize();
+
+		// Verify path
+		$this->assertContains($path, $this->load->get_package_paths());
+
+		// Verify helper
+		$this->assertTrue(function_exists($hlp_func), $hlp_func.' does not exist');
+
+		// Verify library
+		$this->assertTrue(class_exists($lib_class), $lib_class.' does not exist');
+		$this->assertAttributeInstanceOf($lib_class, $lib, $this->ci_obj);
+
+		// Verify driver
+		$this->assertTrue(class_exists($drv_class), $drv_class.' does not exist');
+		$this->assertAttributeInstanceOf($drv_class, $drv, $this->ci_obj);
+
+		// Verify model
+		$this->assertTrue(class_exists($mod_class), $mod_class.' does not exist');
+		$this->assertAttributeInstanceOf($mod_class, $model, $this->ci_obj);
+
+		// Verify config calls
+		$this->assertEquals($cfg['config'], $this->ci_obj->config->loaded);
 	}
 
 }
\ No newline at end of file
diff --git a/tests/codeigniter/helpers/date_helper_test.php b/tests/codeigniter/helpers/date_helper_test.php
index 9feade7..1458acd 100644
--- a/tests/codeigniter/helpers/date_helper_test.php
+++ b/tests/codeigniter/helpers/date_helper_test.php
@@ -168,6 +168,8 @@
 
 	public function test_timespan()
 	{
+		$this->ci_vfs_clone('system/language/english/date_lang.php');
+
 		$loader_cls = $this->ci_core_class('load');
 		$this->ci_instance_var('load', new $loader_cls);
 
diff --git a/tests/codeigniter/helpers/form_helper_test.php b/tests/codeigniter/helpers/form_helper_test.php
index 1a30ed9..48628d2 100644
--- a/tests/codeigniter/helpers/form_helper_test.php
+++ b/tests/codeigniter/helpers/form_helper_test.php
@@ -1,10 +1,12 @@
 <?php
 
-require BASEPATH . 'core/Common.php';
-require BASEPATH . 'helpers/form_helper.php';
-
 class Form_helper_test extends CI_TestCase
 {
+	public function set_up()
+	{
+		$this->helper('form');
+	}
+
 	public function test_form_hidden()
 	{
 		$expected = <<<EOH
diff --git a/tests/codeigniter/helpers/number_helper_test.php b/tests/codeigniter/helpers/number_helper_test.php
index ef6aae1..817db2c 100644
--- a/tests/codeigniter/helpers/number_helper_test.php
+++ b/tests/codeigniter/helpers/number_helper_test.php
@@ -11,31 +11,18 @@
 
 		// Mock away load, too much going on in there,
 		// we'll just check for the expected parameter
-
 		$lang = $this->getMock($lang_cls, array('load'));
 		$lang->expects($this->once())
 			 ->method('load')
 			 ->with($this->equalTo('number'));
 
 		// Assign the proper language array
-
-		$lang->language = $this->_get_lang('number');
+		$lang->language = $this->lang('number');
 
 		// We don't have a controller, so just create
 		// a cheap class to act as our super object.
 		// Make sure it has a lang attribute.
-
-		$obj = new stdClass;
-		$obj->lang = $lang;
-		$this->ci_instance($obj);
-	}
-
-	// Quick helper to actually grab the language
-	// file. Consider moving this to ci_testcase?
-	public function _get_lang($name)
-	{
-		require BASEPATH.'language/english/'.$name.'_lang.php';
-		return $lang;
+		$this->ci_instance_var('lang', $lang);
 	}
 
 	public function test_byte_format()
diff --git a/tests/codeigniter/helpers/text_helper_test.php b/tests/codeigniter/helpers/text_helper_test.php
index f131469..d75d262 100644
--- a/tests/codeigniter/helpers/text_helper_test.php
+++ b/tests/codeigniter/helpers/text_helper_test.php
@@ -64,6 +64,7 @@
 
 	public function test_convert_accented_characters()
 	{
+		$this->ci_vfs_clone('application/config/foreign_chars.php');
 		$this->assertEquals('AAAeEEEIIOOEUUUeY', convert_accented_characters('ÀÂÄÈÊËÎÏÔŒÙÛÜŸ'));
 		$this->assertEquals('a e i o u n ue', convert_accented_characters('á é í ó ú ñ ü'));
 	}
diff --git a/tests/codeigniter/libraries/Encrypt_test.php b/tests/codeigniter/libraries/Encrypt_test.php
index 153a25e..998d806 100644
--- a/tests/codeigniter/libraries/Encrypt_test.php
+++ b/tests/codeigniter/libraries/Encrypt_test.php
@@ -4,11 +4,8 @@
 
 	public function set_up()
 	{
-		$obj = new stdClass;
-		$obj->encrypt = new Mock_Libraries_Encrypt();
-
-		$this->ci_instance($obj);
-		$this->encrypt = $obj->encrypt;
+		$this->encrypt = new Mock_Libraries_Encrypt();
+		$this->ci_instance_var('encrypt', $this->encrypt);
 
 		$this->ci_set_config('encryption_key', "Encryptin'glike@boss!");
 		$this->msg = 'My secret message';
diff --git a/tests/codeigniter/libraries/Parser_test.php b/tests/codeigniter/libraries/Parser_test.php
index b68f44a..394c226 100644
--- a/tests/codeigniter/libraries/Parser_test.php
+++ b/tests/codeigniter/libraries/Parser_test.php
@@ -4,12 +4,8 @@
 
 	public function set_up()
 	{
-		$obj = new stdClass;
-		$obj->parser = new Mock_Libraries_Parser();
-
-		$this->ci_instance($obj);
-
-		$this->parser = $obj->parser;
+		$this->parser = new Mock_Libraries_Parser();
+		$this->ci_instance_var('parser', $this->parser);
 	}
 
 	// --------------------------------------------------------------------
diff --git a/tests/codeigniter/libraries/Session_test.php b/tests/codeigniter/libraries/Session_test.php
index 60d3a5b..14469f7 100644
--- a/tests/codeigniter/libraries/Session_test.php
+++ b/tests/codeigniter/libraries/Session_test.php
@@ -29,13 +29,15 @@
 		$_COOKIE = array();
 
 		// Establish necessary support classes
-		$obj = new stdClass;
 		$cfg = $this->ci_core_class('cfg');
-		$obj->config = new $cfg();
 		$ldr = $this->ci_core_class('load');
-		$obj->load = new $ldr();
-		$obj->input = new Mock_Core_Input(NULL, NULL);
-		$this->ci_instance($obj);
+		$ci = $this->ci_instance();
+		$ci->config = new $cfg();
+		$ci->load = new $ldr();
+		$ci->input = new Mock_Core_Input(NULL, NULL);
+
+		// Make sure string helper is available
+		$this->ci_vfs_clone('system/helpers/string_helper.php');
 
 		// Attach session instance locally
 		$config = array(
diff --git a/tests/codeigniter/libraries/Table_test.php b/tests/codeigniter/libraries/Table_test.php
index edfc83d..ce04b6a 100644
--- a/tests/codeigniter/libraries/Table_test.php
+++ b/tests/codeigniter/libraries/Table_test.php
@@ -4,12 +4,8 @@
 
 	public function set_up()
 	{
-		$obj = new stdClass;
-		$obj->table = new Mock_Libraries_Table();
-
-		$this->ci_instance($obj);
-
-		$this->table = $obj->table;
+		$this->table = new Mock_Libraries_Table();
+		$this->ci_instance_var('table', $this->table);
 	}
 
 	// Setter Methods
diff --git a/tests/codeigniter/libraries/Typography_test.php b/tests/codeigniter/libraries/Typography_test.php
index eb6dacb..5dba062 100644
--- a/tests/codeigniter/libraries/Typography_test.php
+++ b/tests/codeigniter/libraries/Typography_test.php
@@ -4,12 +4,8 @@
 
 	public function set_up()
 	{
-		$obj = new stdClass;
-		$obj->type = new Mock_Libraries_Typography();
-
-		$this->ci_instance($obj);
-
-		$this->type = $obj->type;
+		$this->type = new Mock_Libraries_Typography();
+		$this->ci_instance('type', $this->type);
 	}
 
 	// --------------------------------------------------------------------
diff --git a/tests/codeigniter/libraries/Upload_test.php b/tests/codeigniter/libraries/Upload_test.php
index b4ef7bb..546cebc 100644
--- a/tests/codeigniter/libraries/Upload_test.php
+++ b/tests/codeigniter/libraries/Upload_test.php
@@ -4,18 +4,11 @@
 
 	function set_up()
 	{
-		$obj = new stdClass;
-		$obj->upload = new Mock_Libraries_Upload();
-		$obj->security = new Mock_Core_Security();
-		$obj->lang = new Mock_Core_Lang();
-
-		$this->ci_instance($obj);
-		$this->upload = $obj->upload;
-
-		vfsStreamWrapper::register();
-		vfsStreamWrapper::setRoot(new vfsStreamDirectory('testDir'));
-
-		$this->_test_dir = vfsStreamWrapper::getRoot();
+		$ci = $this->ci_instance();
+		$ci->upload = new Mock_Libraries_Upload();
+		$ci->security = new Mock_Core_Security();
+		$ci->lang = new Mock_Core_Lang();
+		$this->upload = $ci->upload;
 	}
 
 	function test_do_upload()
@@ -64,11 +57,15 @@
 
 	function test_set_filename()
 	{
-		$file1 = vfsStream::newFile('hello-world.txt')->withContent('Hello world.')->at($this->_test_dir);
+		$dir = 'uploads';
+		$isnew = 'helloworld.txt';
+		$exists = 'hello-world.txt';
+		$this->ci_vfs_create($exists, 'Hello world.', $this->ci_app_root, $dir);
+		$path = $this->ci_vfs_path($dir.'/', APPPATH);
 		$this->upload->file_ext = '.txt';
 
-		$this->assertEquals('helloworld.txt', $this->upload->set_filename(vfsStream::url('testDir').'/', 'helloworld.txt'));
-		$this->assertEquals('hello-world1.txt', $this->upload->set_filename(vfsStream::url('testDir').'/', 'hello-world.txt'));
+		$this->assertEquals($isnew, $this->upload->set_filename($path, $isnew));
+		$this->assertEquals('hello-world1.txt', $this->upload->set_filename($path, $exists));
 	}
 
 	function test_set_max_filesize()
@@ -107,7 +104,7 @@
 	function test_set_image_properties()
 	{
 		$this->upload->file_type = 'image/gif';
-		$this->upload->file_temp = 'tests/mocks/uploads/ci_logo.gif';
+		$this->upload->file_temp = realpath(PROJECT_BASE.'tests/mocks/uploads/ci_logo.gif');
 
 		$props = array(
 			'image_width'	=>	170,
@@ -156,7 +153,7 @@
 		$this->assertTrue($this->upload->is_allowed_filetype(FALSE));
 		$this->assertTrue($this->upload->is_allowed_filetype(TRUE));
 
-		$this->upload->file_temp = 'tests/mocks/uploads/ci_logo.gif';
+		$this->upload->file_temp = realpath(PROJECT_BASE.'tests/mocks/uploads/ci_logo.gif');
 		$this->upload->file_ext = '.gif';
 		$this->upload->file_type = 'image/gif';
 		$this->assertTrue($this->upload->is_allowed_filetype());
@@ -179,7 +176,7 @@
 		$this->assertTrue($this->upload->is_allowed_dimensions());
 
 		$this->upload->file_type = 'image/gif';
-		$this->upload->file_temp = 'tests/mocks/uploads/ci_logo.gif';
+		$this->upload->file_temp = realpath(PROJECT_BASE.'tests/mocks/uploads/ci_logo.gif');
 
 		$this->upload->max_width = 10;
 		$this->assertFalse($this->upload->is_allowed_dimensions());
@@ -197,7 +194,9 @@
 		$this->upload->upload_path = '';
 		$this->assertFalse($this->upload->validate_upload_path());
 
-		$this->upload->upload_path = vfsStream::url('testDir');
+		$dir = 'uploads';
+		$this->ci_vfs_mkdir($dir);
+		$this->upload->upload_path = $this->ci_vfs_path($dir);
 		$this->assertTrue($this->upload->validate_upload_path());
 	}
 
@@ -222,20 +221,24 @@
 
 	function test_do_xss_clean()
 	{
-		$file1 = vfsStream::newFile('file1.txt')->withContent('The billy goat was waiting for them.')->at($this->_test_dir);
-		$file2 = vfsStream::newFile('file2.txt')->at($this->_test_dir);
-		$file3 = vfsStream::newFile('file3.txt')->withContent('<script type="text/javascript">alert("Boo! said the billy goat")</script>')->at($this->_test_dir);
+		$dir = 'uploads';
+		$file1 = 'file1.txt';
+		$file2 = 'file2.txt';
+		$file3 = 'file3.txt';
+		$this->ci_vfs_create($file1, 'The billy goat was waiting for them.', $this->ci_vfs_root, $dir);
+		$this->ci_vfs_create($file2, '', $this->ci_vfs_root, $dir);
+		$this->ci_vfs_create($file3, '<script type="text/javascript">alert("Boo! said the billy goat")</script>', $this->ci_vfs_root, $dir);
 
-		$this->upload->file_temp = vfsStream::url('file1.txt');
+		$this->upload->file_temp = $this->ci_vfs_path($file1, $dir);
 		$this->assertTrue($this->upload->do_xss_clean());
 
-		$this->upload->file_temp = vfsStream::url('file2.txt');
+		$this->upload->file_temp = $this->ci_vfs_path($file2, $dir);
 		$this->assertFalse($this->upload->do_xss_clean());
 
-		$this->upload->file_temp = vfsStream::url('file3.txt');
+		$this->upload->file_temp = $this->ci_vfs_path($file3, $dir);
 		$this->assertFalse($this->upload->do_xss_clean());
 
-		$this->upload->file_temp = 'tests/mocks/uploads/ci_logo.gif';
+		$this->upload->file_temp = realpath(PROJECT_BASE.'tests/mocks/uploads/ci_logo.gif');
 		$this->assertTrue($this->upload->do_xss_clean());
 	}
 
diff --git a/tests/codeigniter/libraries/Useragent_test.php b/tests/codeigniter/libraries/Useragent_test.php
index 89383f8..e372655 100644
--- a/tests/codeigniter/libraries/Useragent_test.php
+++ b/tests/codeigniter/libraries/Useragent_test.php
@@ -10,12 +10,11 @@
 		// set a baseline user agent
 		$_SERVER['HTTP_USER_AGENT'] = $this->_user_agent;
 
-		$obj = new stdClass;
-		$obj->agent = new Mock_Libraries_UserAgent();
+		$this->ci_vfs_clone('application/config/user_agents.php');
 
-		$this->ci_instance($obj);
+		$this->agent = new Mock_Libraries_UserAgent();
 
-		$this->agent = $obj->agent;
+		$this->ci_instance_var('agent', $this->agent);
 	}
 
 	// --------------------------------------------------------------------
diff --git a/tests/mocks/autoloader.php b/tests/mocks/autoloader.php
index 88d016b..5b202f1 100644
--- a/tests/mocks/autoloader.php
+++ b/tests/mocks/autoloader.php
@@ -48,32 +48,38 @@
 
 		if (in_array($subclass, $ci_core))
 		{
-			$dir = BASEPATH.'core'.DIRECTORY_SEPARATOR;
+			$dir = SYSTEM_PATH.'core'.DIRECTORY_SEPARATOR;
 			$class = $subclass;
 		}
 		elseif (in_array($subclass, $ci_libraries))
 		{
-			$dir = BASEPATH.'libraries'.DIRECTORY_SEPARATOR;
+			$dir = SYSTEM_PATH.'libraries'.DIRECTORY_SEPARATOR;
 			$class = ($subclass === 'Driver_Library') ? 'Driver' : $subclass;
 		}
 		elseif (in_array($subclass, $ci_drivers))
 		{
-			$dir = BASEPATH.'libraries'.DIRECTORY_SEPARATOR.$subclass.DIRECTORY_SEPARATOR;
+			$dir = SYSTEM_PATH.'libraries'.DIRECTORY_SEPARATOR.$subclass.DIRECTORY_SEPARATOR;
 			$class = $subclass;
 		}
 		elseif (in_array(($parent = strtok($subclass, '_')), $ci_drivers)) {
-			$dir = BASEPATH.'libraries'.DIRECTORY_SEPARATOR.$parent.DIRECTORY_SEPARATOR.'drivers'.DIRECTORY_SEPARATOR;
+			$dir = SYSTEM_PATH.'libraries'.DIRECTORY_SEPARATOR.$parent.DIRECTORY_SEPARATOR.'drivers'.DIRECTORY_SEPARATOR;
 			$class = $subclass;
 		}
+		elseif (preg_match('/^CI_DB_(.+)_(.+)_(driver|forge|result|utility)$/', $class, $m) && count($m) === 4)
+		{
+			$driver_path = SYSTEM_PATH.'database'.DIRECTORY_SEPARATOR.'drivers'.DIRECTORY_SEPARATOR;
+			$dir = $driver_path.$m[1].DIRECTORY_SEPARATOR.'subdrivers'.DIRECTORY_SEPARATOR;
+			$file = $dir.$m[1].'_'.$m[2].'_'.$m[3].'.php';
+		}
 		elseif (preg_match('/^CI_DB_(.+)_(driver|forge|result|utility)$/', $class, $m) && count($m) === 3)
 		{
-			$driver_path = BASEPATH.'database'.DIRECTORY_SEPARATOR.'drivers'.DIRECTORY_SEPARATOR;
+			$driver_path = SYSTEM_PATH.'database'.DIRECTORY_SEPARATOR.'drivers'.DIRECTORY_SEPARATOR;
 			$dir = $driver_path.$m[1].DIRECTORY_SEPARATOR;
 			$file = $dir.$m[1].'_'.$m[2].'.php';
 		}
 		elseif (strpos($class, 'CI_DB') === 0)
 		{
-			$dir = BASEPATH.'database'.DIRECTORY_SEPARATOR;
+			$dir = SYSTEM_PATH.'database'.DIRECTORY_SEPARATOR;
 			$file = $dir.str_replace(array('CI_DB','active_record'), array('DB', 'active_rec'), $subclass).'.php';
 		}
 		else
@@ -104,4 +110,4 @@
 	}
 
 	include_once($file);
-}
+}
\ No newline at end of file
diff --git a/tests/mocks/ci_testcase.php b/tests/mocks/ci_testcase.php
index eda9e1b..f164929 100644
--- a/tests/mocks/ci_testcase.php
+++ b/tests/mocks/ci_testcase.php
@@ -2,7 +2,9 @@
 
 class CI_TestCase extends PHPUnit_Framework_TestCase {
 
-	protected $ci_config;
+	public $ci_vfs_root;
+	public $ci_app_root;
+	public $ci_base_root;
 	protected $ci_instance;
 	protected static $ci_test_instance;
 
@@ -25,13 +27,19 @@
 	public function __construct()
 	{
 		parent::__construct();
-		$this->ci_config = array();
+		$this->ci_instance = new StdClass();
 	}
 
 	// --------------------------------------------------------------------
 
 	public function setUp()
 	{
+		// Setup VFS with base directories
+		$this->ci_vfs_root = vfsStream::setup();
+		$this->ci_app_root = vfsStream::newDirectory('application')->at($this->ci_vfs_root);
+		$this->ci_base_root = vfsStream::newDirectory('system')->at($this->ci_vfs_root);
+		$this->ci_view_root = vfsStream::newDirectory('views')->at($this->ci_app_root);
+
 		if (method_exists($this, 'set_up'))
 		{
 			$this->set_up();
@@ -57,15 +65,27 @@
 
 	// --------------------------------------------------------------------
 
-	public function ci_set_config($key, $val = '')
+	public function ci_set_config($key = '', $val = '')
 	{
+		// Add test config
+		if ( ! isset($this->ci_instance->config))
+		{
+			$this->ci_instance->config = new CI_TestConfig();
+		}
+
+		// Empty key means just do setup above
+		if ($key === '')
+		{
+			return;
+		}
+
 		if (is_array($key))
 		{
-			$this->ci_config = $key;
+			$this->ci_instance->config->config = $key;
 		}
 		else
 		{
-			$this->ci_config[$key] = $val;
+			$this->ci_instance->config->config[$key] = $val;
 		}
 	}
 
@@ -73,7 +93,7 @@
 
 	public function ci_get_config()
 	{
-		return $this->ci_config;
+		return isset($this->ci_instance->config) ? $this->ci_instance->config->config : array();
 	}
 
 	// --------------------------------------------------------------------
@@ -132,7 +152,7 @@
 
 		if ( ! class_exists('CI_'.$class_name))
 		{
-			require_once BASEPATH.'core/'.$class_name.'.php';
+			require_once SYSTEM_PATH.'core/'.$class_name.'.php';
 		}
 
 		$GLOBALS[strtoupper($global_name)] = 'CI_'.$class_name;
@@ -148,6 +168,165 @@
 		$orig = $obj;
 	}
 
+	/**
+	 * Create VFS directory
+	 *
+	 * @param	string	Directory name
+	 * @param	object	Optional root to create in
+	 * @return	object	New directory object
+	 */
+	public function ci_vfs_mkdir($name, $root = NULL)
+	{
+		// Check for root
+		if ( ! $root)
+		{
+			$root = $this->ci_vfs_root;
+		}
+
+		// Return new directory object
+		return vfsStream::newDirectory($name)->at($root);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Create VFS content
+	 *
+	 * @param	string	File name
+	 * @param	string	File content
+	 * @param	object	VFS directory object
+	 * @param	mixed	Optional subdirectory path or array of subs
+	 * @return	void
+	 */
+	public function ci_vfs_create($file, $content = '', $root = NULL, $path = NULL)
+	{
+		// Check for array
+		if (is_array($file))
+		{
+			foreach ($file as $name => $content)
+			{
+				$this->ci_vfs_create($name, $content, $root, $path);
+			}
+			return;
+		}
+
+		// Assert .php extension if none given
+		if (pathinfo($file, PATHINFO_EXTENSION) == '')
+		{
+			$file .= '.php';
+		}
+
+		// Build content
+		$tree = array($file => $content);
+
+		// Check for path
+		$subs = array();
+		if ($path)
+		{
+			// Explode if not array
+			$subs = is_array($path) ? $path : explode('/', trim($path, '/'));
+		}
+
+		// Check for root
+		if ( ! $root)
+		{
+			// Use base VFS root
+			$root = $this->ci_vfs_root;
+		}
+
+		// Handle subdirectories
+		while (($dir = array_shift($subs)))
+		{
+			// See if subdir exists under current root
+			$dir_root = $root->getChild($dir);
+			if ($dir_root)
+			{
+			   	// Yes - recurse into subdir
+				$root = $dir_root;
+			}
+			else
+			{
+				// No - put subdirectory back and quit
+				array_unshift($subs, $dir);
+				break;
+			}
+		}
+
+		// Create any remaining subdirectories
+		if ($subs)
+		{
+			foreach (array_reverse($subs) as $dir)
+			{
+				// Wrap content in subdirectory for creation
+				$tree = array($dir => $tree);
+			}
+		}
+
+		// Create tree
+		vfsStream::create($tree, $root);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Clone a real file into VFS
+	 *
+	 * @param	string	Path from base directory
+	 * @return	bool	TRUE on success, otherwise FALSE
+	 */
+	public function ci_vfs_clone($path)
+	{
+		// Check for array
+		if (is_array($path))
+		{
+			foreach ($path as $file)
+			{
+				$this->ci_vfs_clone($file);
+			}
+			return;
+		}
+
+		// Get real file contents
+		$content = file_get_contents(PROJECT_BASE.$path);
+		if ($content === FALSE)
+		{
+			// Couldn't find file to clone
+			return FALSE;
+		}
+
+		$this->ci_vfs_create(basename($path), $content, NULL, dirname($path));
+		return TRUE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Helper to get a VFS URL path
+	 *
+	 * @param	string	Path
+	 * @param	string	Optional base path
+	 * @return	string	Path URL
+	 */
+	public function ci_vfs_path($path, $base = '')
+	{
+		// Check for base path
+		if ($base)
+		{
+			// Prepend to path
+			$path = rtrim($base, '/').'/'.ltrim($path, '/');
+
+			// Is it already in URL form?
+			if (strpos($path, '://') !== FALSE)
+			{
+				// Done - return path
+				return $path;
+			}
+		}
+
+		// Trim leading slash and return URL
+		return vfsStream::url(ltrim($path, '/'));
+	}
+
 	// --------------------------------------------------------------------
 	// Internals
 	// --------------------------------------------------------------------
@@ -171,7 +350,15 @@
 
 	public function helper($name)
 	{
-		require_once(BASEPATH.'helpers/'.$name.'_helper.php');
+		require_once(SYSTEM_PATH.'helpers/'.$name.'_helper.php');
+	}
+
+	// --------------------------------------------------------------------
+
+	public function lang($name)
+	{
+		require(SYSTEM_PATH.'language/english/'.$name.'_lang.php');
+		return $lang;
 	}
 
 	// --------------------------------------------------------------------
diff --git a/tests/mocks/ci_testconfig.php b/tests/mocks/ci_testconfig.php
new file mode 100644
index 0000000..0c52bb9
--- /dev/null
+++ b/tests/mocks/ci_testconfig.php
@@ -0,0 +1,20 @@
+<?php
+
+class CI_TestConfig {
+
+	public $config = array();
+	public $_config_paths = array(APPPATH);
+	public $loaded = array();
+
+	public function item($key)
+	{
+		return isset($this->config[$key]) ? $this->config[$key] : FALSE;
+	}
+
+	public function load($file, $arg2 = FALSE, $arg3 = FALSE)
+	{
+		$this->loaded[] = $file;
+		return TRUE;
+	}
+
+}
diff --git a/tests/mocks/core/common.php b/tests/mocks/core/common.php
index a655ee1..9289b27 100644
--- a/tests/mocks/core/common.php
+++ b/tests/mocks/core/common.php
@@ -39,6 +39,30 @@
 	}
 }
 
+if ( ! function_exists('get_mimes'))
+{
+	/**
+	 * Returns the MIME types array from config/mimes.php
+	 *
+	 * @return	array
+	 */
+	function &get_mimes()
+	{
+		static $_mimes = array();
+
+		if (empty($_mimes))
+		{
+			$path = realpath(PROJECT_BASE.'application/config/mimes.php');
+			if (is_file($path))
+			{
+				$_mimes = include($path);
+			}
+		}
+
+		return $_mimes;
+	}
+}
+
 // --------------------------------------------------------------------
 
 if ( ! function_exists('load_class'))
@@ -146,9 +170,10 @@
 
 if ( ! function_exists('is_loaded'))
 {
-	function is_loaded()
+	function &is_loaded()
 	{
-		throw new Exception('Bad Isolation: mock up environment');
+		$loaded = array();
+		return $loaded;
 	}
 }
 
diff --git a/tests/mocks/core/loader.php b/tests/mocks/core/loader.php
deleted file mode 100644
index 53d88d5..0000000
--- a/tests/mocks/core/loader.php
+++ /dev/null
@@ -1,31 +0,0 @@
-<?php
-
-class Mock_Core_Loader extends CI_Loader {
-
-	/**
-	 * Since we use paths to load up models, views, etc, we need the ability to
-	 * mock up the file system so when core tests are run, we aren't mucking
-	 * in the application directory.  this will give finer grained control over
-	 * these tests.  So yeah, while this looks odd, I need to overwrite protected
-	 * class vars in the loader.  So here we go...
-	 *
-	 * @covers CI_Loader::__construct()
-	 */
-	public function __construct()
-	{
-		vfsStreamWrapper::register();
-		vfsStreamWrapper::setRoot(new vfsStreamDirectory('application'));
-
-		$this->models_dir 	= vfsStream::newDirectory('models')->at(vfsStreamWrapper::getRoot());
-		$this->libs_dir 	= vfsStream::newDirectory('libraries')->at(vfsStreamWrapper::getRoot());
-		$this->helpers_dir 	= vfsStream::newDirectory('helpers')->at(vfsStreamWrapper::getRoot());
-		$this->views_dir 	= vfsStream::newDirectory('views')->at(vfsStreamWrapper::getRoot());
-
-		$this->_ci_ob_level  		= ob_get_level();
-		$this->_ci_library_paths	= array(vfsStream::url('application').'/', BASEPATH);
-		$this->_ci_helper_paths 	= array(vfsStream::url('application').'/', BASEPATH);
-		$this->_ci_model_paths 		= array(vfsStream::url('application').'/');
-		$this->_ci_view_paths 		= array(vfsStream::url('application').'/views/' => TRUE);
-	}
-
-}
\ No newline at end of file
diff --git a/tests/mocks/database/db.php b/tests/mocks/database/db.php
index 7565853..7e0030e 100644
--- a/tests/mocks/database/db.php
+++ b/tests/mocks/database/db.php
@@ -8,6 +8,16 @@
 	private $config = array();
 
 	/**
+	 * @var string DB driver name
+	 */
+	private static $dbdriver = '';
+
+	/**
+	 * @var string DB sub-driver name
+	 */
+	private static $subdriver = '';
+
+	/**
 	 * Prepare database configuration skeleton
 	 *
 	 * @param  array 	DB configuration to set
@@ -31,6 +41,12 @@
 			throw new InvalidArgumentException('Group '.$group.' not exists');
 		}
 
+		self::$dbdriver = $this->config[$group]['dbdriver'];
+		if (isset($this->config[$group]['subdriver']))
+		{
+			self::$subdriver = $this->config[$group]['subdriver'];
+		}
+
 		$params = array(
 			'dbprefix' => '',
 			'pconnect' => FALSE,
@@ -50,7 +66,7 @@
 		$failover = empty($config['failover']) ? FALSE : $config['failover'];
 
 		$dsn = $config['dbdriver'].'://'.$config['username'].':'.$config['password']
-			       .'@'.$config['hostname'].'/'.$config['database'];
+					.'@'.$config['hostname'].'/'.$config['database'];
 
 		// Build the parameter
 		$other_params = array_slice($config, 6);
@@ -83,7 +99,32 @@
 	 */
 	public static function DB($group, $query_builder = FALSE)
 	{
-		include_once(BASEPATH.'database/DB.php');
+		// Create dummy driver and builder files to "load" - the mocks have
+		// already triggered autoloading of the real files
+		$case = CI_TestCase::instance();
+		$driver = self::$dbdriver;
+		$subdriver = self::$subdriver;
+		$case->ci_vfs_create(array(
+			'DB_driver.php' => '',
+			'DB_forge.php' => '',
+			'DB_query_builder.php' => ''
+		), '', $case->ci_base_root, 'database');
+		if (file_exists(SYSTEM_PATH.'database/drivers/'.$driver.'/'.$driver.'_driver.php'))
+		{
+			$case->ci_vfs_create(array(
+				$driver.'_driver.php' => '',
+				$driver.'_forge.php' => ''
+			), '', $case->ci_base_root, 'database/drivers/'.$driver);
+		}
+		if ($subdriver)
+		{
+			$case->ci_vfs_create(array(
+				$driver.'_'.$subdriver.'_driver.php' => '',
+				$driver.'_'.$subdriver.'_forge.php' => ''
+			), '', $case->ci_base_root, 'database/drivers/'.$driver.'/subdrivers');
+		}
+
+		include_once(SYSTEM_PATH.'database/DB.php');
 
 		try
 		{
diff --git a/tests/mocks/database/schema/skeleton.php b/tests/mocks/database/schema/skeleton.php
index 2226835..d722445 100644
--- a/tests/mocks/database/schema/skeleton.php
+++ b/tests/mocks/database/schema/skeleton.php
@@ -30,7 +30,7 @@
 
 			CI_TestCase::instance()->ci_instance_var('db', $db);
 
-			$loader = new Mock_Core_Loader();
+			$loader = new CI_Loader();
 			$loader->dbforge();
 			$forge = CI_TestCase::instance()->ci_instance_var('dbforge');