Added support for extending individual driver classes and driver unit tests

Signed-off-by: dchill42 <dchill42@gmail.com>
diff --git a/tests/codeigniter/libraries/Driver_test.php b/tests/codeigniter/libraries/Driver_test.php
new file mode 100644
index 0000000..0186cfe
--- /dev/null
+++ b/tests/codeigniter/libraries/Driver_test.php
@@ -0,0 +1,176 @@
+<?php
+
+/**
+ * Driver library base class unit test
+ */
+class Driver_test extends CI_TestCase {
+	/**
+	 * Set up test framework
+	 */
+	public function set_up()
+	{
+		// Set our subclass prefix
+		$this->subclass = 'Mock_Libraries_';
+		$this->ci_set_config('subclass_prefix', $this->subclass);
+
+        // Mock Loader->get_package_paths
+        $paths = 'get_package_paths';
+        $ldr = $this->getMock('CI_Loader', array($paths));
+        $ldr->expects($this->any())->method($paths)->will($this->returnValue(array(APPPATH, BASEPATH)));
+        $this->ci_instance_var('load', $ldr);
+
+		// Create mock driver library
+		$this->name = 'Driver';
+		$this->lib = new Mock_Libraries_Driver();
+	}
+
+	/**
+	 * Test driver child loading
+	 */
+	public function test_load_driver()
+	{
+		// Create driver file
+		$driver = 'basic';
+		$file = $this->name.'_'.$driver;
+		$class = 'CI_'.$file;
+		$prop = 'called';
+		$content = '<?php class '.$class.' extends CI_Driver { public $'.$prop.' = FALSE; '.
+			'public function decorate($parent) { $this->'.$prop.' = TRUE; } }';
+		$this->ci_vfs_create($file, $content, $this->ci_base_root, array('libraries', $this->name, 'drivers'));
+
+		// Make driver valid
+		$this->lib->driver_list($driver);
+
+		// Load driver
+		$this->assertNotNull($this->lib->load_driver($driver));
+
+		// Did lib name get set?
+		$this->assertEquals($this->name, $this->lib->get_name());
+
+		// Was driver loaded?
+		$this->assertObjectHasAttribute($driver, $this->lib);
+		$this->assertAttributeInstanceOf($class, $driver, $this->lib);
+		$this->assertAttributeInstanceOf('CI_Driver', $driver, $this->lib);
+
+		// Was decorate called?
+		$this->assertObjectHasAttribute($prop, $this->lib->$driver);
+		$this->assertTrue($this->lib->$driver->$prop);
+
+		// Do we get an error for an invalid driver?
+		$driver = 'unlisted';
+		$this->setExpectedException('RuntimeException', 'CI Error: Invalid driver requested: '.$this->name.'_'.$driver);
+		$this->lib->load_driver($driver);
+	}
+
+	/**
+	 * Test loading lowercase from app path
+	 */
+	public function test_load_app_driver()
+	{
+		// Create driver file
+		$driver = 'lowpack';
+		$file = $this->name.'_'.$driver;
+		$class = 'CI_'.$file;
+		$content = '<?php class '.$class.' extends CI_Driver {  }';
+		$this->ci_vfs_create(strtolower($file), $content, $this->ci_app_root,
+			array('libraries', $this->name, 'drivers'));
+
+		// Make valid list
+		$nodriver = 'absent';
+		$this->lib->driver_list(array($driver, $nodriver));
+
+		// Load driver
+		$this->assertNotNull($this->lib->load_driver($driver));
+
+		// Was driver loaded?
+		$this->assertObjectHasAttribute($driver, $this->lib);
+		$this->assertAttributeInstanceOf($class, $driver, $this->lib);
+		$this->assertAttributeInstanceOf('CI_Driver', $driver, $this->lib);
+
+		// Do we get an error for a non-existent driver?
+		$this->setExpectedException('RuntimeException', 'CI Error: Unable to load the requested driver: CI_'.
+			$this->name.'_'.$nodriver);
+		$this->lib->load_driver($nodriver);
+	}
+
+	/**
+	 * Test loading driver extension
+	 */
+	public function test_load_driver_ext()
+	{
+		// Create base file
+		$driver = 'extend';
+		$base = $this->name.'_'.$driver;
+		$baseclass = 'CI_'.$base;
+		$content = '<?php class '.$baseclass.' extends CI_Driver {  }';
+		$this->ci_vfs_create($base, $content, $this->ci_base_root, array('libraries', $this->name, 'drivers'));
+
+		// Create driver file
+		$class = $this->subclass.$base;
+		$content = '<?php class '.$class.' extends '.$baseclass.' {  }';
+		$this->ci_vfs_create($class, $content, $this->ci_app_root, array('libraries', $this->name, 'drivers'));
+
+		// Make valid list
+		$this->lib->driver_list($driver);
+
+		// Load driver
+		$this->assertNotNull($this->lib->load_driver($driver));
+
+		// Was driver loaded?
+		$this->assertObjectHasAttribute($driver, $this->lib);
+		$this->assertAttributeInstanceOf($class, $driver, $this->lib);
+		$this->assertAttributeInstanceOf($baseclass, $driver, $this->lib);
+		$this->assertAttributeInstanceOf('CI_Driver', $driver, $this->lib);
+
+		// Create driver extension without base
+		$driver = 'baseless';
+		$base = $this->name.'_'.$driver;
+		$class = $this->subclass.$base;
+		$content = '<?php class '.$class.' extends CI_Driver {  }';
+		$this->ci_vfs_create($class, $content, $this->ci_app_root, array('libraries', $this->name, 'drivers'));
+		$this->lib->driver_list($driver);
+
+		// Do we get an error when base class isn't found?
+		$this->setExpectedException('RuntimeException', 'CI Error: Unable to load the requested class: CI_'.$base);
+		$this->lib->load_driver($driver);
+	}
+
+	/**
+	 * Test decorating driver with parent attributes
+	 */
+	public function test_decorate()
+	{
+		// Create parent with a method and property to access
+		$pclass = 'Test_Parent';
+		$prop = 'parent_prop';
+		$value = 'initial';
+		$method = 'parent_func';
+		$return = 'func return';
+		$code = 'class '.$pclass.' { public $'.$prop.' = \''.$value.'\'; '.
+			'public function '.$method.'() { return \''.$return.'\'; } }';
+		eval($code);
+		$parent = new $pclass();
+
+		// Create child driver to decorate
+		$class = 'Test_Driver';
+		eval('class '.$class.' extends CI_Driver {  }');
+		$child = new $class();
+
+		// Decorate child
+		$child->decorate($parent);
+
+		// Do we get the initial parent property value?
+		$this->assertEquals($value, $child->$prop);
+
+		// Can we change the parent property?
+		$newval = 'changed';
+		$child->$prop = $newval;
+		$this->assertEquals($newval, $parent->$prop);
+
+		// Do we get back the updated value?
+		$this->assertEquals($newval, $child->$prop);
+
+		// Can we call the parent method?
+		$this->assertEquals($return, $child->$method());
+	}
+}
diff --git a/tests/codeigniter/libraries/Session_test.php b/tests/codeigniter/libraries/Session_test.php
index 14469f7..e675b4e 100644
--- a/tests/codeigniter/libraries/Session_test.php
+++ b/tests/codeigniter/libraries/Session_test.php
@@ -6,8 +6,8 @@
 class Session_test extends CI_TestCase {
 	protected $settings = array(
 		'use_cookies' => 0,
-	   	'use_only_cookies' => 0,
-	   	'cache_limiter' => false
+		'use_only_cookies' => 0,
+		'cache_limiter' => false
 	);
 	protected $setting_vals = array();
 	protected $cookie_vals;
@@ -28,11 +28,12 @@
 		$this->cookie_vals = $_COOKIE;
 		$_COOKIE = array();
 
+		// Set subclass prefix to match our mock
+		$this->ci_set_config('subclass_prefix', 'Mock_Libraries_');
+
 		// Establish necessary support classes
-		$cfg = $this->ci_core_class('cfg');
-		$ldr = $this->ci_core_class('load');
 		$ci = $this->ci_instance();
-		$ci->config = new $cfg();
+		$ldr = $this->ci_core_class('load');
 		$ci->load = new $ldr();
 		$ci->input = new Mock_Core_Input(NULL, NULL);
 
@@ -56,11 +57,7 @@
 			'sess_time_to_update' => 300,
 			'time_reference' => 'local',
 			'cookie_prefix' => '',
-			'encryption_key' => 'foobar',
-			'sess_valid_drivers' => array(
-				'Mock_Libraries_Session_native',
-			   	'Mock_Libraries_Session_cookie'
-			)
+			'encryption_key' => 'foobar'
 		);
 		$this->session = new Mock_Libraries_Session($config);
 	}
@@ -83,9 +80,6 @@
 
 	/**
 	 * Test set_userdata() function
-	 *
-	 * @covers  CI_Session::set_userdata
-	 * @covers  CI_Session::userdata
 	 */
 	public function test_set_userdata()
 	{
@@ -117,8 +111,6 @@
 
 	/**
 	 * Test the has_userdata() function
-	 *
-	 * @covers	CI_Session::has_userdata
 	 */
 	public function test_has_userdata()
 	{
@@ -141,8 +133,6 @@
 
 	/**
 	 * Test the all_userdata() function
-	 *
-	 * @covers	CI_Session::all_userdata
 	 */
 	public function test_all_userdata()
 	{
@@ -150,16 +140,16 @@
 		$cdata = array(
 			'one' => 'first',
 			'two' => 'second',
-		   	'three' => 'third',
-		   	'foo' => 'bar',
-		   	'bar' => 'baz'
+			'three' => 'third',
+			'foo' => 'bar',
+			'bar' => 'baz'
 		);
 		$ndata = array(
 			'one' => 'gold',
-		   	'two' => 'silver',
-		   	'three' => 'bronze',
-		   	'foo' => 'baz',
-		   	'bar' => 'foo'
+			'two' => 'silver',
+			'three' => 'bronze',
+			'foo' => 'baz',
+			'bar' => 'foo'
 		);
 		$this->session->cookie->set_userdata($cdata);
 		$this->session->native->set_userdata($ndata);
@@ -177,8 +167,6 @@
 
 	/**
 	 * Test the unset_userdata() function
-	 *
-	 * @covers	CI_Session::unset_userdata
 	 */
 	public function test_unset_userdata()
 	{
@@ -202,9 +190,6 @@
 
 	/**
 	 * Test the flashdata() functions
-	 *
-	 * @covers	CI_Session::set_flashdata
-	 * @covers	CI_Session::flashdata
 	 */
 	public function test_flashdata()
 	{
@@ -234,8 +219,6 @@
 
 	/**
 	 * Test the keep_flashdata() function
-	 *
-	 * @covers	CI_Session::keep_flashdata
 	 */
 	public function test_keep_flashdata()
 	{
@@ -271,25 +254,23 @@
 
 	/**
 	 * Test the all_flashdata() function
-	 *
-	 * @covers	CI_Session::all_flashdata
 	 */
 	public function test_all_flashdata()
 	{
 		// Set a specific series of data for each driver
 		$cdata = array(
 			'one' => 'first',
-		   	'two' => 'second',
-		   	'three' => 'third',
-		   	'foo' => 'bar',
-		   	'bar' => 'baz'
+			'two' => 'second',
+			'three' => 'third',
+			'foo' => 'bar',
+			'bar' => 'baz'
 		);
 		$ndata = array(
 			'one' => 'gold',
-		   	'two' => 'silver',
-		   	'three' => 'bronze',
-		   	'foo' => 'baz',
-		   	'bar' => 'foo'
+			'two' => 'silver',
+			'three' => 'bronze',
+			'foo' => 'baz',
+			'bar' => 'foo'
 		);
 		$this->session->cookie->set_flashdata($cdata);
 		$this->session->native->set_flashdata($ndata);
@@ -303,9 +284,6 @@
 
 	/**
 	 * Test the tempdata() functions
-	 *
-	 * @covers	CI_Session::set_tempdata
-	 * @covers	CI_Session::tempdata
 	 */
 	public function test_set_tempdata()
 	{
@@ -332,8 +310,6 @@
 
 	/**
 	 * Test the unset_tempdata() function
-	 *
-	 * @covers	CI_Session::unset_tempdata
 	 */
 	public function test_unset_tempdata()
 	{
@@ -357,8 +333,6 @@
 
 	/**
 	 * Test the sess_regenerate() function
-	 *
-	 * @covers	CI_Session::sess_regenerate
 	 */
 	public function test_sess_regenerate()
 	{
@@ -378,8 +352,6 @@
 
 	/**
 	 * Test the sess_destroy() function
-	 *
-	 * @covers	CI_Session::sess_destroy
 	 */
 	public function test_sess_destroy()
 	{
@@ -399,4 +371,4 @@
 		$this->session->native->sess_destroy();
 		$this->assertNull($this->session->native->userdata($key));
 	}
-}
\ No newline at end of file
+}