diff --git a/tests/ACLTest.php b/tests/ACLTest.php new file mode 100644 index 0000000000..2f0ced2def --- /dev/null +++ b/tests/ACLTest.php @@ -0,0 +1,755 @@ +ACL(); + } + + public static function tearDownAfterClass() + { + /** + * In case first test fails + */ + $usr_id = User_Adapter::get_usr_id_from_login('test_phpunit2'); + try + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $template = User_Adapter::getInstance($usr_id, $appbox); + $template->delete(); + } + catch (Exception $e) + { + + } + $usr_id = User_Adapter::get_usr_id_from_login('test_phpunit3'); + try + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $cobaye = User_Adapter::getInstance($usr_id, $appbox); + $cobaye->delete(); + } + catch (Exception $e) + { + + } + parent::tearDownAfterClass(); + } + + public function testApply_model() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $template = User_Adapter::create($appbox, 'test_phpunit2', 'blabla2', 'test2@example.com', false); + $template->set_template(self::$user); + + $base_ids = array(); + foreach ($appbox->get_databoxes() as $databox) + { + foreach ($databox->get_collections() as $collection) + { + $base_id = $collection->get_base_id(); + $base_ids[] = $base_id; + } + } + $template->ACL()->give_access_to_base($base_ids); + + $cobaye = User_Adapter::create($appbox, 'test_phpunit3', 'blabla3', 'test3@example.com', false); + $cobaye->ACL()->apply_model($template, $base_ids); + foreach ($appbox->get_databoxes() as $databox) + { + foreach ($databox->get_collections() as $collection) + { + $base_id = $collection->get_base_id(); + $this->assertTrue($cobaye->ACL()->has_access_to_base($base_id)); + } + } + + $template->delete(); + $cobaye->delete(); + } + + public function testRevoke_access_from_bases() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $base_ids = array(); + foreach ($appbox->get_databoxes() as $databox) + { + foreach ($databox->get_collections() as $collection) + { + $base_id = $collection->get_base_id(); + self::$user->ACL()->revoke_access_from_bases(array($base_id)); + $this->assertFalse(self::$user->ACL()->has_access_to_base($base_id)); + self::$user->ACL()->give_access_to_base(array($base_id)); + $this->assertTrue(self::$user->ACL()->has_access_to_base($base_id)); + $base_ids[] = $base_id; + } + } + self::$user->ACL()->revoke_access_from_bases($base_ids); + + foreach ($base_ids as $base_id) + { + $this->assertFalse(self::$user->ACL()->has_access_to_base($base_id)); + } + } + + public function testGive_access_to_base() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + foreach ($appbox->get_databoxes() as $databox) + { + foreach ($databox->get_collections() as $collection) + { + $base_id = $collection->get_base_id(); + $this->assertFalse(self::$user->ACL()->has_access_to_base($base_id)); + self::$user->ACL()->give_access_to_base(array($base_id)); + $this->assertTrue(self::$user->ACL()->has_access_to_base($base_id)); + } + } + } + + public function testGive_access_to_sbas() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + foreach ($appbox->get_databoxes() as $databox) + { + $sbas_id = $databox->get_sbas_id(); + $base_ids = array(); + foreach ($databox->get_collections() as $collection) + { + $base_ids[] = $collection->get_base_id(); + } + + self::$user->ACL()->revoke_access_from_bases($base_ids); + self::$user->ACL()->revoke_unused_sbas_rights(); + $this->assertFalse(self::$user->ACL()->has_access_to_sbas($sbas_id)); + self::$user->ACL()->give_access_to_sbas(array($sbas_id)); + $this->assertTrue(self::$user->ACL()->has_access_to_sbas($sbas_id)); + } + } + + public function testRevoke_unused_sbas_rights() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + foreach ($appbox->get_databoxes() as $databox) + { + $sbas_id = $databox->get_sbas_id(); + $base_ids = array(); + foreach ($databox->get_collections() as $collection) + { + $base_ids[] = $collection->get_base_id(); + } + + self::$user->ACL()->revoke_access_from_bases($base_ids); + self::$user->ACL()->give_access_to_sbas(array($sbas_id)); + $this->assertTrue(self::$user->ACL()->has_access_to_sbas($sbas_id)); + self::$user->ACL()->revoke_unused_sbas_rights(); + $this->assertFalse(self::$user->ACL()->has_access_to_sbas($sbas_id)); + } + } + + public function testRemove_quotas_on_base() + { + $this->testSet_quotas_on_base(); + } + + public function testSet_quotas_on_base() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + foreach ($appbox->get_databoxes() as $databox) + { + foreach ($databox->get_collections() as $collection) + { + $base_id = $collection->get_base_id(); + $droits = 50; + $restes = 40; + self::$user->ACL()->give_access_to_base(array($base_id)); + + self::$user->ACL()->set_quotas_on_base($base_id, $droits, $restes); + $this->assertTrue(self::$user->ACL()->is_restricted_download($base_id)); + + self::$user->ACL()->remove_quotas_on_base($base_id); + $this->assertFalse(self::$user->ACL()->is_restricted_download($base_id)); + + return; + } + } + } + + public function testDuplicate_right_from_bas() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $first = true; + $base_ref = null; + + foreach ($appbox->get_databoxes() as $databox) + { + foreach ($databox->get_collections() as $collection) + { + $base_id = $collection->get_base_id(); + + self::$user->ACL()->give_access_to_base(array($base_id)); + + if ($first) + { + self::$user->ACL()->update_rights_to_base($base_id, array('imgtools' => true, 'chgstatus' => true, 'canaddrecord' => true, 'canputinalbum' => true)); + $base_ref = $base_id; + } + else + { + self::$user->ACL()->duplicate_right_from_bas($base_ref, $base_id); + } + + $this->assertTrue(self::$user->ACL()->has_right_on_base($base_id, 'imgtools')); + $this->assertTrue(self::$user->ACL()->has_right_on_base($base_id, 'chgstatus')); + $this->assertTrue(self::$user->ACL()->has_right_on_base($base_id, 'canaddrecord')); + $this->assertTrue(self::$user->ACL()->has_right_on_base($base_id, 'canputinalbum')); + + $first = false; + } + } + } + + public function testHas_hd_grant() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testHas_right_on_base() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $rights_false = array( + 'imgtools' => false + , 'chgstatus' => false + , 'canaddrecord' => false + , 'canputinalbum' => false + ); + + $rights_true = array( + 'imgtools' => true + , 'chgstatus' => true + , 'canaddrecord' => true + ); + + + foreach ($appbox->get_databoxes() as $databox) + { + foreach ($databox->get_collections() as $collection) + { + $base_id = $collection->get_base_id(); + self::$user->ACL()->give_access_to_base(array($base_id)); + self::$user->ACL()->update_rights_to_base($base_id, $rights_false); + $this->assertFalse(self::$user->ACL()->has_right_on_base($base_id, 'imgtools')); + $this->assertFalse(self::$user->ACL()->has_right_on_base($base_id, 'chgstatus')); + $this->assertFalse(self::$user->ACL()->has_right_on_base($base_id, 'canaddrecord')); + $this->assertFalse(self::$user->ACL()->has_right_on_base($base_id, 'canputinalbum')); + self::$user->ACL()->update_rights_to_base($base_id, $rights_true); + $this->assertTrue(self::$user->ACL()->has_right_on_base($base_id, 'imgtools')); + $this->assertTrue(self::$user->ACL()->has_right_on_base($base_id, 'chgstatus')); + $this->assertTrue(self::$user->ACL()->has_right_on_base($base_id, 'canaddrecord')); + $this->assertFalse(self::$user->ACL()->has_right_on_base($base_id, 'canputinalbum')); + self::$user->ACL()->update_rights_to_base($base_id, $rights_false); + $this->assertFalse(self::$user->ACL()->has_right_on_base($base_id, 'imgtools')); + $this->assertFalse(self::$user->ACL()->has_right_on_base($base_id, 'chgstatus')); + $this->assertFalse(self::$user->ACL()->has_right_on_base($base_id, 'canaddrecord')); + $this->assertFalse(self::$user->ACL()->has_right_on_base($base_id, 'canputinalbum')); + } + } + } + + public function testIs_restricted_download() + { + $this->testSet_quotas_on_base(); + } + + public function testRemaining_download() + { + + $appbox = appbox::get_instance(\bootstrap::getCore()); + foreach ($appbox->get_databoxes() as $databox) + { + foreach ($databox->get_collections() as $collection) + { + $base_id = $collection->get_base_id(); + $droits = 50; + $restes = 40; + self::$user->ACL()->give_access_to_base(array($base_id)); + + self::$user->ACL()->set_quotas_on_base($base_id, $droits, $restes); + $this->assertEquals(40, self::$user->ACL()->remaining_download($base_id)); + + self::$user->ACL()->remove_quotas_on_base($base_id); + $this->assertFalse(self::$user->ACL()->is_restricted_download($base_id)); + + return; + } + } + } + + public function testRemove_remaining() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + foreach ($appbox->get_databoxes() as $databox) + { + foreach ($databox->get_collections() as $collection) + { + $base_id = $collection->get_base_id(); + $droits = 50; + $restes = 40; + self::$user->ACL()->give_access_to_base(array($base_id)); + + self::$user->ACL()->set_quotas_on_base($base_id, $droits, $restes); + $this->assertEquals(40, self::$user->ACL()->remaining_download($base_id)); + self::$user->ACL()->remove_remaining($base_id, 1); + $this->assertEquals(39, self::$user->ACL()->remaining_download($base_id)); + self::$user->ACL()->remove_remaining($base_id, 10); + $this->assertEquals(29, self::$user->ACL()->remaining_download($base_id)); + self::$user->ACL()->remove_remaining($base_id, 100); + $this->assertEquals(0, self::$user->ACL()->remaining_download($base_id)); + + self::$user->ACL()->remove_quotas_on_base($base_id); + $this->assertFalse(self::$user->ACL()->is_restricted_download($base_id)); + + return; + } + } + } + + public function testHas_right() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + + $rights = array( + 'bas_modify_struct' => true + ); + + + $this->assertFalse(self::$user->ACL()->has_right('bas_modify_struct')); + $this->assertFalse(self::$user->ACL()->has_right('bas_modif_th')); + + foreach ($appbox->get_databoxes() as $databox) + { + self::$user->ACL()->give_access_to_sbas(array($databox->get_sbas_id())); + self::$user->ACL()->update_rights_to_sbas($databox->get_sbas_id(), $rights); + break; + } + + $this->assertTrue(self::$user->ACL()->has_right('bas_modify_struct')); + $this->assertFalse(self::$user->ACL()->has_right('bas_modif_th')); + } + + public function testHas_right_on_sbas() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $rights_false = array( + 'bas_modify_struct' => false + , 'bas_manage' => false + , 'bas_chupub' => false + , 'bas_modif_th' => false + ); + + $rights_true = array( + 'bas_modify_struct' => true + , 'bas_manage' => true + , 'bas_chupub' => true + , 'bas_modif_th' => true + ); + + + foreach ($appbox->get_databoxes() as $databox) + { + self::$user->ACL()->give_access_to_sbas(array($databox->get_sbas_id())); + self::$user->ACL()->update_rights_to_sbas($databox->get_sbas_id(), $rights_false); + $this->assertFalse(self::$user->ACL()->has_right_on_sbas($databox->get_sbas_id(), 'bas_modify_struct')); + $this->assertFalse(self::$user->ACL()->has_right_on_sbas($databox->get_sbas_id(), 'bas_manage')); + $this->assertFalse(self::$user->ACL()->has_right_on_sbas($databox->get_sbas_id(), 'bas_chupub')); + $this->assertFalse(self::$user->ACL()->has_right_on_sbas($databox->get_sbas_id(), 'bas_modif_th')); + self::$user->ACL()->update_rights_to_sbas($databox->get_sbas_id(), $rights_true); + $this->assertTrue(self::$user->ACL()->has_right_on_sbas($databox->get_sbas_id(), 'bas_modify_struct')); + $this->assertTrue(self::$user->ACL()->has_right_on_sbas($databox->get_sbas_id(), 'bas_manage')); + $this->assertTrue(self::$user->ACL()->has_right_on_sbas($databox->get_sbas_id(), 'bas_chupub')); + $this->assertTrue(self::$user->ACL()->has_right_on_sbas($databox->get_sbas_id(), 'bas_modif_th')); + self::$user->ACL()->update_rights_to_sbas($databox->get_sbas_id(), $rights_false); + $this->assertFalse(self::$user->ACL()->has_right_on_sbas($databox->get_sbas_id(), 'bas_modify_struct')); + $this->assertFalse(self::$user->ACL()->has_right_on_sbas($databox->get_sbas_id(), 'bas_manage')); + $this->assertFalse(self::$user->ACL()->has_right_on_sbas($databox->get_sbas_id(), 'bas_chupub')); + $this->assertFalse(self::$user->ACL()->has_right_on_sbas($databox->get_sbas_id(), 'bas_modif_th')); + } + } + + public function testGet_mask_and() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + foreach ($appbox->get_databoxes() as $databox) + { + foreach ($databox->get_collections() as $collection) + { + $base_id = $collection->get_base_id(); + + self::$user->ACL()->give_access_to_base(array($base_id)); + self::$user->ACL()->update_rights_to_base($base_id, array('actif' => false)); + $this->assertFalse(self::$user->ACL()->get_mask_and($base_id)); + self::$user->ACL()->update_rights_to_base($base_id, array('actif' => true)); + $this->assertEquals('0', self::$user->ACL()->get_mask_and($base_id)); + self::$user->ACL()->update_rights_to_base($base_id, array('mask_and' => 42)); + $this->assertEquals('42', self::$user->ACL()->get_mask_and($base_id)); + self::$user->ACL()->update_rights_to_base($base_id, array('mask_and' => 1)); + $this->assertEquals('1', self::$user->ACL()->get_mask_and($base_id)); + self::$user->ACL()->update_rights_to_base($base_id, array('mask_and' => 0)); + $this->assertEquals('0', self::$user->ACL()->get_mask_and($base_id)); + } + } + } + + public function testGet_mask_xor() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + foreach ($appbox->get_databoxes() as $databox) + { + foreach ($databox->get_collections() as $collection) + { + $base_id = $collection->get_base_id(); + + self::$user->ACL()->give_access_to_base(array($base_id)); + self::$user->ACL()->update_rights_to_base($base_id, array('actif' => false)); + $this->assertFalse(self::$user->ACL()->get_mask_xor($base_id)); + self::$user->ACL()->update_rights_to_base($base_id, array('actif' => true)); + $this->assertEquals('0', self::$user->ACL()->get_mask_xor($base_id)); + self::$user->ACL()->update_rights_to_base($base_id, array('mask_xor' => 42)); + $this->assertEquals('42', self::$user->ACL()->get_mask_xor($base_id)); + self::$user->ACL()->update_rights_to_base($base_id, array('mask_xor' => 1)); + $this->assertEquals('1', self::$user->ACL()->get_mask_xor($base_id)); + self::$user->ACL()->update_rights_to_base($base_id, array('mask_xor' => 0)); + $this->assertEquals('0', self::$user->ACL()->get_mask_xor($base_id)); + } + } + } + + public function testHas_access_to_base() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $base_ids = array(); + $n = 0; + foreach ($appbox->get_databoxes() as $databox) + { + foreach ($databox->get_collections() as $collection) + { + $base_ids[] = $collection->get_base_id(); + $n ++; + } + self::$user->ACL()->give_access_to_sbas(array($databox->get_sbas_id())); + } + + if ($n === 0) + $this->fail('Not enough collection to test'); + + self::$user->ACL()->give_access_to_base($base_ids); + $bases = array_keys(self::$user->ACL()->get_granted_base()); + + $this->assertEquals(count($base_ids), count($bases)); + + + $sql = 'SELECT actif FROM basusr WHERE usr_id = :usr_id AND base_id = :base_id'; + $stmt = $appbox->get_connection()->prepare($sql); + + foreach ($bases as $base_id) + { + $stmt->execute(array(':usr_id' => $appbox->get_session()->get_usr_id(), ':base_id' => $base_id)); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + $this->assertEquals(1, $row['actif']); + + $this->assertTrue(self::$user->ACL()->has_access_to_base($base_id)); + self::$user->ACL()->update_rights_to_base($base_id, array('actif' => false)); + + $stmt->execute(array(':usr_id' => $appbox->get_session()->get_usr_id(), ':base_id' => $base_id)); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + $this->assertEquals(0, $row['actif']); + + $this->assertFalse(self::$user->ACL()->has_access_to_base($base_id)); + self::$user->ACL()->update_rights_to_base($base_id, array('actif' => true)); + + $stmt->execute(array(':usr_id' => $appbox->get_session()->get_usr_id(), ':base_id' => $base_id)); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + $this->assertEquals(1, $row['actif']); + + $this->assertTrue(self::$user->ACL()->has_access_to_base($base_id)); + self::$user->ACL()->update_rights_to_base($base_id, array('actif' => false)); + $this->assertFalse(self::$user->ACL()->has_access_to_base($base_id)); + } + self::$user->ACL()->give_access_to_base($base_ids); + + foreach ($bases as $base_id) + { + $this->assertTrue(self::$user->ACL()->has_access_to_base($base_id)); + } + $stmt->closeCursor(); + } + + public function testGet_granted_base() + { + + $appbox = appbox::get_instance(\bootstrap::getCore()); + $base_ids = array(); + $n = 0; + foreach ($appbox->get_databoxes() as $databox) + { + foreach ($databox->get_collections() as $collection) + { + $base_ids[] = $collection->get_base_id(); + $n ++; + } + } + + if ($n === 0) + $this->fail('Not enough collection to test'); + + self::$user->ACL()->give_access_to_base($base_ids); + $bases = array_keys(self::$user->ACL()->get_granted_base()); + + $this->assertEquals(count($bases), count($base_ids)); + $this->assertEquals($n, count($base_ids)); + + foreach ($bases as $base_id) + { + try + { + $collection = collection::get_from_base_id($base_id); + $this->assertTrue($collection instanceof collection); + $this->assertEquals($base_id, $collection->get_base_id()); + unset($collection); + } + catch (Exception $e) + { + $this->fail('get granted base should returned OK collection'); + } + } + } + + public function testGet_granted_sbas() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $sbas_ids = array(); + $n = 0; + foreach ($appbox->get_databoxes() as $databox) + { + $sbas_ids[] = $databox->get_sbas_id(); + $n ++; + } + self::$user->ACL()->give_access_to_sbas($sbas_ids); + + $sbas = self::$user->ACL()->get_granted_sbas(); + + $this->assertEquals(count($sbas), count($sbas_ids)); + $this->assertEquals($n, count($sbas_ids)); + + foreach ($sbas as $sbas_id => $databox) + { + try + { + $this->assertTrue($databox instanceof databox); + $this->assertEquals($sbas_id, $databox->get_sbas_id()); + unset($databox); + } + catch (Exception $e) + { + $this->fail('get granted sbas should returned OK collection'); + } + } + } + + public function testHas_access_to_module() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + foreach ($appbox->get_databoxes() as $databox) + { + $base_ids = array(); + foreach ($databox->get_collections() as $collection) + { + $base_id = $collection->get_base_id(); + $base_ids[] = $base_id; + } + self::$user->ACL()->revoke_access_from_bases($base_ids); + self::$user->ACL()->revoke_unused_sbas_rights(); + } + + if (self::$user->is_admin()) + $this->assertTrue(self::$user->ACL()->has_access_to_module('admin')); + else + $this->assertFalse(self::$user->ACL()->has_access_to_module('admin')); + $this->assertFalse(self::$user->ACL()->has_access_to_module('thesaurus')); + $this->assertFalse(self::$user->ACL()->has_access_to_module('upload')); + $this->assertFalse(self::$user->ACL()->has_access_to_module('report')); + + $found = false; + foreach ($appbox->get_databoxes() as $databox) + { + foreach ($databox->get_collections() as $collection) + { + $base_id = $collection->get_base_id(); + $base_ids[] = $base_id; + self::$user->ACL()->update_rights_to_base($base_id, array('canreport' => true)); + $found = true; + break; + } + if ($found) + break; + } + $this->assertTrue(self::$user->ACL()->has_access_to_module('report')); + $this->assertFalse(self::$user->ACL()->has_access_to_module('thesaurus')); + $this->assertFalse(self::$user->ACL()->has_access_to_module('upload')); + + foreach ($appbox->get_databoxes() as $databox) + { + self::$user->ACL()->update_rights_to_sbas($databox->get_sbas_id(), array('bas_modif_th' => true)); + $found = true; + } + $this->assertTrue(self::$user->ACL()->has_access_to_module('report')); + $this->assertTrue(self::$user->ACL()->has_access_to_module('thesaurus')); + $this->assertFalse(self::$user->ACL()->has_access_to_module('upload')); + + + $found = false; + foreach ($appbox->get_databoxes() as $databox) + { + foreach ($databox->get_collections() as $collection) + { + $base_id = $collection->get_base_id(); + $base_ids[] = $base_id; + self::$user->ACL()->update_rights_to_base($base_id, array('canaddrecord' => true)); + $found = true; + break; + } + if ($found) + break; + } + $this->assertTrue(self::$user->ACL()->has_access_to_module('report')); + $this->assertTrue(self::$user->ACL()->has_access_to_module('thesaurus')); + $this->assertTrue(self::$user->ACL()->has_access_to_module('upload')); + } + + public function testis_limited() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $found = false; + + foreach ($appbox->get_databoxes() as $databox) + { + foreach ($databox->get_collections() as $collection) + { + $base_id = $collection->get_base_id(); + + if ( ! self::$user->ACL()->has_access_to_base($base_id)) + continue; + + $this->assertFalse(self::$user->ACL()->is_limited($base_id)); + + self::$user->ACL()->set_limits($base_id, true, new DateTime('-1 day'), new DateTime('+1 day')); + + $this->assertFalse(self::$user->ACL()->is_limited($base_id)); + + self::$user->ACL()->set_limits($base_id, false, new DateTime('-1 day'), new DateTime('+1 day')); + + $this->assertFalse(self::$user->ACL()->is_limited($base_id)); + + self::$user->ACL()->set_limits($base_id, true, new DateTime('+1 day'), new DateTime('+2 day')); + + $this->assertTrue(self::$user->ACL()->is_limited($base_id)); + + self::$user->ACL()->set_limits($base_id, true, new DateTime('-2 day'), new DateTime('-1 day')); + + $this->assertTrue(self::$user->ACL()->is_limited($base_id)); + + self::$user->ACL()->set_limits($base_id, true, new DateTime('-2 day'), new DateTime('+2 day')); + + $this->assertFalse(self::$user->ACL()->is_limited($base_id)); + + self::$user->ACL()->set_limits($base_id, false, new DateTime('-2 day'), new DateTime('+2 day')); + + $this->assertFalse(self::$user->ACL()->is_limited($base_id)); + + $found = true; + } + } + + if ( ! $found) + $this->fail('Unable to test'); + } + + public function testget_limits() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $found = false; + + foreach ($appbox->get_databoxes() as $databox) + { + foreach ($databox->get_collections() as $collection) + { + $base_id = $collection->get_base_id(); + + if ( ! self::$user->ACL()->has_access_to_base($base_id)) + continue; + + $minusone = new DateTime('-1 day'); + + $plusone = new DateTime('+1 day'); + + self::$user->ACL()->set_limits($base_id, true, $minusone, $plusone); + + $limits = self::$user->ACL()->get_limits($base_id); + + $this->assertEquals($limits['dmin'], $minusone); + + $this->assertEquals($limits['dmax'], $plusone); + + $minustwo = new DateTime('-2 day'); + + $plustwo = new DateTime('-2 day'); + + self::$user->ACL()->set_limits($base_id, true, $minustwo, $plustwo); + + $limits = self::$user->ACL()->get_limits($base_id); + + $this->assertEquals($limits['dmin'], $minustwo); + + $this->assertEquals($limits['dmax'], $plustwo); + + self::$user->ACL()->set_limits($base_id, false); + + $this->assertNull(self::$user->ACL()->get_limits($base_id)); + + $found = true; + } + } + + if ( ! $found) + $this->fail('Unable to test'); + } + + public function testset_limits() + { + $this->testget_limits(); + } + +} diff --git a/tests/Alchemy/Phrasea/Application/ApiJsonTest.php b/tests/Alchemy/Phrasea/Application/ApiJsonTest.php new file mode 100644 index 0000000000..3b8113f2b9 --- /dev/null +++ b/tests/Alchemy/Phrasea/Application/ApiJsonTest.php @@ -0,0 +1,1256 @@ +client = $this->createClient(); + } + + public static function setUpBeforeClass() + { + parent::setUpBeforeClass(); + $appbox = appbox::get_instance(\bootstrap::getCore()); + self::$application = API_OAuth2_Application::create($appbox, self::$user, 'test API v1'); + $account = API_OAuth2_Account::load_with_user($appbox, self::$application, self::$user); + self::$token = $account->get_token()->get_value(); + self::$account_id = $account->get_id(); + $_GET['oauth_token'] = self::$token; + } + + public static function tearDownAfterClass() + { + self::$application->delete(); + $_GET = array(); + } + + public function createApplication() + { + return require __DIR__ . '/../../../../lib/Alchemy/Phrasea/Application/Api.php'; + } + + public function testRouteNotFound() + { + $route = '/nothinghere?oauth_token=' . self::$token; + $crawler = $this->client->request('GET', $route); + $content = json_decode($this->client->getResponse()->getContent()); + + $this->evaluateResponseNotFound($this->client->getResponse()); + $this->evaluateMetaJsonNotFound($content); + } + + public function testDatboxListRoute() + { + $crawler = $this->client->request('GET', '/databoxes/list/?oauth_token=' . self::$token, array(), array(), array('HTTP_Accept' => 'application/json')); + $content = json_decode($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaJson200($content); + + + $this->assertObjectHasAttribute('databoxes', $content->response); + foreach ($content->response->databoxes as $databox) + { + $this->assertTrue(is_object($databox), 'Une databox est un objet'); + $this->assertObjectHasAttribute('databox_id', $databox); + $this->assertObjectHasAttribute('name', $databox); + $this->assertObjectHasAttribute('version', $databox); + static::$databoxe_ids[] = $databox->databox_id; + } + } + + /* + * Routes /API/V1/databoxes/DATABOX_ID/xxxxxx + * + */ + + public function testDataboxRecordRoute() + { + foreach (static::$databoxe_ids as $databox_id) + { + $databox = databox::get_instance($databox_id); + + $collection = array_shift($databox->get_collections()); + $system_file = new system_file(__DIR__ . '/../../../testfiles/cestlafete.jpg'); + + $record = record_adapter::create($collection, $system_file); + $record_id = $record->get_record_id(); + $route = '/records/' . $databox_id . '/' . $record_id . '/?oauth_token=' . self::$token; + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + $crawler = $this->client->request('GET', $route); + $content = json_decode($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaJson200($content); + + $this->evaluateGoodRecord($content->response->record); + $record->delete(); + } + $route = '/records/1234567890/1/?oauth_token=' . self::$token; + $this->evaluateNotFoundRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + $route = '/records/kjslkz84spm/sfsd5qfsd5/?oauth_token=' . self::$token; + $this->evaluateBadRequestRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + } + + public function testDataboxCollectionRoute() + { + foreach (static::$databoxe_ids as $databox_id) + { + $route = '/databoxes/' . $databox_id . '/collections/?oauth_token=' . self::$token; + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + + $crawler = $this->client->request('GET', $route); + $content = json_decode($this->client->getResponse()->getContent()); + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaJson200($content); + + $this->assertObjectHasAttribute('collections', $content->response); + foreach ($content->response->collections as $collection) + { + $this->assertTrue(is_object($collection), 'Une collection est un objet'); + $this->assertObjectHasAttribute('base_id', $collection); + $this->assertObjectHasAttribute('coll_id', $collection); + $this->assertObjectHasAttribute('name', $collection); + $this->assertObjectHasAttribute('record_amount', $collection); + $this->assertTrue(is_int($collection->base_id)); + $this->assertGreaterThan(0, $collection->base_id); + $this->assertTrue(is_int($collection->coll_id)); + $this->assertGreaterThan(0, $collection->coll_id); + $this->assertTrue(is_string($collection->name)); + $this->assertTrue(is_int($collection->record_amount)); + } + } + $route = '/databoxes/24892534/collections/?oauth_token=' . self::$token; + $this->evaluateNotFoundRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + $route = '/databoxes/any_bad_id/collections/?oauth_token=' . self::$token; + $this->evaluateBadRequestRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + } + + public function testDataboxStatusRoute() + { + foreach (static::$databoxe_ids as $databox_id) + { + $databox = databox::get_instance($databox_id); + $ref_status = $databox->get_statusbits(); + $route = '/databoxes/' . $databox_id . '/status/?oauth_token=' . self::$token; + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + + $crawler = $this->client->request('GET', $route); + $content = json_decode($this->client->getResponse()->getContent()); + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaJson200($content); + + $this->assertObjectHasAttribute('status', $content->response); + foreach ($content->response->status as $status) + { + $this->assertTrue(is_object($status), 'Un bloc status est un objet'); + $this->assertObjectHasAttribute('bit', $status); + $this->assertTrue(is_int($status->bit)); + $this->assertGreaterThan(3, $status->bit); + $this->assertLessThan(65, $status->bit); + $this->assertObjectHasAttribute('label_on', $status); + $this->assertObjectHasAttribute('label_off', $status); + $this->assertObjectHasAttribute('img_on', $status); + $this->assertObjectHasAttribute('img_off', $status); + $this->assertObjectHasAttribute('searchable', $status); + $this->assertObjectHasAttribute('printable', $status); + $this->assertTrue(is_int($status->searchable)); + $this->assertTrue(in_array($status->searchable, array(0, 1))); + $this->assertTrue($status->searchable === $ref_status[$status->bit]['searchable']); + $this->assertTrue(is_int($status->printable)); + $this->assertTrue(in_array($status->printable, array(0, 1))); + $this->assertTrue($status->printable === $ref_status[$status->bit]['printable']); + $this->assertTrue($status->label_off === $ref_status[$status->bit]['labeloff']); + $this->assertTrue($status->label_on === $ref_status[$status->bit]['labelon']); + $this->assertTrue($status->img_off === $ref_status[$status->bit]['img_off']); + $this->assertTrue($status->img_on === $ref_status[$status->bit]['img_on']); + } + } + $route = '/databoxes/24892534/status/?oauth_token=' . self::$token; + $this->evaluateNotFoundRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + $route = '/databoxes/any_bad_id/status/?oauth_token=' . self::$token; + $this->evaluateBadRequestRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + } + + public function testDataboxMetadatasRoute() + { + foreach (static::$databoxe_ids as $databox_id) + { + $databox = databox::get_instance($databox_id); + $ref_structure = $databox->get_meta_structure(); + + try + { + $ref_structure->get_element('idbarbouze'); + $this->fail('An expected exception has not been raised.'); + } + catch (Exception_Databox_FieldNotFound $e) + { + + } + + $route = '/databoxes/' . $databox_id . '/metadatas/?oauth_token=' . self::$token; + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + + $crawler = $this->client->request('GET', $route); + $content = json_decode($this->client->getResponse()->getContent()); + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaJson200($content); + $this->assertObjectHasAttribute('metadatas', $content->response); + foreach ($content->response->metadatas as $metadatas) + { + $this->assertTrue(is_object($metadatas), 'Un bloc metadata est un objet'); + $this->assertObjectHasAttribute('id', $metadatas); + $this->assertObjectHasAttribute('namespace', $metadatas); + $this->assertObjectHasAttribute('source', $metadatas); + $this->assertObjectHasAttribute('tagname', $metadatas); + $this->assertObjectHasAttribute('name', $metadatas); + $this->assertObjectHasAttribute('separator', $metadatas); + $this->assertObjectHasAttribute('thesaurus_branch', $metadatas); + $this->assertObjectHasAttribute('type', $metadatas); + $this->assertObjectHasAttribute('indexable', $metadatas); + $this->assertObjectHasAttribute('multivalue', $metadatas); + $this->assertObjectHasAttribute('readonly', $metadatas); + $this->assertObjectHasAttribute('required', $metadatas); + + $this->assertTrue(is_int($metadatas->id)); + $this->assertTrue(is_string($metadatas->namespace)); + $this->assertTrue(is_string($metadatas->name)); + $this->assertTrue(is_null($metadatas->source) || is_string($metadatas->source)); + $this->assertTrue(is_string($metadatas->tagname)); + $this->assertTrue((strlen($metadatas->name) > 0)); + $this->assertTrue(is_string($metadatas->separator)); + + if ($metadatas->multivalue) + $this->assertTrue((strlen($metadatas->separator) > 0)); + + $this->assertTrue(is_string($metadatas->thesaurus_branch)); + $this->assertTrue(in_array($metadatas->type, array(databox_field::TYPE_DATE,databox_field::TYPE_STRING, databox_field::TYPE_NUMBER, databox_field::TYPE_TEXT))); + $this->assertTrue(is_bool($metadatas->indexable)); + $this->assertTrue(is_bool($metadatas->multivalue)); + $this->assertTrue(is_bool($metadatas->readonly)); + $this->assertTrue(is_bool($metadatas->required)); + + $element = $ref_structure->get_element($metadatas->id); + $this->assertTrue($element->is_indexable() === $metadatas->indexable); + $this->assertTrue($element->is_required() === $metadatas->required); + $this->assertTrue($element->is_readonly() === $metadatas->readonly); + $this->assertTrue($element->is_multi() === $metadatas->multivalue); + $this->assertTrue($element->get_type() === $metadatas->type); + $this->assertTrue($element->get_tbranch() === $metadatas->thesaurus_branch); + $this->assertTrue($element->get_separator() === $metadatas->separator); + $this->assertTrue($element->get_name() === $metadatas->name); + $this->assertTrue($element->get_metadata_tagname() === $metadatas->tagname); + $this->assertTrue($element->get_metadata_source() === $metadatas->source); + $this->assertTrue($element->get_metadata_namespace() === $metadatas->namespace); + } + } + $route = '/databoxes/24892534/metadatas/?oauth_token=' . self::$token; + $this->evaluateNotFoundRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + $route = '/databoxes/any_bad_id/metadatas/?oauth_token=' . self::$token; + $this->evaluateBadRequestRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + } + + public function testDataboxTermsOfUseRoute() + { + foreach (static::$databoxe_ids as $databox_id) + { + $databox = databox::get_instance($databox_id); + + $route = '/databoxes/' . $databox_id . '/termsOfUse/?oauth_token=' . self::$token; + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + + $crawler = $this->client->request('GET', $route); + $content = json_decode($this->client->getResponse()->getContent()); + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaJson200($content); + + $this->assertObjectHasAttribute('termsOfUse', $content->response); + foreach ($content->response->termsOfUse as $terms) + { + $this->assertTrue(is_object($terms), 'Une bloc cgu est un objet'); + $this->assertObjectHasAttribute('locale', $terms); + $this->assertTrue(in_array($terms->locale, array('fr_FR', 'en_GB', 'ar_SA', 'de_DE', 'es_ES'))); + $this->assertObjectHasAttribute('terms', $terms); + } + } + $route = '/databoxes/24892534/termsOfUse/?oauth_token=' . self::$token; + $this->evaluateNotFoundRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + $route = '/databoxes/any_bad_id/termsOfUse/?oauth_token=' . self::$token; + $this->evaluateBadRequestRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + } + + /* + * + * End /API/V1/databoxes/DATABOX_ID/xxxxxx Routes + * + * ************************************************************************** + * + * Routes /API/V1/records/DATABOX_ID/RECORD_ID/xxxxx + * + */ + + public function testRecordsSearchRoute() + { + + + $crawler = $this->client->request('POST', '/records/search/?oauth_token=' . self::$token); + $content = json_decode($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaJson200($content); + + $response = $content->response; + + $this->assertObjectHasAttribute('total_pages', $response); + $this->assertObjectHasAttribute('current_page', $response); + $this->assertObjectHasAttribute('available_results', $response); + $this->assertObjectHasAttribute('total_results', $response); + $this->assertObjectHasAttribute('error', $response); + $this->assertObjectHasAttribute('warning', $response); + $this->assertObjectHasAttribute('query_time', $response); + $this->assertObjectHasAttribute('search_indexes', $response); + $this->assertObjectHasAttribute('suggestions', $response); + $this->assertObjectHasAttribute('results', $response); + $this->assertObjectHasAttribute('query', $response); + + + $this->assertTrue(is_int($response->total_pages), 'Le nombre de page est un int'); + $this->assertTrue(is_int($response->current_page), 'Le nombre de la page courrante est un int'); + $this->assertTrue(is_int($response->available_results), 'Le nombre de results dispo est un int'); + $this->assertTrue(is_int($response->total_results), 'Le nombre de results est un int'); + $this->assertTrue(is_string($response->error), 'Error est une string'); + $this->assertTrue(is_string($response->warning), 'Warning est une string'); + /** + * @todo null quand erreur + */ +// $this->assertTrue(is_string($response->query_time)); + $this->assertTrue(is_string($response->search_indexes)); + $this->assertTrue(is_array($response->suggestions)); + $this->assertTrue(is_array($response->results)); + $this->assertTrue(is_string($response->query)); + + foreach ($response->results as $record) + { + $this->evaluateGoodRecord($record); + } + } + + public function testRecordsCaptionRoute() + { + foreach (static::$databoxe_ids as $databox_id) + { + $databox = databox::get_instance($databox_id); + + $collection = array_shift($databox->get_collections()); + $system_file = new system_file(__DIR__ . '/../../../testfiles/cestlafete.jpg'); + + $record = record_adapter::create($collection, $system_file); + + $record_id = $record->get_record_id(); + + $route = '/records/' . $databox_id . '/' . $record_id . '/caption/?oauth_token=' . self::$token; + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + + $crawler = $this->client->request('GET', $route); + $content = json_decode($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaJson200($content); + + $this->evaluateRecordsCaptionResponse($content); + $record->delete(); + } + $route = '/records/24892534/51654651553/metadatas/?oauth_token=' . self::$token; + $this->evaluateNotFoundRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + $route = '/records/any_bad_id/sfsd5qfsd5/metadatas/?oauth_token=' . self::$token; + $this->evaluateBadRequestRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + } + + public function testRecordsMetadatasRoute() + { + foreach (static::$databoxe_ids as $databox_id) + { + $databox = databox::get_instance($databox_id); + + $collection = array_shift($databox->get_collections()); + $system_file = new system_file(__DIR__ . '/../../../testfiles/cestlafete.jpg'); + + $record = record_adapter::create($collection, $system_file); + + $record_id = $record->get_record_id(); + + $route = '/records/' . $databox_id . '/' . $record_id . '/metadatas/?oauth_token=' . self::$token; + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + + $crawler = $this->client->request('GET', $route); + $content = json_decode($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaJson200($content); + + $this->evaluateRecordsMetadataResponse($content); + $record->delete(); + } + $route = '/records/24892534/51654651553/metadatas/?oauth_token=' . self::$token; + $this->evaluateNotFoundRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + $route = '/records/any_bad_id/sfsd5qfsd5/metadatas/?oauth_token=' . self::$token; + $this->evaluateBadRequestRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + } + + public function testRecordsStatusRoute() + { + foreach (static::$databoxe_ids as $databox_id) + { + $databox = databox::get_instance($databox_id); + $collection = array_shift($databox->get_collections()); + $system_file = new system_file(__DIR__ . '/../../../testfiles/cestlafete.jpg'); + + $record = record_adapter::create($collection, $system_file); + + $record_id = $record->get_record_id(); + + $route = '/records/' . $databox_id . '/' . $record_id . '/status/?oauth_token=' . self::$token; + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + + $crawler = $this->client->request('GET', $route); + $content = json_decode($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaJson200($content); + + $this->evaluateRecordsStatusResponse($record, $content); + $record->delete(); + } + $route = '/records/24892534/51654651553/status/?oauth_token=' . self::$token; + $this->evaluateNotFoundRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + $route = '/records/any_bad_id/sfsd5qfsd5/status/?oauth_token=' . self::$token; + $this->evaluateBadRequestRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + } + + public function testRecordsEmbedRoute() + { + foreach (static::$databoxe_ids as $databox_id) + { + $databox = databox::get_instance($databox_id); + $collection = array_shift($databox->get_collections()); + $system_file = new system_file(__DIR__ . '/../../../testfiles/cestlafete.jpg'); + + $record = record_adapter::create($collection, $system_file); + + $keys = array_keys($record->get_subdefs()); + + $record_id = $record->get_record_id(); + + $route = '/records/' . $databox_id . '/' . $record_id . '/embed/?oauth_token=' . self::$token; + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + + $crawler = $this->client->request('GET', $route); + $content = json_decode($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaJson200($content); + + foreach ($content->response as $embed) + { + foreach ($keys as $key) + { + $this->assertObjectHasAttribute($key, $embed); + $this->checkEmbed($key, $embed->$key, $record); + } + } + $record->delete(); + } + $route = '/records/24892534/51654651553/embed/?oauth_token=' . self::$token; + $this->evaluateNotFoundRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + $route = '/records/any_bad_id/sfsd5qfsd5/embed/?oauth_token=' . self::$token; + $this->evaluateBadRequestRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + } + + protected function checkEmbed($subdef_name, $embed, record_adapter $record) + { + $this->assertObjectHasAttribute("permalink", $embed); + $this->checkPermalink($embed->permalink, $record->get_subdef($subdef_name)); + $this->assertObjectHasAttribute("height", $embed); + $this->assertEquals($embed->height, $record->get_subdef($subdef_name)->get_height()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_INT, $embed->height); + $this->assertObjectHasAttribute("width", $embed); + $this->assertEquals($embed->width, $record->get_subdef($subdef_name)->get_width()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_INT, $embed->width); + $this->assertObjectHasAttribute("filesize", $embed); + $this->assertEquals($embed->filesize, $record->get_subdef($subdef_name)->get_size()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_INT, $embed->filesize); + $this->assertObjectHasAttribute("player_type", $embed); + $this->assertEquals($embed->player_type, $record->get_subdef($subdef_name)->get_type()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $embed->player_type); + $this->assertObjectHasAttribute("mime_type", $embed); + $this->assertEquals($embed->mime_type, $record->get_subdef($subdef_name)->get_mime()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $embed->mime_type); + } + + protected function checkPermalink($permalink, media_subdef $subdef) + { + if ($subdef->is_physically_present()) + { + $this->assertNotNull($subdef->get_permalink()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_OBJECT, $permalink); + $this->assertObjectHasAttribute("created_on", $permalink); + $this->assertEquals($subdef->get_permalink()->get_created_on()->format(DATE_ATOM), $permalink->created_on); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $permalink->created_on); + $this->assertDateAtom($permalink->created_on); + $this->assertObjectHasAttribute("id", $permalink); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_INT, $permalink->id); + $this->assertEquals($subdef->get_permalink()->get_id(), $permalink->id); + $this->assertObjectHasAttribute("is_activated", $permalink); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_BOOL, $permalink->is_activated); + $this->assertEquals($subdef->get_permalink()->get_is_activated(), $permalink->is_activated); + $this->assertObjectHasAttribute("label", $permalink); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $permalink->label); + $this->assertObjectHasAttribute("last_modified", $permalink); + $this->assertEquals($subdef->get_permalink()->get_last_modified()->format(DATE_ATOM), $permalink->last_modified); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $permalink->last_modified); + $this->assertDateAtom($permalink->last_modified); + $this->assertObjectHasAttribute("page_url", $permalink); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $permalink->page_url); + $this->assertEquals($subdef->get_permalink()->get_page(registry::get_instance()), $permalink->page_url); + $this->checkUrlCode200($permalink->page_url); + $this->assertPermalinkHeaders($permalink->page_url, $subdef); + $this->assertObjectHasAttribute("url", $permalink); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $permalink->url); + $this->assertEquals($subdef->get_permalink()->get_url(), $permalink->url); + $this->checkUrlCode200($permalink->url); + $this->assertPermalinkHeaders($permalink->url, $subdef, "url"); + } + } + + protected function assertPermalinkHeaders($url, media_subdef $subdef, $type_url = "page_url") + { + $headers = http_query::getHttpHeaders($url); + $this->assertEquals(200, $headers["http_code"]); + + switch ($type_url) + { + case "page_url" : + $this->assertTrue(strpos($headers['content_type'], "text/html") === 0); + $this->assertNotEquals($subdef->get_size(), $headers["download_content_length"]); + break; + case "url" : + $this->assertTrue(strpos($headers['content_type'], $subdef->get_mime()) === 0, 'Verify that header ' . $headers['content_type'] . ' contains subdef mime type ' . $subdef->get_mime()); + $this->assertEquals($subdef->get_size(), $headers["download_content_length"]); + break; + } + } + + protected function checkUrlCode200($url) + { + $code = http_query::getHttpCodeFromUrl($url); + $this->assertEquals(200, $code, sprintf('verification de url %s', $url)); + } + + public function testRecordsRelatedRoute() + { + foreach (static::$databoxe_ids as $databox_id) + { + $databox = databox::get_instance($databox_id); + $collection = array_shift($databox->get_collections()); + $system_file = new system_file(__DIR__ . '/../../../testfiles/cestlafete.jpg'); + + $record = record_adapter::create($collection, $system_file); + + $record_id = $record->get_record_id(); + + $route = '/records/' . $databox_id . '/' . $record_id . '/related/?oauth_token=' . self::$token; + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + + $crawler = $this->client->request('GET', $route); + $content = json_decode($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaJson200($content); + $this->assertObjectHasAttribute("baskets", $content->response); + foreach ($content->response->baskets as $basket) + { + $this->evaluateGoodBasket($basket); + } + $record->delete(); + } + $route = '/records/24892534/51654651553/related/?oauth_token=' . self::$token; + $this->evaluateNotFoundRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + $route = '/records/any_bad_id/sfsd5qfsd5/related/?oauth_token=' . self::$token; + $this->evaluateBadRequestRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + } + + public function testRecordsSetMetadatas() + { + + foreach (static::$databoxe_ids as $databox_id) + { + $databox = databox::get_instance($databox_id); + $collection = array_shift($databox->get_collections()); + $system_file = new system_file(__DIR__ . '/../../../testfiles/cestlafete.jpg'); + + $record = record_adapter::create($collection, $system_file); + + $record_id = $record->get_record_id(); + + $route = '/records/' . $databox_id . '/' . $record_id . '/setmetadatas/?oauth_token=' . self::$token; + $caption = $record->get_caption(); + + + $old_datas = array(); + $toupdate = array(); + + foreach ($record->get_databox()->get_meta_structure()->get_elements() as $field) + { + try + { + $values = $record->get_caption()->get_field($field->get_name())->get_values(); + $value = array_pop($values); + $meta_id = $value->getId(); + } + catch (\Exception $e) + { + $meta_id = null; + } + + $toupdate[$field->get_id()] = array( + 'meta_id' => $meta_id + , 'meta_struct_id' => $field->get_id() + , 'value' => 'podom pom pom ' . $field->get_id() + ); + } + + $this->evaluateMethodNotAllowedRoute($route, array('GET', 'PUT', 'DELETE')); + + $crawler = $this->client->request('POST', $route, array('metadatas' => $toupdate)); + $content = json_decode($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaJson200($content); + + $record = $databox->get_record($record_id); + $caption = $record->get_caption(); + + $this->assertEquals(count($caption->get_fields()), count(get_object_vars($content->response->metadatas)), 'Retrived metadatas are the same'); + + foreach ($caption->get_fields() as $field) + { + foreach($field->get_values() as $value) + { + if ($field->is_readonly() === false && $field->is_multi() === false) + { + $saved_value = $toupdate[$field->get_meta_struct_id()]['value']; + $this->assertEquals($value->getValue(), $saved_value); + } + } + } + $this->evaluateRecordsMetadataResponse($content); + + foreach ($content->response->metadatas as $metadata) + { + if (!in_array($metadata->meta_id, array_keys($toupdate))) + continue; + $saved_value = $toupdate[$metadata->meta_id]['value']; + $this->assertEquals($saved_value, $metadata->value); + } + $record->delete(); + } + } + + public function testRecordsSetStatus() + { + + foreach (static::$databoxe_ids as $databox_id) + { + $databox = databox::get_instance($databox_id); + $collection = array_shift($databox->get_collections()); + $system_file = new system_file(__DIR__ . '/../../../testfiles/cestlafete.jpg'); + + if(!$collection instanceof \collection) + $this->fail('unable to find a collection'); + + $record = record_adapter::create($collection, $system_file); + + $record_id = $record->get_record_id(); + + $route = '/records/' . $databox_id . '/' . $record_id . '/setstatus/?oauth_token=' . self::$token; + + $record_status = strrev($record->get_status()); + $status_bits = $databox->get_statusbits(); + + $tochange = array(); + foreach ($status_bits as $n => $datas) + { + $tochange[$n] = substr($record_status, ($n - 1), 1) == '0' ? '1' : '0'; + } + $this->evaluateMethodNotAllowedRoute($route, array('GET', 'PUT', 'DELETE')); + + + $crawler = $this->client->request('POST', $route, array('status' => $tochange)); + $content = json_decode($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaJson200($content); + + $record = $databox->get_record($record_id); + $this->evaluateRecordsStatusResponse($record, $content); + + $record_status = strrev($record->get_status()); + foreach ($status_bits as $n => $datas) + { + $this->assertEquals(substr($record_status, ($n - 1), 1), $tochange[$n]); + } + + foreach ($tochange as $n => $value) + { + $tochange[$n] = $value == '0' ? '1' : '0'; + } + + + $crawler = $this->client->request('POST', $route, array('status' => $tochange)); + $content = json_decode($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaJson200($content); + + $record = $databox->get_record($record_id); + $this->evaluateRecordsStatusResponse($record, $content); + + $record_status = strrev($record->get_status()); + foreach ($status_bits as $n => $datas) + { + $this->assertEquals(substr($record_status, ($n - 1), 1), $tochange[$n]); + } + $record->delete(); + } + } + + public function testMoveRecordToColleciton() + { + foreach (static::$databoxe_ids as $databox_id) + { + $databox = databox::get_instance($databox_id); + $collection = array_shift($databox->get_collections()); + $system_file = new system_file(__DIR__ . '/../../../testfiles/cestlafete.jpg'); + + if(!$collection instanceof \collection) + $this->fail('unable to find a collection'); + + $record = record_adapter::create($collection, $system_file); + + $record_id = $record->get_record_id(); + + $route = '/records/' . $databox_id . '/' . $record_id . '/setcollection/?oauth_token=' . self::$token; + + $base_id = false; + foreach ($databox->get_collections() as $collection) + { + if ($collection->get_base_id() != $record->get_base_id()) + { + $base_id = $collection->get_base_id(); + break; + } + } + if (!$base_id) + { + continue; + } + + $this->evaluateMethodNotAllowedRoute($route, array('GET', 'PUT', 'DELETE')); + + $crawler = $this->client->request('POST', $route, array('base_id' => $base_id)); + $content = json_decode($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaJson200($content); + $record->delete(); + } + } + + public function testSearchBaskets() + { + $route = '/baskets/list/?oauth_token=' . self::$token; + + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + + $crawler = $this->client->request('GET', $route); + $content = json_decode($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaJson200($content); + $this->assertObjectHasAttribute("baskets", $content->response); + + foreach ($content->response->baskets as $basket) + { + $this->evaluateGoodBasket($basket); + } + } + + public function testAddBasket() + { + $route = '/baskets/add/?oauth_token=' . self::$token; + + $this->evaluateMethodNotAllowedRoute($route, array('GET', 'PUT', 'DELETE')); + + $crawler = $this->client->request('POST', $route, array('name' => 'un Joli Nom')); + $content = json_decode($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaJson200($content); + + $this->assertEquals(1, count((array) $content->response)); + $appbox = appbox::get_instance(\bootstrap::getCore()); + $this->assertObjectHasAttribute("basket", $content->response); + + foreach ($content->response->basket as $basket) + { + $this->evaluateGoodBasket($basket); + $this->assertEquals('un Joli Nom', $basket->name); + } + } + + public function testBasketContent() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $usr_id = $appbox->get_session()->get_usr_id(); + + $basket = $this->insertOneBasket(); + + $route = '/baskets/' . $basket->getId() . '/content/?oauth_token=' . self::$token; + + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + + $crawler = $this->client->request('GET', $route); + $content = json_decode($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaJson200($content); + + $this->assertEquals(1, count((array) $content->response)); + + $this->assertObjectHasAttribute("basket_elements", $content->response); + + foreach ($content->response->basket_elements as $basket_str) + { + $this->evaluateGoodBasket($basket_str); + + $this->assertEquals(count($basket->getElements()), count((array) $basket_str->basket_elements)); + foreach ($basket_str->basket_elements as $basket_element) + { + $this->assertObjectHasAttribute('basket_element_id', $basket_element); + $this->assertObjectHasAttribute('order', $basket_element); + $this->assertObjectHasAttribute('record', $basket_element); + $this->assertObjectHasAttribute('validation_item', $basket_element); + $this->assertTrue(is_bool($basket_element->validation_item)); + $this->assertTrue(is_int($basket_element->order)); + $this->assertTrue(is_int($basket_element->basket_element_id)); + $this->evaluateGoodRecord($basket_element->record); + } + } + } + + public function testSetBasketTitle() + { + + $basket = $this->insertOneBasket(); + + $route = '/baskets/' . $basket->getId() . '/setname/?oauth_token=' . self::$token; + + $this->evaluateMethodNotAllowedRoute($route, array('GET', 'PUT', 'DELETE')); + + $crawler = $this->client->request('POST', $route, array('name' => 'un Joli Nom')); + $content = json_decode($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaJson200($content); + + $this->assertEquals(1, count((array) $content->response)); + $this->assertObjectHasAttribute("basket", $content->response); + foreach ($content->response->basket as $basket_str) + { + $this->evaluateGoodBasket($basket_str); + + $this->assertEquals($basket_str->name, 'un Joli Nom'); + } + + $crawler = $this->client->request('POST', $route, array('name' => 'un Joli Nom')); + $content = json_decode($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaJson200($content); + + $this->assertEquals(1, count((array) $content->response)); + + $this->assertObjectHasAttribute("basket", $content->response); + + foreach ($content->response->basket as $basket) + { + $this->evaluateGoodBasket($basket_str); + + $this->assertEquals($basket_str->name, 'un Joli Nom'); + } + + $crawler = $this->client->request('POST', $route, array('name' => 'aéaa')); + $content = json_decode($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaJson200($content); + + $this->assertEquals(1, count((array) $content->response)); + $this->assertObjectHasAttribute("basket", $content->response); + foreach ($content->response->basket as $basket_str) + { + $this->evaluateGoodBasket($basket_str); + $this->assertEquals($basket_str->name, 'aéaa'); + } + + } + + public function testSetBasketDescription() + { + $basket = $this->insertOneBasket(); + + $route = '/baskets/' . $basket->getId() . '/setdescription/?oauth_token=' . self::$token; + + $this->evaluateMethodNotAllowedRoute($route, array('GET', 'PUT', 'DELETE')); + + $crawler = $this->client->request('POST', $route, array('description' => 'une belle desc')); + $content = json_decode($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaJson200($content); + + $this->assertEquals(1, count((array) $content->response)); + + $this->assertObjectHasAttribute("basket", $content->response); + foreach ($content->response->basket as $basket_str) + { + $this->evaluateGoodBasket($basket_str); + + $this->assertEquals($basket_str->description, 'une belle desc'); + } + + + } + + public function testDeleteBasket() + { + $baskets = $this->insertFiveBasket(); + + $route = '/baskets/' . $baskets[0]->getId() . '/delete/?oauth_token=' . self::$token; + + $this->evaluateMethodNotAllowedRoute($route, array('GET', 'PUT', 'DELETE')); + + $crawler = $this->client->request('POST', $route); + $content = json_decode($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaJson200($content); + + $this->assertObjectHasAttribute("baskets", $content->response); + + $found = false; + foreach ($content->response->baskets as $basket) + { + $this->evaluateGoodBasket($basket); + $found = true; + } + if(!$found) + { + $this->fail('There should be four baskets left'); + } + } + + /** + * + * End /API/V1/records/DATABOX_ID/RECORD_ID/xxxxx Routes + * + */ + protected function evaluateNotFoundRoute($route, $methods) + { + foreach ($methods as $method) + { + $crawler = $this->client->request($method, $route); + $content = json_decode($this->client->getResponse()->getContent()); + + $this->evaluateResponseNotFound($this->client->getResponse()); + $this->evaluateMetaJsonNotFound($content); + } + } + + protected function evaluateMethodNotAllowedRoute($route, $methods) + { + foreach ($methods as $method) + { + $crawler = $this->client->request($method, $route); + $content = json_decode($this->client->getResponse()->getContent()); + $this->evaluateResponseMethodNotAllowed($this->client->getResponse()); + $this->evaluateMetaJsonMethodNotAllowed($content); + } + } + + protected function evaluateBadRequestRoute($route, $methods) + { + foreach ($methods as $method) + { + $crawler = $this->client->request($method, $route); + $content = json_decode($this->client->getResponse()->getContent()); + $this->evaluateResponseBadRequest($this->client->getResponse()); + $this->evaluateMetaJsonBadRequest($content); + } + } + + protected function evaluateMetaJson($content) + { + $this->assertTrue(is_object($content), 'La reponse est un objet'); + $this->assertObjectHasAttribute('meta', $content); + $this->assertObjectHasAttribute('response', $content); + $this->assertTrue(is_object($content->meta), 'Le bloc meta est un objet json'); + $this->assertTrue(is_object($content->response), 'Le bloc reponse est un objet json'); + $this->assertEquals('1.1', $content->meta->api_version); + $this->assertNotNull($content->meta->response_time); + $this->assertEquals('UTF-8', $content->meta->charset); + } + + protected function evaluateMetaJson200($content) + { + $this->evaluateMetaJson($content); + $this->assertEquals(200, $content->meta->http_code); + $this->assertNull($content->meta->error_message); + $this->assertNull($content->meta->error_details); + } + + protected function evaluateMetaJsonBadRequest($content) + { + $this->evaluateMetaJson($content); + $this->assertNotNull($content->meta->error_message); + $this->assertNotNull($content->meta->error_details); + $this->assertEquals(400, $content->meta->http_code); + } + + protected function evaluateMetaJsonNotFound($content) + { + $this->evaluateMetaJson($content); + $this->assertNotNull($content->meta->error_message); + $this->assertNotNull($content->meta->error_details); + $this->assertEquals(404, $content->meta->http_code); + } + + protected function evaluateMetaJsonMethodNotAllowed($content) + { + $this->evaluateMetaJson($content); + $this->assertNotNull($content->meta->error_message); + $this->assertNotNull($content->meta->error_details); + $this->assertEquals(405, $content->meta->http_code); + } + + protected function evaluateResponse200(Response $response) + { + $this->assertEquals('UTF-8', $response->getCharset(), 'Test charset response'); + $this->assertEquals(200, $response->getStatusCode(), 'Test status code 200 ' . $response->getContent()); + } + + protected function evaluateResponseBadRequest(Response $response) + { + $this->assertEquals('UTF-8', $response->getCharset(), 'Test charset response'); + $this->assertEquals(400, $response->getStatusCode(), 'Test status code 400 ' . $response->getContent()); + } + + protected function evaluateResponseNotFound(Response $response) + { + $this->assertEquals('UTF-8', $response->getCharset(), 'Test charset response'); + $this->assertEquals(404, $response->getStatusCode(), 'Test status code 404 ' . $response->getContent()); + } + + protected function evaluateResponseMethodNotAllowed(Response $response) + { + $this->assertEquals('UTF-8', $response->getCharset(), 'Test charset response'); + $this->assertEquals(405, $response->getStatusCode(), 'Test status code 405 ' . $response->getContent()); + } + + protected function evaluateGoodBasket($basket) + { + $this->assertTrue(is_object($basket)); + $this->assertObjectHasAttribute('created_on', $basket); + $this->assertObjectHasAttribute('description', $basket); + $this->assertObjectHasAttribute('name', $basket); + $this->assertObjectHasAttribute('pusher_usr_id', $basket); + $this->assertObjectHasAttribute('ssel_id', $basket); + $this->assertObjectHasAttribute('updated_on', $basket); + $this->assertObjectHasAttribute('unread', $basket); + + + if (!is_null($basket->pusher_usr_id)) + { + $this->assertTrue(is_int($basket->pusher_usr_id)); + } + $this->assertTrue(is_string($basket->name)); + $this->assertTrue(is_string($basket->description)); + $this->assertTrue(is_int($basket->ssel_id)); + $this->assertTrue(is_bool($basket->unread)); + $this->assertDateAtom($basket->created_on); + $this->assertDateAtom($basket->updated_on); + } + + protected function evaluateGoodRecord($record) + { + $this->assertObjectHasAttribute('databox_id', $record); + $this->assertTrue(is_int($record->databox_id)); + $this->assertObjectHasAttribute('record_id', $record); + $this->assertTrue(is_int($record->record_id)); + $this->assertObjectHasAttribute('mime_type', $record); + $this->assertTrue(is_string($record->mime_type)); + $this->assertObjectHasAttribute('title', $record); + $this->assertTrue(is_string($record->title)); + $this->assertObjectHasAttribute('original_name', $record); + $this->assertTrue(is_string($record->original_name)); + $this->assertObjectHasAttribute('last_modification', $record); + $this->assertDateAtom($record->last_modification); + $this->assertObjectHasAttribute('created_on', $record); + $this->assertDateAtom($record->created_on); + $this->assertObjectHasAttribute('collection_id', $record); + $this->assertTrue(is_int($record->collection_id)); + $this->assertObjectHasAttribute('thumbnail', $record); + $this->assertObjectHasAttribute('sha256', $record); + $this->assertTrue(is_string($record->sha256)); + $this->assertObjectHasAttribute('technical_informations', $record); + $this->assertObjectHasAttribute('phrasea_type', $record); + $this->assertTrue(is_string($record->phrasea_type)); + $this->assertTrue(in_array($record->phrasea_type, array('audio', 'document', 'image', 'video', 'flash', 'unknown'))); + $this->assertObjectHasAttribute('uuid', $record); + $this->assertTrue(uuid::is_valid($record->uuid)); + + $this->assertTrue(is_object($record->thumbnail)); + $this->assertObjectHasAttribute('player_type', $record->thumbnail); + $this->assertTrue(is_string($record->thumbnail->player_type)); + $this->assertObjectHasAttribute('permalink', $record->thumbnail); + $this->assertObjectHasAttribute('mime_type', $record->thumbnail); + $this->assertTrue(is_string($record->thumbnail->mime_type)); + $this->assertObjectHasAttribute('height', $record->thumbnail); + $this->assertTrue(is_int($record->thumbnail->height)); + $this->assertObjectHasAttribute('width', $record->thumbnail); + $this->assertTrue(is_int($record->thumbnail->width)); + $this->assertObjectHasAttribute('filesize', $record->thumbnail); + $this->assertTrue(is_int($record->thumbnail->filesize)); + + if (is_array($record->technical_informations)) + $this->assertEquals(0, count($record->technical_informations)); + else + $this->assertTrue(is_object($record->technical_informations)); + + foreach ($record->technical_informations as $key => $value) + { + switch ($key) + { + case system_file::TC_DATAS_DURATION: + case 'size': + case system_file::TC_DATAS_WIDTH: + case system_file::TC_DATAS_HEIGHT: + case system_file::TC_DATAS_COLORDEPTH: + $this->assertTrue(is_int($value), 'test technical data ' . $key . ' ' . $value); + break; + default; + $this->assertTrue(is_string($value), 'test technical data ' . $key); + break; + } + } + } + + protected function evaluateRecordsCaptionResponse($content) + { + foreach ($content->response as $field) + { + $this->assertTrue(is_object($field), 'Un bloc field est un objet'); + $this->assertObjectHasAttribute('meta_structure_id', $meta); + $this->assertTrue(is_int($field->meta_structure_id)); + $this->assertObjectHasAttribute('name', $field); + $this->assertTrue(is_string($meta->name)); + $this->assertObjectHasAttribute('value', $field); + $this->assertTrue(is_string($meta->value)); + } + } + + protected function evaluateRecordsMetadataResponse($content) + { + $this->assertObjectHasAttribute("metadatas", $content->response); + foreach ($content->response->metadatas as $meta) + { + $this->assertTrue(is_object($meta), 'Un bloc meta est un objet'); + $this->assertObjectHasAttribute('meta_id', $meta); + $this->assertTrue(is_int($meta->meta_id)); + $this->assertObjectHasAttribute('meta_structure_id', $meta); + $this->assertTrue(is_int($meta->meta_structure_id)); + $this->assertObjectHasAttribute('name', $meta); + $this->assertTrue(is_string($meta->name)); + $this->assertObjectHasAttribute('value', $meta); + + if (is_array($meta->value)) + { + foreach ($meta->value as $val) + { + $this->assertTrue(is_string($val)); + } + } + else + { + $this->assertTrue(is_string($meta->value)); + } + } + } + + protected function evaluateRecordsStatusResponse(record_adapter $record, $content) + { + $status = $record->get_databox()->get_statusbits(); + + + $r_status = strrev($record->get_status()); + $this->assertObjectHasAttribute('status', $content->response); + $this->assertEquals(count((array) $content->response->status), count($status)); + foreach ($content->response->status as $status) + { + $this->assertTrue(is_object($status)); + $this->assertObjectHasAttribute('bit', $status); + $this->assertObjectHasAttribute('state', $status); + $this->assertTrue(is_int($status->bit)); + $this->assertTrue(is_bool($status->state)); + + $retrieved = !!substr($r_status, ($status->bit - 1), 1); + + $this->assertEquals($retrieved, $status->state); + } + } + +} diff --git a/tests/Alchemy/Phrasea/Application/ApiYamlTest.php b/tests/Alchemy/Phrasea/Application/ApiYamlTest.php new file mode 100644 index 0000000000..431f6484a4 --- /dev/null +++ b/tests/Alchemy/Phrasea/Application/ApiYamlTest.php @@ -0,0 +1,1293 @@ +client = $this->createClient(); + } + + public static function setUpBeforeClass() + { + parent::setUpBeforeClass(); + self::$yaml = new Symfony\Component\Yaml\Parser(); + $appbox = appbox::get_instance(\bootstrap::getCore()); + self::$application = API_OAuth2_Application::create($appbox, self::$user, 'test API v1'); + $account = API_OAuth2_Account::load_with_user($appbox, self::$application, self::$user); + self::$token = $account->get_token()->get_value(); + self::$account_id = $account->get_id(); + $_GET['oauth_token'] = self::$token; + } + + public static function tearDownAfterClass() + { + self::$application->delete(); + $_GET = array(); + } + + public function createApplication() + { + return require __DIR__ . '/../../../../lib/Alchemy/Phrasea/Application/Api.php'; + } + + public function testRouteNotFound() + { + $route = '/nothinghere?oauth_token=' . self::$token; + $this->client->request('GET', $route, array(), array(), array("HTTP_CONTENT_TYPE" => "application/yaml", "HTTP_ACCEPT" => "application/yaml")); + $content = self::$yaml->parse($this->client->getResponse()->getContent()); + $this->evaluateResponseNotFound($this->client->getResponse()); + $this->evaluateMetaYamlNotFound($content); + } + + public function testDatboxListRoute() + { + $crawler = $this->client->request('GET', '/databoxes/list/?oauth_token=' . self::$token, array(), array(), array("HTTP_CONTENT_TYPE" => "application/yaml", "HTTP_ACCEPT" => "application/yaml")); + $content = self::$yaml->parse($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + + $this->evaluateMetaYaml200($content); + + $this->assertArrayHasKey('databoxes', $content["response"]); + foreach ($content["response"]["databoxes"] as $databox) + { + $this->assertTrue(is_array($databox), 'Une databox est un array'); + $this->assertArrayHasKey('databox_id', $databox); + $this->assertArrayHasKey('name', $databox); + $this->assertArrayHasKey('version', $databox); + static::$databoxe_ids[] = $databox["databox_id"]; + } + } + + /* + * Routes /API/V1/databoxes/DATABOX_ID/xxxxxx + * + */ + + public function testDataboxRecordRoute() + { + + foreach (static::$databoxe_ids as $databox_id) + { + $databox = databox::get_instance($databox_id); + + $collection = array_shift($databox->get_collections()); + $system_file = new system_file(__DIR__ . '/../../../testfiles/cestlafete.jpg'); + + $record = record_adapter::create($collection, $system_file); + $record_id = $record->get_record_id(); + $route = '/records/' . $databox_id . '/' . $record_id . '/?oauth_token=' . self::$token; + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + $crawler = $this->client->request('GET', $route, array(), array(), array("HTTP_ACCEPT" => "application/yaml")); + $content = self::$yaml->parse($this->client->getResponse()->getContent()); + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaYaml200($content); + + $this->evaluateGoodRecord($content["response"]["record"]); + $record->delete(); + } + $route = '/records/1234567890/1/?oauth_token=' . self::$token; + $this->evaluateNotFoundRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + $route = '/records/kjslkz84spm/sfsd5qfsd5/?oauth_token=' . self::$token; + $this->evaluateBadRequestRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + } + + public function testDataboxCollectionRoute() + { + foreach (static::$databoxe_ids as $databox_id) + { + $route = '/databoxes/' . $databox_id . '/collections/?oauth_token=' . self::$token; + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + + $crawler = $this->client->request('GET', $route, array(), array(), array("HTTP_ACCEPT" => "application/yaml")); + $content = self::$yaml->parse($this->client->getResponse()->getContent()); + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaYaml200($content); + + $this->assertArrayHasKey('collections', $content["response"]); + foreach ($content["response"]["collections"] as $collection) + { + $this->assertTrue(is_array($collection), 'Une collection est un array'); + $this->assertArrayHasKey('base_id', $collection); + $this->assertArrayHasKey('coll_id', $collection); + $this->assertArrayHasKey('name', $collection); + $this->assertArrayHasKey('record_amount', $collection); + $this->assertTrue(is_int($collection["base_id"])); + $this->assertGreaterThan(0, $collection["base_id"]); + $this->assertTrue(is_int($collection["coll_id"])); + $this->assertGreaterThan(0, $collection["coll_id"]); + $this->assertTrue(is_string($collection["name"])); + $this->assertTrue(is_int($collection["record_amount"])); + } + } + $route = '/databoxes/24892534/collections/?oauth_token=' . self::$token; + $this->evaluateNotFoundRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + $route = '/databoxes/any_bad_id/collections/?oauth_token=' . self::$token; + $this->evaluateBadRequestRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + } + + public function testDataboxStatusRoute() + { + foreach (static::$databoxe_ids as $databox_id) + { + $databox = databox::get_instance($databox_id); + $ref_status = $databox->get_statusbits(); + $route = '/databoxes/' . $databox_id . '/status/?oauth_token=' . self::$token; + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + + $crawler = $this->client->request('GET', $route, array(), array(), array("HTTP_ACCEPT" => "application/yaml")); + $content = self::$yaml->parse($this->client->getResponse()->getContent()); + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaYaml200($content); + + $this->assertArrayHasKey('status', $content["response"]); + foreach ($content["response"]["status"] as $status) + { + $this->assertTrue(is_array($status), 'Un bloc status est un array'); + $this->assertArrayHasKey('bit', $status); + $this->assertTrue(is_int($status["bit"])); + $this->assertGreaterThan(3, $status["bit"]); + $this->assertLessThan(65, $status["bit"]); + $this->assertArrayHasKey('label_on', $status); + $this->assertArrayHasKey('label_off', $status); + $this->assertArrayHasKey('img_on', $status); + $this->assertArrayHasKey('img_off', $status); + $this->assertArrayHasKey('searchable', $status); + $this->assertArrayHasKey('printable', $status); + $this->assertTrue(is_int($status["searchable"])); + $this->assertTrue(in_array($status["searchable"], array(0, 1))); + $this->assertTrue($status["searchable"] === $ref_status[$status["bit"]]['searchable']); + $this->assertTrue(is_int($status["printable"])); + $this->assertTrue(in_array($status["printable"], array(0, 1))); + $this->assertTrue($status["printable"] === $ref_status[$status["bit"]]['printable']); + $this->assertTrue($status["label_off"] === $ref_status[$status["bit"]]['labeloff']); + $this->assertTrue($status["label_on"] === $ref_status[$status["bit"]]['labelon']); + $this->assertTrue($status["img_off"] === $ref_status[$status["bit"]]['img_off']); + $this->assertTrue($status["img_on"] === $ref_status[$status["bit"]]['img_on']); + } + } + $route = '/databoxes/24892534/status/?oauth_token=' . self::$token; + $this->evaluateNotFoundRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + $route = '/databoxes/any_bad_id/status/?oauth_token=' . self::$token; + $this->evaluateBadRequestRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + } + + public function testDataboxMetadatasRoute() + { + foreach (static::$databoxe_ids as $databox_id) + { + $databox = databox::get_instance($databox_id); + $ref_structure = $databox->get_meta_structure(); + + try + { + $ref_structure->get_element('idbarbouze'); + $this->fail('An expected exception has not been raised.'); + } + catch (Exception_Databox_FieldNotFound $e) + { + + } + + $route = '/databoxes/' . $databox_id . '/metadatas/?oauth_token=' . self::$token; + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + + $crawler = $this->client->request('GET', $route, array(), array(), array("HTTP_ACCEPT" => "application/yaml")); + $content = self::$yaml->parse($this->client->getResponse()->getContent()); + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaYaml200($content); + $this->assertArrayHasKey('metadatas', $content["response"]); + foreach ($content["response"]["metadatas"] as $metadatas) + { + $this->assertTrue(is_array($metadatas), 'Un bloc metadata est un array'); + $this->assertArrayHasKey('id', $metadatas); + $this->assertArrayHasKey('namespace', $metadatas); + $this->assertArrayHasKey('source', $metadatas); + $this->assertArrayHasKey('tagname', $metadatas); + $this->assertArrayHasKey('name', $metadatas); + $this->assertArrayHasKey('separator', $metadatas); + $this->assertArrayHasKey('thesaurus_branch', $metadatas); + $this->assertArrayHasKey('type', $metadatas); + $this->assertArrayHasKey('indexable', $metadatas); + $this->assertArrayHasKey('multivalue', $metadatas); + $this->assertArrayHasKey('readonly', $metadatas); + $this->assertArrayHasKey('required', $metadatas); + + $this->assertTrue(is_int($metadatas["id"])); + $this->assertTrue(is_string($metadatas["namespace"])); + $this->assertTrue(is_string($metadatas["name"])); + $this->assertTrue(is_string($metadatas["source"]) || is_null($metadatas["source"])); + $this->assertTrue(is_string($metadatas["tagname"])); + $this->assertTrue((strlen($metadatas["name"]) > 0)); + $this->assertTrue(is_string($metadatas["separator"])); + + if ($metadatas["multivalue"]) + $this->assertTrue((strlen($metadatas["separator"]) > 0)); + + $this->assertTrue(is_string($metadatas["thesaurus_branch"])); + $this->assertTrue(in_array($metadatas["type"], array(databox_field::TYPE_DATE, databox_field::TYPE_STRING, databox_field::TYPE_NUMBER, databox_field::TYPE_TEXT))); + $this->assertTrue(is_bool($metadatas["indexable"])); + $this->assertTrue(is_bool($metadatas["multivalue"])); + $this->assertTrue(is_bool($metadatas["readonly"])); + $this->assertTrue(is_bool($metadatas["required"])); + + $element = $ref_structure->get_element($metadatas["id"]); + $this->assertTrue($element->is_indexable() === $metadatas["indexable"]); + $this->assertTrue($element->is_required() === $metadatas["required"]); + $this->assertTrue($element->is_readonly() === $metadatas["readonly"]); + $this->assertTrue($element->is_multi() === $metadatas["multivalue"]); + $this->assertTrue($element->get_type() === $metadatas["type"]); + $this->assertTrue($element->get_tbranch() === $metadatas["thesaurus_branch"]); + $this->assertTrue($element->get_separator() === $metadatas["separator"]); + $this->assertTrue($element->get_name() === $metadatas["name"]); + $this->assertTrue($element->get_metadata_tagname() === $metadatas["tagname"]); + $this->assertTrue($element->get_metadata_source() === $metadatas["source"]); + $this->assertTrue($element->get_metadata_namespace() === $metadatas["namespace"]); + } + } + $route = '/databoxes/24892534/metadatas/?oauth_token=' . self::$token; + $this->evaluateNotFoundRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + $route = '/databoxes/any_bad_id/metadatas/?oauth_token=' . self::$token; + $this->evaluateBadRequestRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + } + + public function testDataboxTermsOfUseRoute() + { + foreach (static::$databoxe_ids as $databox_id) + { + $databox = databox::get_instance($databox_id); + + $route = '/databoxes/' . $databox_id . '/termsOfUse/?oauth_token=' . self::$token; + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + + $crawler = $this->client->request('GET', $route, array(), array(), array("HTTP_ACCEPT" => "application/yaml")); + $content = self::$yaml->parse($this->client->getResponse()->getContent()); + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaYaml200($content); + + $this->assertArrayHasKey('termsOfUse', $content["response"]); + foreach ($content["response"]["termsOfUse"] as $terms) + { + $this->assertTrue(is_array($terms), 'Une bloc cgu est un array'); + $this->assertArrayHasKey('locale', $terms); + $this->assertTrue(in_array($terms["locale"], array('fr_FR', 'en_GB', 'ar_SA', 'de_DE', 'es_ES'))); + $this->assertArrayHasKey('terms', $terms); + } + } + $route = '/databoxes/24892534/termsOfUse/?oauth_token=' . self::$token; + $this->evaluateNotFoundRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + $route = '/databoxes/any_bad_id/termsOfUse/?oauth_token=' . self::$token; + $this->evaluateBadRequestRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + } + + /* + * + * End /API/V1/databoxes/DATABOX_ID/xxxxxx Routes + * + * ************************************************************************** + * + * Routes /API/V1/records/DATABOX_ID/RECORD_ID/xxxxx + * + */ + + public function testRecordsSearchRoute() + { + + + $crawler = $this->client->request('POST', '/records/search/?oauth_token=' . self::$token, array(), array(), array("HTTP_ACCEPT" => "application/yaml")); + $content = self::$yaml->parse($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaYaml200($content); + + $response = $content["response"]; + + $this->assertArrayHasKey('total_pages', $response); + $this->assertArrayHasKey('current_page', $response); + $this->assertArrayHasKey('available_results', $response); + $this->assertArrayHasKey('total_results', $response); + $this->assertArrayHasKey('error', $response); + $this->assertArrayHasKey('warning', $response); + $this->assertArrayHasKey('query_time', $response); + $this->assertArrayHasKey('search_indexes', $response); + $this->assertArrayHasKey('suggestions', $response); + $this->assertArrayHasKey('results', $response); + $this->assertArrayHasKey('query', $response); + + + $this->assertTrue(is_int($response["total_pages"]), 'Le nombre de page est un int'); + $this->assertTrue(is_int($response["current_page"]), 'Le nombre de la page courrante est un int'); + $this->assertTrue(is_int($response["available_results"]), 'Le nombre de results dispo est un int'); + $this->assertTrue(is_int($response["total_results"]), 'Le nombre de results est un int'); + $this->assertTrue(is_string($response["error"]), 'Error est une string'); + $this->assertTrue(is_string($response["warning"]), 'Warning est une string'); + /** + * @todo null quand erreur + */ +// $this->assertTrue(is_string($response["query_time"])); + $this->assertTrue(is_string($response["search_indexes"])); + $this->assertTrue(is_array($response["suggestions"])); + $this->assertTrue(is_array($response["results"])); + $this->assertTrue(is_string($response["query"])); + + foreach ($response["results"] as $record) + { + $this->evaluateGoodRecord($record); + } + } + + public function testRecordsCaptionRoute() + { + foreach (static::$databoxe_ids as $databox_id) + { + $databox = databox::get_instance($databox_id); + + $collection = array_shift($databox->get_collections()); + $system_file = new system_file(__DIR__ . '/../../../testfiles/cestlafete.jpg'); + + $record = record_adapter::create($collection, $system_file); + + $record_id = $record->get_record_id(); + + $route = '/records/' . $databox_id . '/' . $record_id . '/caption/?oauth_token=' . self::$token; + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + + $crawler = $this->client->request('GET', $route , array(), array(), array("HTTP_ACCEPT" => "application/yaml")); + $content = self::$yaml->parse($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaYaml200($content); + + $this->evaluateRecordsCaptionResponse($content); + $record->delete(); + } + $route = '/records/24892534/51654651553/metadatas/?oauth_token=' . self::$token; + $this->evaluateNotFoundRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + $route = '/records/any_bad_id/sfsd5qfsd5/metadatas/?oauth_token=' . self::$token; + $this->evaluateBadRequestRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + } + + public function testRecordsMetadatasRoute() + { + foreach (static::$databoxe_ids as $databox_id) + { + $databox = databox::get_instance($databox_id); + + $collection = array_shift($databox->get_collections()); + $system_file = new system_file(__DIR__ . '/../../../testfiles/cestlafete.jpg'); + + $record = record_adapter::create($collection, $system_file); + + $record_id = $record->get_record_id(); + + $route = '/records/' . $databox_id . '/' . $record_id . '/metadatas/?oauth_token=' . self::$token; + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + + $crawler = $this->client->request('GET', $route, array(), array(), array("HTTP_ACCEPT" => "application/yaml")); + $content = self::$yaml->parse($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaYaml200($content); + + $this->evaluateRecordsMetadataResponse($content); + $record->delete(); + } + $route = '/records/24892534/51654651553/metadatas/?oauth_token=' . self::$token; + $this->evaluateNotFoundRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + $route = '/records/any_bad_id/sfsd5qfsd5/metadatas/?oauth_token=' . self::$token; + $this->evaluateBadRequestRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + } + + public function testRecordsStatusRoute() + { + foreach (static::$databoxe_ids as $databox_id) + { + $databox = databox::get_instance($databox_id); + $collection = array_shift($databox->get_collections()); + $system_file = new system_file(__DIR__ . '/../../../testfiles/cestlafete.jpg'); + + $record = record_adapter::create($collection, $system_file); + + $record_id = $record->get_record_id(); + + $route = '/records/' . $databox_id . '/' . $record_id . '/status/?oauth_token=' . self::$token; + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + + $crawler = $this->client->request('GET', $route, array(), array(), array("HTTP_ACCEPT" => "application/yaml")); + $content = self::$yaml->parse($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaYaml200($content); + + $this->evaluateRecordsStatusResponse($record, $content); + $record->delete(); + } + $route = '/records/24892534/51654651553/status/?oauth_token=' . self::$token; + $this->evaluateNotFoundRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + $route = '/records/any_bad_id/sfsd5qfsd5/status/?oauth_token=' . self::$token; + $this->evaluateBadRequestRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + } + + public function testRecordsEmbedRoute() + { + foreach (static::$databoxe_ids as $databox_id) + { + $databox = databox::get_instance($databox_id); + $collection = array_shift($databox->get_collections()); + $system_file = new system_file(__DIR__ . '/../../../testfiles/cestlafete.jpg'); + + $record = record_adapter::create($collection, $system_file); + + $keys = array_keys($record->get_subdefs()); + + $record_id = $record->get_record_id(); + + $route = '/records/' . $databox_id . '/' . $record_id . '/embed/?oauth_token=' . self::$token; + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + + $crawler = $this->client->request('GET', $route, array(), array(), array("HTTP_ACCEPT" => "application/yaml")); + $content = self::$yaml->parse($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaYaml200($content); + + foreach ($content["response"] as $embed) + { + foreach ($keys as $key) + { + $this->assertArrayHasKey($key, $embed); + $this->checkEmbed($key, $embed[$key], $record); + } + } + $record->delete(); + } + $route = '/records/24892534/51654651553/embed/?oauth_token=' . self::$token; + $this->evaluateNotFoundRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + $route = '/records/any_bad_id/sfsd5qfsd5/embed/?oauth_token=' . self::$token; + $this->evaluateBadRequestRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + } + + protected function assertPermalinkHeaders($url, media_subdef $subdef, $type_url = "page_url") + { + $headers = http_query::getHttpHeaders($url); + $this->assertEquals(200, $headers["http_code"]); + + switch ($type_url) + { + case "page_url" : + $this->assertTrue(strpos($headers['content_type'], "text/html") === 0); + $this->assertNotEquals($subdef->get_size(), $headers["download_content_length"]); + break; + case "url" : + $this->assertTrue(strpos($headers['content_type'], $subdef->get_mime()) === 0, 'Verify that header ' . $headers['content_type'] . ' contains subdef mime type ' . $subdef->get_mime()); + $this->assertEquals($subdef->get_size(), $headers["download_content_length"]); + break; + } + } + + protected function checkEmbed($subdef_name, $embed, record_adapter $record) + { + $this->assertArrayHasKey("permalink", $embed); + $this->checkPermalink($embed["permalink"], $record->get_subdef($subdef_name)); + $this->assertArrayHasKey("height", $embed); + $this->assertEquals($embed["height"], $record->get_subdef($subdef_name)->get_height()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_INT, $embed["height"]); + $this->assertArrayHasKey("width", $embed); + $this->assertEquals($embed["width"], $record->get_subdef($subdef_name)->get_width()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_INT, $embed["width"]); + $this->assertArrayHasKey("filesize", $embed); + $this->assertEquals($embed["filesize"], $record->get_subdef($subdef_name)->get_size()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_INT, $embed["filesize"]); + $this->assertArrayHasKey("player_type", $embed); + $this->assertEquals($embed["player_type"], $record->get_subdef($subdef_name)->get_type()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $embed["player_type"]); + $this->assertArrayHasKey("mime_type", $embed); + $this->assertEquals($embed["mime_type"], $record->get_subdef($subdef_name)->get_mime()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $embed["mime_type"]); + } + + protected function checkPermalink($permalink, media_subdef $subdef) + { + if ($subdef->is_physically_present()) + { + $this->assertNotNull($subdef->get_permalink()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $permalink); + $this->assertArrayHasKey("created_on", $permalink); + $this->assertEquals($subdef->get_permalink()->get_created_on()->format(DATE_ATOM), $permalink["created_on"]); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $permalink["created_on"]); + $this->assertDateAtom($permalink["created_on"]); + $this->assertArrayHasKey("id", $permalink); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_INT, $permalink["id"]); + $this->assertEquals($subdef->get_permalink()->get_id(), $permalink["id"]); + $this->assertArrayHasKey("is_activated", $permalink); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_BOOL, $permalink["is_activated"]); + $this->assertEquals($subdef->get_permalink()->get_is_activated(), $permalink["is_activated"]); + $this->assertArrayHasKey("label", $permalink); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $permalink["label"]); + $this->assertEquals($subdef->get_permalink()->get_label(), $permalink["label"]); + $this->assertArrayHasKey("last_modified", $permalink); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $permalink["last_modified"]); + $this->assertEquals($subdef->get_permalink()->get_last_modified()->format(DATE_ATOM), $permalink["last_modified"]); + $this->assertDateAtom($permalink["last_modified"]); + $this->assertArrayHasKey("page_url", $permalink); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $permalink["page_url"]); + $this->assertEquals($subdef->get_permalink()->get_page(registry::get_instance()), $permalink["page_url"]); + $this->checkUrlCode200($permalink["page_url"]); + $this->assertPermalinkHeaders($permalink["page_url"], $subdef); + $this->assertArrayHasKey("url", $permalink); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $permalink["url"]); + $this->assertEquals($subdef->get_permalink()->get_url(), $permalink["url"]); + $this->checkUrlCode200($permalink["url"]); + $this->assertPermalinkHeaders($permalink["url"], $subdef, "url"); + } + } + + protected function checkUrlCode200($url) + { + $code = http_query::getHttpCodeFromUrl($url); + $this->assertEquals(200, $code); + } + + public function testRecordsRelatedRoute() + { + foreach (static::$databoxe_ids as $databox_id) + { + $databox = databox::get_instance($databox_id); + $collection = array_shift($databox->get_collections()); + $system_file = new system_file(__DIR__ . '/../../../testfiles/cestlafete.jpg'); + + $record = record_adapter::create($collection, $system_file); + + $record_id = $record->get_record_id(); + + $route = '/records/' . $databox_id . '/' . $record_id . '/related/?oauth_token=' . self::$token; + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + + $crawler = $this->client->request('GET', $route, array(), array(), array("HTTP_ACCEPT" => "application/yaml")); + $content = self::$yaml->parse($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaYaml200($content); + $this->assertArrayHasKey("baskets", $content["response"]); + foreach ($content["response"]["baskets"] as $basket) + { + $this->evaluateGoodBasket($basket); + } + $record->delete(); + } + $route = '/records/24892534/51654651553/related/?oauth_token=' . self::$token; + $this->evaluateNotFoundRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + $route = '/records/any_bad_id/sfsd5qfsd5/related/?oauth_token=' . self::$token; + $this->evaluateBadRequestRoute($route, array('GET')); + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + } + + public function testRecordsSetMetadatas() + { + + foreach (static::$databoxe_ids as $databox_id) + { + $databox = databox::get_instance($databox_id); + $collection = array_shift($databox->get_collections()); + $system_file = new system_file(__DIR__ . '/../../../testfiles/cestlafete.jpg'); + + $record = record_adapter::create($collection, $system_file); + + $record_id = $record->get_record_id(); + + $route = '/records/' . $databox_id . '/' . $record_id . '/setmetadatas/?oauth_token=' . self::$token; + $caption = $record->get_caption(); + + + $old_datas = array(); + $toupdate = array(); + + /** + * @todo enhance the test, if there's no field, there's no update + */ + foreach ($caption->get_fields() as $field) + { + foreach($field->get_values() as $value) + { + $old_datas[$value->getId()] = $value->getValue(); + if ($field->is_readonly() === false && $field->is_multi() === false) + { + $toupdate[$value->getId()] = array( + 'meta_struct_id' => $field->get_meta_struct_id(), + 'meta_id' => $value->getId(), + 'value' => array($value->getValue() . ' test') + ); + } + } + } + + + foreach ($record->get_databox()->get_meta_structure()->get_elements() as $field) + { + try + { + $values = $record->get_caption()->get_field($field->get_name())->get_values(); + $value = array_pop($values); + $meta_id = $value->getId(); + } + catch (\Exception $e) + { + $meta_id = null; + } + + $toupdate[$field->get_id()] = array( + 'meta_id' => $meta_id + , 'meta_struct_id' => $field->get_id() + , 'value' => 'podom pom pom ' . $field->get_id() + ); + } + + $this->evaluateMethodNotAllowedRoute($route, array('GET', 'PUT', 'DELETE')); + + $crawler = $this->client->request('POST', $route, array('metadatas' => $toupdate), array(), array("HTTP_ACCEPT" => "application/yaml")); + $content = self::$yaml->parse($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaYaml200($content); + + $record = $databox->get_record($record_id); + $caption = $record->get_caption(); + + $this->assertEquals(count($caption->get_fields()), count($content["response"]["metadatas"])); + + foreach ($caption->get_fields() as $field) + { + foreach($field->get_values() as $value) + { + if ($field->is_readonly() === false && $field->is_multi() === false) + { + $saved_value = $toupdate[$field->get_meta_struct_id()]['value']; + $this->assertEquals($value->getValue(), $saved_value); + } + } + } + $this->evaluateRecordsMetadataResponse($content); + + foreach ($content["response"]["metadatas"] as $metadata) + { + if (!in_array($metadata["meta_id"], array_keys($toupdate))) + continue; + $saved_value = $toupdate[$metadata["meta_id"]]['value']; + $this->assertEquals($saved_value, $metadata["value"]); + } + $record->delete(); + } + } + + public function testRecordsSetStatus() + { + + foreach (static::$databoxe_ids as $databox_id) + { + $databox = databox::get_instance($databox_id); + $collection = array_shift($databox->get_collections()); + $system_file = new system_file(__DIR__ . '/../../../testfiles/cestlafete.jpg'); + + $record = record_adapter::create($collection, $system_file); + + $record_id = $record->get_record_id(); + + $route = '/records/' . $databox_id . '/' . $record_id . '/setstatus/?oauth_token=' . self::$token; + + $record_status = strrev($record->get_status()); + $status_bits = $databox->get_statusbits(); + + $tochange = array(); + foreach ($status_bits as $n => $datas) + { + $tochange[$n] = substr($record_status, ($n - 1), 1) == '0' ? '1' : '0'; + } + $this->evaluateMethodNotAllowedRoute($route, array('GET', 'PUT', 'DELETE')); + + + $crawler = $this->client->request('POST', $route, array('status' => $tochange), array(), array("HTTP_ACCEPT" => "application/yaml")); + $content = self::$yaml->parse($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaYaml200($content); + + $record = $databox->get_record($record_id); + $this->evaluateRecordsStatusResponse($record, $content); + + $record_status = strrev($record->get_status()); + foreach ($status_bits as $n => $datas) + { + $this->assertEquals(substr($record_status, ($n - 1), 1), $tochange[$n]); + } + + foreach ($tochange as $n => $value) + { + $tochange[$n] = $value == '0' ? '1' : '0'; + } + + + $crawler = $this->client->request('POST', $route, array('status' => $tochange), array(), array("HTTP_ACCEPT" => "application/yaml")); + $content = self::$yaml->parse($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaYaml200($content); + + $record = $databox->get_record($record_id); + $this->evaluateRecordsStatusResponse($record, $content); + + $record_status = strrev($record->get_status()); + foreach ($status_bits as $n => $datas) + { + $this->assertEquals(substr($record_status, ($n - 1), 1), $tochange[$n]); + } + $record->delete(); + } + } + + public function testMoveRecordToColleciton() + { + foreach (static::$databoxe_ids as $databox_id) + { + $databox = databox::get_instance($databox_id); + $collection = array_shift($databox->get_collections()); + $system_file = new system_file(__DIR__ . '/../../../testfiles/cestlafete.jpg'); + + $record = record_adapter::create($collection, $system_file); + + $record_id = $record->get_record_id(); + + $route = '/records/' . $databox_id . '/' . $record_id . '/setcollection/?oauth_token=' . self::$token; + + $base_id = false; + foreach ($databox->get_collections() as $collection) + { + if ($collection->get_base_id() != $record->get_base_id()) + { + $base_id = $collection->get_base_id(); + break; + } + } + if (!$base_id) + { + continue; + } + + $this->evaluateMethodNotAllowedRoute($route, array('GET', 'PUT', 'DELETE')); + + $crawler = $this->client->request('POST', $route, array('base_id' => $base_id), array(), array("HTTP_ACCEPT" => "application/yaml")); + + $content = self::$yaml->parse($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaYaml200($content); + $record->delete(); + } + } + + public function testSearchBaskets() + { + $route = '/baskets/list/?oauth_token=' . self::$token; + + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + + $crawler = $this->client->request('GET', $route, array(), array(), array("HTTP_ACCEPT" => "application/yaml")); + $content = self::$yaml->parse($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaYaml200($content); + $this->assertArrayHasKey("baskets", $content["response"]); + + foreach ($content["response"]["baskets"] as $basket) + { + $this->evaluateGoodBasket($basket); + } + } + + public function testAddBasket() + { + $route = '/baskets/add/?oauth_token=' . self::$token; + + $this->evaluateMethodNotAllowedRoute($route, array('GET', 'PUT', 'DELETE')); + + $crawler = $this->client->request('POST', $route, array('name' => 'un Joli Nom'), array(), array("HTTP_ACCEPT" => "application/yaml")); + $content = self::$yaml->parse($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaYaml200($content); + + $this->assertEquals(1, count((array) $content["response"])); + $appbox = appbox::get_instance(\bootstrap::getCore()); + $this->assertArrayHasKey("basket", $content["response"]); + + foreach ($content["response"]["basket"] as $basket) + { + $this->evaluateGoodBasket($basket); + $this->assertEquals('un Joli Nom', $basket["name"]); + } + } + + public function testBasketContent() + { + $basket = $this->insertOneBasket(); + + $route = '/baskets/' . $basket->getId() . '/content/?oauth_token=' . self::$token; + + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + + $crawler = $this->client->request('GET', $route, array(), array(), array("HTTP_ACCEPT" => "application/yaml")); + $content = self::$yaml->parse($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaYaml200($content); + + $this->assertEquals(1, count((array) $content["response"])); + + $this->assertArrayHasKey("basket_elements", $content["response"]); + + foreach ($content["response"]["basket_elements"] as $basket_str) + { + $this->evaluateGoodBasket($basket_str); + + $this->assertEquals(count($basket->getElements()), count((array) $basket_str["basket_elements"])); + foreach ($basket_str["basket_elements"] as $basket_element) + { + $this->assertArrayHasKey('basket_element_id', $basket_element); + $this->assertArrayHasKey('order', $basket_element); + $this->assertArrayHasKey('record', $basket_element); + $this->assertArrayHasKey('validation_item', $basket_element); + $this->assertTrue(is_bool($basket_element["validation_item"])); + $this->assertTrue(is_int($basket_element["order"])); + $this->assertTrue(is_int($basket_element["basket_element_id"])); + $this->evaluateGoodRecord($basket_element["record"]); + } + } + } + + public function testSetBasketTitle() + { + + $basket = $this->insertOneBasket(); + + $route = '/baskets/' . $basket->getId() . '/setname/?oauth_token=' . self::$token; + + $this->evaluateMethodNotAllowedRoute($route, array('GET', 'PUT', 'DELETE')); + + $crawler = $this->client->request('POST', $route, array('name' => 'un Joli Nom'), array(), array("HTTP_ACCEPT" => "application/yaml")); + $content = self::$yaml->parse($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaYaml200($content); + + $this->assertEquals(1, count((array) $content["response"])); + $this->assertArrayHasKey("basket", $content["response"]); + foreach ($content["response"]["basket"] as $basket_str) + { + $this->evaluateGoodBasket($basket_str); + + $this->assertEquals($basket_str["name"], 'un Joli Nom'); + } + + $crawler = $this->client->request('POST', $route, array('name' => 'un Joli Nom'), array(), array("HTTP_ACCEPT" => "application/yaml")); + $content = self::$yaml->parse($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaYaml200($content); + + $this->assertEquals(1, count((array) $content["response"])); + + $this->assertArrayHasKey("basket", $content["response"]); + + foreach ($content["response"]["basket"] as $basket) + { + $this->evaluateGoodBasket($basket_str); + + $this->assertEquals($basket_str["name"], 'un Joli Nom'); + } + + $crawler = $this->client->request('POST', $route, array('name' => 'aéaa'), array(), array("HTTP_ACCEPT" => "application/yaml")); + $content = self::$yaml->parse($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaYaml200($content); + + $this->assertEquals(1, count((array) $content["response"])); + $this->assertArrayHasKey("basket", $content["response"]); + foreach ($content["response"]["basket"] as $basket_str) + { + $this->evaluateGoodBasket($basket_str); + $this->assertEquals($basket_str["name"], 'aéaa'); + } + } + + public function testSetBasketDescription() + { + $basket = $this->insertOneBasket(); + + $route = '/baskets/' . $basket->getId() . '/setdescription/?oauth_token=' . self::$token; + + $this->evaluateMethodNotAllowedRoute($route, array('GET', 'PUT', 'DELETE')); + + $crawler = $this->client->request('POST', $route, array('description' => 'une belle desc'), array(), array("HTTP_ACCEPT" => "application/yaml")); + $content = self::$yaml->parse($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaYaml200($content); + + $this->assertEquals(1, count((array) $content["response"])); + + $this->assertArrayHasKey("basket", $content["response"]); + foreach ($content["response"]["basket"] as $basket_str) + { + $this->evaluateGoodBasket($basket_str); + + $this->assertEquals($basket_str["description"], 'une belle desc'); + } + } + + public function testDeleteBasket() + { + $baskets = $this->insertFiveBasket(); + + $route = '/baskets/' . $baskets[0]->getId() . '/delete/?oauth_token=' . self::$token; + + $this->evaluateMethodNotAllowedRoute($route, array('GET', 'PUT', 'DELETE')); + + $crawler = $this->client->request('POST', $route, array(), array(), array("HTTP_ACCEPT" => "application/yaml")); + $content = self::$yaml->parse($this->client->getResponse()->getContent()); + + $this->evaluateResponse200($this->client->getResponse()); + $this->evaluateMetaYaml200($content); + + $this->assertArrayHasKey("baskets", $content["response"]); + + $found = false; + foreach ($content["response"]["baskets"] as $basket) + { + $this->evaluateGoodBasket($basket); + $found = true; + } + if (!$found) + { + $this->fail('There should be four baskets left'); + } + } + + /** + * + * End /API/V1/records/DATABOX_ID/RECORD_ID/xxxxx Routes + * + */ + protected function assertDateAtom($date) + { + return $this->assertRegExp('/\d{4}[-]\d{2}[-]\d{2}[T]\d{2}[:]\d{2}[:]\d{2}[+]\d{2}[:]\d{2}/', $date); + } + + protected function evaluateNotFoundRoute($route, $methods) + { + foreach ($methods as $method) + { + $crawler = $this->client->request($method, $route, array(), array(), array("HTTP_ACCEPT" => "application/yaml")); + $content = self::$yaml->parse($this->client->getResponse()->getContent()); + + $this->evaluateResponseNotFound($this->client->getResponse()); + $this->evaluateMetaYamlNotFound($content); + } + } + + protected function evaluateMethodNotAllowedRoute($route, $methods) + { + foreach ($methods as $method) + { + $crawler = $this->client->request($method, $route, array(), array(), array("HTTP_ACCEPT" => "application/yaml")); + $content = self::$yaml->parse($this->client->getResponse()->getContent()); + $this->evaluateResponseMethodNotAllowed($this->client->getResponse()); + $this->evaluateMetaYamlMethodNotAllowed($content); + } + } + + protected function evaluateBadRequestRoute($route, $methods) + { + foreach ($methods as $method) + { + $crawler = $this->client->request($method, $route, array(), array(), array("HTTP_ACCEPT" => "application/yaml")); + $content = self::$yaml->parse($this->client->getResponse()->getContent()); + $this->evaluateResponseBadRequest($this->client->getResponse()); + $this->evaluateMetaYamlBadRequest($content); + } + } + + protected function evaluateMetaYaml400($content) + { + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $content, 'La response est un array'); + $this->assertArrayHasKey('meta', $content); + $this->assertArrayHasKey('response', $content); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $content["meta"], 'La response est un array'); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $content["response"], 'La response est un objet'); + $this->assertEquals('1.1', $content["meta"]["api_version"]); + $this->assertNotNull($content["meta"]["response_time"]); + $this->assertEquals('UTF-8', $content["meta"]["charset"]); + } + + protected function evaluateMetaYaml($content) + { + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $content, 'La response est un array'); + $this->assertArrayHasKey('meta', $content); + $this->assertArrayHasKey('response', $content); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $content["meta"], 'La response est un array'); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $content["response"], 'La response est un array'); + $this->assertEquals('1.1', $content["meta"]["api_version"]); + $this->assertNotNull($content["meta"]["response_time"]); + $this->assertEquals('UTF-8', $content["meta"]["charset"]); + } + + protected function evaluateMetaYaml200($content) + { + $this->evaluateMetaYaml($content); + $this->assertEquals(200, $content["meta"]["http_code"]); + $this->assertNull($content["meta"]["error_message"]); + $this->assertNull($content["meta"]["error_details"]); + } + + protected function evaluateMetaYamlBadRequest($content) + { + $this->evaluateMetaYaml($content); + $this->evaluateMetaYaml400($content); + $this->assertNotNull($content["meta"]["error_message"]); + $this->assertNotNull($content["meta"]["error_details"]); + $this->assertEquals(400, $content["meta"]["http_code"]); + } + + protected function evaluateMetaYamlNotFound($content) + { + $this->evaluateMetaYaml($content); + $this->evaluateMetaYaml400($content); + $this->assertNotNull($content["meta"]["error_message"]); + $this->assertNotNull($content["meta"]["error_details"]); + $this->assertEquals(404, $content["meta"]["http_code"]); + } + + protected function evaluateMetaYamlMethodNotAllowed($content) + { + $this->evaluateMetaYaml400($content); + $this->assertNotNull($content["meta"]["error_message"]); + $this->assertNotNull($content["meta"]["error_details"]); + $this->assertEquals(405, $content["meta"]["http_code"]); + } + + protected function evaluateResponse200(Response $response) + { + $this->assertEquals('UTF-8', $response->getCharset(), 'Test charset response'); + $this->assertEquals(200, $response->getStatusCode(), 'Test status code 200 ' . $response->getContent()); + } + + protected function evaluateResponseBadRequest(Response $response) + { + $this->assertEquals('UTF-8', $response->getCharset(), 'Test charset response'); + $this->assertEquals(400, $response->getStatusCode(), 'Test status code 400 ' . $response->getContent()); + } + + protected function evaluateResponseNotFound(Response $response) + { + $this->assertEquals('UTF-8', $response->getCharset(), 'Test charset response'); + $this->assertEquals(404, $response->getStatusCode(), 'Test status code 404 ' . $response->getContent()); + } + + protected function evaluateResponseMethodNotAllowed(Response $response) + { + $this->assertEquals('UTF-8', $response->getCharset(), 'Test charset response'); + $this->assertEquals(405, $response->getStatusCode(), 'Test status code 405 ' . $response->getContent()); + } + + protected function evaluateGoodBasket($basket) + { + $this->assertTrue(is_array($basket)); + $this->assertArrayHasKey('created_on', $basket); + $this->assertArrayHasKey('description', $basket); + $this->assertArrayHasKey('name', $basket); + $this->assertArrayHasKey('pusher_usr_id', $basket); + $this->assertArrayHasKey('ssel_id', $basket); + $this->assertArrayHasKey('updated_on', $basket); + $this->assertArrayHasKey('unread', $basket); + + + if (!is_null($basket["pusher_usr_id"])) + { + $this->assertTrue(is_int($basket["pusher_usr_id"])); + } + $this->assertTrue(is_string($basket["name"])); + $this->assertTrue(is_string($basket["description"])); + $this->assertTrue(is_int($basket["ssel_id"])); + $this->assertTrue(is_bool($basket["unread"])); + $this->assertDateAtom($basket["created_on"]); + $this->assertDateAtom($basket["updated_on"]); + } + + protected function evaluateGoodRecord($record) + { + $this->assertArrayHasKey('databox_id', $record); + $this->assertTrue(is_int($record["databox_id"])); + $this->assertArrayHasKey('record_id', $record); + $this->assertTrue(is_int($record["record_id"])); + $this->assertArrayHasKey('mime_type', $record); + $this->assertTrue(is_string($record["mime_type"])); + $this->assertArrayHasKey('title', $record); + $this->assertTrue(is_string($record["title"])); + $this->assertArrayHasKey('original_name', $record); + $this->assertTrue(is_string($record["original_name"])); + $this->assertArrayHasKey('last_modification', $record); + $this->assertDateAtom($record["last_modification"]); + $this->assertArrayHasKey('created_on', $record); + $this->assertDateAtom($record["created_on"]); + $this->assertArrayHasKey('collection_id', $record); + $this->assertTrue(is_int($record["collection_id"])); + $this->assertArrayHasKey('thumbnail', $record); + $this->assertArrayHasKey('sha256', $record); + $this->assertTrue(is_string($record["sha256"])); + $this->assertArrayHasKey('technical_informations', $record); + $this->assertArrayHasKey('phrasea_type', $record); + $this->assertTrue(is_string($record["phrasea_type"])); + $this->assertTrue(in_array($record["phrasea_type"], array('audio', 'document', 'image', 'video', 'flash', 'unknown'))); + $this->assertArrayHasKey('uuid', $record); + $this->assertTrue(uuid::is_valid($record["uuid"])); + + $this->assertTrue(is_array($record["thumbnail"])); + $this->assertArrayHasKey('permalink', $record["thumbnail"]); + $this->assertArrayHasKey('player_type', $record["thumbnail"]); + $this->assertTrue(is_string($record["thumbnail"]["player_type"])); + $this->assertArrayHasKey('mime_type', $record["thumbnail"]); + $this->assertTrue(is_string($record["thumbnail"]["mime_type"])); + $this->assertArrayHasKey('height', $record["thumbnail"]); + $this->assertTrue(is_int($record["thumbnail"]["height"])); + $this->assertArrayHasKey('width', $record["thumbnail"]); + $this->assertTrue(is_int($record["thumbnail"]["width"])); + $this->assertArrayHasKey('filesize', $record["thumbnail"]); + $this->assertTrue(is_int($record["thumbnail"]["filesize"])); + + /* + * + * @todo + */ + + $this->assertTrue(is_array($record["technical_informations"])); + + foreach ($record["technical_informations"] as $key => $value) + { + switch ($key) + { + case system_file::TC_DATAS_DURATION: + case 'size': + case system_file::TC_DATAS_WIDTH: + case system_file::TC_DATAS_HEIGHT: + case system_file::TC_DATAS_COLORDEPTH: + $this->assertTrue(is_int($value), 'test technical data ' . $key . ' ' . $value); + break; + default; + $this->assertTrue(is_string($value), 'test technical data ' . $key); + break; + } + } + } + + protected function evaluateRecordsCaptionResponse($content) + { + foreach ($content["response"] as $field) + { + $this->assertTrue(is_array($field), 'Un bloc field est un objet'); + $this->assertArrayHasKey('meta_structure_id', $meta); + $this->assertTrue(is_int($field["meta_structure_id"])); + $this->assertArrayHasKey('name', $field); + $this->assertTrue(is_string($meta["name"])); + $this->assertArrayHasKey('value', $field); + $this->assertTrue(is_string($meta["value"])); + } + } + + protected function evaluateRecordsMetadataResponse($content) + { + $this->assertArrayHasKey("metadatas", $content["response"]); + foreach ($content["response"]["metadatas"] as $meta) + { + $this->assertTrue(is_array($meta), 'Un bloc meta est un array'); + $this->assertArrayHasKey('meta_id', $meta); + $this->assertTrue(is_int($meta["meta_id"])); + $this->assertArrayHasKey('meta_structure_id', $meta); + $this->assertTrue(is_int($meta["meta_structure_id"])); + $this->assertArrayHasKey('name', $meta); + $this->assertTrue(is_string($meta["name"])); + $this->assertArrayHasKey('value', $meta); + + if (is_array($meta["value"])) + { + foreach ($meta["value"] as $val) + { + $this->assertTrue(is_string($val)); + } + } + else + { + $this->assertTrue(is_string($meta["value"])); + } + } + } + + protected function evaluateRecordsStatusResponse(record_adapter $record, $content) + { + $status = $record->get_databox()->get_statusbits(); + + + $r_status = strrev($record->get_status()); + $this->assertArrayHasKey('status', $content["response"]); + $this->assertEquals(count((array) $content["response"]["status"]), count($status)); + foreach ($content["response"]["status"] as $status) + { + $this->assertTrue(is_array($status)); + $this->assertArrayHasKey('bit', $status); + $this->assertArrayHasKey('state', $status); + $this->assertTrue(is_int($status["bit"])); + $this->assertTrue(is_bool($status["state"])); + + $retrieved = !!substr($r_status, ($status["bit"] - 1), 1); + + $this->assertEquals($retrieved, $status["state"]); + } + } + +} diff --git a/tests/Alchemy/Phrasea/Application/LightboxTest.php b/tests/Alchemy/Phrasea/Application/LightboxTest.php new file mode 100644 index 0000000000..ab8fb72bb8 --- /dev/null +++ b/tests/Alchemy/Phrasea/Application/LightboxTest.php @@ -0,0 +1,327 @@ +client = $this->createClient(); + $appbox = appbox::get_instance(\bootstrap::getCore()); + $this->feed = Feed_Adapter::create($appbox, self::$user, "salut", 'coucou'); + $publisher = array_shift($this->feed->get_publishers()); + $this->entry = Feed_Entry_Adapter::create($appbox, $this->feed, $publisher, 'title', "sub Titkle", " jean pierre", "jp@test.com"); + $this->item = Feed_Entry_Item::create($appbox, $this->entry, self::$record_1); + } + + public function tearDown() + { + if($this->feed instanceof Feed_Adapter) + $this->feed->delete(); + parent::tearDown(); + } + + public function createApplication() + { + return require __DIR__ . '/../../../../lib/Alchemy/Phrasea/Application/Lightbox.php'; + } + + public function testRouteSlash() + { + $baskets = $this->insertFiveBasket(); + + $this->set_user_agent(self::USER_AGENT_FIREFOX8MAC); + + $crawler = $this->client->request('GET', '/'); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + $this->assertEquals('UTF-8', $this->client->getResponse()->getCharset()); + $this->assertEquals(5, $crawler->filter('div.basket_wrapper')->count()); + + $this->set_user_agent(self::USER_AGENT_IE6); + + $crawler = $this->client->request('GET', '/'); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + $this->assertEquals('UTF-8', $this->client->getResponse()->getCharset()); + $this->assertEquals($crawler->filter('div.basket_wrapper')->count(), count($baskets)); + + $this->set_user_agent(self::USER_AGENT_IPHONE); + + $crawler = $this->client->request('GET', '/'); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + $this->assertEquals('UTF-8', $this->client->getResponse()->getCharset()); + } + + public function testAjaxNoteForm() + { + $basket = $this->insertOneValidationBasket(); + $basket_element = $basket->getELements()->first(); + + $this->set_user_agent(self::USER_AGENT_FIREFOX8MAC); + + $crawler = $this->client->request('GET', '/ajax/NOTE_FORM/' . $basket_element->getId() . '/'); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + $this->assertEquals('', trim($this->client->getResponse()->getContent())); + + $this->set_user_agent(self::USER_AGENT_IE6); + + $crawler = $this->client->request('GET', '/ajax/NOTE_FORM/' . $basket_element->getId() . '/'); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + $this->assertEquals('', trim($this->client->getResponse()->getContent())); + + $this->set_user_agent(self::USER_AGENT_IPHONE); + + $crawler = $this->client->request('GET', '/ajax/NOTE_FORM/' . $basket_element->getId() . '/'); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + $this->assertNotEquals('', trim($this->client->getResponse()->getContent())); + } + + public function testAjaxElement() + { + $basket_element = $this->insertOneBasketElement(); + + $this->set_user_agent(self::USER_AGENT_FIREFOX8MAC); + + $crawler = $this->client->request('GET', '/ajax/LOAD_BASKET_ELEMENT/' . $basket_element->getId() . '/'); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-type')); + $datas = json_decode($this->client->getResponse()->getContent()); + + $this->assertObjectHasAttribute('number', $datas); + $this->assertObjectHasAttribute('title', $datas); + $this->assertObjectHasAttribute('preview', $datas); + $this->assertObjectHasAttribute('options_html', $datas); + $this->assertObjectHasAttribute('agreement_html', $datas); + $this->assertObjectHasAttribute('selector_html', $datas); + $this->assertObjectHasAttribute('note_html', $datas); + $this->assertObjectHasAttribute('caption', $datas); + + $this->set_user_agent(self::USER_AGENT_IE6); + + $crawler = $this->client->request('GET', '/ajax/LOAD_BASKET_ELEMENT/' . $basket_element->getId() . '/'); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-type')); + $datas = json_decode($this->client->getResponse()->getContent()); + + $this->assertObjectHasAttribute('number', $datas); + $this->assertObjectHasAttribute('title', $datas); + $this->assertObjectHasAttribute('preview', $datas); + $this->assertObjectHasAttribute('options_html', $datas); + $this->assertObjectHasAttribute('agreement_html', $datas); + $this->assertObjectHasAttribute('selector_html', $datas); + $this->assertObjectHasAttribute('note_html', $datas); + $this->assertObjectHasAttribute('caption', $datas); + + $this->set_user_agent(self::USER_AGENT_IPHONE); + + $crawler = $this->client->request('GET', '/ajax/LOAD_BASKET_ELEMENT/' . $basket_element->getId() . '/'); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + $this->assertNotEquals('application/json', $this->client->getResponse()->headers->get('Content-type')); + } + + public function testAjaxFeedItem() + { + $this->set_user_agent(self::USER_AGENT_FIREFOX8MAC); + + $crawler = $this->client->request('GET', '/ajax/LOAD_FEED_ITEM/' . $this->entry->get_id() . '/' . $this->item->get_id() . '/'); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-type')); + $datas = json_decode($this->client->getResponse()->getContent()); + + $this->assertObjectHasAttribute('number', $datas); + $this->assertObjectHasAttribute('title', $datas); + $this->assertObjectHasAttribute('preview', $datas); + $this->assertObjectHasAttribute('options_html', $datas); + $this->assertObjectHasAttribute('agreement_html', $datas); + $this->assertObjectHasAttribute('selector_html', $datas); + $this->assertObjectHasAttribute('note_html', $datas); + $this->assertObjectHasAttribute('caption', $datas); + + $this->set_user_agent(self::USER_AGENT_IE6); + + $crawler = $this->client->request('GET', '/ajax/LOAD_FEED_ITEM/' . $this->entry->get_id() . '/' . $this->item->get_id() . '/'); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-type')); + $datas = json_decode($this->client->getResponse()->getContent()); + + $this->assertObjectHasAttribute('number', $datas); + $this->assertObjectHasAttribute('title', $datas); + $this->assertObjectHasAttribute('preview', $datas); + $this->assertObjectHasAttribute('options_html', $datas); + $this->assertObjectHasAttribute('agreement_html', $datas); + $this->assertObjectHasAttribute('selector_html', $datas); + $this->assertObjectHasAttribute('note_html', $datas); + $this->assertObjectHasAttribute('caption', $datas); + + $this->set_user_agent(self::USER_AGENT_IPHONE); + + $crawler = $this->client->request('GET', '/ajax/LOAD_FEED_ITEM/' . $this->entry->get_id() . '/' . $this->item->get_id() . '/'); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + $this->assertNotEquals('application/json', $this->client->getResponse()->headers->get('Content-type')); + } + + public function testValidate() + { + + $basket = $this->insertOneValidationBasket(); + + $this->set_user_agent(self::USER_AGENT_FIREFOX8MAC); + + $crawler = $this->client->request('GET', '/validate/' . $basket->getId() . '/'); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + $this->assertEquals('UTF-8', $this->client->getResponse()->getCharset()); + + $this->set_user_agent(self::USER_AGENT_IE6); + + $crawler = $this->client->request('GET', '/validate/' . $basket->getId() . '/'); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + $this->assertEquals('UTF-8', $this->client->getResponse()->getCharset()); + + $this->set_user_agent(self::USER_AGENT_IPHONE); + + $crawler = $this->client->request('GET', '/validate/' . $basket->getId() . '/'); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + $this->assertEquals('UTF-8', $this->client->getResponse()->getCharset()); + } + + public function testCompare() + { + $basket = $this->insertOneBasket(); + + $this->set_user_agent(self::USER_AGENT_FIREFOX8MAC); + + $crawler = $this->client->request('GET', '/compare/' . $basket->getId() . '/'); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + $this->assertEquals('UTF-8', $this->client->getResponse()->getCharset()); + + $this->set_user_agent(self::USER_AGENT_IE6); + + $crawler = $this->client->request('GET', '/compare/' . $basket->getId() . '/'); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + $this->assertEquals('UTF-8', $this->client->getResponse()->getCharset()); + + $this->set_user_agent(self::USER_AGENT_IPHONE); + + $crawler = $this->client->request('GET', '/compare/' . $basket->getId() . '/'); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + $this->assertEquals('UTF-8', $this->client->getResponse()->getCharset()); + } + + public function testFeedEntry() + { + $this->set_user_agent(self::USER_AGENT_FIREFOX8MAC); + + $crawler = $this->client->request('GET', '/feeds/entry/' . $this->entry->get_id() . '/'); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + $this->assertEquals('UTF-8', $this->client->getResponse()->getCharset()); + + $this->set_user_agent(self::USER_AGENT_IE6); + + $crawler = $this->client->request('GET', '/feeds/entry/' . $this->entry->get_id() . '/'); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + $this->assertEquals('UTF-8', $this->client->getResponse()->getCharset()); + + $this->set_user_agent(self::USER_AGENT_IPHONE); + + $crawler = $this->client->request('GET', '/feeds/entry/' . $this->entry->get_id() . '/'); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + $this->assertEquals('UTF-8', $this->client->getResponse()->getCharset()); + } + + public function testAjaxReport() + { + $validationBasket = $this->insertOneValidationBasket(); + + $this->set_user_agent(self::USER_AGENT_FIREFOX8MAC); + $crawler = $this->client->request('GET', '/ajax/LOAD_REPORT/' . $validationBasket->getId() . '/'); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + $this->assertEquals('UTF-8', $this->client->getResponse()->getCharset()); + } + + public function testAjaxSetNote() + { + $validationBasket = $this->insertOneValidationBasket(); + $validationBasketElement = $validationBasket->getElements()->first(); + + $crawler = $this->client->request('POST', '/ajax/SET_NOTE/' . $validationBasketElement->getId() . '/'); + $this->assertEquals(400, $this->client->getResponse()->getStatusCode()); + + $crawler = $this->client->request( + 'POST' + , '/ajax/SET_NOTE/' . $validationBasketElement->getId() . '/' + , array('note' => 'une jolie note') + ); + + $this->assertEquals(200, $this->client->getResponse()->getStatusCode(), sprintf('set note to element %s ', $validationBasketElement->getId())); + $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-type')); + + $datas = json_decode($this->client->getResponse()->getContent()); + $this->assertTrue(is_object($datas), 'asserting good json datas'); + $this->assertObjectHasAttribute('datas', $datas); + $this->assertObjectHasAttribute('error', $datas); + } + + public function testAjaxSetAgreement() + { + $validationBasket = $this->insertOneValidationBasket(); + $validationBasketElement = $validationBasket->getElements()->first(); + + $crawler = $this->client->request( + 'POST' + , '/ajax/SET_ELEMENT_AGREEMENT/' . $validationBasketElement->getId() . '/' + ); + $this->assertEquals(400, $this->client->getResponse()->getStatusCode()); + + $crawler = $this->client->request( + 'POST' + , '/ajax/SET_ELEMENT_AGREEMENT/' . $validationBasketElement->getId() . '/' + , array('agreement' => 1) + ); + + $this->assertEquals(200, $this->client->getResponse()->getStatusCode(), sprintf('set note to element %s ', $validationBasketElement->getId())); + $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-type')); + + $datas = json_decode($this->client->getResponse()->getContent()); + $this->assertTrue(is_object($datas), 'asserting good json datas'); + $this->assertObjectHasAttribute('datas', $datas); + $this->assertObjectHasAttribute('error', $datas); + } + + public function testAjaxSetRelease() + { + $basket = $this->insertOneBasket(); + + $crawler = $this->client->request('POST', '/ajax/SET_RELEASE/' . $basket->getId() . '/'); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-type')); + $datas = json_decode($this->client->getResponse()->getContent()); + $this->assertTrue(is_object($datas), 'asserting good json datas'); + $this->assertTrue($datas->error); + + $validationBasket = $this->insertOneValidationBasket(); + + $crawler = $this->client->request('POST', '/ajax/SET_RELEASE/' . $validationBasket->getId() . '/'); + + $this->assertEquals(200, $this->client->getResponse()->getStatusCode(), sprintf('set note to element %s ', $validationBasket->getId())); + $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-type')); + + $datas = json_decode($this->client->getResponse()->getContent()); + $this->assertTrue(is_object($datas), 'asserting good json datas'); + $this->assertObjectHasAttribute('datas', $datas); + $this->assertObjectHasAttribute('error', $datas); + } + +} diff --git a/tests/Alchemy/Phrasea/Application/OAuth2Test.php b/tests/Alchemy/Phrasea/Application/OAuth2Test.php new file mode 100644 index 0000000000..7237482df8 --- /dev/null +++ b/tests/Alchemy/Phrasea/Application/OAuth2Test.php @@ -0,0 +1,161 @@ + 'web' + , 'name' => 'test' + , 'description' => 'une description' + , 'callback' => 'http://callback.com/callback/' + , 'website' => 'http://website.com/' + ); + + self::$appli = API_OAuth2_Application::create(appbox::get_instance(\bootstrap::getCore()), self::$user, 'test'); + self::$appli->set_description('une description') + ->set_redirect_uri('http://callback.com/callback/') + ->set_website('http://website.com/') + ->set_type(API_OAuth2_Application::WEB_TYPE); + + + } + + public static function tearDownAfterClass() + { + parent::tearDownAfterClass(); + if (self::$appli !== false) + self::deleteInsertedRow(appbox::get_instance(\bootstrap::getCore()), self::$appli); + } + + public function setUp() + { + parent::setUp(); + $this->client = $this->createClient(); + + $this->queryParameters = array( + "response_type" => "code", + "client_id" => self::$appli->get_client_id(), + "redirect_uri" => self::$appli->get_redirect_uri(), + "scope" => "", + "state" => "valueTest" + ); + } + + public static function deleteInsertedRow(appbox $appbox, API_OAuth2_Application $app) + { + $conn = $appbox->get_connection(); + $sql = ' + DELETE FROM api_applications + WHERE application_id = :id + '; + $t = array(':id' => $app->get_id()); + $stmt = $conn->prepare($sql); + $stmt->execute($t); + $sql = ' + DELETE FROM api_accounts + WHERE api_account_id = :id + '; + $acc = self::getAccount(); + $t = array(':id' => $acc->get_id()); + $stmt = $conn->prepare($sql); + $stmt->execute($t); + } + + public static function getApp($rowId) + { + $sql = "SELECT * FROM api_applications WHERE application_id = :app_id"; + $t = array(":app_id" => $rowId); + $appbox = appbox::get_instance(\bootstrap::getCore()); + $conn = $appbox->get_connection(); + $stmt = $conn->prepare($sql); + $stmt->execute($t); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + return $result; + } + + public static function getAccount() + { + $sql = "SELECT api_account_id FROM api_accounts WHERE application_id = :app_id AND usr_id = :usr_id"; + $t = array(":app_id" => self::$appli->get_id(), ":usr_id" => self::$user->get_id()); + $appbox = appbox::get_instance(\bootstrap::getCore()); + $conn = $appbox->get_connection(); + $stmt = $conn->prepare($sql); + $stmt->execute($t); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + + return new API_OAuth2_Account(appbox::get_instance(\bootstrap::getCore()), $row["api_account_id"]); + } + + public function setQueryParameters($parameter, $value) + { + $this->queryParameters[$parameter] = $value; + } + + public function unsetQueryParameter($parameter) + { + if (isset($this->queryParameters[$parameter])) + unset($this->queryParameters[$parameter]); + } + + public function createApplication() + { + return require __DIR__ . '/../../../../lib/Alchemy/Phrasea/Application/OAuth2.php'; + } + + public function testAuthorizeRedirect() + { + //session off + $apps = API_OAuth2_Application::load_authorized_app_by_user(appbox::get_instance(\bootstrap::getCore()), self::$user); + foreach($apps as $app) + { + if($app->get_client_id() == self::$appli->get_client_id()) + { + $authorize = true; + + $this->client->followRedirects(); + } + } + } + + public function testAuthorize() + { + $acc = self::getAccount(); + $acc->set_revoked(true); // revoked to show form + + $crawler = $this->client->request('GET', '/authorize', $this->queryParameters); + $this->assertTrue($this->client->getResponse()->isSuccessful()); + $this->assertRegExp("/" . self::$appli->get_client_id() . "/", $this->client->getResponse()->getContent()); + $this->assertRegExp("/" . str_replace("/", '\/', self::$appli->get_redirect_uri()) . "/", $this->client->getResponse()->getContent()); + $this->assertRegExp("/" . $this->queryParameters["response_type"] . "/", $this->client->getResponse()->getContent()); + $this->assertRegExp("/" . $this->queryParameters["scope"] . "/", $this->client->getResponse()->getContent()); + $this->assertRegExp("/" . $this->queryParameters["state"] . "/", $this->client->getResponse()->getContent()); + } +} diff --git a/tests/Alchemy/Phrasea/Application/OverviewTest.php b/tests/Alchemy/Phrasea/Application/OverviewTest.php new file mode 100644 index 0000000000..55ba9cc00b --- /dev/null +++ b/tests/Alchemy/Phrasea/Application/OverviewTest.php @@ -0,0 +1,131 @@ +client = $this->createClient(); + } + + public static function setUpBeforeClass() + { + parent::setUpBeforeClass(); + } + + public static function tearDownAfterClass() + { + parent::tearDownAfterClass(); + } + + public function createApplication() + { + return require __DIR__ . '/../../../../lib/Alchemy/Phrasea/Application/Overview.php'; + } + + function testDatafilesRouteAuthenticated() + { + $registry = registry::get_instance(); + $crawler = $this->client->request('GET', '/datafiles/' . self::$record_1->get_sbas_id() . '/' . self::$record_1->get_record_id() . '/preview/'); + $response = $this->client->getResponse(); + + if (self::$record_1->get_preview()->get_baseurl() !== '') + { + $this->assertEquals(302, $response->getStatusCode()); + $url = p4string::delEndSlash($registry->get('GV_ServerName')) . $response->headers->get('Location'); + $headers = http_query::getHttpHeaders($url); + $this->assertEquals(self::$record_1->get_preview()->get_mime(), $headers['content_type']); + $this->assertEquals(self::$record_1->get_preview()->get_size(), $headers['download_content_length']); + } + else + { + $this->assertEquals(200, $response->getStatusCode()); + $content_disposition = explode(';', $response->headers->get('content-disposition')); + $this->assertEquals($content_disposition[0], 'attachment'); + $this->assertEquals(self::$record_1->get_preview()->get_mime(), $response->headers->get('content-type')); + $this->assertEquals(self::$record_1->get_preview()->get_size(), $response->headers->get('content-length')); + } + + $crawler = $this->client->request('GET', '/datafiles/' . self::$record_1->get_sbas_id() . '/' . self::$record_1->get_record_id() . '/asubdefthatdoesnotexists/'); + $response = $this->client->getResponse(); + + $this->assertEquals(404, $response->getStatusCode()); + } + + function testDatafilesRouteNotAuthenticated() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $appbox->get_session()->logout(); + $crawler = $this->client->request('GET', '/datafiles/' . self::$record_1->get_sbas_id() . '/' . self::$record_1->get_record_id() . '/preview/'); + $response = $this->client->getResponse(); + $this->assertEquals(403, $response->getStatusCode()); + + $crawler = $this->client->request('GET', '/datafiles/' . self::$record_1->get_sbas_id() . '/' . self::$record_1->get_record_id() . '/notfoundreview/'); + $response = $this->client->getResponse(); + $this->assertEquals(403, $response->getStatusCode()); + } + + function testPermalinkAuthenticated() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $this->assertTrue($appbox->get_session()->is_authenticated()); + $this->get_a_permalink(); + } + + function testPermalinkNotAuthenticated() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $appbox->get_session()->logout(); + $this->assertFalse($appbox->get_session()->is_authenticated()); + $this->get_a_permalink(); + } + + protected function get_a_permalink() + { + $token = self::$record_1->get_preview()->get_permalink()->get_token(); + $url = '/permalink/v1/whateverIwannt/' . self::$record_1->get_sbas_id() . '/' . self::$record_1->get_record_id() . '/' . $token . '/preview/'; + + $crawler = $this->client->request('GET', $url); + $response = $this->client->getResponse(); + + if (self::$record_1->get_preview()->get_baseurl() !== '') + { + $this->assertEquals(302, $response->getStatusCode()); + } + else + { + $this->assertEquals(200, $response->getStatusCode()); + } + + $url = $url . 'view/'; + $crawler = $this->client->request('GET', $url); + $response = $this->client->getResponse(); + $this->assertEquals(200, $response->getStatusCode()); + } + +} diff --git a/tests/Alchemy/Phrasea/Application/RootTest.php b/tests/Alchemy/Phrasea/Application/RootTest.php new file mode 100644 index 0000000000..ae676e32a7 --- /dev/null +++ b/tests/Alchemy/Phrasea/Application/RootTest.php @@ -0,0 +1,63 @@ +client = $this->createClient(); + } + + public function testRouteSlash() + { + $crawler = $this->client->request('GET', '/'); + $response = $this->client->getResponse(); + $this->assertEquals(302, $response->getStatusCode()); + $this->assertRegExp('/^\/login\/\?redirect=[\/a-zA-Z]+/', $response->headers->get('location')); + } + + public function testRouteRobots() + { + $registry = \registry::get_instance(); + + $original_value = $registry->get('GV_allow_search_engine'); + + $registry->set('GV_allow_search_engine', false, \registry::TYPE_BOOLEAN); + + $crawler = $this->client->request('GET', '/robots.txt'); + $response = $this->client->getResponse(); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('text/plain; charset=UTF-8', $response->headers->get('Content-Type')); + $this->assertEquals('UTF-8', $response->getCharset()); + + $this->assertRegExp('/^Disallow: \/$/m', $response->getContent()); + + $registry = \registry::get_instance(); + $registry->set('GV_allow_search_engine', true, \registry::TYPE_BOOLEAN); + + $crawler = $this->client->request('GET', '/robots.txt'); + $response = $this->client->getResponse(); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('text/plain; charset=UTF-8', $response->headers->get('Content-Type')); + $this->assertEquals('UTF-8', $response->getCharset()); + + $this->assertRegExp('/^Allow: \/$/m', $response->getContent()); + + $registry->set('GV_allow_search_engine', $original_value, \registry::TYPE_BOOLEAN); + } + +} diff --git a/tests/Alchemy/Phrasea/Application/SetupTest.php b/tests/Alchemy/Phrasea/Application/SetupTest.php new file mode 100644 index 0000000000..f9f1beb052 --- /dev/null +++ b/tests/Alchemy/Phrasea/Application/SetupTest.php @@ -0,0 +1,33 @@ +client = $this->createClient(); + } + + public function testRouteSlash() + { + $crawler = $this->client->request('GET', '/'); + $response = $this->client->getResponse(); + $this->assertEquals(302, $response->getStatusCode()); + $this->assertEquals('/login/', $response->headers->get('location')); + } + +} diff --git a/tests/Alchemy/Phrasea/Cache/ApcCacheTest.php b/tests/Alchemy/Phrasea/Cache/ApcCacheTest.php new file mode 100644 index 0000000000..a03f1e5de3 --- /dev/null +++ b/tests/Alchemy/Phrasea/Cache/ApcCacheTest.php @@ -0,0 +1,53 @@ +markTestSkipped('Apc is not installed'); + } + $this->object = new \Alchemy\Phrasea\Cache\ApcCache; + } + + public function testIsServer() + { + $this->assertTrue(is_bool($this->object->isServer())); + } + + public function testGetStats() + { + $this->assertTrue(is_array($this->object->getStats()) || is_null($this->object->getStats())); + } + + public function testGet() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testDeleteMulti() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + diff --git a/tests/Alchemy/Phrasea/Cache/ArrayCacheTest.php b/tests/Alchemy/Phrasea/Cache/ArrayCacheTest.php new file mode 100644 index 0000000000..c329467562 --- /dev/null +++ b/tests/Alchemy/Phrasea/Cache/ArrayCacheTest.php @@ -0,0 +1,53 @@ +object = new \Alchemy\Phrasea\Cache\ArrayCache; + } + + public function testIsServer() + { + $this->assertTrue(is_bool($this->object->isServer())); + } + + public function testGetStats() + { + $this->assertTrue(is_array($this->object->getStats()) || is_null($this->object->getStats())); + } + + public function testGet() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testDeleteMulti() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + diff --git a/tests/Alchemy/Phrasea/Cache/ManagerTest.php b/tests/Alchemy/Phrasea/Cache/ManagerTest.php new file mode 100644 index 0000000000..4e8f3bae1e --- /dev/null +++ b/tests/Alchemy/Phrasea/Cache/ManagerTest.php @@ -0,0 +1,51 @@ +object = new \Alchemy\Phrasea\Cache\Manager(self::); + } + + /** + * @covers {className}::{origMethodName} + * @todo Implement testFlushAll(). + */ + public function testFlushAll() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers {className}::{origMethodName} + * @todo Implement testGet(). + */ + public function testGet() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + diff --git a/tests/Alchemy/Phrasea/Cache/MemcacheCacheTest.php b/tests/Alchemy/Phrasea/Cache/MemcacheCacheTest.php new file mode 100644 index 0000000000..6dc6733bcf --- /dev/null +++ b/tests/Alchemy/Phrasea/Cache/MemcacheCacheTest.php @@ -0,0 +1,66 @@ +object = new \Alchemy\Phrasea\Cache\MemcacheCache; + + if(!class_exists('Memcache')) + { + $this->markTestSkipped('No memcache extension'); + } + + $memcache = new Memcache(); + if(!@$memcache->connect('localhost', 11211)) + { + $this->markTestSkipped('No memcache server'); + } + + $this->object->setMemcache($memcache); + } + + public function testIsServer() + { + $this->assertTrue(is_bool($this->object->isServer())); + } + + public function testGetStats() + { + $this->assertTrue(is_array($this->object->getStats()) || is_null($this->object->getStats())); + } + + public function testGet() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testDeleteMulti() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + diff --git a/tests/Alchemy/Phrasea/Cache/RedisCacheTest.php b/tests/Alchemy/Phrasea/Cache/RedisCacheTest.php new file mode 100644 index 0000000000..47d8476e52 --- /dev/null +++ b/tests/Alchemy/Phrasea/Cache/RedisCacheTest.php @@ -0,0 +1,70 @@ +connect('127.0.0.1', 6379); + } + catch(\Exception $e) + { + $ok = false; + } + if (!$ok) + { + $this->markTestSkipped('The ' . __CLASS__ . ' requires the use of redis'); + } + } + else + { + $this->markTestSkipped('The ' . __CLASS__ . ' requires the use of redis'); + } + + $cache = new \Alchemy\Phrasea\Cache\RedisCache(); + $cache->setRedis($redis); + // Test save + $cache->save('test_key', 'testing this out'); + + // Test contains to test that save() worked + $this->assertTrue($cache->contains('test_key')); + + $cache->save('test_key1', 'testing this out', 20); + + // Test contains to test that save() worked + $this->assertTrue($cache->contains('test_key1')); + + // Test fetch + $this->assertEquals('testing this out', $cache->fetch('test_key')); + + // Test delete + $cache->save('test_key2', 'test2'); + $cache->delete('test_key2'); + $this->assertFalse($cache->contains('test_key2')); + + $this->assertEquals($redis, $cache->getRedis()); + } + +} diff --git a/tests/Alchemy/Phrasea/Cache/XcacheCacheTest.php b/tests/Alchemy/Phrasea/Cache/XcacheCacheTest.php new file mode 100644 index 0000000000..c50423ebc3 --- /dev/null +++ b/tests/Alchemy/Phrasea/Cache/XcacheCacheTest.php @@ -0,0 +1,58 @@ +markTestSkipped('Xcache not loaded'); + } + + $this->object = new \Alchemy\Phrasea\Cache\XcacheCache; + } + + public function testIsServer() + { + $this->assertTrue(is_bool($this->object->isServer())); + } + + public function testGetStats() + { + $this->assertTrue(is_array($this->object->getStats()) || is_null($this->object->getStats())); + } + + public function testGet() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testDeleteMulti() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + diff --git a/tests/Alchemy/Phrasea/Controller/Admin/DescriptionTest.php b/tests/Alchemy/Phrasea/Controller/Admin/DescriptionTest.php new file mode 100644 index 0000000000..2788b74830 --- /dev/null +++ b/tests/Alchemy/Phrasea/Controller/Admin/DescriptionTest.php @@ -0,0 +1,270 @@ +client = $this->createClient(); + } + + /** + * Default route test + */ + public function testRouteDescription() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $databox = array_shift($appbox->get_databoxes()); + $name = "testtest" . uniqid(); + $field = \databox_field::create($databox, $name); + $id = $field->get_id(); + $this->client->request("POST", "/description/" . $databox->get_sbas_id() . "/", array( + 'field_ids' => array($id) + , 'name_' . $id => $name + , 'multi_' . $id => 1 + , 'indexable_' . $id => 1 + , 'src_' . $id => '/rdf:RDF/rdf:Description/IPTC:SupplementalCategories' + , 'required_' . $id => 0 + , 'readonly_' . $id => 0 + , 'type_' . $id => 'string' + , 'vocabulary_' . $id => 'User' + )); + + $this->assertTrue($this->client->getResponse()->isRedirect()); + $field->delete(); + } + + public function testPostDelete() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $databox = array_shift($appbox->get_databoxes()); + $name = "test" . uniqid(); + $field = \databox_field::create($databox, $name); + $id = $field->get_id(); + + $this->client->request("POST", "/description/" . $databox->get_sbas_id() . "/", array( + 'todelete_ids' => array($id) + )); + + $this->assertTrue($this->client->getResponse()->isRedirect()); + + try + { + $field = \databox_field::get_instance($databox, $id); + $field->delete(); + $this->fail("should raise an exception"); + } + catch (\Exception $e) + { + + } + } + + public function testPostCreate() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $databox = array_shift($appbox->get_databoxes()); + + $name = 'test' . uniqid(); + + $this->client->request("POST", "/description/" . $databox->get_sbas_id() . "/", array( + 'newfield' => $name + )); + + $this->assertTrue($this->client->getResponse()->isRedirect()); + + $fields = $databox->get_meta_structure(); + $find = false; + + foreach ($fields as $field) + { + if ($field->get_name() === databox_field::generateName($name)) + { + $field->delete(); + $find = true; + } + } + + if (!$find) + { + $this->fail("should have create a new field"); + } + } + + public function testPostDescriptionException() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $databox = array_shift($appbox->get_databoxes()); + + $this->client->request("POST", "/description/" . $databox->get_sbas_id() . "/", array( + 'todelete_ids' => array('unknow_id') + )); + + $this->assertTrue($this->client->getResponse()->isRedirect()); + + $name = "test" . uniqid(); + $field = \databox_field::create($databox, $name); + $id = $field->get_id(); + $this->client->request("POST", "/description/" . $databox->get_sbas_id() . "/", array( + 'field_ids' => array($id) + , 'name_' . $id => $name + , 'multi_' . $id => 1 + , 'indexable_' . $id => 1 + , 'src_' . $id => '/rdf:RDF/rdf:Description/IPTC:SupplementalCategories' + , 'required_' . $id => 0 + , 'readonly_' . $id => 0 + , 'type_' . $id => 'string' + , 'vocabulary_' . $id => 'Unknow_Vocabulary' + )); + + $this->assertTrue($this->client->getResponse()->isRedirect()); + $field->delete(); + + $name = "test" . uniqid(); + $field = \databox_field::create($databox, $name); + $id = $field->get_id(); + $this->client->request("POST", "/description/" . $databox->get_sbas_id() . "/", array( + 'field_ids' => array($id) + , 'multi_' . $id => 1 + , 'indexable_' . $id => 1 + , 'src_' . $id => '/rdf:RDF/rdf:Description/IPTC:SupplementalCategories' + , 'required_' . $id => 0 + , 'readonly_' . $id => 0 + , 'type_' . $id => 'string' + , 'vocabulary_' . $id => 'Unknow_Vocabulary' + )); + + $this->assertTrue($this->client->getResponse()->isRedirect()); + $field->delete(); + + $name = "test" . uniqid(); + $field = \databox_field::create($databox, $name); + $field->set_multi(false); + $field->set_indexable(false); + $field->set_required(true); + $field->set_readonly(true); + $id = $field->get_id(); + $this->client->request("POST", "/description/" . $databox->get_sbas_id() . "/", array( + 'field_ids' => array($id) + , 'name_' . $id => $name + , 'multi_' . $id => 1 + , 'indexable_' . $id => 1 + , 'src_' . $id => 'unknow_Source' + , 'required_' . $id => 0 + , 'readonly_' . $id => 0 + , 'type_' . $id => 'string' + , 'vocabulary_' . $id => 'Unknow_Vocabulary' + )); + + $this->assertTrue($this->client->getResponse()->isRedirect()); + $this->assertTrue($field->is_readonly()); + $this->assertTrue($field->is_required()); + $this->assertFalse($field->is_multi()); + $this->assertFalse($field->is_indexable()); + $field->delete(); + + + $name = "test" . uniqid(); + $field = \databox_field::create($databox, $name); + $id = $field->get_id(); + $this->client->request("POST", "/description/" . $databox->get_sbas_id() . "/", array( + 'field_ids' => array('unknow_id') + , 'name_' . $id => $name + , 'multi_' . $id => 1 + , 'indexable_' . $id => 1 + , 'src_' . $id => '/rdf:RDF/rdf:Description/IPTC:SupplementalCategories' + , 'required_' . $id => 0 + , 'readonly_' . $id => 0 + , 'type_' . $id => 'string' + , 'vocabulary_' . $id => 'Unknow_Vocabulary' + )); + + $this->assertTrue($this->client->getResponse()->isRedirect()); + $field->delete(); + } + + public function testPostDescriptionRights() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $session = $appbox->get_session(); + $auth = new Session_Authentication_None(self::$user_alt1); + $session->authenticate($auth); + + $databox = array_shift($appbox->get_databoxes()); + $name = "test" . uniqid(); + $field = \databox_field::create($databox, $name); + $id = $field->get_id(); + $this->client->request("POST", "/description/" . $databox->get_sbas_id() . "/", array( + 'field_ids' => array($id) + , 'name_' . $id => $name + , 'multi_' . $id => 1 + , 'indexable_' . $id => 1 + , 'src_' . $id => '/rdf:RDF/rdf:Description/IPTC:SupplementalCategories' + , 'required_' . $id => 0 + , 'readonly_' . $id => 0 + , 'type_' . $id => 'string' + , 'vocabulary_' . $id => 'User' + )); + $this->assertFalse($this->client->getResponse()->isOk()); + $this->assertEquals("You are not allowed to access this zone", $this->client->getResponse()->getContent()); + $field->delete(); + } + + public function testGetDescriptionException() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $session = $appbox->get_session(); + $auth = new Session_Authentication_None(self::$user_alt1); + $session->authenticate($auth); + + $databox = array_shift($appbox->get_databoxes()); + + $this->client->request("GET", "/description/" . $databox->get_sbas_id() . "/"); + $this->assertFalse($this->client->getResponse()->isOk()); + $this->assertEquals("You are not allowed to access this zone", $this->client->getResponse()->getContent()); + } + + public function testGetDescription() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $databox = array_shift($appbox->get_databoxes()); + + $this->client->request("GET", "/description/" . $databox->get_sbas_id() . "/"); + $this->assertTrue($this->client->getResponse()->isOk()); + } + + +} diff --git a/tests/Alchemy/Phrasea/Controller/Admin/FieldsTest.php b/tests/Alchemy/Phrasea/Controller/Admin/FieldsTest.php new file mode 100644 index 0000000000..495172ce14 --- /dev/null +++ b/tests/Alchemy/Phrasea/Controller/Admin/FieldsTest.php @@ -0,0 +1,86 @@ +client = $this->createClient(); + } + + /** + * Default route test + */ + public function testCheckMulti() + { + $appbox = \appbox::get_instance(\bootstrap::getCore()); + $databox = array_shift($appbox->get_databoxes()); + + $field = \databox_field::create($databox, "test" . time()); + $source = $field->get_source(); + + $this->client->request("GET", "/fields/checkmulti/", array( + 'souce' => $source, 'multi' => 'false')); + + $response = $this->client->getResponse(); + $this->assertEquals("application/json", $response->headers->get("content-type")); + $datas = json_decode($response->getContent()); + $this->assertTrue(is_object($datas)); + $this->assertTrue(!!$datas->result); + $this->assertEquals($field->is_multi(), !!$datas->is_multi); + $field->delete(); + } + + public function testCheckReadOnly() + { + $appbox = \appbox::get_instance(\bootstrap::getCore()); + $databox = array_shift($appbox->get_databoxes()); + + $field = \databox_field::create($databox, "test" . time()); + $source = $field->get_source(); + + $this->client->request("GET", "/fields/checkreadonly/", array( + 'souce' => $source, 'readonly' => 'false')); + + $response = $this->client->getResponse(); + $this->assertEquals("application/json", $response->headers->get("content-type")); + $datas = json_decode($response->getContent()); + $this->assertTrue(is_object($datas)); + $this->assertTrue(!!$datas->result); + $this->assertEquals($field->is_readonly(), !!$datas->is_readonly); + + $field->delete(); + } + +} diff --git a/tests/Alchemy/Phrasea/Controller/Admin/PublicationTest.php b/tests/Alchemy/Phrasea/Controller/Admin/PublicationTest.php new file mode 100644 index 0000000000..0f4a93da22 --- /dev/null +++ b/tests/Alchemy/Phrasea/Controller/Admin/PublicationTest.php @@ -0,0 +1,445 @@ +client = $this->createClient(); + } + + public function createApplication() + { + return require __DIR__ . '/../../../../../lib/Alchemy/Phrasea/Application/Admin.php'; + } + + public function testList() + { + $crawler = $this->client->request('GET', '/publications/list/'); + $pageContent = $this->client->getResponse()->getContent(); + $this->assertTrue($this->client->getResponse()->isOk()); + $feeds = Feed_Collection::load_all(appbox::get_instance(\bootstrap::getCore()), self::$user); + + foreach ($feeds->get_feeds() as $feed) + { + $this->assertRegExp('/\/admin\/publications\/feed\/' . $feed->get_id() . '/', $pageContent); + if ($feed->get_collection() != null) + $this->assertRegExp('/' . $feed->get_collection()->get_name() . '/', $pageContent); + if ($feed->is_owner(self::$user)) + $this->assertEquals(1, $crawler->filterXPath("//form[@action='/admin/publications/feed/" . $feed->get_id() . "/delete/']")->count()); + } + } + + public function testCreate() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + foreach ($appbox->get_databoxes() as $databox) + { + foreach ($databox->get_collections() as $collection) + { + $base_id = $collection->get_base_id(); + break; + } + } + $feeds = Feed_Collection::load_all($appbox, self::$user); + $count = sizeof($feeds->get_feeds()); + + $crawler = $this->client->request('POST', '/publications/create/', array("title" => "hello", "subtitle" => "coucou", "base_id" => $base_id)); + + $this->assertTrue($this->client->getResponse()->isRedirect('/admin/publications/list/')); + + $feeds = Feed_Collection::load_all(appbox::get_instance(\bootstrap::getCore()), self::$user); + $count_after = sizeof($feeds->get_feeds()); + $this->assertGreaterThan($count, $count_after); + + $feed = array_pop($feeds->get_feeds()); + + $feed->delete(); + } + + public function testGetFeed() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $feed = Feed_Adapter::create($appbox, self::$user, "salut", 'coucou'); + $crawler = $this->client->request('GET', '/publications/feed/' . $feed->get_id() . '/'); + $this->assertTrue($this->client->getResponse()->isOk()); + $this->assertEquals(1, $crawler->filterXPath("//form[@action='/admin/publications/feed/" . $feed->get_id() . "/update/']")->count()); + $this->assertEquals(1, $crawler->filterXPath("//input[@value='salut']")->count()); + $this->assertEquals(1, $crawler->filterXPath("//input[@value='coucou']")->count()); + + $feed->delete(); + } + + public function testUpdateFeedNotOwner() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + //is not owner + $stub = $this->getMock("user_adapter", array(), array(), "", false); + //return a different userid + $stub->expects($this->any())->method("get_id")->will($this->returnValue(99999999)); + + $feed = Feed_Adapter::create($appbox, $stub, "salut", 'coucou'); + $this->client->request("POST", "/publications/feed/" . $feed->get_id() . "/update/"); + $this->assertTrue($this->client->getResponse()->isRedirect(), 'update fails, i\'m redirected'); + $this->assertTrue( + strpos( + $this->client->getResponse()->headers->get('Location') + , '/admin/publications/feed/' . $feed->get_id() . '/?' + ) === 0); + $feed->delete(); + } + + public function testUpdatedFeedException() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $feed = Feed_Adapter::create($appbox, self::$user, "salut", 'coucou'); + + $this->client->request("POST", "/publications/feed/" . $feed->get_id() . "/update/", array( + 'title' => 'test' + , 'subtitle' => 'test' + , 'public' => '1' + )); + + $feed = new Feed_Adapter($appbox, $feed->get_id()); + + $this->assertTrue( + strpos( + $this->client->getResponse()->headers->get('Location') + , '/admin/publications/list/' + ) === 0); + + $this->assertEquals('test', $feed->get_title()); + $this->assertEquals('test', $feed->get_subtitle()); + $this->assertTrue($feed->is_public()); + $this->assertNull($feed->get_collection()); + + $feed->delete(); + } + + public function testUpdatedFeedOwner() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $feed = Feed_Adapter::create($appbox, self::$user, "salut", 'coucou'); + + $this->client->request("POST", "/publications/feed/" . $feed->get_id() . "/update/", array( + 'title' => 'test' + , 'subtitle' => 'test' + , 'public' => '1' + , 'base_id' => self::$collection->get_base_id() + )); + + $this->assertTrue( + strpos( + $this->client->getResponse()->headers->get('Location') + , '/admin/publications/list/' + ) === 0); + + + $feed = new Feed_Adapter($appbox, $feed->get_id()); + + $collection = $feed->get_collection(); + + $this->assertEquals('test', $feed->get_title()); + $this->assertEquals('test', $feed->get_subtitle()); + $this->assertFalse($feed->is_public()); + $this->assertEquals(self::$collection->get_base_id(), $collection->get_base_id()); + + $this->assertTrue( + strpos( + $this->client->getResponse()->headers->get('Location') + , '/admin/publications/list/' + ) === 0); + + $feed->delete(); + } + + public function testIconUploadErrorOwner() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + //is not owner + $stub = $this->getMock("user_adapter", array(), array(), "", false); + //return a different userid + $stub->expects($this->any())->method("get_id")->will($this->returnValue(99999999)); + + + $feed = Feed_Adapter::create($appbox, $stub, "salut", 'coucou'); + + $this->client->request("POST", "/publications/feed/" . $feed->get_id() . "/iconupload/", array(), array()); + + $response = $this->client->getResponse(); + $this->assertTrue($response->isOk()); + $this->assertRegexp("/ERROR:you are not allowed/", $response->getContent()); + + $feed->delete(); + } + + public function testIconUploadErrorFileData() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $feed = Feed_Adapter::create($appbox, self::$user, "salut", 'coucou'); + + $this->client->request( + "POST" + , "/publications/feed/" . $feed->get_id() . "/iconupload/" + , array() + , array('Filedata' => array('error' => 1)) + ); + $response = $this->client->getResponse(); + $this->assertTrue($response->isOk()); + $this->assertRegexp("/ERROR:error while upload/", $response->getContent()); + + $feed->delete(); + } + + public function testIconUploadErrorFileType() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $feed = Feed_Adapter::create($appbox, self::$user, "salut", 'coucou'); + + $this->client->request( + "POST" + , "/publications/feed/" . $feed->get_id() . "/iconupload/" + , array() + , array('Filedata' => array('error' => 0, 'tmp_name' => __DIR__ . '/../../../../testfiles/test013.ai')) + ); + $response = $this->client->getResponse(); + $this->assertTrue($response->isOk()); + $this->assertRegexp("/ERROR:bad filetype/", $response->getContent()); + + $feed->delete(); + } + + public function testIconUploadErrorTooLarge() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $feed = Feed_Adapter::create($appbox, self::$user, "salut", 'coucou'); + + $this->client->request( + "POST" + , "/publications/feed/" . $feed->get_id() . "/iconupload/" + , array() + , array('Filedata' => array('error' => 0, 'tmp_name' => __DIR__ . '/../../../../testfiles/heavy500.jpg')) + ); + $response = $this->client->getResponse(); + $this->assertTrue($response->isOk()); + $this->assertRegexp("/ERROR:file too large/", $response->getContent()); + + $feed->delete(); + } + + public function testIconUploadErrorNotSquareA() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $feed = Feed_Adapter::create($appbox, self::$user, "salut", 'coucou'); + + $this->client->request( + "POST" + , "/publications/feed/" . $feed->get_id() . "/iconupload/" + , array() + , array('Filedata' => array('error' => 0, 'tmp_name' => __DIR__ . '/../../../../testfiles/recta_logo.gif')) + ); + $response = $this->client->getResponse(); + $this->assertTrue($response->isOk()); + $this->assertRegexp("/ERROR:file is not square/", $response->getContent()); + + $feed->delete(); + } + + public function testIconUploadErrorNotSquareB() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $feed = Feed_Adapter::create($appbox, self::$user, "salut", 'coucou'); + + $this->client->request( + "POST" + , "/publications/feed/" . $feed->get_id() . "/iconupload/" + , array() + , array('Filedata' => array('error' => 0, 'tmp_name' => __DIR__ . '/../../../../testfiles/rectb_logo.gif')) + ); + $response = $this->client->getResponse(); + $this->assertTrue($response->isOk()); + $this->assertRegexp("/ERROR:file is not square/", $response->getContent()); + + $feed->delete(); + } + + public function testIconUpload() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $feed = Feed_Adapter::create($appbox, self::$user, "salut", 'coucou'); + + copy(__DIR__ . '/../../../../testfiles/logocoll.gif', __DIR__ . '/../../../../testfiles/logocoll1.gif'); + $this->client->request( + "POST" + , "/publications/feed/" . $feed->get_id() . "/iconupload/" + , array() + , array('Filedata' => array('error' => 0, 'tmp_name' => __DIR__ . '/../../../../testfiles/logocoll1.gif')) + ); + $response = $this->client->getResponse(); + $this->assertTrue($response->isOk()); + + $feed = new Feed_Adapter($appbox, $feed->get_id()); + try + { + $file = new SplFileObject(__DIR__ . '/../../../../testfiles/logocoll1.gif'); + $this->fail('logo not deleted'); + } + catch (\Exception $e) + { + + } + + $this->assertRegexp("#FILEHREF:/custom/feed_" . $feed->get_id() . ".jpg?[0-9]*#", $response->getContent()); + + $feed->delete(); + } + + public function testAddPublisher() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $feed = Feed_Adapter::create($appbox, self::$user, "salut", 'coucou'); + + $this->client->request("POST", "/publications/feed/" . $feed->get_id() . "/addpublisher/", array( + 'usr_id' => self::$user_alt1->get_id() + )); + + $response = $this->client->getResponse(); + $this->assertTrue($response->isRedirect()); + + $feed = new Feed_Adapter($appbox, $feed->get_id()); + $publishers = $feed->get_publishers(); + + $this->assertTrue(isset($publishers[self::$user_alt1->get_id()])); + $this->assertTrue( + strpos( + $this->client->getResponse()->headers->get('Location') + , '/admin/publications/feed/' . $feed->get_id() . '/' + ) === 0); + + $feed->delete(); + } + + public function testAddPublisherException() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $feed = Feed_Adapter::create($appbox, self::$user, "salut", 'coucou'); + + $this->client->request("POST", "/publications/feed/" . $feed->get_id() . "/addpublisher/"); + + $feed = new Feed_Adapter($appbox, $feed->get_id()); + $response = $this->client->getResponse(); + $this->assertTrue($response->isRedirect()); + $this->assertTrue( + strpos( + $this->client->getResponse()->headers->get('Location') + , '/admin/publications/feed/' . $feed->get_id() . '/?err' + ) === 0); + + $feed->delete(); + } + + public function testRemovePublisher() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $feed = Feed_Adapter::create($appbox, self::$user, "salut", 'coucou'); + + $this->client->request("POST", "/publications/feed/" . $feed->get_id() . "/removepublisher/", array( + 'usr_id' => self::$user_alt1->get_id() + )); + + $response = $this->client->getResponse(); + $this->assertTrue($response->isRedirect()); + + $feed = new Feed_Adapter($appbox, $feed->get_id()); + $publishers = $feed->get_publishers(); + + $this->assertFalse(isset($publishers[self::$user_alt1->get_id()])); + $this->assertTrue( + strpos( + $this->client->getResponse()->headers->get('Location') + , '/admin/publications/feed/' . $feed->get_id() . '/' + ) === 0); + + $feed->delete(); + } + + public function testRemovePublisherException() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $feed = Feed_Adapter::create($appbox, self::$user, "salut", 'coucou'); + + $this->client->request("POST", "/publications/feed/" . $feed->get_id() . "/removepublisher/"); + + $response = $this->client->getResponse(); + $this->assertTrue($response->isRedirect()); + + $feed = new Feed_Adapter($appbox, $feed->get_id()); + + $this->assertTrue( + strpos( + $this->client->getResponse()->headers->get('Location') + , '/admin/publications/feed/' . $feed->get_id() . '/?err' + ) === 0); + + $feed->delete(); + } + + public function testDeleteFeed() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $feed = Feed_Adapter::create($appbox, self::$user, "salut", 'coucou'); + + $this->client->request("POST", "/publications/feed/" . $feed->get_id() . "/delete/"); + + $response = $this->client->getResponse(); + $this->assertTrue($response->isRedirect()); + + try + { + $feed = new Feed_Adapter($appbox, $feed->get_id()); + $feed->delete(); + $this->fail("fail deleting feed"); + } + catch(\Exception $e) + { + + } + + } + +} diff --git a/tests/Alchemy/Phrasea/Controller/Admin/RootTest.php b/tests/Alchemy/Phrasea/Controller/Admin/RootTest.php new file mode 100644 index 0000000000..e10ce1475c --- /dev/null +++ b/tests/Alchemy/Phrasea/Controller/Admin/RootTest.php @@ -0,0 +1,54 @@ +client = $this->createClient(); + } + + /** + * Default route test + */ + public function testRouteSlash() + { + $this->client->request('GET', '/', array('section' => 'base:featured')); + $this->assertTrue($this->client->getResponse()->isOk()); + + $this->client->request('GET', '/'); + $this->assertTrue($this->client->getResponse()->isOk()); + } + +} diff --git a/tests/Alchemy/Phrasea/Controller/Admin/SubdefsTest.php b/tests/Alchemy/Phrasea/Controller/Admin/SubdefsTest.php new file mode 100644 index 0000000000..e8ed195fd2 --- /dev/null +++ b/tests/Alchemy/Phrasea/Controller/Admin/SubdefsTest.php @@ -0,0 +1,118 @@ +client = $this->createClient(); + } + + /** + * Default route test + */ + public function testRouteGetSubdef() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $databox = array_shift($appbox->get_databoxes()); + $this->client->request("GET", "/subdefs/" . $databox->get_sbas_id() . "/"); + $this->assertTrue($this->client->getResponse()->isOk()); + } + + public function testPostRouteAddSubdef() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $databox = array_shift($appbox->get_databoxes()); + $this->client->request("POST", "/subdefs/" . $databox->get_sbas_id() . "/", array('add_subdef' => array( + 'class' => 'thumbnail', + 'name' => 'aname', + 'group' => 'image' + ))); + $this->assertTrue($this->client->getResponse()->isRedirect()); + $subdefs = $databox->get_subdef_structure(); + $subdefs->get_subdef("image", "aname"); + $subdefs->delete_subdef('image', 'aname'); + } + + public function testPostRouteDeleteSubdef() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $databox = array_shift($appbox->get_databoxes()); + $subdefs = $databox->get_subdef_structure(); + $subdefs->add_subdef("image", "name", "class"); + $this->client->request("POST", "/subdefs/" . $databox->get_sbas_id() . "/", array('delete_subdef' => 'group_name')); + $this->assertTrue($this->client->getResponse()->isRedirect()); + try + { + $subdefs->get_subdef("image", "name"); + $this->fail("should raise an exception"); + } + catch (\Exception $e) + { + + } + } + + public function testPostRouteAddSubdefWithNoParams() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $databox = array_shift($appbox->get_databoxes()); + $subdefs = $databox->get_subdef_structure(); + $subdefs->add_subdef("image", "name", "class"); + $this->client->request("POST", "/subdefs/" . $databox->get_sbas_id() . "/", array('subdefs' => array( + 'image_name' + ) + , 'image_name_class' => 'class' + , 'image_name_downloadable' => 0 + , 'image_name_mediatype' => 'image' + , 'image_name_image' => array( + 'size' => 400 + , 'resolution' => 83 + , 'strip' => 0 + , 'quality' => 90 + )) + ); + $this->assertTrue($this->client->getResponse()->isRedirect()); + $subdef = $subdefs->get_subdef("image", "name"); + /* @var $subdef \databox_subdefAbstract */ + $this->assertFalse($subdef->is_downloadable()); + $options = $subdef->get_options(); + $this->assertEquals(400, $options["size"]); + $this->assertEquals(83, $options["resolution"]); + $this->assertEquals(90, $options["quality"]); + $this->assertFalse($options["strip"]); + $subdefs->delete_subdef("image", "name"); + } + +} diff --git a/tests/Alchemy/Phrasea/Controller/Admin/UsersTest.php b/tests/Alchemy/Phrasea/Controller/Admin/UsersTest.php new file mode 100644 index 0000000000..d71087a6d5 --- /dev/null +++ b/tests/Alchemy/Phrasea/Controller/Admin/UsersTest.php @@ -0,0 +1,366 @@ +usersParameters = array("users" => implode(';', array(self::$user->get_id(), self::$user_alt1->get_id()))); + parent::setUp(); + $this->client = $this->createClient(); + } + + public function testRouteRightsPost() + { + $this->client->request('POST', '/users/rights/', $this->usersParameters); + $response = $this->client->getResponse(); + $this->assertTrue($response->isOk()); + } + + public function testRouteRightsGet() + { + $this->client->request('GET', '/users/rights/', $this->usersParameters); + $response = $this->client->getResponse(); + $this->assertTrue($response->isOk()); + } + + public function testRouteDelete() + { + $appbox = \appbox::get_instance(\bootstrap::getCore()); + + $username = uniqid('user_'); + $user = User_Adapter::create($appbox, $username, "test", $username . "@email.com", false); + $id = $user->get_id(); + + $this->client->request('POST', '/users/delete/', array('users' => $id)); + $response = $this->client->getResponse(); + $this->assertTrue($response->isRedirect()); + try + { + $user = User_Adapter::getInstance($id, $appbox); + $user->delete(); + $this->fail("user not deleted"); + } + catch (\Exception $e) + { + + } + } + + public function testRouteRightsApply() + { + $appbox = \appbox::get_instance(\bootstrap::getCore()); + + $username = uniqid('user_'); + $user = User_Adapter::create($appbox, $username, "test", $username . "@email.com", false); + + $base_id = self::$collection->get_base_id(); + $_GET['values'] = 'canreport_' . $base_id . '=1&manage_' . self::$collection->get_base_id() . '=1&canpush_' . self::$collection->get_base_id() . '=1'; + $_GET['user_infos'] = "user_infos[email]=" . $user->get_email(); + + $this->client->request('POST', '/users/rights/apply/', array('users' => $user->get_id())); + $response = $this->client->getResponse(); + $this->assertTrue($response->isOK()); + $this->assertEquals("application/json", $response->headers->get("content-type")); + $datas = json_decode($response->getContent()); + $this->assertTrue(is_object($datas)); + $datas = json_decode($response->getContent()); + $this->assertFalse($datas->error); + $this->assertTrue($user->ACL()->has_right_on_base($base_id, "manage")); + $this->assertTrue($user->ACL()->has_right_on_base($base_id, "canpush")); + $this->assertTrue($user->ACL()->has_right_on_base($base_id, "canreport")); + $user->delete(); + } + + public function testRouteRightsApplyException() + { + $this->markTestIncomplete(); + $_GET = array(); + $appbox = \appbox::get_instance(\bootstrap::getCore()); + $username = uniqid('user_'); + $user = User_Adapter::create($appbox, $username, "test", $username . "@email.com", false); + $base_id = self::$collection->get_base_id(); + $_GET['values'] = 'canreport_' . $base_id . '=1&manage_' . self::$collection->get_base_id() . '=1&canpush_' . self::$collection->get_base_id() . '=1'; + $_GET['user_infos'] = "user_infos[email]=" . $user->get_email(); + $this->client->request('POST', '/users/rights/apply/', array('users' => $user->get_id())); + $response = $this->client->getResponse(); + $this->assertTrue($response->isOK()); + $this->assertEquals("application/json", $response->headers->get("content-type")); + $datas = json_decode($response->getContent()); + $this->assertTrue(is_object($datas)); + $this->assertTrue($datas->error); + $user->delete(); + } + + public function testRouteQuota() + { + $base_id = array_pop(array_keys(self::$user->ACL()->get_granted_base())); + $params = array('base_id'=>$base_id, 'users'=>self::$user->get_id()); + + $this->client->request('POST', '/users/rights/quotas/', $params); + $response = $this->client->getResponse(); + $this->assertTrue($response->isOK()); + } + + public function testRouteQuotaAdd() + { + $params = array( + 'base_id' => self::$collection->get_base_id() + , 'quota' => '1', 'droits' => 38, 'restes' => 15); + $this->client->request('POST', '/users/rights/quotas/apply/', $params); + $response = $this->client->getResponse(); + $this->assertTrue($response->isOK()); + } + + public function testRouteQuotaRemove() + { + $base_id = array_pop(array_keys(self::$user->ACL()->get_granted_base())); + $params = array('base_id'=>$base_id, 'users'=>self::$user->get_id()); + + $this->client->request('POST', '/users/rights/quotas/apply/', $params); + $response = $this->client->getResponse(); + $this->assertTrue($response->isOK()); + } + + public function testRouteRightTime() + { + $base_id = array_pop(array_keys(self::$user->ACL()->get_granted_base())); + $params = array('base_id'=>$base_id, 'users'=>self::$user->get_id()); + + $this->client->request('POST', '/users/rights/time/', $params); + $response = $this->client->getResponse(); + $this->assertTrue($response->isOK()); + } + + public function testRouteRightTimeApply() + { + $appbox = \appbox::get_instance(\bootstrap::getCore()); + $username = uniqid('user_'); + $user = User_Adapter::create($appbox, $username, "test", $username . "@email.com", false); + $base_id = self::$collection->get_base_id(); + $date = new \Datetime(); + $date->modify("-10 days"); + $dmin = $date->format(DATE_ATOM); + $date->modify("+30 days"); + $dmax = $date->format(DATE_ATOM); + $this->client->request('POST', '/users/rights/time/apply/', array('base_id' => $base_id, 'dmin' => $dmin, 'dmax' => $dmax, 'limit' => 1, 'users' => $user->get_id())); + $response = $this->client->getResponse(); + $this->assertTrue($response->isOK()); +// $this->assertTrue($user->ACL()->is_limited($base_id)); + $user->delete(); + } + + public function testRouteRightMask() + { + $base_id = array_pop(array_keys(self::$user->ACL()->get_granted_base())); + $params = array('base_id'=>$base_id, 'users'=>self::$user->get_id()); + + $this->client->request('POST', '/users/rights/masks/', $params); + $response = $this->client->getResponse(); + $this->assertTrue($response->isOK()); + } + + public function testRouteRightMaskApply() + { + $this->markTestIncomplete(); + $base_id = self::$collection->get_base_id(); + $username = uniqid('user_'); + $user = User_Adapter::create($appbox, $username, "test", $username . "@email.com", false); + $this->client->request('POST', '/users/rights/masks/apply/', array( + 'base_id' => $base_id, 'vand_and', 'vand_or', 'vxor_or', 'vxor_and' + )); + $response = $this->client->getResponse(); + $this->assertTrue($response->isOK()); + $user->delete(); + } + + public function testRouteSearch() + { + $this->client->request('POST', '/users/search/'); + $response = $this->client->getResponse(); + $this->assertTrue($response->isOK()); + } + + public function testRoutesearchExport() + { + $this->client->request('POST', '/users/search/export/'); + $response = $this->client->getResponse(); + $this->assertTrue($response->isOK()); + $this->assertEquals("text/plain; charset=UTF-8", $response->headers->get("Content-type")); + $this->assertEquals("attachment; filename=export.txt", $response->headers->get("content-disposition")); + } + + public function testRouteThSearch() + { + $this->client->request('GET', '/users/typeahead/search/', array('term' => 'admin')); + $response = $this->client->getResponse(); + $this->assertTrue($response->isOK()); + $this->assertEquals("application/json", $response->headers->get("content-type")); + } + + public function testRouteApplyTp() + { + $appbox = \appbox::get_instance(\bootstrap::getCore()); + + $templateName = uniqid('template_'); + $template = User_Adapter::create($appbox, $templateName, "test", $templateName . "@email.com", false); + $template->set_template(self::$user); + + $username = uniqid('user_'); + $user = User_Adapter::create($appbox, $username, "test", $username . "@email.com", false); + + $this->client->request('POST', '/users/apply_template/', array( + 'template' => $template->get_id() + , 'users' => $user->get_id()) + ); + + $response = $this->client->getResponse(); + $this->assertTrue($response->isRedirect()); + + $template->delete(); + $user->delete(); + } + + public function testRouteCreateException() + { + $this->client->request('POST', '/users/create/', array('value' => '', 'template' => '1')); + $response = $this->client->getResponse(); + $this->assertTrue($response->isOK()); + $this->assertEquals("application/json", $response->headers->get("content-type")); + $datas = json_decode($response->getContent()); + $this->assertTrue(is_object($datas)); + $this->assertTrue($datas->error); + } + + public function testRouteCreateExceptionUser() + { + $this->client->request('POST', '/users/create/', array('value' => '', 'template' => '0')); + $response = $this->client->getResponse(); + $this->assertTrue($response->isOK()); + $this->assertEquals("application/json", $response->headers->get("content-type")); + $datas = json_decode($response->getContent()); + $this->assertTrue(is_object($datas)); + $this->assertTrue($datas->error); + } + + public function testRouteCreateUser() + { + $appbox = \appbox::get_instance(\bootstrap::getCore()); + + $username = uniqid('user_'); + $user = User_Adapter::create($appbox, $username, "test", $username . "@email.com", false); + + $this->client->request('POST', '/users/create/', array('value' => $username . "@email.com", 'template' => '0')); + + $response = $this->client->getResponse(); + + $this->assertTrue($response->isOK()); + $this->assertEquals("application/json", $response->headers->get("content-type")); + $datas = json_decode($response->getContent()); + $this->assertTrue(is_object($datas)); + $this->assertFalse($datas->error); + + try + { + $user = \User_Adapter::getInstance((int) $datas->data, $appbox); + $user->delete(); + } + catch (\Exception $e) + { + $this->fail("could not delete created user " . $e->getMessage()); + } + } + + + public function testRouteExportCsv() + { + $this->client->request('POST', '/users/export/csv/'); + $response = $this->client->getResponse(); + $this->assertTrue($response->isOK()); + $this->assertRegexp("#text/csv#", $response->headers->get("content-type")); + $this->assertRegexp("#charset=UTF-8#", $response->headers->get("content-type")); + $this->assertEquals("attachment; filename=export.txt", $response->headers->get("content-disposition")); + } + + + public function testResetRights() + { + $appbox = \appbox::get_instance(self::$core); + $username = uniqid('user_'); + $user = User_Adapter::create($appbox, $username, "test", $username . "@email.com", false); + + $user->ACL()->give_access_to_sbas(array_keys($appbox->get_databoxes())); + + foreach ($appbox->get_databoxes() as $databox) + { + + $rights = array( + 'bas_manage' => '1' + , 'bas_modify_struct' => '1' + , 'bas_modif_th' => '1' + , 'bas_chupub' => '1' + ); + + $user->ACL()->update_rights_to_sbas($databox->get_sbas_id(), $rights); + + foreach ($databox->get_collections() as $collection) + { + $base_id = $collection->get_base_id(); + $user->ACL()->give_access_to_base(array($base_id)); + + $rights = array( + 'canputinalbum' => '1' + , 'candwnldhd' => '1' + , 'candwnldsubdef' => '1' + , 'nowatermark' => '1' + ); + + $user->ACL()->update_rights_to_base($collection->get_base_id(), $rights); + break; + } + } +// + + $this->client->request('POST', '/users/rights/reset/', array('users' => $user->get_id())); + $response = $this->client->getResponse(); + $this->assertTrue($response->isOK()); + $this->assertEquals("application/json", $response->headers->get("content-type")); + $datas = json_decode($response->getContent()); + $this->assertTrue(is_object($datas)); + $this->assertFalse($datas->error); + $this->assertFalse($user->ACL()->has_access_to_base($base_id)); + $user->delete(); + } + +} diff --git a/tests/Alchemy/Phrasea/Controller/BoilerPlate.php b/tests/Alchemy/Phrasea/Controller/BoilerPlate.php new file mode 100644 index 0000000000..8a2d124894 --- /dev/null +++ b/tests/Alchemy/Phrasea/Controller/BoilerPlate.php @@ -0,0 +1,69 @@ +client = $this->createClient(); + } + + public function tearDown() + { + parent::tearDown(); + } + + /** + * Default route test + */ + public function testRouteSlash() + { + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} diff --git a/tests/Alchemy/Phrasea/Controller/Prod/BasketTest.php b/tests/Alchemy/Phrasea/Controller/Prod/BasketTest.php new file mode 100644 index 0000000000..ac557bf83f --- /dev/null +++ b/tests/Alchemy/Phrasea/Controller/Prod/BasketTest.php @@ -0,0 +1,694 @@ +client = $this->createClient(); + } + + public function createApplication() + { + return require __DIR__ . '/../../../../../lib/Alchemy/Phrasea/Application/Prod.php'; + } + + public function testRootPost() + { + $route = '/baskets/'; + + $records = array( + self::$record_1->get_serialize_key(), + self::$record_2->get_serialize_key(), + ' ', + '42', + self::$record_no_access->get_serialize_key() + ); + + $lst = implode(';', $records); + + $this->client->request( + 'POST', $route, array( + 'name' => 'panier', + 'desc' => 'mon beau panier', + 'lst' => $lst) + ); + + $response = $this->client->getResponse(); + + $query = self::$core->getEntityManager()->createQuery( + 'SELECT COUNT(b.id) FROM \Entities\Basket b' + ); + + $count = $query->getSingleScalarResult(); + + $this->assertEquals(1, $count); + + $this->assertEquals(302, $response->getStatusCode()); + + $query = self::$core->getEntityManager()->createQuery( + 'SELECT b FROM \Entities\Basket b' + ); + + + $basket = array_shift($query->getResult()); + /* @var $basket \Entities\Basket */ + $this->assertEquals(2, $basket->getElements()->count()); + } + + public function testRootPostJSON() + { + $route = '/baskets/'; + + $this->client->request( + 'POST' + , $route + , array( + 'name' => 'panier', + 'desc' => 'mon beau panier', + ) + , array() + , array( + "HTTP_ACCEPT" => "application/json" + ) + ); + + $response = $this->client->getResponse(); + + $query = self::$core->getEntityManager()->createQuery( + 'SELECT COUNT(b.id) FROM \Entities\Basket b' + ); + + $count = $query->getSingleScalarResult(); + + $this->assertEquals(1, $count); + + $this->assertEquals(200, $response->getStatusCode()); + } + + public function testCreateGet() + { + $route = '/baskets/create/'; + + $crawler = $this->client->request('GET', $route); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + + $filter = "form[action='/prod/baskets/']"; + $this->assertEquals(1, $crawler->filter($filter)->count()); + + $filter = "form[action='/prod/baskets/'] input[name='name']"; + $this->assertEquals(1, $crawler->filter($filter)->count()); + + $filter = "form[action='/prod/baskets/'] textarea[name='description']"; + $this->assertEquals(1, $crawler->filter($filter)->count()); + } + + public function testBasketGet() + { + $basket = $this->insertOneBasket(); + + $route = sprintf('/baskets/%s/', $basket->getId()); + + $crawler = $this->client->request('GET', $route); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + } + + public function testBasketDeleteElementPost() + { + /* @var $em \Doctrine\ORM\EntityManager */ + $em = self::$core->getEntityManager(); + + $basket = $this->insertOneBasket(); + + $record = self::$record_1; + + $basket_element = new \Entities\BasketElement(); + $basket_element->setBasket($basket); + $basket_element->setRecord($record); + $basket_element->setLastInBasket(); + + $basket->addBasketElement($basket_element); + + $em->persist($basket); + + $em->flush(); + + $route = sprintf( + "/baskets/%s/delete/%s/", $basket->getId(), $basket_element->getId() + ); + + $crawler = $this->client->request('POST', $route); + + $response = $this->client->getResponse(); + + $em = self::$core->getEntityManager(); + /* @var $em \Doctrine\ORM\EntityManager */ + + $em->refresh($basket); + + $this->assertEquals(302, $response->getStatusCode()); + + $this->assertEquals(0, $basket->getElements()->count()); + } + + public function testBasketDeleteElementPostJSON() + { + /* @var $em \Doctrine\ORM\EntityManager */ + $em = self::$core->getEntityManager(); + + $basket = $this->insertOneBasket(); + + $record = self::$record_1; + + $basket_element = new \Entities\BasketElement(); + $basket_element->setBasket($basket); + $basket_element->setRecord($record); + $basket_element->setLastInBasket(); + + $basket->addBasketElement($basket_element); + + $em->persist($basket); + + $em->flush(); + + $route = sprintf( + "/baskets/%s/delete/%s/", $basket->getId(), $basket_element->getId() + ); + + $crawler = $this->client->request( + 'POST', $route, array(), array(), array( + "HTTP_ACCEPT" => "application/json") + ); + + $response = $this->client->getResponse(); + + $em->refresh($basket); + + $this->assertEquals(200, $response->getStatusCode()); + + $this->assertEquals(0, $basket->getElements()->count()); + } + + public function testBasketDeletePost() + { + $basket = $this->insertOneBasket(); + + $route = sprintf('/baskets/%s/delete/', $basket->getId()); + + $crawler = $this->client->request('POST', $route); + + $response = $this->client->getResponse(); + + $query = self::$core->getEntityManager()->createQuery( + 'SELECT COUNT(b.id) FROM \Entities\Basket b' + ); + + $count = $query->getSingleScalarResult(); + + $this->assertEquals(0, $count); + + $this->assertEquals(302, $response->getStatusCode()); + } + + public function testBasketDeletePostJSON() + { + $basket = $this->insertOneBasket(); + + $route = sprintf('/baskets/%s/delete/', $basket->getId()); + + $crawler = $this->client->request( + 'POST', $route, array(), array(), array( + "HTTP_ACCEPT" => "application/json") + ); + + $this->client->getRequest()->setRequestFormat('json'); + + $response = $this->client->getResponse(); + + $query = self::$core->getEntityManager()->createQuery( + 'SELECT COUNT(b.id) FROM \Entities\Basket b' + ); + + $count = $query->getSingleScalarResult(); + + $this->assertEquals(0, $count); + + $this->assertEquals(200, $response->getStatusCode()); + } + + public function testBasketUpdatePost() + { + $basket = $this->insertOneBasket(); + + $route = sprintf('/baskets/%s/update/', $basket->getId()); + + $crawler = $this->client->request( + 'POST', $route, array( + 'name' => 'new_name', + 'description' => 'new_desc') + ); + + $response = $this->client->getResponse(); + + $em = self::$core->getEntityManager(); + /* @var $em \Doctrine\ORM\EntityManager */ + $basket = $em->getRepository('Entities\Basket')->find($basket->getId()); + + $this->assertEquals('new_name', $basket->getName()); + $this->assertEquals('new_desc', $basket->getDescription()); + + $this->assertEquals(302, $response->getStatusCode()); + } + + public function testBasketUpdatePostJSON() + { + $basket = $this->insertOneBasket(); + + $route = sprintf('/baskets/%s/update/', $basket->getId()); + + $crawler = $this->client->request( + 'POST', $route, array( + 'name' => 'new_name', + 'description' => 'new_desc' + ), array(), array( + "HTTP_ACCEPT" => "application/json") + ); + + $response = $this->client->getResponse(); + + $em = self::$core->getEntityManager(); + /* @var $em \Doctrine\ORM\EntityManager */ + $basket = $em->getRepository('Entities\Basket')->find($basket->getId()); + + $this->assertEquals('new_name', $basket->getName()); + $this->assertEquals('new_desc', $basket->getDescription()); + + $this->assertEquals(200, $response->getStatusCode()); + } + + public function testReorderGet() + { + $basket = $this->insertOneBasketEnv(); + + $route = sprintf("/baskets/%s/reorder/", $basket->getId()); + + $crawler = $this->client->request("GET", $route); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + + foreach ($basket->getElements() as $elements) + { + $filter = sprintf("form[action='/prod/baskets/%s/reorder/']", $elements->getId()); + $this->assertEquals(1, $crawler->filter($filter)->count()); + } + } + + public function testBasketUpdateGet() + { + $basket = $this->insertOneBasket(); + + $route = sprintf('/baskets/%s/update/', $basket->getId()); + + $crawler = $this->client->request( + 'GET', $route, array( + 'name' => 'new_name', + 'description' => 'new_desc') + ); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + + $filter = "form[action='/prod/baskets/" . $basket->getId() . "/update/']"; + $this->assertEquals($crawler->filter($filter)->count(), 1); + + $node = $crawler + ->filter('input[name=name]'); + + $this->assertEquals($basket->getName(), $node->attr('value')); + + $node = $crawler + ->filter('textarea[name=description]'); + + $this->assertEquals($basket->getDescription(), $node->text()); + } + + public function testBasketArchivedPost() + { + $basket = $this->insertOneBasket(); + + $route = sprintf('/baskets/%s/archive/', $basket->getId()); + + $crawler = $this->client->request('POST', $route, array('archive' => '1')); + + $response = $this->client->getResponse(); + + $em = self::$core->getEntityManager(); + /* @var $em \Doctrine\ORM\EntityManager */ + $basket = $em->getRepository('Entities\Basket')->find($basket->getId()); + + $this->assertTrue($basket->getArchived()); + + $crawler = $this->client->request('POST', $route, array('archive' => '0')); + + $response = $this->client->getResponse(); + + $em->refresh($basket); + + $this->assertFalse($basket->getArchived()); + + $this->assertEquals(302, $response->getStatusCode()); + } + + public function testBasketArchivedPostJSON() + { + $basket = $this->insertOneBasket(); + + $route = sprintf('/baskets/%s/archive/', $basket->getId()); + + $crawler = $this->client->request( + 'POST', $route, array( + 'archive' => '1' + ), array(), array( + "HTTP_ACCEPT" => "application/json" + ) + ); + + $response = $this->client->getResponse(); + + $em = self::$core->getEntityManager(); + /* @var $em \Doctrine\ORM\EntityManager */ + $basket = $em->getRepository('Entities\Basket')->find($basket->getId()); + + $this->assertTrue($basket->getArchived()); + + $crawler = $this->client->request( + 'POST', $route, array( + 'archive' => '0' + ), array(), array( + "HTTP_ACCEPT" => "application/json" + ) + ); + + $response = $this->client->getResponse(); + + $em->refresh($basket); + + $this->assertFalse($basket->getArchived()); + + $this->assertEquals(200, $response->getStatusCode()); + } + + public function testAddElementPost() + { + $basket = $this->insertOneBasket(); + + $route = sprintf('/baskets/%s/addElements/', $basket->getId()); + + $records = array( + self::$record_1->get_serialize_key(), + self::$record_2->get_serialize_key(), + ' ', + '42', + 'abhak', + self::$record_no_access->get_serialize_key(), + ); + + $lst = implode(';', $records); + + $crawler = $this->client->request('POST', $route, array('lst' => $lst)); + + $response = $this->client->getResponse(); + + $this->assertEquals(302, $response->getStatusCode()); + + $em = self::$core->getEntityManager(); + /* @var $em \Doctrine\ORM\EntityManager */ + $basket = $em->getRepository('Entities\Basket')->find($basket->getId()); + + $this->assertEquals(2, $basket->getElements()->count()); + } + + + public function testAddElementToValidationPost() + { + + $em = self::$core->getEntityManager(); + + $datas = $em->getRepository('Entities\ValidationData')->findAll(); + $countDatas = count($datas); + + $basket = $this->insertOneBasket(); + + $validationSession = new \Entities\ValidationSession(); + + $validationSession->setDescription('Une description au hasard'); + $validationSession->setName('Un nom de validation'); + + $expires = new \DateTime(); + $expires->modify('+1 week'); + + $validationSession->setExpires($expires); + $validationSession->setInitiator(self::$user); + + $em->persist($validationSession); + + $basket->setValidation($validationSession); + + $validationSession->setBasket($basket); + + $validationParticipant = new \Entities\ValidationParticipant(); + $validationParticipant->setSession($validationSession); + $validationParticipant->setUser(self::$user_alt1); + + $em->persist($validationParticipant); + + $validationSession->addValidationParticipant($validationParticipant); + + $em->flush(); + + $route = sprintf('/baskets/%s/addElements/', $basket->getId()); + + $records = array( + self::$record_1->get_serialize_key(), + self::$record_2->get_serialize_key(), + ' ', + '42', + 'abhak', + self::$record_no_access->get_serialize_key(), + ); + + $lst = implode(';', $records); + + $this->client->request('POST', $route, array('lst' => $lst)); + + $response = $this->client->getResponse(); + + $this->assertEquals(302, $response->getStatusCode()); + + $em = self::$core->getEntityManager(); + /* @var $em \Doctrine\ORM\EntityManager */ + $basket = $em->getRepository('Entities\Basket')->find($basket->getId()); + + $this->assertEquals(2, $basket->getElements()->count()); + + $datas = $em->getRepository('Entities\ValidationData')->findAll(); + + $this->assertTrue($countDatas < count($datas), 'assert that '.count($datas).' > '.$countDatas ); + } + + + public function testAddElementPostJSON() + { + $basket = $this->insertOneBasket(); + + $route = sprintf('/baskets/%s/addElements/', $basket->getId()); + + $records = array( + self::$record_1->get_serialize_key(), + self::$record_2->get_serialize_key() + ); + + $lst = implode(';', $records); + + $crawler = $this->client->request( + 'POST', $route, array( + 'lst' => $lst + ), array(), array( + "HTTP_ACCEPT" => "application/json" + ) + ); + + $response = $this->client->getResponse(); + + $em = self::$core->getEntityManager(); + /* @var $em \Doctrine\ORM\EntityManager */ + $basket = $em->getRepository('Entities\Basket')->find($basket->getId()); + + $this->assertEquals(200, $response->getStatusCode()); + + $this->assertEquals(2, $basket->getElements()->count()); + } + + public function testRouteStealElements() + { + $em = self::$core->getEntityManager(); + + $BasketElement = $this->insertOneBasketElement(); + + $Basket_1 = $BasketElement->getBasket(); + + $Basket_2 = $this->insertOneBasket(); + + $route = sprintf('/baskets/%s/stealElements/', $Basket_2->getId()); + + $this->client->request( + 'POST', $route, array( + 'elements' => array($BasketElement->getId(), 'ufdsd') + ), array() + ); + + $response = $this->client->getResponse(); + + $this->assertTrue($response->isRedirect()); + + $em = self::$core->getEntityManager(); + /* @var $em \Doctrine\ORM\EntityManager */ + + $basket = $em->getRepository('Entities\Basket')->find($Basket_1->getId()); + $this->assertInstanceOf('\Entities\Basket', $basket); + $this->assertEquals(0, $basket->getElements()->count()); + + $basket = $em->getRepository('Entities\Basket')->find($Basket_2->getId()); + $this->assertInstanceOf('\Entities\Basket', $basket); + $this->assertEquals(1, $basket->getElements()->count()); + + } + + public function testRouteStealElementsJson() + { + $em = self::$core->getEntityManager(); + + $BasketElement = $this->insertOneBasketElement(); + + $Basket_1 = $BasketElement->getBasket(); + + $Basket_2 = $this->insertOneBasket(); + + $route = sprintf('/baskets/%s/stealElements/', $Basket_2->getId()); + + $this->client->request( + 'POST', $route, array( + 'elements' => array($BasketElement->getId()) + ), array() + , array( + "HTTP_ACCEPT" => "application/json" + ) + ); + + $response = $this->client->getResponse(); + + + $this->assertEquals(200, $response->getStatusCode()); + + $datas = (array) json_decode($response->getContent()); + + $this->assertArrayHasKey('message', $datas); + $this->assertArrayHasKey('success', $datas); + $this->assertTrue($datas['success']); + + $basket = $em->getRepository('Entities\Basket')->find($Basket_1->getId()); + $this->assertInstanceOf('\Entities\Basket', $basket); + $this->assertEquals(0, $basket->getElements()->count()); + + $basket = $em->getRepository('Entities\Basket')->find($Basket_2->getId()); + $this->assertInstanceOf('\Entities\Basket', $basket); + $this->assertEquals(1, $basket->getElements()->count()); + + } + + /** + * Test when i remove a basket, all relations are removed too : + * - basket elements + * - validations sessions + * - validation participants + */ + public function testRemoveBasket() + { + $basket = $this->insertOneBasketEnv(); + + $em = self::$core->getEntityManager(); + /* @var $em \Doctrine\ORM\EntityManager */ + $basket = $em->find("Entities\Basket", $basket->getId()); + + $em->remove($basket); + + $em->flush(); + + $query = $em->createQuery( + 'SELECT COUNT(v.id) FROM \Entities\ValidationParticipant v' + ); + + $count = $query->getSingleScalarResult(); + + $this->assertEquals(0, $count); + + $query = $em->createQuery( + 'SELECT COUNT(b.id) FROM \Entities\BasketElement b' + ); + + $count = $query->getSingleScalarResult(); + + $this->assertEquals(0, $count); + + $query = $em->createQuery( + 'SELECT COUNT(v.id) FROM \Entities\ValidationSession v' + ); + + $count = $query->getSingleScalarResult(); + + $this->assertEquals(0, $count); + + + $query = $em->createQuery( + 'SELECT COUNT(b.id) FROM \Entities\Basket b' + ); + + $count = $query->getSingleScalarResult(); + + $this->assertEquals(0, $count); + } + +} diff --git a/tests/Alchemy/Phrasea/Controller/Prod/BridgeTest.php b/tests/Alchemy/Phrasea/Controller/Prod/BridgeTest.php new file mode 100644 index 0000000000..39c6701ed0 --- /dev/null +++ b/tests/Alchemy/Phrasea/Controller/Prod/BridgeTest.php @@ -0,0 +1,438 @@ +client = $this->createClient(); + try + { + self::$api = Bridge_Api::get_by_api_name(appbox::get_instance(\bootstrap::getCore()), 'apitest'); + } + catch (Bridge_Exception_ApiNotFound $e) + { + self::$api = Bridge_Api::create(appbox::get_instance(\bootstrap::getCore()), 'apitest'); + } + + try + { + self::$account = Bridge_Account::load_account_from_distant_id(appbox::get_instance(\bootstrap::getCore()), self::$api, self::$user, 'kirikoo'); + } + catch (Bridge_Exception_AccountNotFound $e) + { + self::$account = Bridge_Account::create(appbox::get_instance(\bootstrap::getCore()), self::$api, self::$user, 'kirikoo', 'coucou'); + } + } + + public function tearDown() + { + parent::tearDown(); + if (self::$api instanceof Bridge_Api) + self::$api->delete(); + if (self::$account instanceof Bridge_Account) + self::$account->delete(); + } + + public function createApplication() + { + return include realpath(__DIR__ . '/../../../../../lib/Alchemy/Phrasea/Application/Prod.php'); + } + + /** + * @todo create a new basket dont take an existing one + */ + public function testManager() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $accounts = Bridge_Account::get_accounts_by_user($appbox, self::$user); + $usr_id = self::$user->get_id(); + + $basket = $this->insertOneBasket(); + + $crawler = $this->client->request('POST', '/bridge/manager/', array('ssel' => $basket->getId())); + $pageContent = $this->client->getResponse()->getContent(); + $this->assertTrue($this->client->getResponse()->isOk()); + } + + public function testLogin() + { + $this->client->request('GET', '/bridge/login/Apitest/'); + $test = new Bridge_Api_Apitest(registry::get_instance(), new Bridge_Api_Auth_None()); + $this->assertTrue($this->client->getResponse()->getStatusCode() == 302); + $this->assertTrue($this->client->getResponse()->isRedirect($test->get_auth_url())); + } + + public function testCallBackFailed() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $session = $appbox->get_session(); + $crawler = $this->client->request('GET', '/bridge/callback/unknow_api/'); + $this->assertTrue($this->client->getResponse()->isOk()); + } + + public function testCallBackAccountAlreadyDefined() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $crawler = $this->client->request('GET', '/bridge/callback/apitest/'); + $this->assertTrue($this->client->getResponse()->isOk()); + $pageContent = $this->client->getResponse()->getContent(); + //check for errors in the crawler + $phpunit = $this; + $crawler + ->filter('div') + ->reduce(function ($node, $i) use ($phpunit) + { + if (!$node->getAttribute('class')) + { + return false; + } + elseif ($node->getAttribute('class') == 'error_auth') + { + $phpunit->fail("Erreur callback"); + } + }); + $settings = self::$account->get_settings(); + $this->assertEquals("kikoo", $settings->get("auth_token")); + $this->assertEquals("kooki", $settings->get("refresh_token")); + $this->assertEquals("biloute", $settings->get("access_token")); + $settings->delete("auth_token"); + $settings->delete("refresh_token"); + $settings->delete("access_token"); + } + + public function testCallBackAccountNoDefined() + { + if (self::$account instanceof Bridge_Account) + self::$account->delete(); + $crawler = $this->client->request('GET', '/bridge/callback/apitest/'); + $this->assertTrue($this->client->getResponse()->isOk()); + $phpunit = $this; + $crawler + ->filter('div') + ->reduce(function ($node, $i) use ($phpunit) + { + if (!$node->getAttribute('class')) + { + return false; + } + elseif ($node->getAttribute('class') == 'error_auth') + { + $phpunit->fail("Erreur callback"); + } + }); + try + { + self::$account = Bridge_Account::load_account_from_distant_id(appbox::get_instance(\bootstrap::getCore()), self::$api, self::$user, 'kirikoo'); + $settings = self::$account->get_settings(); + $this->assertEquals("kikoo", $settings->get("auth_token")); + $this->assertEquals("kooki", $settings->get("refresh_token")); + $this->assertEquals("biloute", $settings->get("access_token")); + $settings->delete("auth_token"); + $settings->delete("refresh_token"); + $settings->delete("access_token"); + } + catch (Bridge_Exception_AccountNotFound $e) + { + $this->fail("No account created after callback"); + } + + if (!self::$account instanceof Bridge_Account) + self::$account = Bridge_Account::create(appbox::get_instance(\bootstrap::getCore()), self::$api, self::$user, 'kirikoo', 'coucou'); + } + + public function testLogout() + { + self::$account->get_settings()->set("auth_token", "somethingNotNull"); //connected + $url = sprintf('/bridge/adapter/%d/logout/', self::$account->get_id()); + $this->client->request('GET', $url); + $redirect = sprintf("/prod/bridge/adapter/%s/load-elements/%s/", self::$account->get_id(), self::$account->get_api()->get_connector()->get_default_element_type()); + $this->assertTrue($this->client->getResponse()->isRedirect($redirect)); + $this->assertNull(self::$account->get_settings()->get("auth_token")); + } + + public function testLoadElements() + { + self::$account->get_settings()->set("auth_token", "somethingNotNull"); //connected + $url = sprintf("/bridge/adapter/%s/load-elements/%s/", self::$account->get_id(), self::$account->get_api()->get_connector()->get_default_element_type()); + $account = new Bridge_Account(appbox::get_instance(\bootstrap::getCore()), self::$api, self::$account->get_id()); + $crawler = $this->client->request('GET', $url, array("page" => 1)); + $this->assertTrue($this->client->getResponse()->isOk()); + $this->assertNotContains(self::$account->get_api()->generate_login_url(registry::get_instance(), self::$account->get_api()->get_connector()->get_name()), $this->client->getResponse()->getContent()); + } + + public function testLoadRecords() + { + self::$account->get_settings()->set("auth_token", "somethingNotNull"); //connected + $url = sprintf("/bridge/adapter/%s/load-records/", self::$account->get_id()); + $crawler = $this->client->request('GET', $url, array("page" => 1)); + $elements = Bridge_Element::get_elements_by_account(appbox::get_instance(\bootstrap::getCore()), self::$account); + $this->assertTrue($this->client->getResponse()->isOk()); + $this->assertEquals(sizeof($elements), $crawler->filterXPath("//table/tr")->count()); + $this->assertNotContains(self::$account->get_api()->generate_login_url(registry::get_instance(), self::$account->get_api()->get_connector()->get_name()), $this->client->getResponse()->getContent()); + } + + public function testLoadRecordsDisconnected() + { + $this->client->followRedirects(); + self::$account->get_settings()->set("auth_token", null); //deconnected + $url = sprintf("/bridge/adapter/%s/load-records/", self::$account->get_id()); + $crawler = $this->client->request('GET', $url, array("page" => 1)); + $pageContent = $this->client->getResponse()->getContent(); + $this->assertContains($url, $pageContent); + $this->deconnected($crawler, $pageContent); + } + + public function testLoadContainers() + { + self::$account->get_settings()->set("auth_token", "somethingNotNull"); //connected + $url = sprintf("/bridge/adapter/%s/load-containers/%s/", self::$account->get_id(), self::$account->get_api()->get_connector()->get_default_container_type()); + $crawler = $this->client->request('GET', $url, array("page" => 1)); + $elements = Bridge_Element::get_elements_by_account(appbox::get_instance(\bootstrap::getCore()), self::$account); + $this->assertTrue($this->client->getResponse()->isOk()); + $this->assertNotContains(self::$account->get_api()->generate_login_url(registry::get_instance(), self::$account->get_api()->get_connector()->get_name()), $this->client->getResponse()->getContent()); + } + + public function testLoadContainersDisconnected() + { + $this->client->followRedirects(); + self::$account->get_settings()->set("auth_token", null); //deconnected + $url = sprintf("/bridge/adapter/%s/load-containers/%s/", self::$account->get_id(), self::$account->get_api()->get_connector()->get_default_container_type()); + $crawler = $this->client->request('GET', $url, array("page" => 1)); + $pageContent = $this->client->getResponse()->getContent(); + $this->assertContains($url, $pageContent); + $this->deconnected($crawler, $pageContent); + } + + public function testLoadElementsDisconnected() + { + $this->client->followRedirects(); + self::$account->get_settings()->set("auth_token", null); //deconnected + $url = sprintf("/bridge/adapter/%s/load-elements/%s/", self::$account->get_id(), self::$account->get_api()->get_connector()->get_default_element_type()); + $crawler = $this->client->request('GET', $url, array("page" => 1)); + $this->assertTrue($this->client->getResponse()->isOk()); + $pageContent = $this->client->getResponse()->getContent(); + $this->assertContains($url, $pageContent); + $this->deconnected($crawler, $pageContent); + } + + public function testLogoutDeconnected() + { + $this->client->followRedirects(); + self::$account->get_settings()->set("auth_token", null); //deconnected + $url = sprintf('/bridge/adapter/%d/logout/', self::$account->get_id()); + $crawler = $this->client->request('GET', $url); + $pageContent = $this->client->getResponse()->getContent(); + $this->assertContains("/adapter/" . self::$account->get_id() . "/logout/", $pageContent); + $this->deconnected($crawler, $pageContent); + } + + public function testActionDeconnected() + { + $this->client->followRedirects(); + self::$account->get_settings()->set("auth_token", null); //deconnected + $url = sprintf("/bridge/action/%s/une action/%s/", self::$account->get_id(), self::$account->get_api()->get_connector()->get_default_element_type()); + $crawler = $this->client->request('GET', $url); + $pageContent = $this->client->getResponse()->getContent(); + $this->assertContains($url, $pageContent); + $this->deconnected($crawler, $pageContent); + } + + public function testActionUnknow() + { + self::$account->get_settings()->set("auth_token", "somethingNotNull"); //connected + $url = sprintf("/bridge/action/%s/ajjfhfjozqd/%s/", self::$account->get_id(), self::$account->get_api()->get_connector()->get_default_element_type()); + try + { + $crawler = $this->client->request('GET', $url, array("elements_list" => "1;2;3")); + $this->fail("expected Exception here"); + } + catch (Exception $e) + { + + } + + try + { + $crawler = $this->client->request('POST', $url, array("elements_list" => "1;2;3")); + $this->fail("expected Exception here"); + } + catch (Exception $e) + { + + } + } + + public function testActionModifyTooManyElements() + { + self::$account->get_settings()->set("auth_token", "somethingNotNull"); //connected + $url = sprintf("/bridge/action/%s/modify/%s/", self::$account->get_id(), self::$account->get_api()->get_connector()->get_default_element_type()); + $crawler = $this->client->request('GET', $url, array("element_list" => "1_2;1_3;1_4")); + $redirect = sprintf("/prod/bridge/adapter/%s/load-elements/%s/", self::$account->get_id(), self::$account->get_api()->get_connector()->get_default_element_type()); + $this->assertTrue($this->client->getResponse()->isRedirect()); + $this->assertContains($redirect, $this->client->getResponse()->headers->get("location")); + $this->assertContains("error=", $this->client->getResponse()->headers->get("location")); + $this->assertNotContains(self::$account->get_api()->generate_login_url(registry::get_instance(), self::$account->get_api()->get_connector()->get_name()), $this->client->getResponse()->getContent()); + + $this->client->request('POST', $url, array("element_list" => "1_2;1_3;1_4")); + $this->assertTrue($this->client->getResponse()->isRedirect()); + } + + public function testActionModifyElement() + { + self::$account->get_settings()->set("auth_token", "somethingNotNull"); //connected + $url = sprintf("/bridge/action/%s/modify/%s/", self::$account->get_id(), self::$account->get_api()->get_connector()->get_default_element_type()); + $crawler = $this->client->request('GET', $url, array("elements_list" => "element123qcs789")); + $this->assertTrue($this->client->getResponse()->isOk()); + $this->assertNotContains(self::$account->get_api()->generate_login_url(registry::get_instance(), self::$account->get_api()->get_connector()->get_name()), $this->client->getResponse()->getContent()); + + $this->client->request('POST', $url, array("elements_list" => "element123qcs789")); + $this->assertTrue($this->client->getResponse()->isRedirect()); + } + + public function testActionModifyElementError() + { + self::$account->get_settings()->set("auth_token", "somethingNotNull"); + Bridge_Api_Apitest::$hasError = true; + $url = sprintf("/bridge/action/%s/modify/%s/", self::$account->get_id(), self::$account->get_api()->get_connector()->get_default_element_type()); + $this->client->request('POST', $url, array("elements_list" => "element123qcs789")); + $this->assertTrue($this->client->getResponse()->isOk()); + } + + public function testActionModifyElementException() + { + self::$account->get_settings()->set("auth_token", "somethingNotNull"); + Bridge_Api_Apitest::$hasException = true; + $url = sprintf("/bridge/action/%s/modify/%s/", self::$account->get_id(), self::$account->get_api()->get_connector()->get_default_element_type()); + $this->client->request('POST', $url, array("elements_list" => "element123qcs789")); + $this->assertTrue($this->client->getResponse()->isRedirect()); + $this->assertRegexp('/error/', $this->client->getResponse()->headers->get('location')); + } + + public function testActionDeleteElement() + { + self::$account->get_settings()->set("auth_token", "somethingNotNull"); + $url = sprintf("/bridge/action/%s/deleteelement/%s/", self::$account->get_id(), self::$account->get_api()->get_connector()->get_default_element_type()); + $this->client->request('GET', $url, array("elements_list" => "element123qcs789")); + $this->assertTrue($this->client->getResponse()->isOk()); + + Bridge_Api_Apitest::$hasException = true; + $url = sprintf("/bridge/action/%s/deleteelement/%s/", self::$account->get_id(), self::$account->get_api()->get_connector()->get_default_element_type()); + $this->client->request('POST', $url, array("elements_list" => "element123qcs789")); + $this->assertTrue($this->client->getResponse()->isRedirect()); + $this->assertRegexp('/error/', $this->client->getResponse()->headers->get('location')); + + $url = sprintf("/bridge/action/%s/deleteelement/%s/", self::$account->get_id(), self::$account->get_api()->get_connector()->get_default_element_type()); + $this->client->request('POST', $url, array("elements_list" => "element123qcs789")); + $this->assertTrue($this->client->getResponse()->isRedirect()); + } + + public function testActionCreateContainer() + { + self::$account->get_settings()->set("auth_token", "somethingNotNull"); //connected + + $url = sprintf("/bridge/action/%s/createcontainer/%s/", self::$account->get_id(), self::$account->get_api()->get_connector()->get_default_container_type()); + $this->client->request('GET', $url); + $this->assertTrue($this->client->getResponse()->isOk()); + + + Bridge_Api_Apitest::$hasException = true; + $url = sprintf("/bridge/action/%s/createcontainer/%s/", self::$account->get_id(), self::$account->get_api()->get_connector()->get_default_element_type()); + $this->client->request('POST', $url); + $this->assertTrue($this->client->getResponse()->isRedirect()); + $this->assertRegexp('/error/', $this->client->getResponse()->headers->get('location')); + + $url = sprintf("/bridge/action/%s/createcontainer/%s/", self::$account->get_id(), self::$account->get_api()->get_connector()->get_default_container_type()); + $this->client->request('POST', $url, array('title' => 'test', 'description' => 'description')); + $this->assertTrue($this->client->getResponse()->isRedirect()); + $this->assertRegexp('/success/', $this->client->getResponse()->headers->get('location')); + } + + /** + * @todo no templates declared for modify a container in any apis + */ + public function testActionModifyContainer() + { + $this->markTestSkipped("No templates declared for modify a container in any apis"); + self::$account->get_settings()->set("auth_token", "somethingNotNull"); //connected + $url = sprintf("/bridge/action/%s/modify/%s/", self::$account->get_id(), self::$account->get_api()->get_connector()->get_default_container_type()); + $crawler = $this->client->request('GET', $url, array("elements_list" => "containerudt456shn")); + $this->assertTrue($this->client->getResponse()->isOk()); + $pageContent = $this->client->getResponse()->getContent(); + $this->assertNotContains(self::$account->get_api()->generate_login_url(registry::get_instance(), self::$account->get_api()->get_connector()->get_name()), $this->client->getResponse()->getContent()); + + $this->client->request('POST', $url, array("elements_list" => "containerudt456shn")); + $this->assertTrue($this->client->getResponse()->isOk()); + } + + public function testActionMoveInto() + { + self::$account->get_settings()->set("auth_token", "somethingNotNull"); //connected + $url = sprintf("/bridge/action/%s/moveinto/%s/", self::$account->get_id(), self::$account->get_api()->get_connector()->get_default_element_type()); + $crawler = $this->client->request('GET', $url, array("elements_list" => "containerudt456shn", 'destination' => self::$account->get_api()->get_connector()->get_default_container_type())); + $this->assertNotContains("http://dev.phrasea.net/prod/bridge/login/youtube/", $this->client->getResponse()->getContent()); + $this->assertTrue($this->client->getResponse()->isOk()); + + $this->client->request('POST', $url, array("elements_list" => "containerudt456shn", 'destination' => self::$account->get_api()->get_connector()->get_default_container_type())); + $this->assertRegexp('/success/', $this->client->getResponse()->headers->get('location')); + $this->assertTrue($this->client->getResponse()->isRedirect()); + + Bridge_Api_Apitest::$hasException = true; + $this->client->request('POST', $url, array("elements_list" => "containerudt456shn", 'destination' => self::$account->get_api()->get_connector()->get_default_container_type())); + $this->assertRegexp('/error/', $this->client->getResponse()->headers->get('location')); + $this->assertTrue($this->client->getResponse()->isRedirect()); + } + + public function deconnected($crawler, $pageContent) + { + $this->assertTrue($this->client->getResponse()->isOk()); + $this->assertContains("prod/bridge/login/" . mb_strtolower(self::$account->get_api()->get_connector()->get_name()) . "/", $pageContent); + } + + public function testUpload() + { + self::$account->get_settings()->set("auth_token", "somethingNotNull"); + $url = "/bridge/upload/"; + $this->client->request('GET', $url, array("account_id" => self::$account->get_id())); + + $response = $this->client->getResponse(); + $this->assertTrue($response->isOk()); + + $records = array( + self::$record_1->get_serialize_key() + ); + + Bridge_Api_Apitest::$hasError = true; + $lst = implode(';', $records); + $this->client->request('POST', $url, array("account_id" => self::$account->get_id(), 'lst' => $lst)); + $response = $this->client->getResponse(); + $this->assertTrue($response->isOk()); + + $this->client->request('POST', $url, array("account_id" => self::$account->get_id(), 'lst' => $lst)); + $response = $this->client->getResponse(); + $this->assertTrue($response->isRedirect()); + } + +} diff --git a/tests/Alchemy/Phrasea/Controller/Prod/EditTest.php b/tests/Alchemy/Phrasea/Controller/Prod/EditTest.php new file mode 100644 index 0000000000..bd9ce41674 --- /dev/null +++ b/tests/Alchemy/Phrasea/Controller/Prod/EditTest.php @@ -0,0 +1,98 @@ +client = $this->createClient(); + } + + /** + * Default route test + */ + public function testRouteSlash() + { + $this->client->request('POST', '/records/edit/', array('lst' => self::$record_1->get_serialize_key())); + + $response = $this->client->getResponse(); + + $this->assertTrue($response->isOk()); + } + + public function testApply() + { + $this->client->request('POST', '/records/edit/apply/', array('lst' => self::$record_1->get_serialize_key())); + + $response = $this->client->getResponse(); + + $this->assertTrue($response->isOk()); + } + + + public function testVocabulary() + { + $this->client->request('GET', '/records/edit/vocabulary/Zanzibar/'); + + $response = $this->client->getResponse(); + $this->assertTrue($response->isOk()); + $datas = json_decode($response->getContent()); + $this->assertFalse($datas->success); + + $this->client->request('GET', '/records/edit/vocabulary/User/'); + + $response = $this->client->getResponse(); + $this->assertTrue($response->isOk()); + $datas = json_decode($response->getContent()); + $this->assertFalse($datas->success); + + $params = array('sbas_id'=>self::$collection->get_sbas_id()); + $this->client->request('GET', '/records/edit/vocabulary/Zanzibar/', $params); + + $response = $this->client->getResponse(); + $this->assertTrue($response->isOk()); + $datas = json_decode($response->getContent()); + $this->assertFalse($datas->success); + + $params = array('sbas_id'=>self::$collection->get_sbas_id()); + $this->client->request('GET', '/records/edit/vocabulary/User/', $params); + + $response = $this->client->getResponse(); + $this->assertTrue($response->isOk()); + $datas = json_decode($response->getContent()); + $this->assertTrue($datas->success); + } + +} diff --git a/tests/Alchemy/Phrasea/Controller/Prod/FeedTest.php b/tests/Alchemy/Phrasea/Controller/Prod/FeedTest.php new file mode 100644 index 0000000000..7d41766553 --- /dev/null +++ b/tests/Alchemy/Phrasea/Controller/Prod/FeedTest.php @@ -0,0 +1,473 @@ +client = $this->createClient(); + + $this->feed = Feed_Adapter::create( + $appbox, self::$user, $this->feed_title, $this->feed_subtitle + ); + + $this->publisher = Feed_Publisher_Adapter::getPublisher( + $appbox, $this->feed, self::$user + ); + + $this->entry = Feed_Entry_Adapter::create( + $appbox + , $this->feed + , $this->publisher + , $this->entry_title + , $this->entry_subtitle + , $this->entry_authorname + , $this->entry_authormail + ); + + $this->item = Feed_Entry_Item::create($appbox, $this->entry, self::$record_1); + } + + public function tearDown() + { + if ($this->feed instanceof Feed_Adapter) + { + $this->feed->delete(); + } + else if($this->entry instanceof Feed_Entry_Adapter) + { + $this->entry->delete(); + if ($this->publisher instanceof Feed_Publisher_Adapter) + { + $this->publisher->delete(); + } + } + + parent::tearDown(); + } + + public function createApplication() + { + return require __DIR__ . '/../../../../../lib/Alchemy/Phrasea/Application/Prod.php'; + } + + public function testRequestAvailable() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $crawler = $this->client->request('POST', '/feeds/requestavailable/'); + $this->assertTrue($this->client->getResponse()->isOk()); + $feeds = Feed_Collection::load_all($appbox, self::$user); + foreach ($feeds->get_feeds() as $one_feed) + { + if ($one_feed->is_publisher(self::$user)) + { + $this->assertEquals(1, $crawler->filterXPath("//input[@value='" . $one_feed->get_id() . "']")->count()); + } + } + } + + public function testEntryCreate() + { + $params = array( + "feed_id" => $this->feed->get_id() + , "title" => "salut" + , "subtitle" => "coucou" + , "author_name" => "robert" + , "author_email" => "robert@kikoo.mail" + , 'lst' => self::$record_1->get_serialize_key() + ); + + $crawler = $this->client->request('POST', '/feeds/entry/create/', $params); + $this->assertTrue($this->client->getResponse()->isOk()); + $this->assertEquals("application/json", $this->client->getResponse()->headers->get("content-type")); + $pageContent = json_decode($this->client->getResponse()->getContent()); + $this->assertTrue(is_object($pageContent)); + $this->assertFalse($pageContent->error); + $this->assertFalse($pageContent->message); + } + + public function testEntryCreateError() + { + $params = array( + "feed_id" => 'unknow' + , "title" => "salut" + , "subtitle" => "coucou" + , "author_name" => "robert" + , "author_email" => "robert@kikoo.mail" + , 'lst' => self::$record_1->get_serialize_key() + ); + $crawler = $this->client->request('POST', '/feeds/entry/create/', $params); + $this->assertTrue($this->client->getResponse()->isOk()); + $this->assertEquals("application/json", $this->client->getResponse()->headers->get("content-type")); + $pageContent = json_decode($this->client->getResponse()->getContent()); + $this->assertTrue(is_object($pageContent)); + $this->assertTrue($pageContent->error); + $this->assertTrue(is_string($pageContent->message)); + } + + public function testEntryEdit() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $crawler = $this->client->request('GET', '/feeds/entry/' . $this->entry->get_id() . '/edit/'); + $pageContent = $this->client->getResponse()->getContent(); + + foreach ($this->entry->get_content() as $content) + { + $this->assertEquals(1, $crawler->filterXPath("//input[@value='" . $content->get_id() . "' and @name='item_id']")->count()); + } + + $this->assertEquals(1, $crawler->filterXPath("//form[@action='/prod/feeds/entry/" . $this->entry->get_id() . "/update/']")->count()); + $this->assertEquals(1, $crawler->filterXPath("//input[@value='" . $this->entry_title . "']")->count()); + $this->assertEquals($this->entry_subtitle, $crawler->filterXPath("//textarea[@id='feed_add_subtitle']")->text()); + $this->assertEquals(1, $crawler->filterXPath("//input[@value='" . $this->entry_authorname . "']")->count()); + $this->assertEquals(1, $crawler->filterXPath("//input[@value='" . $this->entry_authormail . "']")->count()); + } + + public function testEntryEditUnauthorized() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $feed = Feed_Adapter::create( + $appbox, self::$user_alt1, $this->feed_title, $this->feed_subtitle + ); + + $publisher = Feed_Publisher_Adapter::getPublisher( + $appbox, $feed, self::$user_alt1 + ); + + $entry = Feed_Entry_Adapter::create( + $appbox + , $feed + , $publisher + , $this->entry_title + , $this->entry_subtitle + , $this->entry_authorname + , $this->entry_authormail + ); + + + $crawler = $this->client->request('GET', '/feeds/entry/' . $entry->get_id() . '/edit/'); + + $response = $this->client->getResponse(); + + $this->assertFalse($response->isOk()); + + $feed->delete(); + } + + public function testEntryUpdate() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $params = array( + "feed_id" => $this->feed->get_id() + , "title" => "dog" + , "subtitle" => "cat" + , "author_name" => "bird" + , "author_email" => "mouse" + , 'lst' => self::$record_1->get_serialize_key() + ); + + $crawler = $this->client->request('POST', '/feeds/entry/' . $this->entry->get_id() . '/update/', $params); + $this->assertTrue($this->client->getResponse()->isOk()); + $this->assertEquals("application/json", $this->client->getResponse()->headers->get("content-type")); + $pageContent = json_decode($this->client->getResponse()->getContent()); + $this->assertTrue(is_object($pageContent)); + $this->assertFalse($pageContent->error); + $this->assertTrue(is_string($pageContent->message)); + $this->assertTrue(is_string($pageContent->datas)); + $this->assertRegExp("/entry_" . $this->entry->get_id() . "/", $pageContent->datas); + } + + public function testEntryUpdateNotFound() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $params = array( + "feed_id" => 9999999 + , "title" => "dog" + , "subtitle" => "cat" + , "author_name" => "bird" + , "author_email" => "mouse" + , 'lst' => self::$record_1->get_serialize_key() + ); + + $crawler = $this->client->request('POST', '/feeds/entry/99999999/update/', $params); + + $response = $this->client->getResponse(); + + $pageContent = json_decode($response->getContent()); + + $this->assertTrue($response->isOk()); + $this->assertEquals("application/json", $response->headers->get("content-type")); + $this->assertTrue(is_object($pageContent)); + $this->assertTrue($pageContent->error); + $this->assertTrue(is_string($pageContent->message)); + } + + public function testEntryUpdateFailed() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $params = array( + "feed_id" => 9999999 + , "title" => "dog" + , "subtitle" => "cat" + , "author_name" => "bird" + , "author_email" => "mouse" + , 'sorted_lst' => self::$record_1->get_serialize_key() . ";" . self::$record_2->get_serialize_key() . ";12345;" . "unknow_unknow" + ); + + $crawler = $this->client->request('POST', '/feeds/entry/' . $this->entry->get_id() . '/update/', $params); + + $response = $this->client->getResponse(); + + $this->assertTrue($response->isOk()); + $pageContent = json_decode($response->getContent()); + + $this->assertEquals("application/json", $response->headers->get("content-type")); + $this->assertTrue(is_object($pageContent)); + $this->assertTrue($pageContent->error); + $this->assertTrue(is_string($pageContent->message)); + } + + public function testEntryUpdateUnauthorized() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + /** + * I CREATE A FEED THAT IS NOT MINE + * */ + $feed = Feed_Adapter::create($appbox, self::$user_alt1, "salut", 'coucou'); + $publisher = Feed_Publisher_Adapter::getPublisher($appbox, $feed, self::$user_alt1); + $entry = Feed_Entry_Adapter::create($appbox, $feed, $publisher, "hello", "coucou", "salut", "bonjour"); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_1); + + $params = array( + "feed_id" => $feed->get_id() + , "title" => "dog" + , "subtitle" => "cat" + , "author_name" => "bird" + , "author_email" => "mouse" + , 'lst' => self::$record_1->get_serialize_key() + ); + + $crawler = $this->client->request('POST', '/feeds/entry/' . $entry->get_id() . '/update/', $params); + + $response = $this->client->getResponse(); + + $this->assertTrue($response->isOk()); + $pageContent = json_decode($response->getContent()); + + $this->assertEquals("application/json", $response->headers->get("content-type")); + $this->assertTrue(is_object($pageContent)); + $this->assertTrue($pageContent->error); + $this->assertTrue(is_string($pageContent->message)); + + $feed->delete(); + } + + public function testDelete() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $crawler = $this->client->request('POST', '/feeds/entry/' . $this->entry->get_id() . '/delete/'); + + $this->assertTrue($this->client->getResponse()->isOk()); + $this->assertEquals("application/json", $this->client->getResponse()->headers->get("content-type")); + + $pageContent = json_decode($this->client->getResponse()->getContent()); + + $this->assertTrue(is_object($pageContent)); + $this->assertFalse($pageContent->error); + $this->assertTrue(is_string($pageContent->message)); + + try + { + Feed_Entry_Adapter::load_from_id($appbox, $this->entry->get_id()); + $this->fail("Failed to delete entry"); + } + catch (Exception $e) + { + + } + } + + public function testDeleteNotFound() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $crawler = $this->client->request('POST', '/feeds/entry/9999999/delete/'); + + $response = $this->client->getResponse(); + + $pageContent = json_decode($this->client->getResponse()->getContent()); + + $this->assertTrue($response->isOk()); + $this->assertEquals("application/json", $response->headers->get("content-type")); + $this->assertTrue(is_object($pageContent)); + $this->assertTrue($pageContent->error); + $this->assertTrue(is_string($pageContent->message)); + } + + public function testDeleteUnauthorized() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + /** + * I CREATE A FEED + * */ + $feed = Feed_Adapter::create($appbox, self::$user_alt1, "salut", 'coucou'); + + $publisher = Feed_Publisher_Adapter::getPublisher($appbox, $feed, self::$user_alt1); + $entry = Feed_Entry_Adapter::create($appbox, $feed, $publisher, "hello", "coucou", "salut", "bonjour"); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_1); + + $crawler = $this->client->request('POST', '/feeds/entry/' . $entry->get_id() . '/delete/'); + + $response = $this->client->getResponse(); + + $pageContent = json_decode($this->client->getResponse()->getContent()); + + $this->assertTrue($response->isOk()); + $this->assertEquals("application/json", $response->headers->get("content-type")); + $this->assertTrue(is_object($pageContent)); + $this->assertTrue($pageContent->error); + $this->assertTrue(is_string($pageContent->message)); + + $feed->delete(); + } + + public function testRoot() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $crawler = $this->client->request('GET', '/feeds/'); + + $pageContent = $this->client->getResponse()->getContent(); + + $this->assertTrue($this->client->getResponse()->isOk()); + + $feeds = Feed_Collection::load_all($appbox, self::$user); + + foreach ($feeds->get_feeds() as $one_feed) + { + $path = "//div[@class='submenu']/a[@href='/prod/feeds/feed/" . $one_feed->get_id() . "/']"; + + $msg = sprintf("user %s has access to feed %s", self::$user->get_id(), $one_feed->get_id()); + + if ($one_feed->has_access(self::$user)) + { + $this->assertEquals(1, $crawler->filterXPath($path)->count(), $msg); + } + else + { + $this->fail('Feed_collection::load_all should return feed where I got access'); + } + } + } + + public function testGetFeed() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $feeds = Feed_Collection::load_all($appbox, self::$user); + + $crawler = $this->client->request('GET', '/feeds/feed/' . $this->feed->get_id() . "/"); + $pageContent = $this->client->getResponse()->getContent(); + + foreach ($feeds->get_feeds() as $one_feed) + { + $path = "//div[@class='submenu']/a[@href='/prod/feeds/feed/" . $one_feed->get_id() . "/']"; + + $msg = sprintf("user %s has access to feed %s", self::$user->get_id(), $one_feed->get_id()); + + if ($one_feed->has_access(self::$user)) + { + $this->assertEquals(1, $crawler->filterXPath($path)->count(), $msg); + } + else + { + $this->fail('Feed_collection::load_all should return feed where I got access'); + } + } + } + + public function testSuscribeAggregate() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $feeds = Feed_Collection::load_all($appbox, self::$user); + $crawler = $this->client->request('GET', '/feeds/subscribe/aggregated/'); + $this->assertTrue($this->client->getResponse()->isOk()); + $this->assertEquals("application/json", $this->client->getResponse()->headers->get("content-type")); + $pageContent = json_decode($this->client->getResponse()->getContent()); + $this->assertTrue(is_object($pageContent)); + $this->assertTrue(is_string($pageContent->texte)); + $suscribe_link = $feeds->get_aggregate()->get_user_link(registry::get_instance(), self::$user, Feed_Adapter::FORMAT_RSS, null, false)->get_href(); + $this->assertContains($suscribe_link, $pageContent->texte); + } + + public function testSuscribe() + { + $crawler = $this->client->request('GET', '/feeds/subscribe/' . $this->feed->get_id() . '/'); + $this->assertTrue($this->client->getResponse()->isOk()); + $this->assertEquals("application/json", $this->client->getResponse()->headers->get("content-type")); + $pageContent = json_decode($this->client->getResponse()->getContent()); + $this->assertTrue(is_object($pageContent)); + $this->assertTrue(is_string($pageContent->texte)); + $suscribe_link = $this->feed->get_user_link(registry::get_instance(), self::$user, Feed_Adapter::FORMAT_RSS, null, false)->get_href(); + $this->assertContains($suscribe_link, $pageContent->texte); + } + +} diff --git a/tests/Alchemy/Phrasea/Controller/Prod/LanguageTest.php b/tests/Alchemy/Phrasea/Controller/Prod/LanguageTest.php new file mode 100644 index 0000000000..3dce73b26a --- /dev/null +++ b/tests/Alchemy/Phrasea/Controller/Prod/LanguageTest.php @@ -0,0 +1,47 @@ +client = $this->createClient(); + } + + public function createApplication() + { + return require __DIR__ . '/../../../../../lib/Alchemy/Phrasea/Application/Prod.php'; + } + + public function testRootPost() + { + $route = '/language/'; + + $this->client->request("GET", $route); + $this->assertTrue($this->client->getResponse()->isOk()); + $this->assertEquals("application/json", $this->client->getResponse()->headers->get("content-type")); + $pageContent = json_decode($this->client->getResponse()->getContent()); + $this->assertTrue(is_object($pageContent)); + } + +} diff --git a/tests/Alchemy/Phrasea/Controller/Prod/MoveCollectionTest.php b/tests/Alchemy/Phrasea/Controller/Prod/MoveCollectionTest.php new file mode 100644 index 0000000000..5809437f08 --- /dev/null +++ b/tests/Alchemy/Phrasea/Controller/Prod/MoveCollectionTest.php @@ -0,0 +1,64 @@ +client = $this->createClient(); + } + + /** + * Default route test + */ + public function testRouteSlash() + { + $this->client->request('POST', '/records/movecollection/', array('lst' => self::$record_1->get_serialize_key())); + + $response = $this->client->getResponse(); + + $this->assertTrue($response->isOk()); + } + + public function testApply() + { + + $this->client->request('POST', '/records/movecollection/apply/', array('lst' => self::$record_1->get_serialize_key(), 'base_id' => self::$collection->get_base_id())); + + $response = $this->client->getResponse(); + + $this->assertTrue($response->isOk()); + } + +} diff --git a/tests/Alchemy/Phrasea/Controller/Prod/MustacheLoaderTest.php b/tests/Alchemy/Phrasea/Controller/Prod/MustacheLoaderTest.php new file mode 100644 index 0000000000..03fc9b7183 --- /dev/null +++ b/tests/Alchemy/Phrasea/Controller/Prod/MustacheLoaderTest.php @@ -0,0 +1,61 @@ +client = $this->createClient(); + } + + public function testRouteSlash() + { + + $this->client->request('GET', '/MustacheLoader/'); + + $response = $this->client->getResponse(); + /* @var $response \Symfony\Component\HttpFoundation\Response */ + + $this->assertEquals(400, $response->getStatusCode()); + $this->assertFalse($response->isOk()); + + $this->client->request('GET', '/MustacheLoader/', array('template' => '/../../../../config/config.yml')); + + $response = $this->client->getResponse(); + /* @var $response \Symfony\Component\HttpFoundation\Response */ + + $this->assertEquals(400, $response->getStatusCode()); + $this->assertFalse($response->isOk()); + + $this->client->request('GET', '/MustacheLoader/', array('template' => 'patator_lala')); + + $response = $this->client->getResponse(); + /* @var $response \Symfony\Component\HttpFoundation\Response */ + + $this->assertEquals(404, $response->getStatusCode()); + $this->assertFalse($response->isOk()); + + $this->client->request('GET', '/MustacheLoader/', array('template' => 'Feedback-Badge')); + + $response = $this->client->getResponse(); + /* @var $response \Symfony\Component\HttpFoundation\Response */ + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertTrue($response->isOk()); + } + +} diff --git a/tests/Alchemy/Phrasea/Controller/Prod/PrinterTest.php b/tests/Alchemy/Phrasea/Controller/Prod/PrinterTest.php new file mode 100644 index 0000000000..c916c843c5 --- /dev/null +++ b/tests/Alchemy/Phrasea/Controller/Prod/PrinterTest.php @@ -0,0 +1,96 @@ +client = $this->createClient(); + } + + /** + * Default route test + */ + public function testRouteSlash() + { + $records = array( + self::$record_1->get_serialize_key(), + self::$record_2->get_serialize_key() + ); + + $lst = implode(';', $records); + + $crawler = $this->client->request('POST', '/printer/', array('lst' => $lst)); + + $response = $this->client->getResponse(); + + $this->assertTrue($response->isOk()); + } + + public function testRoutePrintPdf() + { + $records = array( + self::$record_1->get_serialize_key(), + self::$record_2->get_serialize_key(), + self::$record_3->get_serialize_key(), + self::$record_4->get_serialize_key(), + ); + + $lst = implode(';', $records); + + $layouts = array( + \Alchemy\Phrasea\Out\Module\PDF::LAYOUT_PREVIEW, + \Alchemy\Phrasea\Out\Module\PDF::LAYOUT_PREVIEWCAPTION, + \Alchemy\Phrasea\Out\Module\PDF::LAYOUT_PREVIEWCAPTIONTDM, + \Alchemy\Phrasea\Out\Module\PDF::LAYOUT_THUMBNAILLIST, + \Alchemy\Phrasea\Out\Module\PDF::LAYOUT_THUMBNAILGRID + ); + + foreach ($layouts as $layout) + { + $crawler = $this->client->request('POST', '/printer/print.pdf', array( + 'lst' => $lst, + 'lay' => $layout + ) + ); + + $response = $this->client->getResponse(); + + $this->assertEquals("application/pdf", $response->headers->get("content-type")); + + $this->assertTrue($response->isOk()); + } + } + +} diff --git a/tests/Alchemy/Phrasea/Controller/Prod/PushTest.php b/tests/Alchemy/Phrasea/Controller/Prod/PushTest.php new file mode 100644 index 0000000000..b2f7f7de5c --- /dev/null +++ b/tests/Alchemy/Phrasea/Controller/Prod/PushTest.php @@ -0,0 +1,167 @@ +client = $this->createClient(); + } + + /** + * Default route test + */ + public function testRoutePOSTSendSlash() + { + $route = '/push/sendform/'; + + $this->client->request('POST', $route); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('UTF-8', $response->getCharset()); + } + + public function testRoutePOSTValidateSlash() + { + $route = '/push/validateform/'; + + $this->client->request('POST', $route); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('UTF-8', $response->getCharset()); + } + + public function testRoutePOSTsend() + { + $route = '/push/send/'; + + $records = array( + self::$record_1->get_serialize_key() + , self::$record_2->get_serialize_key() + ); + + $receivers = array( + array('usr_id'=>self::$user_alt1->get_id(), 'HD'=>1) + , array('usr_id'=>self::$user_alt2->get_id(), 'HD'=>0) + ); + + $params = array( + 'lst' => implode(';', $records) + , 'participants' => $receivers + ); + + $this->client->request('POST', $route, $params); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('UTF-8', $response->getCharset()); + + $datas = (array) json_decode($response->getContent()); + + $this->assertArrayHasKey('message', $datas); + $this->assertArrayHasKey('success', $datas); + + $this->assertTrue($datas['success'], 'Result is successful'); + } + + public function testRoutePOSTvalidate() + { + $route = '/push/validate/'; + + $records = array( + self::$record_1->get_serialize_key() + , self::$record_2->get_serialize_key() + ); + + $participants = array( + array( + 'usr_id' => self::$user_alt1->get_id(), + 'agree'=> 0, + 'see_others'=> 1, + 'HD'=> 0, + ) + , array( + 'usr_id' => self::$user_alt2->get_id(), + 'agree'=> 1, + 'see_others'=> 0, + 'HD'=> 1, + ) + ); + + $params = array( + 'lst' => implode(';', $records) + , 'participants' => $participants + ); + + $this->client->request('POST', $route, $params); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('UTF-8', $response->getCharset()); + + $datas = (array) json_decode($response->getContent()); + + $this->assertArrayHasKey('message', $datas); + $this->assertArrayHasKey('success', $datas); + + $this->assertTrue($datas['success'], 'Result is successful'); + } + + public function testRouteGETsearchuser() + { + $route = '/push/search-user/'; + + $params = array( + 'query' => '' + ); + + $this->client->request('GET', $route, $params); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('UTF-8', $response->getCharset()); + + $datas = (array) json_decode($response->getContent()); + + $this->assertTrue(is_array($datas), 'Json is valid'); + } + +} diff --git a/tests/Alchemy/Phrasea/Controller/Prod/RootTest.php b/tests/Alchemy/Phrasea/Controller/Prod/RootTest.php new file mode 100644 index 0000000000..867a774956 --- /dev/null +++ b/tests/Alchemy/Phrasea/Controller/Prod/RootTest.php @@ -0,0 +1,56 @@ +client = $this->createClient(); + } + + /** + * Default route test + */ + public function testRouteSlash() + { + $crawler = $this->client->request('GET', '/'); + + + $response = $this->client->getResponse(); + /* @var $response \Symfony\Component\HttpFoundation\Response */ + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('UTF-8', $response->getCharset()); + + } + +} diff --git a/tests/Alchemy/Phrasea/Controller/Prod/StoryTest.php b/tests/Alchemy/Phrasea/Controller/Prod/StoryTest.php new file mode 100644 index 0000000000..51431a8e0d --- /dev/null +++ b/tests/Alchemy/Phrasea/Controller/Prod/StoryTest.php @@ -0,0 +1,228 @@ +client = $this->createClient(); + $this->purgeDatabase(); + } + + public function createApplication() + { + return require __DIR__ . '/../../../../../lib/Alchemy/Phrasea/Application/Prod.php'; + } + + public function testRootPost() + { + $route = "/story/"; + + $collections = self::$core->getAuthenticatedUser() + ->ACL() + ->get_granted_base(array('canaddrecord')); + + $collection = array_shift($collections); + + $crawler = $this->client->request( + 'POST', $route, array( + 'base_id' => $collection->get_base_id(), + 'name' => 'test story' + ) + ); + + $response = $this->client->getResponse(); + + $this->assertEquals(302, $response->getStatusCode()); + + $query = self::$core->getEntityManager()->createQuery( + 'SELECT COUNT(w.id) FROM \Entities\StoryWZ w' + ); + + $count = $query->getSingleScalarResult(); + + $this->assertEquals(1, $count); + } + + public function testRootPostJSON() + { + $route = "/story/"; + + $collections = self::$core->getAuthenticatedUser() + ->ACL() + ->get_granted_base(array('canaddrecord')); + + $collection = array_shift($collections); + + $crawler = $this->client->request( + 'POST', $route, array( + 'base_id' => $collection->get_base_id(), + 'name' => 'test story'), array(), array( + "HTTP_ACCEPT" => "application/json") + ); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + } + + public function testCreateGet() + { + $route = "/story/create/"; + + $crawler = $this->client->request('GET', $route); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + + $filter = "form[action='/prod/story/']"; + $this->assertEquals(1, $crawler->filter($filter)->count()); + + $filter = "form[action='/prod/story/'] input[name='name']"; + $this->assertEquals(1, $crawler->filter($filter)->count()); + + $filter = "form[action='/prod/story/'] select[name='base_id']"; + $this->assertEquals(1, $crawler->filter($filter)->count()); + } + + public function testByIds() + { + $story = self::$story_1; + + $route = sprintf("/story/%d/%d/", $story->get_sbas_id(), $story->get_record_id()); + + $crawler = $this->client->request('GET', $route); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + } + + public function testAddElementsToStory() + { + $story = self::$story_1; + + $route = sprintf("/story/%s/%s/addElements/", $story->get_sbas_id(), $story->get_record_id()); + + $records = array( + self::$record_1->get_serialize_key(), + self::$record_2->get_serialize_key() + ); + + $lst = implode(';', $records); + + $crawler = $this->client->request('POST', $route, array('lst' => $lst)); + + $response = $this->client->getResponse(); + + $this->assertEquals(302, $response->getStatusCode()); + + $this->assertEquals(2, self::$story_1->get_children()->get_count()); + } + + public function testAddElementsToStoryJSON() + { + $story = self::$story_1; + + $route = sprintf("/story/%s/%s/addElements/", $story->get_sbas_id(), $story->get_record_id()); + + $records = array( + self::$record_1->get_serialize_key(), + self::$record_2->get_serialize_key() + ); + + $lst = implode(';', $records); + + $crawler = $this->client->request('POST', $route, array('lst' => $lst) + , array(), array( + "HTTP_ACCEPT" => "application/json")); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + + $this->assertEquals(2, self::$story_1->get_children()->get_count()); + } + + public function testRemoveElementFromStory() + { + $story = self::$story_1; + + $records = array( + self::$record_1, + self::$record_2 + ); + + $totalRecords = count($records); + $n = 0; + foreach ($records as $record) + { + /* @var $record \record_adapter */ + $route = sprintf( + "/story/%s/%s/delete/%s/%s/" + , $story->get_sbas_id() + , $story->get_record_id() + , $record->get_sbas_id() + , $record->get_record_id() + ); + + if (($n % 2) === 0) + { + $crawler = $this->client->request('POST', $route); + + $response = $this->client->getResponse(); + + $this->assertEquals(302, $response->getStatusCode()); + } + else + { + $crawler = $this->client->request( + 'POST', $route, array(), array(), array( + "HTTP_ACCEPT" => "application/json") + ); + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + } + $n++; + + $this->assertEquals($totalRecords - $n, self::$story_1->get_children()->get_count()); + } + } + +} diff --git a/tests/Alchemy/Phrasea/Controller/Prod/TooltipTest.php b/tests/Alchemy/Phrasea/Controller/Prod/TooltipTest.php new file mode 100644 index 0000000000..5c68f8e19e --- /dev/null +++ b/tests/Alchemy/Phrasea/Controller/Prod/TooltipTest.php @@ -0,0 +1,196 @@ +client = $this->createClient(); + } + + public function createApplication() + { + return require __DIR__ . '/../../../../../lib/Alchemy/Phrasea/Application/Prod.php'; + } + + public function testRouteBasket() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $basket = $this->insertOneBasket(); + + $crawler = $this->client->request('POST', '/tooltip/basket/' . $basket->getId() . '/'); + $pageContent = $this->client->getResponse()->getContent(); + $this->assertTrue($this->client->getResponse()->isOk()); + + $crawler = $this->client->request('POST', '/tootltip/basket/notanid/'); + $pageContent = $this->client->getResponse()->getContent(); + $this->assertFalse($this->client->getResponse()->isOk()); + + $crawler = $this->client->request('POST', '/tooltip/basket/-5/'); + $pageContent = $this->client->getResponse()->getContent(); + $this->assertFalse($this->client->getResponse()->isOk()); + } + + public function testRoutePreview() + { + $route = '/tooltip/preview/' . self::$record_1->get_sbas_id() + . '/' . self::$record_1->get_record_id() . '/'; + + $crawler = $this->client->request('POST', $route); + $pageContent = $this->client->getResponse()->getContent(); + $this->assertTrue($this->client->getResponse()->isOk()); + } + + public function testRouteCaption() + { + + $route_base = '/tooltip/caption/' . self::$record_1->get_sbas_id() + . '/' . self::$record_1->get_record_id() . '/%s/'; + + $routes = array( + sprintf($route_base, 'answer') + , sprintf($route_base, 'lazaret') + , sprintf($route_base, 'preview') + , sprintf($route_base, 'basket') + , sprintf($route_base, 'overview') + ); + + foreach ($routes as $route) + { + $crawler = $this->client->request('POST', $route); + $pageContent = $this->client->getResponse()->getContent(); + $this->assertTrue($this->client->getResponse()->isOk()); + } + } + + public function testRouteCaptionSearchEngine() + { + $route_base = '/tooltip/caption/' . self::$record_1->get_sbas_id() + . '/' . self::$record_1->get_record_id() . '/%s/'; + + $routes = array( + sprintf($route_base, 'answer') + , sprintf($route_base, 'lazaret') + , sprintf($route_base, 'preview') + , sprintf($route_base, 'basket') + , sprintf($route_base, 'overview') + ); + + foreach ($routes as $route) + { + $option = new \searchEngine_options(); + $crawler = $this->client->request('POST', $route, array('options_serial' => serialize($option))); + + $this->assertTrue($this->client->getResponse()->isOk()); + } + } + + public function testRouteTCDatas() + { + $route = '/tooltip/tc_datas/' . self::$record_1->get_sbas_id() + . '/' . self::$record_1->get_record_id() . '/'; + + $crawler = $this->client->request('POST', $route); + $pageContent = $this->client->getResponse()->getContent(); + $this->assertTrue($this->client->getResponse()->isOk()); + } + + public function testRouteMetasFieldInfos() + { + $databox = self::$record_1->get_databox(); + + foreach ($databox->get_meta_structure() as $field) + { + $route = '/tooltip/metas/FieldInfos/' . $databox->get_sbas_id() + . '/' . $field->get_id() . '/'; + + $crawler = $this->client->request('POST', $route); + $pageContent = $this->client->getResponse()->getContent(); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + } + } + + public function testRouteMetasDCESInfos() + { + $databox = self::$record_1->get_databox(); + $dces = array( + databox_field::DCES_CONTRIBUTOR => new databox_Field_DCES_Contributor() + , databox_field::DCES_COVERAGE => new databox_Field_DCES_Coverage() + , databox_field::DCES_CREATOR => new databox_Field_DCES_Creator() + , databox_field::DCES_DESCRIPTION => new databox_Field_DCES_Description() + ); + + foreach ($databox->get_meta_structure() as $field) + { + $dces_element = array_shift($dces); + $field->set_dces_element($dces_element); + + $route = '/tooltip/DCESInfos/' . $databox->get_sbas_id() + . '/' . $field->get_id() . '/'; + + if ($field->get_dces_element() !== null) + { + $crawler = $this->client->request('POST', $route); + $this->assertGreaterThan(0, strlen($this->client->getResponse()->getContent())); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + } + else + { + $crawler = $this->client->request('POST', $route); + $this->assertEquals(0, strlen($this->client->getResponse()->getContent())); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + } + } + } + + public function testRouteMetaRestrictions() + { + $databox = self::$record_1->get_databox(); + + foreach ($databox->get_meta_structure() as $field) + { + + $route = '/tooltip/metas/restrictionsInfos/' . $databox->get_sbas_id() + . '/' . $field->get_id() . '/'; + + $crawler = $this->client->request('POST', $route); + $this->assertGreaterThan(0, strlen($this->client->getResponse()->getContent())); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + } + } + + public function testRouteStory() + { + $databox = self::$story_1->get_databox(); + + + $route = '/tooltip/Story/' . $databox->get_sbas_id() + . '/' . self::$story_1->get_record_id() . '/'; + + $this->client->request('POST', $route); + $this->assertTrue($this->client->getResponse()->isOk()); + } + + public function testUser() + { + + $route = '/tooltip/user/' . self::$user->get_id() . '/'; + $this->client->request('POST', $route); + $this->assertTrue($this->client->getResponse()->isOk()); + } + +} diff --git a/tests/Alchemy/Phrasea/Controller/Prod/UsrListsTest.php b/tests/Alchemy/Phrasea/Controller/Prod/UsrListsTest.php new file mode 100644 index 0000000000..b21669aa02 --- /dev/null +++ b/tests/Alchemy/Phrasea/Controller/Prod/UsrListsTest.php @@ -0,0 +1,483 @@ +client = $this->createClient(); + } + + /** + * Default route test + */ + public function testRouteSlash() + { + $entry1 = $this->insertOneUsrListEntry(self::$user, self::$user); + $entry2 = $this->insertOneUsrListEntry(self::$user, self::$user_alt1); + $entry3 = $this->insertOneUsrListEntry(self::$user, self::$user); + $entry4 = $this->insertOneUsrListEntry(self::$user, self::$user_alt1); + $entry5 = $this->insertOneUsrListEntry(self::$user_alt1, self::$user_alt1); + $entry6 = $this->insertOneUsrListEntry(self::$user_alt1, self::$user_alt2); + + $route = '/lists/all/'; + + $this->client->request('GET', $route, array(), array(), array("HTTP_CONTENT_TYPE" => "application/json", "HTTP_ACCEPT" => "application/json")); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('UTF-8', $response->getCharset()); + + $datas = (array) json_decode($response->getContent()); + + $this->assertEquals(4, count($datas['result'])); + } + + protected function checkList($list, $owners = true, $users = true) + { + $this->assertInstanceOf('stdClass', $list); + $this->assertObjectHasAttribute('name', $list); + $this->assertObjectHasAttribute('created', $list); + $this->assertObjectHasAttribute('updated', $list); + $this->assertObjectHasAttribute('owners', $list); + $this->assertObjectHasAttribute('users', $list); + + if ($owners) + $this->assertTrue(count($list->owners) > 0); + + foreach ($list->owners as $owner) + { + $this->checkOwner($owner); + } + + if ($users) + $this->assertTrue(count($list->users) > 0); + + foreach ($list->users as $user) + { + $this->checkUser($user); + } + } + + public function testPostList() + { + $route = '/lists/list/'; + + $this->client->request('POST', $route); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('UTF-8', $response->getCharset()); + + $datas = (array) json_decode($response->getContent()); + + $this->assertArrayHasKey('success', $datas); + $this->assertArrayHasKey('message', $datas); + + $this->assertFalse($datas['success']); + + $this->client->request('POST', $route, array('name' => 'New List')); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('UTF-8', $response->getCharset()); + + $datas = (array) json_decode($response->getContent()); + + $this->assertArrayHasKey('success', $datas); + $this->assertArrayHasKey('message', $datas); + + $this->assertTrue($datas['success']); + } + + public function testGetList() + { + $entry = $this->insertOneUsrListEntry(self::$user, self::$user_alt1); + $list_id = $entry->getList()->getId(); + + $route = '/lists/list/' . $list_id . '/'; + + $this->client->request('GET', $route, array(), array(), array("HTTP_CONTENT_TYPE" => "application/json", "HTTP_ACCEPT" => "application/json")); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('UTF-8', $response->getCharset()); + +// $datas = (array) json_decode($response->getContent()); +// +// $this->assertTrue(is_array($datas)); +// $this->assertArrayhasKey('result', $datas); +// $this->checkList($datas['result']); + } + + public function testPostUpdate() + { + $entry = $this->insertOneUsrListEntry(self::$user, self::$user_alt1); + $list_id = $entry->getList()->getId(); + + $route = '/lists/list/' . $list_id . '/update/'; + + $this->client->request('POST', $route); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('UTF-8', $response->getCharset()); + + $datas = (array) json_decode($response->getContent()); + + $this->assertArrayHasKey('success', $datas); + $this->assertArrayHasKey('message', $datas); + + $this->assertFalse($datas['success']); + + + $this->client->request('POST', $route, array('name' => 'New NAME')); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('UTF-8', $response->getCharset()); + + $datas = (array) json_decode($response->getContent()); + + $this->assertArrayHasKey('success', $datas); + $this->assertArrayHasKey('message', $datas); + + $this->assertTrue($datas['success']); + } + + public function testPostDelete() + { + $entry = $this->insertOneUsrListEntry(self::$user, self::$user_alt1); + $list_id = $entry->getList()->getId(); + + $route = '/lists/list/' . $list_id . '/delete/'; + + $this->client->request('POST', $route); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('UTF-8', $response->getCharset()); + + $datas = (array) json_decode($response->getContent()); + + $this->assertArrayHasKey('success', $datas); + $this->assertArrayHasKey('message', $datas); + + $this->assertTrue($datas['success']); + + $repository = self::$core->getEntityManager()->getRepository('Entities\UsrList'); + + $this->assertNull($repository->find($list_id)); + } + + public function testPostRemoveEntry() + { + $entry = $this->insertOneUsrListEntry(self::$user, self::$user_alt1); + $list_id = $entry->getList()->getId(); + $usr_id = $entry->getUser()->get_id(); + $entry_id = $entry->getId(); + + $route = '/lists/list/' . $list_id . '/remove/' . $usr_id . '/'; + + $this->client->request('POST', $route, array(), array(), array("HTTP_CONTENT_TYPE" => "application/json", "HTTP_ACCEPT" => "application/json")); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('UTF-8', $response->getCharset()); + + $datas = (array) json_decode($response->getContent()); + + $this->assertArrayHasKey('success', $datas); + $this->assertArrayHasKey('message', $datas); + + $this->assertTrue($datas['success']); + + $repository = self::$core->getEntityManager()->getRepository('Entities\UsrListEntry'); + + $this->assertNull($repository->find($entry_id)); + } + + public function testPostAddEntry() + { + $list = $this->insertOneUsrList(self::$user); + + $this->assertEquals(0, $list->getEntries()->count()); + + $route = '/lists/list/' . $list->getId() . '/add/'; + + $this->client->request('POST', $route, array('usr_ids' => array(self::$user->get_id())), array(), array("HTTP_CONTENT_TYPE" => "application/json", "HTTP_ACCEPT" => "application/json")); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('UTF-8', $response->getCharset()); + + $datas = (array) json_decode($response->getContent()); + + $this->assertArrayHasKey('success', $datas); + $this->assertArrayHasKey('message', $datas); + + $this->assertTrue($datas['success']); + + $repository = self::$core->getEntityManager()->getRepository('Entities\UsrList'); + + $list = $repository->find($list->getId()); + + $this->assertEquals(1, $list->getEntries()->count()); + } + + public function testPostShareList() + { + $list = $this->insertOneUsrList(self::$user); + + $this->assertEquals(1, $list->getOwners()->count()); + + $route = '/lists/list/' . $list->getId() . '/share/' . self::$user_alt1->get_id() . '/'; + + $this->client->request('POST', $route); + + $response = $this->client->getResponse(); + + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEquals('UTF-8', $response->getCharset()); + + + $route = '/lists/list/' . $list->getId() . '/share/' . self::$user_alt1->get_id() . '/'; + + $this->client->request('POST', $route, array('role' => 'general')); + + $response = $this->client->getResponse(); + + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEquals('UTF-8', $response->getCharset()); + + + + $route = '/lists/list/' . $list->getId() . '/share/' . self::$user_alt1->get_id() . '/'; + + $this->client->request('POST', $route, array('role' => \Entities\UsrListOwner::ROLE_ADMIN)); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('UTF-8', $response->getCharset()); + + + $datas = (array) json_decode($response->getContent()); + + $this->assertArrayHasKey('success', $datas); + $this->assertArrayHasKey('message', $datas); + + $this->assertTrue($datas['success']); + + $repository = self::$core->getEntityManager()->getRepository('Entities\UsrList'); + + $list = $repository->find($list->getId()); + + $this->assertEquals(2, $list->getOwners()->count()); + } + + public function testPostUnShareList() + { + + $list = $this->insertOneUsrList(self::$user); + + $this->assertEquals(1, $list->getOwners()->count()); + + $route = '/lists/list/' . $list->getId() . '/share/' . self::$user_alt1->get_id() . '/'; + + $this->client->request('POST', $route, array('role' => \Entities\UsrListOwner::ROLE_ADMIN)); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('UTF-8', $response->getCharset()); + + + $datas = (array) json_decode($response->getContent()); + + $this->assertArrayHasKey('success', $datas); + $this->assertArrayHasKey('message', $datas); + + $this->assertTrue($datas['success']); + + $repository = self::$core->getEntityManager()->getRepository('Entities\UsrList'); + + $list = $repository->find($list->getId()); + + $this->assertEquals(2, $list->getOwners()->count()); + + + + $route = '/lists/list/' . $list->getId() . '/unshare/' . self::$user_alt1->get_id() . '/'; + + $this->client->request('POST', $route); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('UTF-8', $response->getCharset()); + + $datas = (array) json_decode($response->getContent()); + + $this->assertTrue($datas['success']); + + $repository = self::$core->getEntityManager()->getRepository('Entities\UsrList'); + + $list = $repository->find($list->getId()); + + self::$core->getEntityManager()->refresh($list); + + $this->assertEquals(1, $list->getOwners()->count()); + } + + public function testPostUnShareFail() + { + + $list = $this->insertOneUsrList(self::$user); + + $this->assertEquals(1, $list->getOwners()->count()); + + $route = '/lists/list/' . $list->getId() . '/share/' . self::$user_alt1->get_id() . '/'; + + $this->client->request('POST', $route, array('role' => \Entities\UsrListOwner::ROLE_ADMIN)); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('UTF-8', $response->getCharset()); + + + $datas = (array) json_decode($response->getContent()); + + $this->assertArrayHasKey('success', $datas); + $this->assertArrayHasKey('message', $datas); + + $this->assertTrue($datas['success']); + + + + $route = '/lists/list/' . $list->getId() . '/share/' . self::$user->get_id() . '/'; + + $this->client->request('POST', $route, array('role' => \Entities\UsrListOwner::ROLE_USER)); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('UTF-8', $response->getCharset()); + + + $datas = (array) json_decode($response->getContent()); + + $this->assertArrayHasKey('success', $datas); + $this->assertArrayHasKey('message', $datas); + + $this->assertFalse($datas['success']); + + + + $route = '/lists/list/' . $list->getId() . '/share/' . self::$user_alt1->get_id() . '/'; + + $this->client->request('POST', $route, array('role' => \Entities\UsrListOwner::ROLE_USER)); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('UTF-8', $response->getCharset()); + + + $datas = (array) json_decode($response->getContent()); + + $this->assertArrayHasKey('success', $datas); + $this->assertArrayHasKey('message', $datas); + + $this->assertTrue($datas['success']); + + + + $repository = self::$core->getEntityManager()->getRepository('Entities\UsrList'); + + $list = $repository->find($list->getId()); + + $this->assertEquals(2, $list->getOwners()->count()); + + + + $route = '/lists/list/' . $list->getId() . '/unshare/' . self::$user_alt1->get_id() . '/'; + + $this->client->request('POST', $route); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('UTF-8', $response->getCharset()); + + $datas = (array) json_decode($response->getContent()); + + $this->assertTrue($datas['success']); + } + + protected function checkOwner($owner) + { + $this->assertInstanceOf('stdClass', $owner); + $this->assertObjectHasAttribute('usr_id', $owner); + $this->assertObjectHasAttribute('display_name', $owner); + $this->assertObjectHasAttribute('position', $owner); + $this->assertObjectHasAttribute('job', $owner); + $this->assertObjectHasAttribute('company', $owner); + $this->assertObjectHasAttribute('email', $owner); + $this->assertObjectHasAttribute('role', $owner); + $this->assertTrue(ctype_digit($owner->role)); + } + + protected function checkUser($user) + { + $this->assertInstanceOf('stdClass', $user); + $this->assertObjectHasAttribute('usr_id', $user); + $this->assertObjectHasAttribute('display_name', $user); + $this->assertObjectHasAttribute('position', $user); + $this->assertObjectHasAttribute('job', $user); + $this->assertObjectHasAttribute('company', $user); + $this->assertObjectHasAttribute('email', $user); + } + +} diff --git a/tests/Alchemy/Phrasea/Controller/Prod/WorkZoneTest.php b/tests/Alchemy/Phrasea/Controller/Prod/WorkZoneTest.php new file mode 100644 index 0000000000..bfa9c4e485 --- /dev/null +++ b/tests/Alchemy/Phrasea/Controller/Prod/WorkZoneTest.php @@ -0,0 +1,213 @@ +client = $this->createClient(); + $this->purgeDatabase(); + } + + public function createApplication() + { + return require __DIR__ . '/../../../../../lib/Alchemy/Phrasea/Application/Prod.php'; + } + + public function testRootGet() + { + + $this->insertOneWZ(); + + $route = "/WorkZone/"; + + $this->client->request('GET', $route); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + } + + public function testAttachStoryToWZ() + { + $story = self::$story_1; + /* @var $story \Record_Adapter */ + $route = sprintf("/WorkZone/attachStories/"); + + $this->client->request('POST', $route); + $response = $this->client->getResponse(); + + $this->assertEquals(400, $response->getStatusCode()); + $this->assertFalse($response->isOk()); + + $this->client->request('POST', $route, array('stories' => array($story->get_serialize_key()))); + $response = $this->client->getResponse(); + + $this->assertEquals(302, $response->getStatusCode()); + + $em = self::$core->getEntityManager(); + /* @var $em \Doctrine\ORM\EntityManager */ + $query = $em->createQuery( + 'SELECT COUNT(w.id) FROM \Entities\StoryWZ w' + ); + + $count = $query->getSingleScalarResult(); + + $this->assertEquals(1, $count); + + $story2 = self::$story_2; + + $stories = array($story->get_serialize_key(), $story2->get_serialize_key()); + + $this->client->request('POST', $route, array('stories' => $stories)); + $response = $this->client->getResponse(); + + $this->assertEquals(302, $response->getStatusCode()); + + + + $em = self::$core->getEntityManager(); + /* @var $em \Doctrine\ORM\EntityManager */ + $query = $em->createQuery( + 'SELECT COUNT(w.id) FROM \Entities\StoryWZ w' + ); + + $count = $query->getSingleScalarResult(); + + $this->assertEquals(2, $count); + + $query = $em->createQuery( + 'SELECT w FROM \Entities\StoryWZ w' + ); + + $storyWZ = $query->getResult(); + $em->remove(array_shift($storyWZ)); + + $em->flush(); + + + //attach JSON + $this->client->request('POST', $route, array('stories' => $story->get_serialize_key()), array(), array( + "HTTP_ACCEPT" => "application/json") + ); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + + //test already attached + $this->client->request('POST', $route, array('stories' => $story->get_serialize_key()), array(), array( + "HTTP_ACCEPT" => "application/json") + ); + + $response = $this->client->getResponse(); + + $this->assertEquals(200, $response->getStatusCode()); + } + + public function testDetachStoryFromWZ() + { + $story = self::$story_1; + + $route = sprintf("/WorkZone/detachStory/%s/%s/", $story->get_sbas_id(), $story->get_record_id()); + //story not yet Attched + + $this->client->request('POST', $route); + $response = $this->client->getResponse(); + + $this->assertEquals(404, $response->getStatusCode()); + $this->assertFalse($response->isOk()); + + //attach + $attachRoute = sprintf("/WorkZone/attachStories/"); + $this->client->request('POST', $attachRoute, array('stories' => array($story->get_serialize_key()))); + + $query = self::$core->getEntityManager()->createQuery( + 'SELECT COUNT(w.id) FROM \Entities\StoryWZ w' + ); + + $count = $query->getSingleScalarResult(); + + $this->assertEquals(1, $count); + + + //detach + $this->client->request('POST', $route); + $response = $this->client->getResponse(); + $this->assertEquals(302, $response->getStatusCode()); + + $query = self::$core->getEntityManager()->createQuery( + 'SELECT COUNT(w.id) FROM \Entities\StoryWZ w' + ); + + $count = $query->getSingleScalarResult(); + + $this->assertEquals(0, $count); + + //attach + $this->client->request('POST', $attachRoute, array('stories' => array($story->get_serialize_key()))); + + //detach JSON + $this->client->request('POST', $route, array(), array(), array( + "HTTP_ACCEPT" => "application/json") + ); + $response = $this->client->getResponse(); + $this->assertEquals(200, $response->getStatusCode()); + } + + public function testBrowse() + { + $this->client->request("GET", "/WorkZone/Browse/"); + $response = $this->client->getResponse(); + $this->assertTrue($response->isOk()); + } + + public function testBrowseSearch() + { + $this->client->request("GET", "/WorkZone/Browse/Search/"); + $response = $this->client->getResponse(); + $this->assertTrue($response->isOk()); + } + + public function testBrowseBasket() + { + $basket = $this->insertOneBasket(); + $this->client->request("GET", "/WorkZone/Browse/Basket/" . $basket->getId() . "/"); + $response = $this->client->getResponse(); + $this->assertTrue($response->isOk()); + } + + public function testDetachStoryFromWZNotFound() + { + $story = self::$story_1; + + $route = sprintf("/WorkZone/detachStory/%s/%s/", $story->get_sbas_id(), 'unknow'); + //story not yet Attched + } + +} diff --git a/tests/Alchemy/Phrasea/Controller/Root/RSSFeedTest.php b/tests/Alchemy/Phrasea/Controller/Root/RSSFeedTest.php new file mode 100644 index 0000000000..cb05832b98 --- /dev/null +++ b/tests/Alchemy/Phrasea/Controller/Root/RSSFeedTest.php @@ -0,0 +1,889 @@ +client = $this->createClient(); + self::$feed = Feed_Adapter::create(appbox::get_instance(\bootstrap::getCore()), self::$user, 'title', 'subtitle'); + self::$publisher = Feed_Publisher_Adapter::getPublisher(appbox::get_instance(\bootstrap::getCore()), self::$feed, self::$user); + self::$entry = Feed_Entry_Adapter::create(appbox::get_instance(\bootstrap::getCore()), self::$feed, self::$publisher, 'title_entry', 'subtitle', 'hello', "test@mail.com"); + Feed_Entry_Item::create(appbox::get_instance(\bootstrap::getCore()), self::$entry, self::$record_1); + Feed_Entry_Item::create(appbox::get_instance(\bootstrap::getCore()), self::$entry, self::$record_2); + self::$feed->set_public(true); + } + + public function tearDown() + { + if (self::$publisher instanceof Feed_Publisher_Adapter) + { + self::$publisher->delete(); + } + if (self::$entry instanceof Feed_Entry_Adapter) + { + self::$entry->delete(); + } + if (self::$feed instanceof Feed_Adapter) + { + self::$feed->delete(); + } + parent::tearDown(); + } + + public static function setUpBeforeClass() + { + parent::setUpBeforeClass(); + + $appbox = appbox::get_instance(\bootstrap::getCore()); + $auth = new Session_Authentication_None(self::$user); + $appbox->get_session()->authenticate($auth); + + self::$feed_1_private = Feed_Adapter::create($appbox, self::$user, self::$feed_1_private_title, self::$feed_1_private_subtitle); + self::$feed_1_private->set_public(false); + self::$feed_1_private->set_icon(new system_file(__DIR__ . '/../../../../testfiles/logocoll.gif')); + + self::$feed_2_private = Feed_Adapter::create($appbox, self::$user, self::$feed_2_private_title, self::$feed_2_private_subtitle); + self::$feed_2_private->set_public(false); + + self::$feed_3_public = Feed_Adapter::create($appbox, self::$user, self::$feed_3_public_title, self::$feed_3_public_subtitle); + self::$feed_3_public->set_public(true); + self::$feed_3_public->set_icon(new system_file(__DIR__ . '/../../../../testfiles/logocoll.gif')); + + self::$feed_4_public = Feed_Adapter::create($appbox, self::$user, self::$feed_4_public_title, self::$feed_4_public_subtitle); + self::$feed_4_public->set_public(true); + + $publisher = array_shift(self::$feed_4_public->get_publishers()); + + for ($i = 1; $i != 15; $i++) + { + $entry = Feed_Entry_Adapter::create($appbox, self::$feed_4_public, $publisher, 'titre entry', 'soustitre entry', 'Jean-Marie Biggaro', 'author@example.com'); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_1); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_2); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_3); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_4); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_5); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_6); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_7); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_8); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_9); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_10); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_11); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_12); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_13); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_14); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_15); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_16); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_17); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_18); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_19); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_20); + $entry = Feed_Entry_Adapter::create($appbox, self::$feed_1_private, $publisher, 'titre entry', 'soustitre entry', 'Jean-Marie Biggaro', 'author@example.com'); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_1); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_2); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_3); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_4); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_5); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_6); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_7); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_8); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_9); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_10); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_11); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_12); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_13); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_14); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_15); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_16); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_17); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_18); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_19); + $item = Feed_Entry_Item::create($appbox, $entry, self::$record_20); + + self::$feed_4_entries[] = $entry; + } + + + self::$public_feeds = Feed_Collection::load_public_feeds($appbox); + self::$private_feeds = Feed_Collection::load_all($appbox, self::$user); + $appbox->get_session()->logout(); + } + + public static function tearDownAfterClass() + { + self::$feed_1_private->delete(); + self::$feed_2_private->delete(); + self::$feed_3_public->delete(); + self::$feed_4_public->delete(); + + parent::tearDownAfterClass(); + } + + public function createApplication() + { + return require __DIR__ . '/../../../../../lib/Alchemy/Phrasea/Application/Root.php'; + } + + public function testPublicFeedAggregated() + { + self::$public_feeds->get_aggregate(); + $this->client->request('GET', '/feeds/aggregated/atom/'); + $response = $this->client->getResponse(); + + $this->evaluateResponse200($response); + $this->evaluateGoodXML($response); + + $this->evaluateAtom($response); + } + + protected function evaluateAtom(Response $response) + { + $dom_doc = new DOMDocument(); + $doc->preserveWhiteSpace = false; + $dom_doc->loadXML($response->getContent()); + + $xpath = new DOMXPath($dom_doc); + $xpath->registerNamespace('atom', 'http://www.w3.org/2005/Atom'); + + $this->assertEquals(1, $xpath->query('/atom:feed/atom:title')->length); + $this->assertEquals(1, $xpath->query('/atom:feed/atom:updated')->length); + $this->assertEquals(1, $xpath->query('/atom:feed/atom:link[@rel="self"]')->length); + $this->assertEquals(1, $xpath->query('/atom:feed/atom:id')->length); + $this->assertEquals(1, $xpath->query('/atom:feed/atom:generator')->length); + $this->assertEquals(1, $xpath->query('/atom:feed/atom:subtitle')->length); + } + + protected function evaluateGoodXML(Response $response) + { + $dom_doc = new DOMDocument(); + $dom_doc->loadXML($response->getContent()); + $this->assertInstanceOf('DOMDocument', $dom_doc); + $this->assertEquals($dom_doc->saveXML(), $response->getContent()); + } + + protected function evaluateResponse200(Response $response) + { + $this->assertEquals(200, $response->getStatusCode(), 'Test status code '); + $this->assertEquals('UTF-8', $response->getCharset(), 'Test charset response'); + } + +//$app->get('/feeds/feed/{id}/{format}/', function($id, $format) use ($app, $appbox, $display_feed) + public function testPublicFeed() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $auth = new Session_Authentication_None(self::$user); + $appbox->get_session()->authenticate($auth); + + $link = self::$feed_3_public->get_user_link($appbox->get_registry(), self::$user, Feed_Adapter::FORMAT_ATOM)->get_href(); + $link = str_replace($appbox->get_registry()->get('GV_ServerName') . 'feeds/', '/', $link); + + $appbox->get_session()->logout(); + + $this->client->request('GET', "/feeds" . $link); + $response = $this->client->getResponse(); + + $this->evaluateResponse200($response); + $this->evaluateGoodXML($response); + + $this->evaluateAtom($response); + } + +//$app->get('/feeds/userfeed/aggregated/{token}/{format}/', function($token, $format) use ($app, $appbox, $display_feed) + public function testUserFeedAggregated() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $auth = new Session_Authentication_None(self::$user); + $appbox->get_session()->authenticate($auth); + + $link = self::$private_feeds->get_aggregate()->get_user_link($appbox->get_registry(), self::$user, Feed_Adapter::FORMAT_ATOM)->get_href(); + $link = str_replace($appbox->get_registry()->get('GV_ServerName') . 'feeds/', '/', $link); + + $appbox->get_session()->logout(); + + $this->client->request('GET', "/feeds" . $link); + $response = $this->client->getResponse(); + + $this->evaluateResponse200($response); + $this->evaluateGoodXML($response); + + $this->evaluateAtom($response); + } + +//$app->get('/feeds/userfeed/{token}/{id}/{format}/', function($token, $id, $format) use ($app, $appbox, $display_feed) + public function testUserFeed() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $auth = new Session_Authentication_None(self::$user); + $appbox->get_session()->authenticate($auth); + + $link = self::$feed_1_private->get_user_link($appbox->get_registry(), self::$user, Feed_Adapter::FORMAT_ATOM)->get_href(); + $link = str_replace($appbox->get_registry()->get('GV_ServerName') . 'feeds/', '/', $link); + + $appbox->get_session()->logout(); + + $this->client->request('GET', "/feeds" . $link); + $response = $this->client->getResponse(); + + $this->evaluateResponse200($response); + $this->evaluateGoodXML($response); + + $this->evaluateAtom($response); + } + + public function testGetFeedFormat() + { + $feeds = Feed_Collection::load_public_feeds(appbox::get_instance(\bootstrap::getCore())); + $feed = array_shift($feeds->get_feeds()); + + $crawler = $this->client->request("GET", "/feeds/feed/" . $feed->get_id() . "/rss/"); + $this->assertEquals("application/rss+xml", $this->client->getResponse()->headers->get("content-type")); + $xml = $this->client->getResponse()->getContent(); + + $this->verifyXML($xml); + $this->verifyRSS($feed, $xml); + + $crawler = $this->client->request("GET", "/feeds/feed/" . $feed->get_id() . "/atom/"); + $this->assertEquals("application/atom+xml", $this->client->getResponse()->headers->get("content-type")); + $xml = $this->client->getResponse()->getContent(); + $this->verifyXML($xml); + $this->verifyATOM($feed, $xml); + } + + public function testCooliris() + { + $crawler = $this->client->request("GET", "/feeds/cooliris/"); + $this->assertTrue($this->client->getResponse()->isOk()); + $this->assertEquals("application/rss+xml", $this->client->getResponse()->headers->get("content-type")); + $xml = $this->client->getResponse()->getContent(); + $this->verifyXML($xml); + } + + public function testAggregatedRss() + { + $feeds = Feed_Collection::load_public_feeds(appbox::get_instance(\bootstrap::getCore())); + $all_feeds = $feeds->get_feeds(); + foreach ($all_feeds as $feed) + { + $this->assertTrue($feed->is_public()); + } + $crawler = $this->client->request("GET", "/feeds/aggregated/rss/"); + $this->assertTrue($this->client->getResponse()->isOk()); + $this->assertEquals("application/rss+xml", $this->client->getResponse()->headers->get("content-type")); + $xml = $this->client->getResponse()->getContent(); + $this->verifyXML($xml); + } + + public function testAggregatedAtom() + { + $feeds = Feed_Collection::load_public_feeds(appbox::get_instance(\bootstrap::getCore())); + $all_feeds = $feeds->get_feeds(); + foreach ($all_feeds as $feed) + { + $this->assertTrue($feed->is_public()); + } + $crawler = $this->client->request("GET", "/feeds/aggregated/atom/"); + $this->assertTrue($this->client->getResponse()->isOk()); + $this->assertEquals("application/atom+xml", $this->client->getResponse()->headers->get("content-type")); + $xml = $this->client->getResponse()->getContent(); + $this->verifyXML($xml); + } + + public function testUnknowFeedId() + { + $crawler = $this->client->request("GET", "/feeds/feed/0/"); + $this->assertFalse($this->client->getResponse()->isOk()); + $this->assertEquals(404, $this->client->getResponse()->getStatusCode()); + $crawler = $this->client->request("GET", "/feeds/feed/titi/"); + $this->assertFalse($this->client->getResponse()->isOk()); + $this->assertEquals(404, $this->client->getResponse()->getStatusCode()); + } + + public function testGetFeedId() + { + $feeds = Feed_Collection::load_public_feeds(appbox::get_instance(\bootstrap::getCore())); + $all_feeds = $feeds->get_feeds(); + $feed = array_shift($all_feeds); + + $crawler = $this->client->request("GET", "/feeds/feed/" . $feed->get_id() . "/rss/"); + $this->assertTrue($this->client->getResponse()->isOk()); + $xml = $this->client->getResponse()->getContent(); + $this->verifyXML($xml); + $this->verifyRSS($feed, $xml); + + $crawler = $this->client->request("GET", "/feeds/feed/" . $feed->get_id() . "/atom/"); + $this->assertTrue($this->client->getResponse()->isOk()); + $xml = $this->client->getResponse()->getContent(); + $this->verifyATOM($feed, $xml); + } + + public function testPrivateFeedAccess() + { + $private_feed = Feed_Adapter::create(appbox::get_instance(\bootstrap::getCore()), self::$user, 'title', 'subtitle'); + $private_feed->set_public(false); + $this->client->request("GET", "/feeds/feed/" . $private_feed->get_id() . "/rss/"); + $this->assertFalse($this->client->getResponse()->isOk()); + $this->assertEquals(403, $this->client->getResponse()->getStatusCode()); + $private_feed->delete(); + } + + public function verifyXML($xml) + { + /** + * XML is not verified due to Validator Service bug + */ + + return; + + try + { + $validator = new W3CFeedRawValidator($xml); + $response = $validator->validate(); + + $this->assertTrue($response->isValid(), $xml . "\n" . $response); + } + catch (W3CFeedValidatorException $e) + { + print "\nCould not use W3C FEED VALIDATOR API : " . $e->getMessage() . "\n"; + } + } + + function verifyRSS(Feed_Adapter $feed, $xml_string) + { + $dom_doc = new DOMDocument(); + $dom_doc->loadXML($xml_string); + + $xpath = new DOMXPath($dom_doc); + $xpath->registerNamespace("media", "http://search.yahoo.com/mrss/"); + $xpath->registerNamespace("atom", "http://www.w3.org/2005/Atom"); + $xpath->registerNamespace("dc", "http://purl.org/dc/elements/1.1/"); + $this->checkRSSRootNode($xpath, $feed); + $this->checkRSSEntryNode($xpath, $feed); + } + + function checkRSSRootNode(DOMXPath $xpath, Feed_Adapter $feed) + { + $channel = $xpath->query("/rss/channel"); + foreach ($channel->item(0)->childNodes as $child) + { + if ($child->nodeType !== XML_TEXT_NODE) + { + switch ($child->nodeName) + { + case 'title': + $this->assertEquals($feed->get_title(), $child->nodeValue); + break; + case 'dc:title': + $this->assertEquals($feed->get_title(), $child->nodeValue); + break; + case 'description': + $this->assertEquals($feed->get_subtitle(), $child->nodeValue); + break; + case 'link': + $this->assertEquals($feed->get_homepage_link(registry::get_instance(), Feed_Adapter::FORMAT_RSS, 1)->get_href(), $child->nodeValue); + break; + case 'pubDate': + $this->assertTrue(new DateTime() >= new DateTime($child->nodeValue)); + break; + case 'generator': + $this->assertEquals("Phraseanet", $child->nodeValue); + break; + case 'docs': + $this->assertEquals("http://blogs.law.harvard.edu/tech/rss", $child->nodeValue); + break; + case 'atom:link': + foreach ($child->attributes as $attribute) + { + if ($attribute->name == "href") + { + $this->assertEquals($feed->get_homepage_link(registry::get_instance(), Feed_Adapter::FORMAT_RSS, 1)->get_href(), $attribute->value); + break; + } + } + break; + } + } + } + } + + function checkRSSEntryNode(DOMXPath $xpath, Feed_Adapter $feed) + { + $list_entries = $xpath->query("/rss/channel/item"); + $count = 0; + $offset_start = 0; + $n_entries = 20; + $collection = $feed->get_entries($offset_start, $n_entries); + $entries = $collection->get_entries(); + + foreach ($list_entries as $node) + { + if (sizeof($entries) == 0) + { + $offset_start = ($offset_start++) * $n_entries; + $collection = $feed->get_entries($offset_start, $n_entries); + $entries = $collection->get_entries(); + if (sizeof($entries) == 0) //no more + break; + } + $feed_entry = array_shift($entries); + switch ($node->nodeName) + { + case 'title': + $this->assertEquals($feed_entry->get_title(), $node->nodeValue); + break; + case 'description': + $this->assertEquals($feed_entry->get_subtitle(), $node->nodeValue); + break; + case 'author': + $author = sprintf( + '%s (%s)' + , $feed_entry->get_author_email() + , $feed_entry->get_author_name() + ); + $this->assertEquals($author, $node->nodeValue); + break; + case 'pubDate': + $this->assertEquals($feed_entry->get_created_on()->format(DATE_RFC2822), $node->nodeValue); + break; + case 'guid': + $this->assertEquals($feed_entry->get_link()->get_href(), $node->nodeValue); + break; + case 'link': + $this->assertEquals($feed_entry->get_link()->get_href(), $node->nodeValue); + break; + } + $count++; + $this->checkRSSEntryItemsNode($xpath, $feed_entry, $count); + } + $this->assertEquals($feed->get_count_total_entries(), $count); + } + + function checkRSSEntryItemsNode(DOMXPath $xpath, Feed_Entry_Adapter $entry, $count) + { + $content = $entry->get_content(); + $available_medium = array('image', 'audio', 'video'); + array_walk($content, $this->removeBadItems($content, $available_medium)); + $media_group = $xpath->query("/rss/channel/item[" . $count . "]/media:group"); + $this->assertEquals(sizeof($content), $media_group->length); + + foreach ($media_group as $media) + { + $entry_item = array_shift($content); + $this->verifyMediaItem($entry_item, $media); + } + } + + public function verifyMediaItem(Feed_Entry_Item $item, DOMNode $node) + { + foreach ($node->childNodes as $node) + { + if ($node->nodeType !== XML_TEXT_NODE) + { + switch ($node->nodeName) + { + case 'media:content' : + $this->checkMediaContentAttributes($item, $node); + break; + case 'media:thumbnail': + default : + $this->checkOptionnalMediaGroupNode($node, $item); + break; + } + } + } + } + + public function parseAttributes(DOMNode $node) + { + $current_attributes = array(); + foreach ($node->attributes as $attribute) + { + $current_attributes[$attribute->name] = $attribute->value; + } + + return $current_attributes; + } + + public function checkMediaContentAttributes(Feed_Entry_Item $entry_item, DOMNode $node) + { + $current_attributes = $this->parseAttributes($node); + $is_thumbnail = false; + $record = $entry_item->get_record(); + + if (substr($current_attributes["url"], 0 - strlen("/preview/")) == "/preview/") + { + $ressource = $record->get_subdef('preview'); + } + else + { + $ressource = $record->get_thumbnail(); + $is_thumbnail = true; + } + + $permalink = $ressource->get_permalink(); + + foreach ($current_attributes as $attribute => $value) + { + switch ($attribute) + { + case "url": + $this->assertEquals($permalink->get_url(), $value); + break; + case "fileSize": + $this->assertEquals($ressource->get_size(), $value); + break; + case "type": + $this->assertEquals($ressource->get_mime(), $value); + break; + case "medium": + $this->assertEquals(strtolower($record->get_type()), $value); + break; + case "isDefault": + !$is_thumbnail ? $this->assertEquals("true", $value) : $this->assertEquals("false", $value); + break; + case "expression": + $this->assertEquals("full", $value); + break; + case "bitrate": + $this->assertEquals($value); + break; + case "height": + $this->assertEquals($ressource->get_height(), $value); + break; + case "width": + $this->assertEquals($ressource->get_width(), $value); + break; + case "duration" : + $this->assertEquals($record->get_duration(), $value); + break; + case "framerate": + case "samplingrate": + case "channels": + case "lang": + break; + default: + $this->fail($attribute . " is not valid"); + break; + } + } + } + + public function checkOptionnalMediaGroupNode(DOMNode $node, Feed_Entry_Item $entry_item) + { + $fields = array( + 'title' => array( + 'dc_field' => databox_Field_DCESAbstract::Title, + 'media_field' => array( + 'name' => 'media:title', + 'attributes' => array( + 'type' => 'plain' + ) + ), + 'separator' => ' ' + ) + , 'description' => array( + 'dc_field' => databox_Field_DCESAbstract::Description, + 'media_field' => array( + 'name' => 'media:description', + 'attributes' => array() + ), + 'separator' => ' ' + ) + , 'contributor' => array( + 'dc_field' => databox_Field_DCESAbstract::Contributor, + 'media_field' => array( + 'name' => 'media:credit', + 'attributes' => array( + 'role' => 'contributor', + 'scheme' => 'urn:ebu' + ) + ), + 'separator' => ' ' + ) + , 'director' => array( + 'dc_field' => databox_Field_DCESAbstract::Creator, + 'media_field' => array( + 'name' => 'media:credit', + 'attributes' => array( + 'role' => 'creator', + 'scheme' => 'urn:ebu' + ) + ), + 'separator' => ' ' + ) + , 'publisher' => array( + 'dc_field' => databox_Field_DCESAbstract::Publisher, + 'media_field' => array( + 'name' => 'media:credit', + 'attributes' => array( + 'role' => 'publisher', + 'scheme' => 'urn:ebu' + ) + ), + 'separator' => ' ' + ) + , 'rights' => array( + 'dc_field' => databox_Field_DCESAbstract::Rights, + 'media_field' => array( + 'name' => 'media:copyright', + 'attributes' => array() + ), + 'separator' => ' ' + ) + , 'keywords' => array( + 'dc_field' => databox_Field_DCESAbstract::Subject, + 'media_field' => array( + 'name' => 'media:keywords', + 'attributes' => array() + ), + 'separator' => ', ' + ) + ); + + + foreach ($fields as $key_field => $field) + { + if ($field["media_field"]["name"] == $node->nodeName) + { + if ($p4field = $entry_item->get_record()->get_caption()->get_dc_field($field["dc_field"])) + { + $this->assertEquals($p4field->get_serialized_values($field["separator"]), $node->nodeValue + , sprintf('Asserting good value for DC %s', $field["dc_field"])); + if (sizeof($field["media_field"]["attributes"]) > 0) + { + foreach ($node->attributes as $attribute) + { + $this->assertTrue(array_key_exists($attribute->name, $field["media_field"]["attributes"]), "Checkin attribute " . $attribute->name . " for " . $field['media_field']['name']); + $this->assertEquals($attribute->value, $field["media_field"]["attributes"][$attribute->name], "Checkin attribute " . $attribute->name . " for " . $field['media_field']['name']); + } + } + } + else + { + $this->fail("Missing media:entry"); + } + break; + } + } + } + + public function removeBadItems(Array &$item_entries, Array $available_medium) + { + $remove = function($entry_item, $key) use (&$item_entries, $available_medium) + { + $preview_sd = $entry_item->get_record()->get_subdef('preview'); + $url_preview = $preview_sd->get_permalink(); + $thumbnail_sd = $entry_item->get_record()->get_thumbnail(); + $url_thumb = $thumbnail_sd->get_permalink(); + + if (!in_array(strtolower($entry_item->get_record()->get_type()), $available_medium)) + { + unset($item_entries[$key]); //remove + } + + if (!$url_thumb || !$url_preview) + { + unset($item_entries[$key]); //remove + } + }; + + return $remove; + } + + public function verifyATOM(Feed_Adapter $feed, $xml_string) + { + $this->verifyXML($xml_string); + $dom_doc = new DOMDocument(); + $dom_doc->loadXML($xml_string); + + $xpath = new DOMXPath($dom_doc); + $xpath->registerNamespace("media", "http://search.yahoo.com/mrss/"); + $xpath->registerNamespace("Atom", "http://www.w3.org/2005/Atom"); + + $this->checkATOMRootNode($dom_doc, $xpath, $feed); + } + + public function checkATOMRootNode(DOMDocument $dom_doc, DOMXPath $xpath, Feed_Adapter $feed) + { + $ids = $xpath->query('/Atom:feed/Atom:id'); + $this->assertEquals($feed->get_homepage_link(registry::get_instance(), Feed_Adapter::FORMAT_ATOM, 1)->get_href(), $ids->item(0)->nodeValue); + + $titles = $xpath->query('/Atom:feed/Atom:title'); + $this->assertEquals($feed->get_title(), $titles->item(0)->nodeValue); + + $subtitles = $xpath->query('/Atom:feed/Atom:subtitle'); + if ($subtitles->length > 0) + $this->assertEquals($feed->get_subtitle(), $subtitles->item(0)->nodeValue); + + $updateds = $xpath->query('/Atom:feed/Atom:updated'); + $this->assertTrue(new DateTime() >= new DateTime($updateds->item(0)->nodeValue)); + + $entries_item = $xpath->query('/Atom:feed/Atom:entry'); + + $count = 0; + $offset_start = 0; + $n_entries = 20; + $collection = $feed->get_entries($offset_start, $n_entries); + $entries = $collection->get_entries(); + + foreach ($entries_item as $entry) + { + if (sizeof($entries) == 0) + { + $offset_start = ($offset_start++) * $n_entries; + $collection = $feed->get_entries($offset_start, $n_entries); + $entries = $collection->get_entries(); + if (sizeof($entries) == 0) //no more + break; + } + $feed_entry = array_shift($entries); + $this->checkATOMEntryNode($entry, $xpath, $feed, $feed_entry); + $count++; + } + $this->assertEquals($feed->get_count_total_entries(), $count); + } + + public function checkATOMEntryNode(DOMNode $node, DOMXPath $xpath, Feed_Adapter $feed, Feed_Entry_Adapter $entry) + { + foreach ($node->childNodes as $child) + { + if ($child->nodeType !== XML_TEXT_NODE) + { + switch ($child->nodeName) + { + case 'id': + $this->assertEquals(sprintf('%sentry/%d/', $feed->get_homepage_link(registry::get_instance(), Feed_Adapter::FORMAT_ATOM, 1)->get_href(), $entry->get_id()), $child->nodeValue); + break; + case 'link': + foreach ($child->attributes as $attribute) + { + if ($attribute->name == "href") + { + $this->assertEquals(sprintf('%sentry/%d/', $feed->get_homepage_link(registry::get_instance(), Feed_Adapter::FORMAT_ATOM, 1)->get_href(), $entry->get_id()), $attribute->value); + break; + } + } + break; + case 'updated': + $this->assertEquals($entry->get_updated_on()->format(DATE_ATOM), $child->nodeValue); + break; + case 'published': + $this->assertEquals($entry->get_created_on()->format(DATE_ATOM), $child->nodeValue); + break; + case 'title': + $this->assertEquals($entry->get_title(), $child->nodeValue); + break; + case 'content': + $this->assertEquals($entry->get_subtitle(), $child->nodeValue); + break; + case 'author': + foreach ($node->childNodes as $child) + { + if ($child->nodeType !== XML_TEXT_NODE && $child->nodeName == "email") + $this->assertEquals($entry->get_author_email(), $child->nodeValue); + if ($child->nodeType !== XML_TEXT_NODE && $child->nodeName == "name") + $this->assertEquals($entry->get_author_name(), $child->nodeValue); + } + break; + } + } + } + + $content = $entry->get_content(); + + + $available_medium = array('image', 'audio', 'video'); + + array_walk($content, $this->removeBadItems($content, $available_medium)); + + + $media_group = $xpath->query("/Atom:feed/Atom:entry[0]/media:group"); + + if ($media_group->length > 0) + { + foreach ($media_group as $media) + { + + $entry_item = array_shift($content); + if ($entry_item instanceof Feed_Entry_Item) + { + $this->verifyMediaItem($entry_item, $media); + } + } + } + } + +} diff --git a/tests/Alchemy/Phrasea/Controller/Setup/FakeSetupApplication.inc b/tests/Alchemy/Phrasea/Controller/Setup/FakeSetupApplication.inc new file mode 100644 index 0000000000..ef94e897eb --- /dev/null +++ b/tests/Alchemy/Phrasea/Controller/Setup/FakeSetupApplication.inc @@ -0,0 +1,34 @@ +get('/', function() use ($app) + { + return $app->redirect('/setup/installer/'); + }); + + $app->mount('/installer/', new Controller\Installer()); + $app->mount('/test', new ControllerUtils\PathFileTest()); + $app->mount('/connection_test', new ControllerUtils\ConnectionTest()); + + + $app->error(function($e){ + + }); + + return $app; + }); diff --git a/tests/Alchemy/Phrasea/Controller/Setup/FakeUpgradeApplication.inc b/tests/Alchemy/Phrasea/Controller/Setup/FakeUpgradeApplication.inc new file mode 100644 index 0000000000..1e26903014 --- /dev/null +++ b/tests/Alchemy/Phrasea/Controller/Setup/FakeUpgradeApplication.inc @@ -0,0 +1,34 @@ +get('/', function() use ($app) + { + return $app->redirect('/setup/upgrader/'); + }); + + $app->mount('/upgrader/', new Controller\Upgrader()); + $app->mount('/test', new ControllerUtils\PathFileTest()); + $app->mount('/connection_test', new ControllerUtils\ConnectionTest()); + + + $app->error(function($e){ + }); + + + return $app; + }); diff --git a/tests/Alchemy/Phrasea/Controller/Setup/InstallerTest.php b/tests/Alchemy/Phrasea/Controller/Setup/InstallerTest.php new file mode 100644 index 0000000000..a9a6cf818f --- /dev/null +++ b/tests/Alchemy/Phrasea/Controller/Setup/InstallerTest.php @@ -0,0 +1,77 @@ +client = $this->createClient(); + } + + /** + * Default route test + */ + public function testRouteSlash() + { + $this->client->request('GET', '/'); + + $response = $this->client->getResponse(); + /* @var $response \Symfony\Component\HttpFoundation\Response */ + + $this->assertEquals(302, $response->getStatusCode()); + $this->assertEquals('/setup/installer/', $response->headers->get('location')); + } + + public function testRouteInstaller() + { + $this->client->request('GET', '/installer/'); + + $response = $this->client->getResponse(); + /* @var $response \Symfony\Component\HttpFoundation\Response */ + + $this->assertEquals(302, $response->getStatusCode()); + $this->assertEquals('/setup/installer/step2/', $response->headers->get('location')); + } + + public function testRouteInstallerStep2() + { + $this->client->request('GET', '/installer/step2/'); + + $response = $this->client->getResponse(); + /* @var $response \Symfony\Component\HttpFoundation\Response */ + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertTrue($response->isOk()); + } + +} diff --git a/tests/Alchemy/Phrasea/Controller/Setup/UpgraderTest.php b/tests/Alchemy/Phrasea/Controller/Setup/UpgraderTest.php new file mode 100644 index 0000000000..ee11cd07a7 --- /dev/null +++ b/tests/Alchemy/Phrasea/Controller/Setup/UpgraderTest.php @@ -0,0 +1,92 @@ +client = $this->createClient(); + } + + /** + * Default route test + */ + public function testRouteSlash() + { + $this->client->request('GET', '/'); + + $response = $this->client->getResponse(); + /* @var $response \Symfony\Component\HttpFoundation\Response */ + + $this->assertEquals(302, $response->getStatusCode()); + $this->assertEquals('/setup/upgrader/', $response->headers->get('location')); + } + + /** + * Default route test + */ + public function testRouteUpgrader() + { + $this->client->request('GET', '/upgrader/'); + + $response = $this->client->getResponse(); + /* @var $response \Symfony\Component\HttpFoundation\Response */ + + $this->assertEquals(200, $response->getStatusCode()); + } + /** + * Default route test + */ + public function testRouteStatus() + { + $this->client->request('GET', '/upgrader/status/'); + + $response = $this->client->getResponse(); + /* @var $response \Symfony\Component\HttpFoundation\Response */ + + $this->assertEquals(200, $response->getStatusCode()); + } + /** + * Default route test + */ + public function testRouteExecute() + { + $this->client->request('POST', '/upgrader/execute/'); + + $response = $this->client->getResponse(); + /* @var $response \Symfony\Component\HttpFoundation\Response */ + $this->assertEquals(302, $response->getStatusCode()); + $this->assertEquals('/', $response->headers->get('location')); + } + +} diff --git a/tests/Alchemy/Phrasea/Controller/Utils/ConnectionTestTest.php b/tests/Alchemy/Phrasea/Controller/Utils/ConnectionTestTest.php new file mode 100644 index 0000000000..a1e59adc97 --- /dev/null +++ b/tests/Alchemy/Phrasea/Controller/Utils/ConnectionTestTest.php @@ -0,0 +1,127 @@ +client = $this->createClient(); + } + + /** + * Default route test + */ + public function testRouteMysql() + { + $configuration = \Alchemy\Phrasea\Core\Configuration::build(); + + $chooseConnexion = $configuration->getPhraseanet()->get('database'); + + $connexion = $configuration->getConnexion($chooseConnexion); + + $params = array( + "hostname" => $connexion->get('host'), + "port" => $connexion->get('port'), + "user" => $connexion->get('user'), + "password" => $connexion->get('password'), + "dbname" => $connexion->get('dbname') + ); + + $this->client->request("GET", "/tests/connection/mysql/", $params); + $response = $this->client->getResponse(); + $this->assertTrue($response->isOk()); + } + + public function testRouteMysqlFailed() + { + $configuration = \Alchemy\Phrasea\Core\Configuration::build(); + + $chooseConnexion = $configuration->getPhraseanet()->get('database'); + + $connexion = $configuration->getConnexion($chooseConnexion); + + $params = array( + "hostname" => $connexion->get('host'), + "port" => $connexion->get('port'), + "user" => $connexion->get('user'), + "password" => "fakepassword", + "dbname" => $connexion->get('dbname') + ); + + $this->client->request("GET", "/tests/connection/mysql/", $params); + $response = $this->client->getResponse(); + $content = json_decode($this->client->getResponse()->getContent()); + $this->assertEquals("application/json", $this->client->getResponse()->headers->get("content-type")); + $this->assertTrue($response->isOk()); + $this->assertTrue(is_object($content)); + $this->assertObjectHasAttribute('connection', $content); + $this->assertObjectHasAttribute('database', $content); + $this->assertObjectHasAttribute('is_empty', $content); + $this->assertObjectHasAttribute('is_appbox', $content); + $this->assertObjectHasAttribute('is_databox', $content); + $this->assertFalse($content->connection); + } + + public function testRouteMysqlDbFailed() + { + $configuration = \Alchemy\Phrasea\Core\Configuration::build(); + + $chooseConnexion = $configuration->getPhraseanet()->get('database'); + + $connexion = $configuration->getConnexion($chooseConnexion); + + $params = array( + "hostname" => $connexion->get('host'), + "port" => $connexion->get('port'), + "user" => $connexion->get('user'), + "password" => $connexion->get('password'), + "dbname" => "fake-DTABASE-name" + ); + + $this->client->request("GET", "/tests/connection/mysql/", $params); + $response = $this->client->getResponse(); + $content = json_decode($this->client->getResponse()->getContent()); + $this->assertEquals("application/json", $this->client->getResponse()->headers->get("content-type")); + $this->assertTrue($response->isOk()); + $this->assertTrue(is_object($content)); + $this->assertObjectHasAttribute('connection', $content); + $this->assertObjectHasAttribute('database', $content); + $this->assertObjectHasAttribute('is_empty', $content); + $this->assertObjectHasAttribute('is_appbox', $content); + $this->assertObjectHasAttribute('is_databox', $content); + $this->assertFalse($content->database); + } + +} + diff --git a/tests/Alchemy/Phrasea/Controller/Utils/PathFileTestTest.php b/tests/Alchemy/Phrasea/Controller/Utils/PathFileTestTest.php new file mode 100644 index 0000000000..b4b728c9a2 --- /dev/null +++ b/tests/Alchemy/Phrasea/Controller/Utils/PathFileTestTest.php @@ -0,0 +1,73 @@ +client = $this->createClient(); + } + + /** + * Default route test + */ + public function testRoutePath() + { + $file = new \SplFileObject(__DIR__ . '/../../../../testfiles/cestlafete.jpg'); + $this->client->request("GET", "/tests/pathurl/path/", array('path' => $file->getPathname())); + + $response = $this->client->getResponse(); + $this->assertTrue($response->isOk()); + $this->assertEquals("application/json", $this->client->getResponse()->headers->get("content-type")); + $content = json_decode($this->client->getResponse()->getContent()); + $this->assertTrue(is_object($content)); + $this->assertObjectHasAttribute('exists', $content); + $this->assertObjectHasAttribute('file', $content); + $this->assertObjectHasAttribute('dir', $content); + $this->assertObjectHasAttribute('readable', $content); + $this->assertObjectHasAttribute('executable', $content); + } + + public function testRouteUrl() + { + $this->client->request("GET", "/tests/pathurl/url/", array('url' => "www.google.com")); + + $response = $this->client->getResponse(); + $this->assertTrue($response->isOk()); + $this->assertEquals("application/json", $this->client->getResponse()->headers->get("content-type")); + $content = json_decode($this->client->getResponse()->getContent()); + $this->assertTrue(is_object($content)); + } + +} diff --git a/tests/Alchemy/Phrasea/Core/Configuration/ConfigurationTest.php b/tests/Alchemy/Phrasea/Core/Configuration/ConfigurationTest.php new file mode 100644 index 0000000000..1e66ff0262 --- /dev/null +++ b/tests/Alchemy/Phrasea/Core/Configuration/ConfigurationTest.php @@ -0,0 +1,500 @@ +markTestSkipped('To rewrite'); + parent::setUp(); + + $specNotInstalled = $this->getMock( + '\Alchemy\Phrasea\Core\Configuration\Application' + , array('getConfigurationsFile') + ); + + $specNotInstalled->expects($this->any()) + ->method('getConfigurationsFile') + ->will( + $this->throwException(new Exception) + ); + + $specExperience = $this->getMock( + '\Alchemy\Phrasea\Core\Configuration\Application' + , array('getConfigurationsFile') + ); + + $specExperience->expects($this->any()) + ->method('getConfigurationsFile') + ->will( + $this->returnValue( + new \SplFileObject(__DIR__ . '/confTestFiles/config.yml') + ) + ); + + $handler = new Configuration\Handler($specNotInstalled); + $this->confNotInstalled = new PhraseaCore\Configuration($handler); + + + $handler = new Configuration\Handler($specExperience); + $this->object = new PhraseaCore\Configuration($handler); + } + + public function testGetEnvironment() + { + $this->assertEquals("dev", $this->object->getEnvironnement()); + $this->assertEquals(null, $this->confNotInstalled->getEnvironnement()); + } + + public function testSetEnvironment() + { + $this->object->setEnvironnement("test"); + $this->assertEquals("test", $this->object->getEnvironnement()); + $this->confNotInstalled->setEnvironnement("prod"); + $this->assertEquals("prod", $this->confNotInstalled->getEnvironnement()); + + try + { + $this->object->setEnvironnement("unknow"); + $this->fail("should raise exception"); + } + catch (\Exception $e) + { + + } + } + + public function testIsDebug() + { + $this->object->setEnvironnement("test"); + $this->assertTrue($this->object->isDebug()); + $this->object->setEnvironnement("dev"); + $this->assertTrue($this->object->isDebug()); + $this->object->setEnvironnement("prod"); + $this->assertFalse($this->object->isDebug()); + $this->object->setEnvironnement("no_debug"); + $this->assertFalse($this->object->isDebug()); + } + + public function testIsMaintened() + { + $this->object->setEnvironnement("test"); + $this->assertFalse($this->object->isMaintained()); + $this->object->setEnvironnement("dev"); + $this->assertFalse($this->object->isMaintained()); + $this->object->setEnvironnement("prod"); + $this->assertFalse($this->object->isMaintained()); + $this->object->setEnvironnement("no_maintenance"); + $this->assertFalse($this->object->isMaintained()); + } + + public function testIsDisplayingErrors() + { + $this->object->setEnvironnement("test"); + $this->assertTrue($this->object->isDisplayingErrors()); + $this->object->setEnvironnement("dev"); + $this->assertTrue($this->object->isDisplayingErrors()); + $this->object->setEnvironnement("prod"); + $this->assertFalse($this->object->isDisplayingErrors()); + $this->object->setEnvironnement("no_display_errors"); + $this->assertFalse($this->object->isDisplayingErrors()); + } + + public function testGetPhraseanet() + { + $this->object->setEnvironnement("test"); + $this->assertInstanceOf("\Symfony\Component\DependencyInjection\ParameterBag\ParameterBag", $this->object->getPhraseanet()); + $this->object->setEnvironnement("dev"); + $this->assertInstanceOf("\Symfony\Component\DependencyInjection\ParameterBag\ParameterBag", $this->object->getPhraseanet()); + $this->object->setEnvironnement("prod"); + $this->assertInstanceOf("\Symfony\Component\DependencyInjection\ParameterBag\ParameterBag", $this->object->getPhraseanet()); + $this->object->setEnvironnement("missing_phraseanet"); + try + { + $this->object->getPhraseanet(); + $this->fail("should raise an exeception"); + } + catch (\Exception $e) + { + + } + } + + public function testisInstalled() + { + $this->assertFalse($this->confNotInstalled->isInstalled()); + $this->assertTrue($this->object->isInstalled()); + } + + public function testGetConfiguration() + { + $config = $this->object->getConfiguration(); + $this->assertInstanceOf("\Symfony\Component\DependencyInjection\ParameterBag\ParameterBag", $config); + $this->assertNotEmpty($config->all()); + $config = $this->confNotInstalled->getConfiguration(); + $this->assertEmpty($config->all()); + } + + public function testGetConnexions() + { + $connexions = $this->object->getConnexions(); + $this->assertInstanceOf("\Symfony\Component\DependencyInjection\ParameterBag\ParameterBag", $connexions); + $this->assertGreaterThan(0, sizeof($connexions->all())); + } + + public function testGetConnexion() + { + $connexion = $this->object->getConnexion(); + $this->assertInstanceOf("\Symfony\Component\DependencyInjection\ParameterBag\ParameterBag", $connexion); + $this->assertGreaterThan(0, sizeof($connexion->all())); + } + + public function testGetConnexionException() + { + try + { + $this->object->getConnexion('unknow_connexion'); + $this->fail('should raise an exception'); + } + catch (\Exception $e) + { + + } + } + + public function testGetFile() + { + $this->assertInstanceOf("\SplFileObject", $this->object->getFile()); + } + + public function testGetFileExeption() + { + try + { + $this->assertInstanceOf("\SplFileObject", $this->confNotInstalled->getFile()); + $this->fail("should raise an excpetion"); + } + catch (\Exception $e) + { + + } + } + + public function testAll() + { + $all = $this->object->all(); + $this->assertTrue(is_array($all)); + $this->assertArrayHasKey("test", $all); + $this->assertArrayHasKey("dev", $all); + $this->assertArrayHasKey("prod", $all); + $this->assertArrayHasKey("environment", $all); + } + + public function testGetServices() + { + $services = $this->object->getServices(); + $this->assertInstanceOf("\Symfony\Component\DependencyInjection\ParameterBag\ParameterBag", $services); + $this->assertGreaterThan(0, sizeof($services->all())); + } + + public function testGetService() + { + $services = $this->object->getService('TemplateEngine\Twig'); + $this->assertInstanceOf("\Symfony\Component\DependencyInjection\ParameterBag\ParameterBag", $services); + $this->assertGreaterThan(0, sizeof($services->all())); + } + + public function testGetServiceException() + { + try + { + $this->object->getService('unknow_service'); + $this->fail('should raise an exception'); + } + catch (\Exception $e) + { + + } + } + + public function testWrite() + { + touch(__DIR__ . "/confTestFiles/yamlWriteTest.yml"); + + $stub = $this->getMock( + '\Alchemy\Phrasea\Core\Configuration\Application' + , array('getConfigurationPathName') + ); + + $file = new \SplFileObject(__DIR__ . "/confTestFiles/yamlWriteTest.yml"); + + $stub->expects($this->any()) + ->method('getConfigurationPathName') + ->will( + $this->returnValue($file->getPathname()) + ); + + $handler = new Configuration\Handler($stub); + + $configuration = new PhraseaCore\Configuration($handler); + + $arrayToBeWritten = array( + 'hello' => 'world' + , 'key' => array( + 'keyone' => 'valueone' + , 'keytwo' => 'valuetwo' + ) + ); + + $configuration->write($arrayToBeWritten, 0, true); + + $all = $configuration->all(); + + $this->assertArrayHasKey("hello", $all); + $this->assertArrayHasKey("key", $all); + $this->assertTrue(is_array($all["key"])); + } + + public function testWriteException() + { + touch(__DIR__ . "/confTestFiles/yamlWriteTest.yml"); + + $stub = $this->getMock( + '\Alchemy\Phrasea\Core\Configuration\Application' + , array('getConfigurationPathName') + ); + + $file = new \SplFileObject(__DIR__ . "/confTestFiles/yamlWriteTest.yml"); + + $stub->expects($this->any()) + ->method('getConfigurationPathName') + ->will( + $this->returnValue("unknow_path") + ); + + $handler = new Configuration\Handler($stub); + + $configuration = new PhraseaCore\Configuration($handler); + + $arrayToBeWritten = array( + 'hello' => 'world' + , 'key' => array( + 'keyone' => 'valueone' + , 'keytwo' => 'valuetwo' + ) + ); + + try + { + $configuration->write($arrayToBeWritten); + $this->fail("should raise an exception"); + } + catch (\exception $e) + { + + } + } + + public function testDelete() + { + touch(__DIR__ . "/confTestFiles/yamlWriteTest.yml"); + + $stub = $this->getMock( + '\Alchemy\Phrasea\Core\Configuration\Application' + , array('getConfigurationPathName') + ); + + $file = new \SplFileObject(__DIR__ . "/confTestFiles/yamlWriteTest.yml"); + + $stub->expects($this->any()) + ->method('getConfigurationPathName') + ->will( + $this->returnValue($file->getPathname()) + ); + + $handler = new Configuration\Handler($stub); + + $configuration = new PhraseaCore\Configuration($handler); + + $configuration->delete(); + + $this->assertFileNotExists($file->getPathname()); + } + + public function testDeleteException() + { + touch(__DIR__ . "/confTestFiles/yamlWriteTest.yml"); + + $stub = $this->getMock( + '\Alchemy\Phrasea\Core\Configuration\Application' + , array('getConfigurationPathName') + ); + + $file = new \SplFileObject(__DIR__ . "/confTestFiles/yamlWriteTest.yml"); + + $stub->expects($this->any()) + ->method('getConfigurationPathName') + ->will( + $this->returnValue("unknow_path") + ); + + $handler = new Configuration\Handler($stub); + + $configuration = new PhraseaCore\Configuration($handler); + + try + { + $configuration->delete(); + $this->fail("should raise an exception"); + } + catch (\Exception $e) + { + + } + + $this->assertFileExists($file->getPathname()); + + unlink(__DIR__ . "/confTestFiles/yamlWriteTest.yml"); + } + + public function testGetTemplating() + { + try + { + $templating = $this->object->getTemplating(); + } + catch (\Exception $e) + { + $this->fail("not template_engine provided"); + } + $this->assertTrue(is_string($templating)); + } + + public function testGetOrm() + { + try + { + $orm = $this->object->getOrm(); + } + catch (\Exception $e) + { + $this->fail("not template_engine provided"); + } + $this->assertTrue(is_string($orm)); + } + + public function testGetServiceFile() + { + $this->assertInstanceOf("\SplFileObject", $this->object->getServiceFile()); + } + + public function testGetConnexionFile() + { + $this->assertInstanceOf("\SplFileObject", $this->object->getConnexionFile()); + } + + public function testRefresh() + { + $this->confNotInstalled->refresh(); + $this->assertFalse($this->confNotInstalled->isInstalled()); + $this->assertInstanceOf("\Symfony\Component\DependencyInjection\ParameterBag\ParameterBag", $this->confNotInstalled->getConfiguration()); + + touch(__DIR__ . "/confTestFiles/yamlWriteTest.yml"); + + $stub = $this->getMock( + '\Alchemy\Phrasea\Core\Configuration\Application' + , array('getConfigurationPathName') + ); + + $file = new \SplFileObject(__DIR__ . "/confTestFiles/yamlWriteTest.yml"); + + $stub->expects($this->any()) + ->method('getConfigurationPathName') + ->will( + $this->returnValue($file->getPathname()) + ); + + $handler = new Configuration\Handler($stub); + + $configuration = new PhraseaCore\Configuration($handler); + + $newScope = array("prod" => array('key' => 'value', 'key2' => 'value2')); + + //append new conf + $configuration->write($newScope, FILE_APPEND); + + try + { + $configuration->getConfiguration(); //it is not loaded + $this->fail("should raise an exception"); + } + catch (\Exception $e) + { + + } + + $configuration->refresh(); //reload conf + $prod = $configuration->getConfiguration(); + $this->assertInstanceOf("\Symfony\Component\DependencyInjection\ParameterBag\ParameterBag", $prod); + + unlink(__DIR__ . "/confTestFiles/yamlWriteTest.yml"); + } + +} + diff --git a/tests/Alchemy/Phrasea/Core/Configuration/confTestFiles/config.yml b/tests/Alchemy/Phrasea/Core/Configuration/confTestFiles/config.yml new file mode 100644 index 0000000000..c9e7870da4 --- /dev/null +++ b/tests/Alchemy/Phrasea/Core/Configuration/confTestFiles/config.yml @@ -0,0 +1,69 @@ +environment: dev +dev: + phraseanet: + servername: 'http://dev.phrasea.net/' + maintenance: false + debug: true + display_errors: true + database: main_connexion + template_engine: twig_debug + orm: doctrine_dev + cache: array_cache +prod: + phraseanet: + servername: 'http://dev.phrasea.net/' + maintenance: false + debug: false + display_errors: false + database: main_connexion + template_engine: twig + orm: doctrine_prod + cache: apc_cache +test: + phraseanet: + servername: 'http://dev.phrasea.net/' + maintenance: false + debug: true + display_errors: true + database: main_connexion + template_engine: twig_debug + orm: doctrine_test + cache: array_cache + +no_debug: + phraseanet: + servername: 'http://dev.phrasea.net/' + maintenance: false + ##debug: true + display_errors: true + database: main_connexion + template_engine: twig_debug + orm: doctrine_test + cache: array_cache + +no_maintenance: + phraseanet: + servername: 'http://dev.phrasea.net/' + ##maintenance: false + debug: true + display_errors: true + database: main_connexion + template_engine: twig_debug + orm: doctrine_test + cache: array_cache + +no_display_errors: + phraseanet: + servername: 'http://dev.phrasea.net/' + maintenance: false + debug: true + ##display_errors: true + database: main_connexion + template_engine: twig_debug + orm: doctrine_test + cache: array_cache + +missing_phraseanet: + template_engine: twig_debug + orm: doctrine_test + cache: array_cache diff --git a/tests/Alchemy/Phrasea/Core/Configuration/confTestFiles/test.json b/tests/Alchemy/Phrasea/Core/Configuration/confTestFiles/test.json new file mode 100644 index 0000000000..43133b13c7 --- /dev/null +++ b/tests/Alchemy/Phrasea/Core/Configuration/confTestFiles/test.json @@ -0,0 +1,12 @@ +{ + "meta": { + "api_version": "1.0", + "request": "GET /api/v1/feeds/288/content/", + "response_time": "2011-07-27T15:52:04+02:00", + "http_code": 200, + "error_message": null, + "error_details": null, + "charset": "UTF-8" + }, + "response": {} +} \ No newline at end of file diff --git a/tests/Alchemy/Phrasea/Core/Service/Cache/ApcCacheTest.php b/tests/Alchemy/Phrasea/Core/Service/Cache/ApcCacheTest.php new file mode 100644 index 0000000000..051234b261 --- /dev/null +++ b/tests/Alchemy/Phrasea/Core/Service/Cache/ApcCacheTest.php @@ -0,0 +1,74 @@ +getDriver(); + $this->assertTrue($service instanceof \Doctrine\Common\Cache\CacheProvider); + } + else + { + try + { + $cache->getDriver(); + $this->fail("should raise an exception"); + } + catch (\Exception $e) + { + + } + } + } + + public function testServiceException() + { + $cache = new \Alchemy\Phrasea\Core\Service\Cache\ApcCache( + self::$core, array() + ); + + try + { + $cache->getDriver(); + $this->fail("should raise an exception"); + } + catch (\Exception $e) + { + + } + } + + public function testType() + { + $cache = new \Alchemy\Phrasea\Core\Service\Cache\ApcCache( + self::$core, array() + ); + + $this->assertEquals("apc", $cache->getType()); + } + +} diff --git a/tests/Alchemy/Phrasea/Core/Service/Cache/ArrayCacheTest.php b/tests/Alchemy/Phrasea/Core/Service/Cache/ArrayCacheTest.php new file mode 100644 index 0000000000..3b76d13503 --- /dev/null +++ b/tests/Alchemy/Phrasea/Core/Service/Cache/ArrayCacheTest.php @@ -0,0 +1,59 @@ +getDriver(); + $this->assertTrue($service instanceof \Doctrine\Common\Cache\CacheProvider); + } + + public function testServiceException() + { + $cache = new \Alchemy\Phrasea\Core\Service\Cache\ArrayCache( + self::$core, array() + ); + + try + { + $cache->getDriver(); + $this->fail("should raise an exception"); + } + catch (\Exception $e) + { + + } + } + + public function testType() + { + $cache = new \Alchemy\Phrasea\Core\Service\Cache\ArrayCache( + self::$core, array() + ); + + $this->assertEquals("array", $cache->getType()); + } + +} diff --git a/tests/Alchemy/Phrasea/Core/Service/Cache/MemcacheCacheTest.php b/tests/Alchemy/Phrasea/Core/Service/Cache/MemcacheCacheTest.php new file mode 100644 index 0000000000..60c2b9750f --- /dev/null +++ b/tests/Alchemy/Phrasea/Core/Service/Cache/MemcacheCacheTest.php @@ -0,0 +1,92 @@ +getDriver(); + $this->assertTrue($service instanceof \Doctrine\Common\Cache\CacheProvider); + } + else + { + try + { + $cache->getDriver(); + $this->fail("should raise an exception"); + } + catch (\Exception $e) + { + + } + } + } + + public function testServiceException() + { + $cache = new \Alchemy\Phrasea\Core\Service\Cache\MemcacheCache( + self::$core, array() + ); + + try + { + $cache->getDriver(); + $this->fail("should raise an exception"); + } + catch (\Exception $e) + { + + } + } + + public function testType() + { + $cache = new \Alchemy\Phrasea\Core\Service\Cache\MemcacheCache( + self::$core, array() + ); + + $this->assertEquals("memcache", $cache->getType()); + } + + public function testHost() + { + $cache = new \Alchemy\Phrasea\Core\Service\Cache\MemcacheCache( + self::$core, array() + ); + + $this->assertEquals(\Alchemy\Phrasea\Core\Service\Cache\MemcacheCache::DEFAULT_HOST, $cache->getHost()); + } + + public function testPort() + { + $cache = new \Alchemy\Phrasea\Core\Service\Cache\MemcacheCache( + self::$core, array() + ); + + $this->assertEquals(\Alchemy\Phrasea\Core\Service\Cache\MemcacheCache::DEFAULT_PORT, $cache->getPort()); + } + +} diff --git a/tests/Alchemy/Phrasea/Core/Service/Cache/XcacheCacheTest.php b/tests/Alchemy/Phrasea/Core/Service/Cache/XcacheCacheTest.php new file mode 100644 index 0000000000..af18fd919b --- /dev/null +++ b/tests/Alchemy/Phrasea/Core/Service/Cache/XcacheCacheTest.php @@ -0,0 +1,74 @@ +getDriver(); + $this->assertTrue($service instanceof \Doctrine\Common\Cache\CacheProvider); + } + else + { + try + { + $cache->getDriver(); + $this->fail("should raise an exception"); + } + catch(\Exception $e) + { + + } + } + } + + public function testServiceException() + { + $cache = new \Alchemy\Phrasea\Core\Service\Cache\XcacheCache( + self::$core, array() + ); + + try + { + $cache->getDriver(); + $this->fail("should raise an exception"); + } + catch (\Exception $e) + { + + } + } + + public function testType() + { + $cache = new \Alchemy\Phrasea\Core\Service\Cache\XcacheCache( + self::$core, array() + ); + + $this->assertEquals("xcache", $cache->getType()); + } + +} diff --git a/tests/Alchemy/Phrasea/Core/Service/Log/Doctrine/MonologTest.php b/tests/Alchemy/Phrasea/Core/Service/Log/Doctrine/MonologTest.php new file mode 100644 index 0000000000..20c2829939 --- /dev/null +++ b/tests/Alchemy/Phrasea/Core/Service/Log/Doctrine/MonologTest.php @@ -0,0 +1,71 @@ + "rotate" + , "filename" => "test" + , 'output' => 'json' + , 'channel' => 'test' + ); + + public function setUp() + { + parent::setUp(); + } + + public function testService() + { + + $log = new \Alchemy\Phrasea\Core\Service\Log\Doctrine\Monolog( + self::$core, $this->options + ); + + $this->assertInstanceOf("\Doctrine\Logger\MonologSQLLogger", $log->getDriver()); + } + + public function testType() + { + $log = new \Alchemy\Phrasea\Core\Service\Log\Doctrine\Monolog( + self::$core, $this->options + ); + + $this->assertEquals("doctrine_monolog", $log->getType()); + } + + public function testExceptionBadOutput() + { + try + { + $this->options["output"] = "unknowOutput"; + $log = new \Alchemy\Phrasea\Core\Service\Log\Doctrine\Monolog( + self::$core, $this->options + ); + $log->getDriver(); + $this->fail("should raise an exception"); + } + catch (\Exception $e) + { + + } + } + +} diff --git a/tests/Alchemy/Phrasea/Core/Service/Log/Doctrine/PhpechoTest.php b/tests/Alchemy/Phrasea/Core/Service/Log/Doctrine/PhpechoTest.php new file mode 100644 index 0000000000..ebb4b9f950 --- /dev/null +++ b/tests/Alchemy/Phrasea/Core/Service/Log/Doctrine/PhpechoTest.php @@ -0,0 +1,42 @@ +assertInstanceOf("\Doctrine\DBAL\Logging\EchoSQLLogger", $log->getDriver()); + } + + public function testType() + { + $log = new \Alchemy\Phrasea\Core\Service\Log\Doctrine\Phpecho( + self::$core, array() + ); + + $this->assertEquals("phpecho", $log->getType()); + } + +} diff --git a/tests/Alchemy/Phrasea/Core/Service/Log/MonologTest.php b/tests/Alchemy/Phrasea/Core/Service/Log/MonologTest.php new file mode 100644 index 0000000000..4ce16b5aae --- /dev/null +++ b/tests/Alchemy/Phrasea/Core/Service/Log/MonologTest.php @@ -0,0 +1,127 @@ +options = array( + "handler" => "rotate" + , "filename" => "test" + , "channel" => "test" + ); + } + + + public function testService() + { + $log = new \Alchemy\Phrasea\Core\Service\Log\Monolog( + self::$core, $this->options + ); + + $this->assertInstanceOf("\Monolog\Logger", $log->getDriver()); + } + + public function testType() + { + $log = new \Alchemy\Phrasea\Core\Service\Log\Monolog( + self::$core, $this->options + ); + + $this->assertEquals("monolog", $log->getType()); + } + + public function testExceptionMissingOptions() + { + try + { + $log = new \Alchemy\Phrasea\Core\Service\Log\Monolog( + self::$core, $this->options + ); + $this->fail("should raise an exception"); + } + catch (\Exception $e) + { + + } + } + + public function testExceptionMissingHandler() + { + try + { + unset($this->options["handler"]); + $log = new \Alchemy\Phrasea\Core\Service\Log\Monolog( + self::$core, $this->options + ); + $this->fail("should raise an exception"); + } + catch (\Exception $e) + { + + } + } + + public function testExceptionUnknowHandler() + { + try + { + $this->options["handler"] = "unknowHandler"; + $log = new \Alchemy\Phrasea\Core\Service\Log\Monolog( + self::$core, $this->options + ); + $this->fail("should raise an exception"); + } + catch (\Exception $e) + { + + } + } + + public function testMissingFile() + { + try + { + unset($this->options["filename"]); + $log = new \Alchemy\Phrasea\Core\Service\Log\Monolog( + self::$core, $this->options + ); + $this->fail("should raise an exception"); + } + catch (\Exception $e) + { + + } + } + + public function testStreamLogger() + { + + $this->options["handler"] = "stream"; + $log = new \Alchemy\Phrasea\Core\Service\Log\Monolog( + self::$core, $this->options + ); + $this->assertInstanceOf("\Monolog\Logger", $log->getDriver()); + } + +} diff --git a/tests/Alchemy/Phrasea/Core/Service/Orm/DoctrineTest.php b/tests/Alchemy/Phrasea/Core/Service/Orm/DoctrineTest.php new file mode 100644 index 0000000000..3f732cb07e --- /dev/null +++ b/tests/Alchemy/Phrasea/Core/Service/Orm/DoctrineTest.php @@ -0,0 +1,222 @@ +options = array( + "debug" => false + , "log" => array('service' => "Log\\sql_logger") + , "dbal" => "main_connexion" + , "cache" => array( + "metadata" => array('service' => "Cache\\array_cache") + , "query" => array('service' => "Cache\\array_cache") + , "result" => array('service' => "Cache\\array_cache") + ) + ); + } + + public function testService() + { + $doctrine = new \Alchemy\Phrasea\Core\Service\Orm\Doctrine( + self::$core, $this->options + ); + + $this->assertInstanceOf("\Doctrine\ORM\EntityManager", $doctrine->getDriver()); + } + + public function testType() + { + $doctrine = new \Alchemy\Phrasea\Core\Service\Orm\Doctrine( + self::$core, $this->options + ); + + $this->assertEquals("doctrine", $doctrine->getType()); + } + + public function testExceptionMissingOptions() + { + try + { + $doctrine = new \Alchemy\Phrasea\Core\Service\Orm\Doctrine( + self::$core, $this->options + ); + $this->fail("should raise an exception"); + } + catch (\Exception $e) + { + + } + } + + public function testNoCacheInOptions() + { + $this->markTestSkipped('To rewrite'); + unset($this->options["cache"]); + $doctrine = new \Alchemy\Phrasea\Core\Service\Orm\Doctrine( + self::$core, $this->options + ); + + foreach ($doctrine->getCacheServices()->all() as $service) + { + $this->assertEquals("array", $service->getType()); + } + } + + public function testUnknowCache() + { + $this->options["cache"]["result"] = "unknowCache"; + + try + { + $doctrine = new \Alchemy\Phrasea\Core\Service\Orm\Doctrine( + self::$core, $this->options + ); + $this->fail("An exception should be raised"); + } + catch (\Exception $e) + { + + } + } + + public function testIsDebug() + { + $doctrine = new \Alchemy\Phrasea\Core\Service\Orm\Doctrine( + self::$core, $this->options + ); + + $this->assertFalse($doctrine->isDebug()); + + $this->options['debug'] = true; + $doctrine = new \Alchemy\Phrasea\Core\Service\Orm\Doctrine( + self::$core, $this->options + ); + + $this->assertTrue($doctrine->isDebug()); + } + + public function testGetCacheServices() + { + $this->markTestSkipped('To rewrite'); + $doctrine = new \Alchemy\Phrasea\Core\Service\Orm\Doctrine( + self::$core, $this->options + ); + $this->assertInstanceOf("\Symfony\Component\DependencyInjection\ParameterBag\ParameterBag" + , $doctrine->getCacheServices()); + + foreach ($doctrine->getCacheServices()->all() as $service) + { + $this->assertEquals("array", $service->getType()); + } + + $this->options['orm']["cache"] = array( + "metadata" => "array_cache" + , "query" => "apc_cache" + , "result" => "xcache_cache" + ); + + if (extension_loaded("apc") && extension_loaded("xcache")) + { + $doctrine = new \Alchemy\Phrasea\Core\Service\Orm\Doctrine( + self::$core, $this->options + ); + $this->assertInstanceOf("\Symfony\Component\DependencyInjection\ParameterBag\ParameterBag" + , $doctrine->getCacheServices()); + + foreach ($doctrine->getCacheServices()->all() as $key => $service) + { + if ($key === "metadata") + $this->assertEquals("array", $service->getType()); + elseif ($key === "query") + $this->assertEquals("apc", $service->getType()); + elseif ($key === "result") + $this->assertEquals("xcache", $service->getType()); + } + } + else + { + try + { + $doctrine = new \Alchemy\Phrasea\Core\Service\Orm\Doctrine( + self::$core, $this->options + ); + $this->fail("An exception should be raised"); + } + catch (\Exception $e) + { + + } + } + } + + public function testExceptionUnknowLogService() + { + try + { + $this->options["log"] = "unknowLogger"; + $doctrine = new \Alchemy\Phrasea\Core\Service\Orm\Doctrine( + self::$core, $this->options + ); + $this->fail("should raise an exception"); + } + catch (\Exception $e) + { + + } + } + + public function testExceptionMissingDbal() + { + try + { + unset($this->options["dbal"]); + $doctrine = new \Alchemy\Phrasea\Core\Service\Orm\Doctrine( + self::$core, $this->options + ); + $this->fail("should raise an exception"); + } + catch (\Exception $e) + { + + } + } + + public function testExceptionUnknowDbal() + { + try + { + $this->options["dbal"] = "unknowDbal"; + $doctrine = new \Alchemy\Phrasea\Core\Service\Orm\Doctrine( + self::$core, $this->options + ); + $this->fail("should raise an exception"); + } + catch (\Exception $e) + { + + } + } + +} diff --git a/tests/Alchemy/Phrasea/Core/Service/ServiceAbtractTest.php b/tests/Alchemy/Phrasea/Core/Service/ServiceAbtractTest.php new file mode 100644 index 0000000000..4eda06158e --- /dev/null +++ b/tests/Alchemy/Phrasea/Core/Service/ServiceAbtractTest.php @@ -0,0 +1,49 @@ +getMockForAbstractClass( + "\Alchemy\Phrasea\Core\Service\ServiceAbstract" + , array( + self::$core + , array('option' => 'my_options') + ) + ); + + $this->object = $stub; + } + + public function testGetOptions() + { + $this->assertTrue(is_array($this->object->getOptions())); + $this->assertEquals(array('option' => 'my_options'), $this->object->getOptions()); + } + +} diff --git a/tests/Alchemy/Phrasea/Core/Service/TemplateEngine/TwigTest.php b/tests/Alchemy/Phrasea/Core/Service/TemplateEngine/TwigTest.php new file mode 100644 index 0000000000..49fc8de7c4 --- /dev/null +++ b/tests/Alchemy/Phrasea/Core/Service/TemplateEngine/TwigTest.php @@ -0,0 +1,64 @@ +options = array( + 'debug' => true + ,'charset' => 'utf-8' + ,'strict_variables' => true + ,'autoescape' => true + ,'optimizer' => true + ); + } + + public function testService() + { + $doctrine = new \Alchemy\Phrasea\Core\Service\TemplateEngine\Twig( + self::$core, $this->options + ); + + $this->assertInstanceOf("\Twig_Environment", $doctrine->getDriver()); + } + + public function testServiceExcpetion() + { + $doctrine = new \Alchemy\Phrasea\Core\Service\TemplateEngine\Twig( + self::$core, $this->options + ); + + $this->assertInstanceOf("\Twig_Environment", $doctrine->getDriver()); + } + + public function testType() + { + $doctrine = new \Alchemy\Phrasea\Core\Service\TemplateEngine\Twig( + self::$core, $this->options + ); + + $this->assertEquals("twig", $doctrine->getType()); + } + +} diff --git a/tests/Alchemy/Phrasea/Core/ServiceBuilder/AbstractBuilderTest.php b/tests/Alchemy/Phrasea/Core/ServiceBuilder/AbstractBuilderTest.php new file mode 100644 index 0000000000..5c4978e67a --- /dev/null +++ b/tests/Alchemy/Phrasea/Core/ServiceBuilder/AbstractBuilderTest.php @@ -0,0 +1,63 @@ +getMock( + "\Alchemy\Phrasea\Core\Service\Builder" + , array( + self::$core + , '' + , new \Symfony\Component\DependencyInjection\ParameterBag\ParameterBag() + ) + ); + $this->fail("should raise an exception"); + } + catch (\Exception $e) + { + + } + } + + public function testConstructExceptionCreate() + { + try + { + $stub = $this->getMock( + "\\Alchemy\\Phrasea\\Core\\Service\\Builder" + , array( + self::$core, + 'test', + new \Symfony\Component\DependencyInjection\ParameterBag\ParameterBag(), + ) + ); + $this->fail("should raise an exception"); + } + catch (\Exception $e) + { + + } + } + +} diff --git a/tests/Alchemy/Phrasea/Core/ServiceBuilder/CacheBuilderTest.php b/tests/Alchemy/Phrasea/Core/ServiceBuilder/CacheBuilderTest.php new file mode 100644 index 0000000000..b72c3a89af --- /dev/null +++ b/tests/Alchemy/Phrasea/Core/ServiceBuilder/CacheBuilderTest.php @@ -0,0 +1,50 @@ + "unknow") + ); + + try + { + $service = Alchemy\Phrasea\Core\Service\Builder::create(self::$core, $configuration); + $this->fail("An exception should be raised"); + } + catch (\Exception $e) + { + + } + } + + public function testCreate() + { + $configuration = new Symfony\Component\DependencyInjection\ParameterBag\ParameterBag( + array("type" => "Cache\\ArrayCache") + ); + + $service = Alchemy\Phrasea\Core\Service\Builder::create(self::$core, $configuration); + $this->assertInstanceOf("\Alchemy\Phrasea\Core\Service\ServiceAbstract", $service); + } + +} diff --git a/tests/Alchemy/Phrasea/Core/ServiceBuilder/LogBuilderTest.php b/tests/Alchemy/Phrasea/Core/ServiceBuilder/LogBuilderTest.php new file mode 100644 index 0000000000..ac71b8851c --- /dev/null +++ b/tests/Alchemy/Phrasea/Core/ServiceBuilder/LogBuilderTest.php @@ -0,0 +1,67 @@ + "unknow", "options" => array()) + ); + + try + { + $service = Alchemy\Phrasea\Core\Service\Builder::create(self::$core, $configuration); + $this->fail("An exception should be raised"); + } + catch (\Exception $e) + { + + } + } + + public function testCreate() + { + $configuration = new Symfony\Component\DependencyInjection\ParameterBag\ParameterBag( + array("type" => "Log\\Doctrine\\Monolog", "options" => array( + "handler" => "rotate" + , "filename" => "test" + , 'channel' => 'Test' + , 'output' => 'json' + , 'max_day' => '1' + ) + ) + ); + + $service = Alchemy\Phrasea\Core\Service\Builder::create(self::$core, $configuration); + $this->assertInstanceOf("\Alchemy\Phrasea\Core\Service\ServiceAbstract", $service); + } + + public function testCreateNamespace() + { + $configuration = new Symfony\Component\DependencyInjection\ParameterBag\ParameterBag( + array("type" => "Log\\Doctrine\\Phpecho", "options" => array()) + ); + + $service = Alchemy\Phrasea\Core\Service\Builder::create(self::$core, $configuration); + $this->assertInstanceOf("\Alchemy\Phrasea\Core\Service\ServiceAbstract", $service); + } + +} diff --git a/tests/Alchemy/Phrasea/Core/ServiceBuilder/OrmBuilderTest.php b/tests/Alchemy/Phrasea/Core/ServiceBuilder/OrmBuilderTest.php new file mode 100644 index 0000000000..fed9088119 --- /dev/null +++ b/tests/Alchemy/Phrasea/Core/ServiceBuilder/OrmBuilderTest.php @@ -0,0 +1,62 @@ + "unknow", "options" => array()) + ); + + try + { + $service = Alchemy\Phrasea\Core\Service\Builder::create(self::$core, $configuration); + $this->fail("An exception should be raised"); + } + catch (\Exception $e) + { + + } + } + + public function testCreate() + { + $registry = $this->getMock("\RegistryInterface"); + + $configuration = new Symfony\Component\DependencyInjection\ParameterBag\ParameterBag( + array("type" => "Orm\\Doctrine", "options" => array( + "debug" => false + , "log" => array('service'=>"Log\\query_logger") + , "dbal" => "main_connexion" + , "cache" => array( + "metadata" => "Cache\\array_cache" + , "query" => "Cache\\array_cache" + , "result" => "Cache\\array_cache" + ) + ) + ) + ); + + $service = Alchemy\Phrasea\Core\Service\Builder::create(self::$core, $configuration); + $this->assertInstanceOf("\Alchemy\Phrasea\Core\Service\ServiceAbstract", $service); + } + +} diff --git a/tests/Alchemy/Phrasea/Core/ServiceBuilder/TemplateBuilderTest.php b/tests/Alchemy/Phrasea/Core/ServiceBuilder/TemplateBuilderTest.php new file mode 100644 index 0000000000..b2aea9c3bd --- /dev/null +++ b/tests/Alchemy/Phrasea/Core/ServiceBuilder/TemplateBuilderTest.php @@ -0,0 +1,58 @@ + "unknow", "options" => array()) + ); + + try + { + $service = Alchemy\Phrasea\Core\Service\Builder::create(self::$core, $configuration); + $this->fail("An exception should be raised"); + } + catch (\Exception $e) + { + + } + } + + public function testCreate() + { + $configuration = new Symfony\Component\DependencyInjection\ParameterBag\ParameterBag( + array( + "type" => "TemplateEngine\\Twig" + , "options" => array( + 'debug' => 'true' + , 'charset' => 'UTF-8' + , 'strict_variables' => 'true' + , 'autoescape' => 'true' + , 'optimizer' => 'true' + )) + ); + + $service = Alchemy\Phrasea\Core\Service\Builder::create(self::$core, $configuration); + $this->assertInstanceOf("\Alchemy\Phrasea\Core\Service\ServiceAbstract", $service); + } + +} diff --git a/tests/Alchemy/Phrasea/Core/VersionTest.php b/tests/Alchemy/Phrasea/Core/VersionTest.php new file mode 100644 index 0000000000..456cf6ae9d --- /dev/null +++ b/tests/Alchemy/Phrasea/Core/VersionTest.php @@ -0,0 +1,36 @@ +assertTrue(is_string(Version::getNumber())); + $this->assertRegExp('/[\d]{1}\.[\d]{1,2}\.[\d]{1,2}/', Version::getNumber()); + } + + public function testGetName() + { + $this->assertTrue(is_string(Version::getName())); + $this->assertTrue(strlen(Version::getName()) > 3); + } +} diff --git a/tests/Alchemy/Phrasea/CoreTest.php b/tests/Alchemy/Phrasea/CoreTest.php new file mode 100644 index 0000000000..c17d004c5c --- /dev/null +++ b/tests/Alchemy/Phrasea/CoreTest.php @@ -0,0 +1,146 @@ +assertInstanceOf('\Alchemy\Phrasea\Core\Version', self::$core->getVersion()); + } + + public function testCoreRegistry() + { + $this->assertInstanceOf('\registryInterface', self::$core->getRegistry()); + } + + public function testCoreEntityManager() + { + $this->assertInstanceOf('\Doctrine\ORM\EntityManager', self::$core->getEntityManager()); + } + + public function testCoreTemplateEngine() + { + $this->assertInstanceOf('\Twig_Environment', self::$core->getTwig()); + } + + public function testCoreSerializer() + { + $this->assertInstanceOf('\Symfony\Component\Serializer\Serializer', self::$core->getSerializer()); + } + + public function testCoreConfiguration() + { + $this->assertInstanceOf('\Alchemy\Phrasea\Core\Configuration', self::$core->getConfiguration()); + } + + public function testIsAuthenticathed() + { + $this->assertFalse(self::$core->isAuthenticated()); + $appbox = appbox::get_instance(\bootstrap::getCore()); + $session = $appbox->get_session(); + $auth = new Session_Authentication_None(self::$user); + $session->authenticate($auth); + $this->assertTrue(self::$core->isAuthenticated()); + $session->logout(); + } + + public function testGetAuthenticathed() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $session = $appbox->get_session(); + $auth = new Session_Authentication_None(self::$user); + $session->authenticate($auth); + $this->assertInstanceOf("\User_Adapter", self::$core->getAuthenticatedUser()); + $session->logout(); + } + + public function testGetAvailableLanguages() + { + $languages = \Alchemy\Phrasea\Core::getAvailableLanguages(); + $this->assertTrue(is_array($languages)); + $this->assertEquals(5, count($languages)); + $this->assertTrue(array_key_exists("ar_SA", $languages)); + $this->assertTrue(array_key_exists("de_DE", $languages)); + $this->assertTrue(array_key_exists("en_GB", $languages)); + $this->assertTrue(array_key_exists("es_ES", $languages)); + $this->assertTrue(array_key_exists("fr_FR", $languages)); + } + + public function testGetPhpConf() + { + \Alchemy\Phrasea\Core::initPHPConf(); +// $this->assertEquals("4096", ini_get('output_buffering')); + $this->assertGreaterThanOrEqual(2048, (int) ini_get('memory_limit')); + $this->assertEquals("6143", ini_get('error_reporting')); + $this->assertEquals("UTF-8", ini_get('default_charset')); + $this->assertEquals("1", ini_get('session.use_cookies')); + $this->assertEquals("1", ini_get('session.use_only_cookies')); + $this->assertEquals("0", ini_get('session.auto_start')); + $this->assertEquals("1", ini_get('session.hash_function')); + $this->assertEquals("6", ini_get('session.hash_bits_per_character')); + $this->assertEquals("1", ini_get('allow_url_fopen')); + } + + public function testGetEnv() + { + $core = new \Alchemy\Phrasea\Core("test"); + $this->assertEquals("test", $core->getEnv()); + } + + public function testNotInstalled() + { + + if (!extension_loaded('test_helpers')) + { + $this->fail("test_helpers extension required"); + } + + set_new_overload(array($this, 'newCallback')); + + $specification = new \Alchemy\Phrasea\Core\Configuration\ApplicationSpecification(); + + $class = $this->getMock( + '\Alchemy\Phrasea\Core\Configuration' + , array('isInstalled') + , array($specification) + , 'ConfMock' + ); + + $class->expects($this->any()) + ->method('isInstalled') + ->will($this->returnValue(false)); + + $core = new \Alchemy\Phrasea\Core("test"); + + $this->assertInstanceOf("\Setup_Registry", $core->getRegistry()); + + unset_new_overload(); + } + + protected function newCallback($className) + { + switch ($className) + { + case 'Alchemy\Phrasea\Core\Configuration': return 'ConfMock'; + default: return $className; + } + } + +} diff --git a/tests/Alchemy/Phrasea/Helper/Record/BridgeTest.php b/tests/Alchemy/Phrasea/Helper/Record/BridgeTest.php new file mode 100644 index 0000000000..62e0146d19 --- /dev/null +++ b/tests/Alchemy/Phrasea/Helper/Record/BridgeTest.php @@ -0,0 +1,34 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} diff --git a/tests/Alchemy/Phrasea/Helper/Record/EditTest.php b/tests/Alchemy/Phrasea/Helper/Record/EditTest.php new file mode 100644 index 0000000000..2a5b332138 --- /dev/null +++ b/tests/Alchemy/Phrasea/Helper/Record/EditTest.php @@ -0,0 +1,146 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * + * @todo Implement testHas_thesaurus(). + */ + public function testHas_thesaurus() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * + * @todo Implement testGet_javascript_elements_ids(). + */ + public function testGet_javascript_elements_ids() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * + * @todo Implement testGet_javascript_elements(). + */ + public function testGet_javascript_elements() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * + * @todo Implement testGet_javascript_sugg_values(). + */ + public function testGet_javascript_sugg_values() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * + * @todo Implement testGet_javascript_status(). + */ + public function testGet_javascript_status() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * + * @todo Implement testGet_javascript_fields(). + */ + public function testGet_javascript_fields() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * + * @todo Implement testGet_status(). + */ + public function testGet_status() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * + * @todo Implement testGet_fields(). + */ + public function testGet_fields() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * + * @todo Implement testExecute(). + */ + public function testExecute() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} diff --git a/tests/Alchemy/Phrasea/Helper/Record/FeedTest.php b/tests/Alchemy/Phrasea/Helper/Record/FeedTest.php new file mode 100644 index 0000000000..b30477df88 --- /dev/null +++ b/tests/Alchemy/Phrasea/Helper/Record/FeedTest.php @@ -0,0 +1,34 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} diff --git a/tests/Alchemy/Phrasea/Helper/Record/HelperTest.php b/tests/Alchemy/Phrasea/Helper/Record/HelperTest.php new file mode 100644 index 0000000000..19b69c8b0b --- /dev/null +++ b/tests/Alchemy/Phrasea/Helper/Record/HelperTest.php @@ -0,0 +1,194 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * + * @todo Implement testGet_original_basket(). + */ + public function testGet_original_basket() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * + * @todo Implement testIs_single_grouping(). + */ + public function testIs_single_grouping() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * + * @todo Implement testGet_grouping_head(). + */ + public function testGet_grouping_head() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * + * @todo Implement testGet_elements(). + */ + public function testGet_elements() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * + * @todo Implement testHas_many_sbas(). + */ + public function testHas_many_sbas() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * + * @todo Implement testIs_possible(). + */ + public function testIs_possible() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * + * @todo Implement testGet_count_not_actionable(). + */ + public function testGet_count_not_actionable() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * + * @todo Implement testGet_count_actionable(). + */ + public function testGet_count_actionable() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * + * @todo Implement testGet_count_actionable_groupings(). + */ + public function testGet_count_actionable_groupings() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * + * @todo Implement testGet_count_element_received(). + */ + public function testGet_count_element_received() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * + * @todo Implement testGet_sbas_id(). + */ + public function testGet_sbas_id() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * + * @todo Implement testGet_serialize_list(). + */ + public function testGet_serialize_list() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * + * @todo Implement testGrep_records(). + */ + public function testGrep_records() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} diff --git a/tests/Alchemy/Phrasea/Helper/Record/MoveCollectionTest.php b/tests/Alchemy/Phrasea/Helper/Record/MoveCollectionTest.php new file mode 100644 index 0000000000..eefdac0c3a --- /dev/null +++ b/tests/Alchemy/Phrasea/Helper/Record/MoveCollectionTest.php @@ -0,0 +1,62 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * + * @todo Implement testPropose(). + */ + public function testPropose() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * + * @todo Implement testExecute(). + */ + public function testExecute() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} diff --git a/tests/Alchemy/Phrasea/Helper/Record/PrinterTest.php b/tests/Alchemy/Phrasea/Helper/Record/PrinterTest.php new file mode 100644 index 0000000000..8dd83fd111 --- /dev/null +++ b/tests/Alchemy/Phrasea/Helper/Record/PrinterTest.php @@ -0,0 +1,50 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * + * @todo Implement testGet_count_thumbnail(). + */ + public function testGet_count_thumbnail() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} diff --git a/tests/Alchemy/Phrasea/Helper/Record/PushTest.php b/tests/Alchemy/Phrasea/Helper/Record/PushTest.php new file mode 100644 index 0000000000..00eafb77d1 --- /dev/null +++ b/tests/Alchemy/Phrasea/Helper/Record/PushTest.php @@ -0,0 +1,39 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} diff --git a/tests/Alchemy/Phrasea/Helper/Record/TooltipTest.php b/tests/Alchemy/Phrasea/Helper/Record/TooltipTest.php new file mode 100644 index 0000000000..1bf165132d --- /dev/null +++ b/tests/Alchemy/Phrasea/Helper/Record/TooltipTest.php @@ -0,0 +1,34 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} diff --git a/tests/Alchemy/Phrasea/Loader/AutoloaderTest.php b/tests/Alchemy/Phrasea/Loader/AutoloaderTest.php new file mode 100644 index 0000000000..226498b9a2 --- /dev/null +++ b/tests/Alchemy/Phrasea/Loader/AutoloaderTest.php @@ -0,0 +1,53 @@ +addPath('fixture', __DIR__ . '/Fixtures'); + $autoloader->loadClass($testClassName); + $this->assertTrue(class_exists($testClassName)); + } + + public function testAddPath() + { + $autoloader = new Alchemy\Phrasea\Loader\Autoloader(); + $pathNb = count($autoloader->getPaths()); + $autoloader->addPath('fixture', __DIR__ . '/Fixtures'); + $this->assertGreaterThan($pathNb, count($autoloader->getPaths())); + $this->assertArrayHasKey('fixture', $autoloader->getPaths()); + } + + public function testGetPath() + { + $autoloader = new Alchemy\Phrasea\Loader\Autoloader(); + $this->assertTrue(is_array($autoloader->getPaths())); + $this->assertTrue(2 === count($autoloader->getPaths())); + $this->assertArrayHasKey('config', $autoloader->getPaths()); + $this->assertArrayHasKey('library', $autoloader->getPaths()); + } +} diff --git a/tests/Alchemy/Phrasea/Loader/CacheAutoloaderTest.php b/tests/Alchemy/Phrasea/Loader/CacheAutoloaderTest.php new file mode 100644 index 0000000000..fd604e2474 --- /dev/null +++ b/tests/Alchemy/Phrasea/Loader/CacheAutoloaderTest.php @@ -0,0 +1,125 @@ +apc = true; + } + + if (extension_loaded('xcache')) + { + $this->xcache = true; + } + } + + public function testConstruct() + { + if (!$this->apc && !$this->xcache) + { + try + { + $autoloader = new Alchemy\Phrasea\Loader\CacheAutoloader('test_prefix_'); + $this->fail("should raise an exception"); + } + catch(\Exception $e) + { + + } + } + } + + public function testFindFileApc() + { + if ($this->apc) + { + if (!(ini_get('apc.enabled') && ini_get('apc.enable_cli'))) + { + $this->markTestSkipped('The apc extension is available, but not enabled.'); + } + else + { + apc_clear_cache('user'); + } + + $autoloader = new Alchemy\Phrasea\Loader\CacheAutoloader('test_prefix_'); + $cacheAdapter = $autoloader->getAdapter(); + $this->assertEquals($autoloader->findFile('Test_HelloCache'), $cacheAdapter->fetch('test_prefix_Test_Hello')); + } + } + + public function testGetPrefix() + { + if ($this->apc) + { + $autoloader = new Alchemy\Phrasea\Loader\CacheAutoloader('test_prefix_'); + $this->assertEquals('test_prefix_', $autoloader->getPrefix()); + } + } + + public function testRegister() + { + if ($this->apc) + { + if (!(ini_get('apc.enabled') && ini_get('apc.enable_cli'))) + { + $this->markTestSkipped('The apc extension is available, but not enabled.'); + } + else + { + apc_clear_cache('user'); + } + $autoloader = new Alchemy\Phrasea\Loader\CacheAutoloader('test_prefix_'); + $autoloader->addPath('fixture', __DIR__ . '/Fixtures'); + $autoloader->register(); + $this->assertTrue(class_exists("Test_test")); + } + } + + public function testFindFileXcache() + { + if ($this->xcache) + { + $this->marktestSkipped("can't use xcache in cli mode"); + } + } + + public function tearDown() + { + if (ini_get('apc.enabled') && ini_get('apc.enable_cli')) + { + apc_clear_cache('user'); + } + parent::tearDown(); + } + +} diff --git a/tests/Alchemy/Phrasea/Loader/Fixtures/Test/Hello.class.php b/tests/Alchemy/Phrasea/Loader/Fixtures/Test/Hello.class.php new file mode 100644 index 0000000000..0860cedf9e --- /dev/null +++ b/tests/Alchemy/Phrasea/Loader/Fixtures/Test/Hello.class.php @@ -0,0 +1,22 @@ +object = new \Alchemy\Phrasea\Vocabulary\ControlProvider\UserProvider(); + } + + /** + * Verify that Type is scalar and that the classname is like {Type}Provider + */ + public function testGetType() + { + $type = $this->object->getType(); + + $this->assertTrue(is_scalar($type)); + + $classname = array_pop(explode('\\', get_class($this->object))); + + $this->assertEquals($classname, $type . 'Provider'); + } + + public function testGetName() + { + $this->assertTrue(is_scalar($this->object->getName())); + } + + public function testFind() + { + $results = $this->object->find('BABE', self::$user, self::$collection->get_databox()); + + $this->assertInstanceOf('\\Doctrine\\Common\\Collections\\ArrayCollection', $results); + + $results = $this->object->find(self::$user->get_email(), self::$user, self::$collection->get_databox()); + + $this->assertInstanceOf('\\Doctrine\\Common\\Collections\\ArrayCollection', $results); + $this->assertTrue($results->count() > 0); + + $results = $this->object->find(self::$user->get_firstname(), self::$user, self::$collection->get_databox()); + + $this->assertInstanceOf('\\Doctrine\\Common\\Collections\\ArrayCollection', $results); + $this->assertTrue($results->count() > 0); + + $results = $this->object->find(self::$user->get_lastname(), self::$user, self::$collection->get_databox()); + + $this->assertInstanceOf('\\Doctrine\\Common\\Collections\\ArrayCollection', $results); + $this->assertTrue($results->count() > 0); + } + + public function testValidate() + { + $this->assertFalse($this->object->validate(-200)); + $this->assertFalse($this->object->validate('A')); + $this->assertTrue($this->object->validate(self::$user->get_id())); + } + + public function testGetValue() + { + try + { + $this->object->getValue(-200); + $this->fail('Should raise an exception'); + } + catch(\Exception $e) + { + + } + + try + { + $this->object->getValue('A'); + $this->fail('Should raise an exception'); + } + catch(\Exception $e) + { + + } + + $this->assertEquals(self::$user->get_display_name(), $this->object->getValue(self::$user->get_id())); + } + +} + +?> diff --git a/tests/Alchemy/Phrasea/Vocabulary/ControllerTest.php b/tests/Alchemy/Phrasea/Vocabulary/ControllerTest.php new file mode 100644 index 0000000000..7ab33c47a8 --- /dev/null +++ b/tests/Alchemy/Phrasea/Vocabulary/ControllerTest.php @@ -0,0 +1,41 @@ +assertInstanceOf('\\Alchemy\\Phrasea\\Vocabulary\\ControlProvider\\UserProvider', $provider); + + try + { + $provider = \Alchemy\Phrasea\Vocabulary\Controller::get('Zebulon'); + $this->fail('Should raise an exception'); + } + catch(\Exception $e) + { + + } + } + + public function testGetAvailable() + { + $available = \Alchemy\Phrasea\Vocabulary\Controller::getAvailable(); + + $this->assertTrue(is_array($available)); + + foreach($available as $controller) + { + $this->assertInstanceOf('\\Alchemy\\Phrasea\\Vocabulary\\ControlProvider\\ControlProviderInterface', $controller); + } + } + +} diff --git a/tests/Alchemy/Phrasea/Vocabulary/TermTest.php b/tests/Alchemy/Phrasea/Vocabulary/TermTest.php new file mode 100644 index 0000000000..dc5a751123 --- /dev/null +++ b/tests/Alchemy/Phrasea/Vocabulary/TermTest.php @@ -0,0 +1,74 @@ +control = new Alchemy\Phrasea\Vocabulary\ControlProvider\UserProvider(); + + $this->object = new Term($this->value, $this->context); + $this->basicObject = new Term($this->basicValue); + $this->objectWithControl = new Term($this->value2, $this->context2, $this->control, $this->id); + } + + public function testGetValue() + { + $this->assertEquals($this->basicValue, $this->basicObject->getValue()); + $this->assertEquals($this->value, $this->object->getValue()); + $this->assertEquals($this->value2, $this->objectWithControl->getValue()); + } + + public function testGetContext() + { + $this->assertEquals(null, $this->basicObject->getContext()); + $this->assertEquals($this->context, $this->object->getContext()); + $this->assertEquals($this->context2, $this->objectWithControl->getContext()); + } + + public function testGetType() + { + $this->assertEquals(null, $this->basicObject->getType()); + $this->assertEquals(null, $this->object->getType()); + $this->assertEquals($this->control, $this->objectWithControl->getType()); + } + + public function testGetId() + { + $this->assertEquals(null, $this->basicObject->getId()); + $this->assertEquals(null, $this->object->getId()); + $this->assertEquals($this->id, $this->objectWithControl->getId()); + } + +} diff --git a/tests/Bridge/Api/Auth/Bridge_Api_Auth_AbstractTest.php b/tests/Bridge/Api/Auth/Bridge_Api_Auth_AbstractTest.php new file mode 100644 index 0000000000..6878ba397c --- /dev/null +++ b/tests/Bridge/Api/Auth/Bridge_Api_Auth_AbstractTest.php @@ -0,0 +1,23 @@ +getMockForAbstractClass('Bridge_Api_Auth_Abstract'); + $setting = $this->getMock("Bridge_AccountSettings", array(), array(), '' , false); + $return = $stub->set_settings($setting); + $this->assertEquals($stub, $return); + } + +} + +?> diff --git a/tests/Bridge/Api/Auth/Bridge_Api_Auth_FlickrTest.php b/tests/Bridge/Api/Auth/Bridge_Api_Auth_FlickrTest.php new file mode 100644 index 0000000000..d62b978f81 --- /dev/null +++ b/tests/Bridge/Api/Auth/Bridge_Api_Auth_FlickrTest.php @@ -0,0 +1,186 @@ +object = new Bridge_Api_Auth_Flickr(); + } + + public function testParse_request_token() + { + $this->assertNull($this->object->parse_request_token()); + $_GET["frob"] = "123"; + $this->assertEquals("123", $this->object->parse_request_token()); + unset($_GET["frob"]); + $this->assertNull($this->object->parse_request_token()); + } + + public function testConnect() + { + $api = $this->getMock("Phlickr_Api", array(), array(), "", false); + //mock api method + $api->expects($this->once()) + ->method("setAuthTokenFromFrob") + ->will($this->returnValue("un_token")); + + $api->expects($this->once()) + ->method("setAuthToken") + ->with($this->equalTo("un_token")); + + $api->expects($this->once()) + ->method("isAuthValid") + ->will($this->returnValue(true)); + + $stub = $this->getMock("Bridge_Api_Auth_Flickr", array("get_api")); + + $stub->expects($this->any()) + ->method("get_api") + ->will($this->returnValue($api)); + + $return = $stub->connect("123"); + + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $return); + $this->assertArrayHasKey("auth_token", $return); + $this->assertEquals("un_token", $return["auth_token"]); + } + + public function testBadConnect() + { + $api = $this->getMock("Phlickr_Api", array(), array(), "", false); + //mock api method + $api->expects($this->once()) + ->method("setAuthTokenFromFrob") + ->will($this->returnValue("un_token")); + + $api->expects($this->once()) + ->method("isAuthValid") + ->will($this->returnValue(false)); + + $stub = $this->getMock("Bridge_Api_Auth_Flickr", array("get_api")); + + $stub->expects($this->any()) + ->method("get_api") + ->will($this->returnValue($api)); + + $this->setExpectedException("Bridge_Exception_ApiConnectorAccessTokenFailed"); + + $return = $stub->connect("123"); + } + + public function testReconnect() + { + $this->assertEquals($this->object, $this->object->reconnect()); + } + + public function testDisconnect() + { + $setting = $this->getMock("Bridge_AccountSettings", array("set"), array(), "", false); + + $setting->expects($this->once()) + ->method("set") + ->with($this->equalTo("auth_token"), $this->isNull()); + + $this->object->set_settings($setting); + + $return = $this->object->disconnect(); + + $this->assertEquals($this->object, $return); + } + + public function testIs_connected() + { + $setting = $this->getMock("Bridge_AccountSettings", array("get"), array(), "", false); + + $setting->expects($this->any()) + ->method("get") + ->with($this->equalTo("auth_token")) + ->will($this->onConsecutiveCalls("123456", 123456, null)); + + $this->object->set_settings($setting); + + $this->assertTrue($this->object->is_connected()); + $this->assertTrue($this->object->is_connected()); + $this->assertFalse($this->object->is_connected()); + } + + public function testGet_auth_signatures() + { + $setting = $this->getMock("Bridge_AccountSettings", array("get"), array(), "", false); + + $setting->expects($this->once()) + ->method("get") + ->with($this->equalTo("auth_token")) + ->will($this->returnValue("123")); + + $this->object->set_settings($setting); + + $return = $this->object->get_auth_signatures(); + + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $return); + $this->assertArrayHasKey("auth_token", $return); + $this->assertEquals("123", $return["auth_token"]); + } + + public function testSet_parameters() + { + $parameters = array( + "caca" => "boudain" + , "sirop" => "fraise" + , "choco" => "banane" + , "pirouette" => "cacahuete" + ); + + $return = $this->object->set_parameters($parameters); + + $this->assertEquals(0, sizeof(get_object_vars($this->object))); + $this->assertEquals($return, $this->object); + } + + public function testGet_auth_url() + { + $api = $this->getMock("Phlickr_Api", array(), array(), "", false); + //mock api method + $api->expects($this->any()) + ->method("requestFrob") + ->will($this->returnValue("un_token")); + + $api->expects($this->any()) + ->method("buildAuthUrl") + ->with($this->equalTo("write"), $this->equalTo("un_token")) + ->will($this->returnValue("une_super_url")); + + $stub = $this->getMock("Bridge_Api_Auth_Flickr", array("get_api")); + + $stub->expects($this->any()) + ->method("get_api") + ->will($this->returnValue($api)); + + $params = array("permissions" => "write"); + + $stub->set_parameters($params); + + $this->assertEquals("une_super_url", $stub->get_auth_url()); + $this->assertEquals("une_super_url", $stub->get_auth_url($params)); + } + +} + +?> diff --git a/tests/Bridge/Api/Auth/Bridge_Api_Auth_OAuth2Test.php b/tests/Bridge/Api/Auth/Bridge_Api_Auth_OAuth2Test.php new file mode 100644 index 0000000000..3f4fe86a60 --- /dev/null +++ b/tests/Bridge/Api/Auth/Bridge_Api_Auth_OAuth2Test.php @@ -0,0 +1,152 @@ +object = new Bridge_Api_Auth_OAuth2(); + + $this->parameters = array( + 'client_id' => "client_id" + , 'client_secret' => "client_secret" + , 'redirect_uri' => "redirect_uri" + , 'scope' => 'super_scope' + , 'response_type' => 'code' + , 'token_endpoint' => "one_token_endpoint" + , 'auth_endpoint' => "one_auth_endpoint" + ); + } + + + public function testParse_request_token() + { + $this->object->set_parameters($this->parameters); + $_GET = array("code" => "12345"); + $token = $this->object->parse_request_token(); + $this->assertEquals("12345", $token); + $this->parameters["response_type"] = "blabla"; + $this->object->set_parameters($this->parameters); + $this->assertNull($this->object->parse_request_token()); + } + + public function testConnect() + { + $this->setExpectedException("Bridge_Exception_ApiConnectorAccessTokenFailed"); + + $this->object->connect("123"); + } + + public function testReconnect() + { + $setting = $this->getMock("Bridge_AccountSettings", array("get"), array(), "", false); + + $setting->expects($this->once()) + ->method("get") + ->with($this->equalTo("refresh_token")) + ->will($this->returnValue("123")); + + $this->object->set_settings($setting); + + $this->setExpectedException("Bridge_Exception_ApiConnectorAccessTokenFailed"); + + $this->object->reconnect(); + } + + public function testDisconnect() + { + $setting = $this->getMock("Bridge_AccountSettings", array("set"), array(), "", false); + + $setting->expects($this->once()) + ->method("set") + ->with($this->equalTo("auth_token"), $this->isNull()); + + $this->object->set_settings($setting); + + $return = $this->object->disconnect(); + + $this->assertEquals($this->object, $return); + } + + public function testIs_connected() + { + $setting = $this->getMock("Bridge_AccountSettings", array("get"), array(), "", false); + + $setting->expects($this->any()) + ->method("get") + ->with($this->equalTo("auth_token")) + ->will($this->onConsecutiveCalls("123456",123456, null)); + + $this->object->set_settings($setting); + + $this->assertTrue($this->object->is_connected()); + $this->assertTrue($this->object->is_connected()); + $this->assertFalse($this->object->is_connected()); + } + + public function testGet_auth_signatures() + { + $setting = $this->getMock("Bridge_AccountSettings", array("get"), array(), "", false); + + $setting->expects($this->once()) + ->method("get") + ->with($this->equalTo("auth_token")) + ->will($this->returnValue("123")); + + $this->object->set_settings($setting); + + $return = $this->object->get_auth_signatures(); + + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $return); + $this->assertArrayHasKey("auth_token", $return); + $this->assertEquals("123" , $return["auth_token"]); + } + + public function testSet_parameters() + { + $parameters = array( + "client_id" => "cid" + ,"allo"=>"hello" + ,"yo" => "coucou" + ,"response_type" => "hihi" + ); + + $return = $this->object->set_parameters($parameters); + + $this->assertEquals(0 , sizeof(get_object_vars($this->object))); + $this->assertEquals($return, $this->object); + } + + public function testGet_auth_url() + { + $this->object->set_parameters($this->parameters); + $expected_url = "one_auth_endpoint?response_type=code&client_id=client_id&redirect_uri=redirect_uri&scope=super_scope"; + $this->assertEquals($expected_url, $this->object->get_auth_url()); + + $more_params = array("test" => "test"); + $this->assertEquals($expected_url."&test=test", $this->object->get_auth_url($more_params)); + + $more_params = array("response_type" => "test"); + $this->assertNotEquals($expected_url, $this->object->get_auth_url($more_params)); + } + +} + +?> diff --git a/tests/Bridge/Api/Bridge_Api_AbstractCollectionTest.php b/tests/Bridge/Api/Bridge_Api_AbstractCollectionTest.php new file mode 100644 index 0000000000..d5ac544767 --- /dev/null +++ b/tests/Bridge/Api/Bridge_Api_AbstractCollectionTest.php @@ -0,0 +1,189 @@ +getMockForAbstractClass('Bridge_Api_AbstractCollection'); + $this->assertNull($stub->get_total_items()); + $stub->set_total_items("3"); + $this->assertEquals(3, $stub->get_total_items()); + } + + /** + * @todo Implement testSet_total_items(). + */ + public function testSet_total_items() + { + $stub = $this->getMockForAbstractClass('Bridge_Api_AbstractCollection'); + $return = $stub->set_total_items("3"); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_INT, $stub->get_total_items()); + $this->assertEquals(3, $stub->get_total_items()); + $this->assertEquals($return, $stub); + } + + /** + * @todo Implement testGet_items_per_page(). + */ + public function testGet_items_per_page() + { + $stub = $this->getMockForAbstractClass('Bridge_Api_AbstractCollection'); + $this->assertNull($stub->get_items_per_page()); + $stub->set_items_per_page("3"); + $this->assertEquals(3, $stub->get_items_per_page()); + } + + /** + * @todo Implement testSet_items_per_page(). + */ + public function testSet_items_per_page() + { + $stub = $this->getMockForAbstractClass('Bridge_Api_AbstractCollection'); + $return = $stub->set_items_per_page("3"); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_INT, $stub->get_items_per_page()); + $this->assertEquals(3, $stub->get_items_per_page()); + $this->assertEquals($return, $stub); + } + + /** + * @todo Implement testGet_current_page(). + */ + public function testGet_current_page() + { + $stub = $this->getMockForAbstractClass('Bridge_Api_AbstractCollection'); + $this->assertEquals(1, $stub->get_current_page()); + $stub->set_current_page("3"); + $this->assertEquals(3, $stub->get_current_page()); + } + + /** + * @todo Implement testSet_current_page(). + */ + public function testSet_current_page() + { + $stub = $this->getMockForAbstractClass('Bridge_Api_AbstractCollection'); + $return = $stub->set_current_page("3"); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_INT, $stub->get_current_page()); + $this->assertEquals(3, $stub->get_current_page()); + $this->assertEquals($return, $stub); + $return = $stub->set_current_page(-4); + $this->assertEquals(3, $stub->get_current_page()); + } + + /** + * @todo Implement testGet_total_page(). + */ + public function testGet_total_page() + { + $stub = $this->getMockForAbstractClass('Bridge_Api_AbstractCollection'); + $this->assertEquals(1, $stub->get_total_page()); + $stub->set_total_page("3"); + $this->assertEquals(3, $stub->get_total_page()); + } + + /** + * @todo Implement testSet_total_page(). + */ + public function testSet_total_page() + { + $stub = $this->getMockForAbstractClass('Bridge_Api_AbstractCollection'); + $return = $stub->set_total_page("3"); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_INT, $stub->get_total_page()); + $this->assertEquals(3, $stub->get_total_page()); + $this->assertEquals($return, $stub); + $return = $stub->set_total_page(-4); + $this->assertEquals(3, $stub->get_total_page()); + } + + /** + * @todo Implement testHas_next_page(). + */ + public function testHas_next_page() + { + $stub = $this->getMockForAbstractClass('Bridge_Api_AbstractCollection'); + $stub->set_current_page(2); + $stub->set_total_page(2); + $this->assertFalse($stub->has_next_page()); + $stub->set_current_page(1); + $stub->set_total_page(2); + $this->assertTrue($stub->has_next_page()); + $stub->set_current_page(3); + $stub->set_total_page(2); + $this->assertFalse($stub->has_next_page()); + } + + /** + * @todo Implement testHas_previous_page(). + */ + public function testHas_previous_page() + { + $stub = $this->getMockForAbstractClass('Bridge_Api_AbstractCollection'); + $stub->set_current_page(2); + $this->assertTrue($stub->has_previous_page()); + $stub->set_current_page(1); + $this->assertFalse($stub->has_previous_page()); + $stub->set_current_page(0); + $this->assertFalse($stub->has_previous_page()); + } + + /** + * @todo Implement testHas_more_than_one_page(). + */ + public function testHas_more_than_one_page() + { + $stub = $this->getMockForAbstractClass('Bridge_Api_AbstractCollection'); + $stub->set_total_page(2); + $this->assertTrue($stub->has_more_than_one_page()); + $stub->set_total_page(1); + $this->assertFalse($stub->has_more_than_one_page()); + $stub->set_total_page(0); + $this->assertFalse($stub->has_more_than_one_page()); + } + + /** + * @todo Implement testGet_elements(). + */ + public function testGet_elements() + { + $stub = $this->getMockForAbstractClass('Bridge_Api_AbstractCollection'); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $stub->get_elements()); + $this->assertEquals(array(), $stub->get_elements()); + } + +} + +?> diff --git a/tests/Bridge/Api/Bridge_Api_AbstractTest.php b/tests/Bridge/Api/Bridge_Api_AbstractTest.php new file mode 100644 index 0000000000..64cdcdde22 --- /dev/null +++ b/tests/Bridge/Api/Bridge_Api_AbstractTest.php @@ -0,0 +1,264 @@ +auth = $this->getMock("Bridge_Api_Auth_Interface"); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + public function tearDown() + { + + } + + public static function setUpBeforeClass() + { + parent::setUpBeforeClass(); + try + { + self::$api = Bridge_Api::get_by_api_name(appbox::get_instance(\bootstrap::getCore()), 'apitest'); + } + catch (Bridge_Exception_ApiNotFound $e) + { + self::$api = Bridge_Api::create(appbox::get_instance(\bootstrap::getCore()), 'apitest'); + } + + try + { + self::$account = Bridge_Account::load_account_from_distant_id(appbox::get_instance(\bootstrap::getCore()), self::$api, self::$user, 'kirikoo'); + } + catch (Bridge_Exception_AccountNotFound $e) + { + self::$account = Bridge_Account::create(appbox::get_instance(\bootstrap::getCore()), self::$api, self::$user, 'kirikoo', 'coucou'); + } + } + + public static function tearDownAfterClass() + { + parent::tearDownAfterClass(); + self::$api->delete(); + if (self::$account instanceof Bridge_Account) + self::$account->delete(); + } + + /** + * @todo Implement testSet_auth_settings(). + */ + public function testSet_auth_settings() + { + $stub = $this->getMockForAbstractClass('Bridge_Api_Abstract', array(registry::get_instance(), $this->auth, "Mock_Bridge_Api_Abstract")); + + $settings = self::$account->get_settings(); + + $stub->expects($this->once()) + ->method('set_transport_authentication_params'); + + $return = $stub->set_auth_settings($settings); + + $this->assertEquals($stub, $return); + } + + /** + * @todo Implement testConnect(). + */ + public function testConnectGood() + { + $stub = $this->getMock('Bridge_Api_Abstract', array("is_configured", "initialize_transport", "set_auth_params", "set_transport_authentication_params"), array(registry::get_instance(), $this->auth, "Mock_Bridge_Api_Abstract")); + + $stub->expects($this->once()) + ->method('is_configured') + ->will($this->returnValue(TRUE)); + + $this->auth->expects($this->once()) + ->method('parse_request_token') + ->will($this->returnValue("token")); + $this->auth->expects($this->once()) + ->method('connect') + ->will($this->returnValue(array("coucou"))); + + $return = $stub->connect(); + + $this->assertEquals(array("coucou"), $return); + } + + public function testConnectBad() + { + $stub = $this->getMock('Bridge_Api_Abstract', array("is_configured", "initialize_transport", "set_auth_params", "set_transport_authentication_params"), array(registry::get_instance(), $this->auth, "Mock_Bridge_Api_Abstract")); + + $stub->expects($this->once()) + ->method('is_configured') + ->will($this->returnValue(FALSE)); + + $this->setExpectedException("Bridge_Exception_ApiConnectorNotConfigured"); + + $stub->connect(); + } + + /** + * @todo Implement testReconnect(). + */ + public function testReconnect() + { + $stub = $this->getMock('Bridge_Api_Abstract', array("is_configured", "initialize_transport", "set_auth_params", "set_transport_authentication_params"), array(registry::get_instance(), $this->auth, "Mock_Bridge_Api_Abstract")); + + $stub->expects($this->once()) + ->method('is_configured') + ->will($this->returnValue(TRUE)); + + $this->auth->expects($this->once()) + ->method('reconnect'); + + $return = $stub->reconnect(); + + $this->assertEquals($stub, $return); + } + + /** + * @todo Implement testReconnect(). + */ + public function testReconnectBad() + { + $stub = $this->getMock('Bridge_Api_Abstract', array("is_configured", "initialize_transport", "set_auth_params", "set_transport_authentication_params"), array(registry::get_instance(), $this->auth, "Mock_Bridge_Api_Abstract")); + + $stub->expects($this->once()) + ->method('is_configured') + ->will($this->returnValue(FALSE)); + + $this->setExpectedException("Bridge_Exception_ApiConnectorNotConfigured"); + + $stub->reconnect(); + } + + /** + * @todo Implement testDisconnect(). + */ + public function testDisconnect() + { + $stub = $this->getMock('Bridge_Api_Abstract', array("is_configured", "initialize_transport", "set_auth_params", "set_transport_authentication_params"), array(registry::get_instance(), $this->auth, "Mock_Bridge_Api_Abstract")); + + $stub->expects($this->once()) + ->method('is_configured') + ->will($this->returnValue(TRUE)); + + $this->auth->expects($this->once()) + ->method('disconnect'); + + $return = $stub->disconnect(); + + $this->assertEquals($stub, $return); + } + + /** + * @todo Implement testDisconnect(). + */ + public function testDisconnectBad() + { + $stub = $this->getMock('Bridge_Api_Abstract', array("is_configured", "initialize_transport", "set_auth_params", "set_transport_authentication_params"), array(registry::get_instance(), $this->auth, "Mock_Bridge_Api_Abstract")); + + $stub->expects($this->once()) + ->method('is_configured') + ->will($this->returnValue(FALSE)); + + $this->setExpectedException("Bridge_Exception_ApiConnectorNotConfigured"); + + $stub->disconnect(); + } + + /** + * @todo Implement testIs_connected(). + */ + public function testIs_connected() + { + $stub = $this->getMockForAbstractClass('Bridge_Api_Abstract', array(registry::get_instance(), $this->auth, "Mock_Bridge_Api_Abstract")); + + $this->auth->expects($this->once()) + ->method('is_connected') + ->will($this->returnValue(TRUE)); + + $return = $stub->is_connected(); + + $this->assertEquals(TRUE, $return); + } + + /** + * @todo Implement testGet_auth_url(). + */ + public function testGet_auth_url() + { + $stub = $this->getMockForAbstractClass('Bridge_Api_Abstract', array(registry::get_instance(), $this->auth, "Mock_Bridge_Api_Abstract")); + + $this->auth->expects($this->once()) + ->method('get_auth_url') + ->with($this->isType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY)) + ->will($this->returnValue("une url")); + + $return = $stub->get_auth_url(); + + $this->assertEquals("une url", $return); + } + + /** + * @todo Implement testSet_locale(). + */ + public function testSet_locale() + { + $stub = $this->getMockForAbstractClass('Bridge_Api_Abstract', array(registry::get_instance(), $this->auth, "Mock_Bridge_Api_Abstract")); + + $stub->set_locale("fr"); + + $this->assertEquals("fr", $stub->get_locale()); + } + + /** + * @todo Implement testIs_valid_object_id(). + */ + public function testIs_valid_object_id() + { + $stub = $this->getMockForAbstractClass('Bridge_Api_Abstract', array(registry::get_instance(), $this->auth, "Mock_Bridge_Api_Abstract")); + + $this->assertTrue($stub->is_valid_object_id("abc")); + $this->assertTrue($stub->is_valid_object_id(123)); + $this->assertTrue($stub->is_valid_object_id(12.25)); + $this->assertFalse($stub->is_valid_object_id(array())); + $this->assertFalse($stub->is_valid_object_id(true)); + } + + /** + * @todo Implement testHandle_exception(). + */ + public function testHandle_exception() + { + $stub = $this->getMockForAbstractClass('Bridge_Api_Abstract', array(registry::get_instance(), $this->auth, "Mock_Bridge_Api_Abstract")); + $e = new Exception("hihi"); + $void = $stub->handle_exception($e); + $this->assertNull($void); + } + +} + +?> diff --git a/tests/Bridge/Api/Bridge_Api_ContainerCollectionTest.php b/tests/Bridge/Api/Bridge_Api_ContainerCollectionTest.php new file mode 100644 index 0000000000..99ad8de6f6 --- /dev/null +++ b/tests/Bridge/Api/Bridge_Api_ContainerCollectionTest.php @@ -0,0 +1,27 @@ +getMock("Bridge_Api_ContainerInterface"); + $collection->add_element(new $container); + $i++; + } + $this->assertEquals(5, sizeof($collection->get_elements())); + } + +} + +?> diff --git a/tests/Bridge/Api/Bridge_Api_DailymotionTest.php b/tests/Bridge/Api/Bridge_Api_DailymotionTest.php new file mode 100644 index 0000000000..2dcd9185af --- /dev/null +++ b/tests/Bridge/Api/Bridge_Api_DailymotionTest.php @@ -0,0 +1,358 @@ +object = $this->getMock("Bridge_Api_Dailymotion", array("initialize_transport", "set_auth_params"), array(registry::get_instance(), $this->getMock("Bridge_Api_Auth_Interface"))); + + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + + } + + /** + * @todo Implement testConnect(). + */ + public function testConnect() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testReconnect(). + */ + public function testReconnect() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_user_id(). + */ + public function testGet_user_id() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_user_name(). + */ + public function testGet_user_name() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_name(). + */ + public function testGet_name() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_icon_url(). + */ + public function testGet_icon_url() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_image_url(). + */ + public function testGet_image_url() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_url(). + */ + public function testGet_url() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_infos(). + */ + public function testGet_infos() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_default_element_type(). + */ + public function testGet_default_element_type() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_default_container_type(). + */ + public function testGet_default_container_type() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_element_types(). + */ + public function testGet_element_types() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_container_types(). + */ + public function testGet_container_types() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_object_class_from_type(). + */ + public function testGet_object_class_from_type() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testList_elements(). + */ + public function testList_elements() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testList_containers(). + */ + public function testList_containers() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testUpdate_element(). + */ + public function testUpdate_element() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testCreate_container(). + */ + public function testCreate_container() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testAdd_element_to_container(). + */ + public function testAdd_element_to_container() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testDelete_object(). + */ + public function testDelete_object() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testAcceptable_records(). + */ + public function testAcceptable_records() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_element_status(). + */ + public function testGet_element_status() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testMap_connector_to_element_status(). + */ + public function testMap_connector_to_element_status() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_error_message_from_status(). + */ + public function testGet_error_message_from_status() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testHandle_exception(). + */ + public function testHandle_exception() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testUpload(). + */ + public function testUpload() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_element_from_id(). + */ + public function testGet_element_from_id() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_container_from_id(). + */ + public function testGet_container_from_id() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_category_list(). + */ + public function testGet_category_list() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/Bridge/Api/Bridge_Api_ElementCollectionTest.php b/tests/Bridge/Api/Bridge_Api_ElementCollectionTest.php new file mode 100644 index 0000000000..a62c1769ca --- /dev/null +++ b/tests/Bridge/Api/Bridge_Api_ElementCollectionTest.php @@ -0,0 +1,28 @@ +getMock("Bridge_Api_ElementInterface"); + $collection->add_element(new $element); + $i++; + } + $this->assertEquals(5, sizeof($collection->get_elements())); + } + +} + +?> diff --git a/tests/Bridge/Api/Bridge_Api_FlickrTest.php b/tests/Bridge/Api/Bridge_Api_FlickrTest.php new file mode 100644 index 0000000000..f1f4b091bd --- /dev/null +++ b/tests/Bridge/Api/Bridge_Api_FlickrTest.php @@ -0,0 +1,346 @@ +object = $this->getMock("Bridge_Api_Flickr", array("initialize_transport", "set_auth_params"), array(registry::get_instance(), $this->getMock("Bridge_Api_Auth_Interface"))); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + public function tearDown() + { + + } + + /** + * @todo Implement testConnect(). + */ + public function testConnect() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testDisconnect(). + */ + public function testDisconnect() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_user_name(). + */ + public function testGet_user_name() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_user_id(). + */ + public function testGet_user_id() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_name(). + */ + public function testGet_name() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_icon_url(). + */ + public function testGet_icon_url() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_image_url(). + */ + public function testGet_image_url() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_url(). + */ + public function testGet_url() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_infos(). + */ + public function testGet_infos() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_default_element_type(). + */ + public function testGet_default_element_type() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_default_container_type(). + */ + public function testGet_default_container_type() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_element_from_id(). + */ + public function testGet_element_from_id() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_container_from_id(). + */ + public function testGet_container_from_id() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testList_containers(). + */ + public function testList_containers() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testUpdate_element(). + */ + public function testUpdate_element() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testCreate_container(). + */ + public function testCreate_container() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testAdd_element_to_container(). + */ + public function testAdd_element_to_container() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testDelete_object(). + */ + public function testDelete_object() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testList_elements(). + */ + public function testList_elements() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_element_status(). + */ + public function testGet_element_status() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testMap_connector_to_element_status(). + */ + public function testMap_connector_to_element_status() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_error_message_from_status(). + */ + public function testGet_error_message_from_status() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testUpload(). + */ + public function testUpload() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testAcceptable_records(). + */ + public function testAcceptable_records() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_object_class_from_type(). + */ + public function testGet_object_class_from_type() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_element_types(). + */ + public function testGet_element_types() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_container_types(). + */ + public function testGet_container_types() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_category_list(). + */ + public function testGet_category_list() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/Bridge/Api/Bridge_Api_YoutubeTest.php b/tests/Bridge/Api/Bridge_Api_YoutubeTest.php new file mode 100644 index 0000000000..aea9e2f95d --- /dev/null +++ b/tests/Bridge/Api/Bridge_Api_YoutubeTest.php @@ -0,0 +1,349 @@ +object = $this->getMock("Bridge_Api_Youtube", array("initialize_transport", "set_auth_params"), array(registry::get_instance(), $this->getMock("Bridge_Api_Auth_Interface"))); + } + + /** + * @todo Implement testConnect(). + */ + public function testConnect() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testReconnect(). + */ + public function testReconnect() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_user_id(). + */ + public function testGet_user_id() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_user_name(). + */ + public function testGet_user_name() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_name(). + */ + public function testGet_name() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_icon_url(). + */ + public function testGet_icon_url() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_image_url(). + */ + public function testGet_image_url() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_url(). + */ + public function testGet_url() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_infos(). + */ + public function testGet_infos() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_default_element_type(). + */ + public function testGet_default_element_type() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_default_container_type(). + */ + public function testGet_default_container_type() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_element_types(). + */ + public function testGet_element_types() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_container_types(). + */ + public function testGet_container_types() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_object_class_from_type(). + */ + public function testGet_object_class_from_type() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testList_elements(). + */ + public function testList_elements() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testList_containers(). + */ + public function testList_containers() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testUpdate_element(). + */ + public function testUpdate_element() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testCreate_container(). + */ + public function testCreate_container() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testAdd_element_to_container(). + */ + public function testAdd_element_to_container() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testDelete_object(). + */ + public function testDelete_object() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testAcceptable_records(). + */ + public function testAcceptable_records() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_element_status(). + */ + public function testGet_element_status() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testMap_connector_to_element_status(). + */ + public function testMap_connector_to_element_status() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_error_message_from_status(). + */ + public function testGet_error_message_from_status() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testHandle_exception(). + */ + public function testHandle_exception() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testUpload(). + */ + public function testUpload() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + + /** + * @todo Implement testGet_element_from_id(). + */ + public function testGet_element_from_id() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_category_list(). + */ + public function testGet_category_list() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_container_from_id(). + */ + public function testGet_container_from_id() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/Bridge/Api/Dailymotion/Bridge_Api_Dailymotion_ContainerTest.php b/tests/Bridge/Api/Dailymotion/Bridge_Api_Dailymotion_ContainerTest.php new file mode 100644 index 0000000000..9da28622ed --- /dev/null +++ b/tests/Bridge/Api/Dailymotion/Bridge_Api_Dailymotion_ContainerTest.php @@ -0,0 +1,117 @@ +test = array( + 'id' => '01234567' + ,'description' => 'one description' + , 'name' => 'hello container' + ); + } + + /** + * @todo Implement testGet_created_on(). + */ + public function testGet_created_on() + { + $this->object = new Bridge_Api_Dailymotion_Container($this->test, 'playlist', 'thumb', 'url'); + $this->assertNull($this->object->get_created_on()); + } + + /** + * @todo Implement testGet_description(). + */ + public function testGet_description() + { + $this->object = new Bridge_Api_Dailymotion_Container($this->test, 'playlist', 'thumb', 'url'); + $this->assertEquals($this->test['description'], $this->object->get_description()); + unset($this->test["description"]); + $this->object = new Bridge_Api_Dailymotion_Element($this->test, 'blabla'); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object->get_description()); + $this->assertEmpty($this->object->get_description()); + } + + /** + * @todo Implement testGet_id(). + */ + public function testGet_id() + { + $this->object = new Bridge_Api_Dailymotion_Container($this->test, 'playlist', 'thumb', 'url'); + $this->assertEquals($this->test['id'], $this->object->get_id()); + unset($this->test["id"]); + $this->object = new Bridge_Api_Dailymotion_Element($this->test, 'blabla'); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object->get_id()); + $this->assertEmpty($this->object->get_id()); + } + + /** + * @todo Implement testGet_thumbnail(). + */ + public function testGet_thumbnail() + { + $this->object = new Bridge_Api_Dailymotion_Container($this->test, 'playlist', 'thumb', 'url'); + $this->assertEquals('thumb', $this->object->get_thumbnail()); + } + + /** + * @todo Implement testGet_title(). + */ + public function testGet_title() + { + $this->object = new Bridge_Api_Dailymotion_Container($this->test, 'playlist', 'thumb', 'url'); + $this->assertEquals($this->test['name'], $this->object->get_title()); + unset($this->test["name"]); + $this->object = new Bridge_Api_Dailymotion_Element($this->test, 'blabla'); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object->get_title()); + $this->assertEmpty($this->object->get_title()); + } + + /** + * @todo Implement testGet_type(). + */ + public function testGet_type() + { + $this->object = new Bridge_Api_Dailymotion_Container($this->test, 'playlist', 'thumb', 'url'); + $this->assertEquals('playlist', $this->object->get_type()); + } + + /** + * @todo Implement testGet_updated_on(). + */ + public function testGet_updated_on() + { + $this->object = new Bridge_Api_Dailymotion_Container($this->test, 'playlist', 'thumb', 'url'); + $this->assertNull($this->object->get_updated_on()); + } + + /** + * @todo Implement testGet_url(). + */ + public function testGet_url() + { + $this->object = new Bridge_Api_Dailymotion_Container($this->test, 'playlist', 'thumb', 'url'); + $this->assertEquals('url', $this->object->get_url()); + } + +} + +?> diff --git a/tests/Bridge/Api/Dailymotion/Bridge_Api_Dailymotion_ElementTest.php b/tests/Bridge/Api/Dailymotion/Bridge_Api_Dailymotion_ElementTest.php new file mode 100644 index 0000000000..bf782e59c5 --- /dev/null +++ b/tests/Bridge/Api/Dailymotion/Bridge_Api_Dailymotion_ElementTest.php @@ -0,0 +1,170 @@ +test = array( + 'created_time' => time() + , 'description' => 'Description of a dailymotion element' + , 'id' => "1" + , 'thumbnail_medium_url' => 'thumbnail_medium_url' + , 'title' => 'title of dailymotion lement' + , 'modified_time' => time() + , 'url' => 'www.my.element/url' + , 'private' => 1 + , 'views_total' => '34' + , 'ratings_total' => '4' + , 'duration' => 80 + , 'channel' => 'animation' + ); + } + + public function testGet_created_on() + { + $this->object = new Bridge_Api_Dailymotion_Element($this->test, 'blabla'); + $this->assertEquals(DateTime::createFromFormat('U', $this->test['created_time']), $this->object->get_created_on()); + } + + public function testGet_description() + { + $this->object = new Bridge_Api_Dailymotion_Element($this->test, 'blabla'); + $this->assertEquals($this->test['description'], $this->object->get_description()); + unset($this->test["description"]); + $this->object = new Bridge_Api_Dailymotion_Element($this->test, 'blabla'); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object->get_description()); + $this->assertEmpty($this->object->get_description()); + } + + public function testGet_id() + { + $this->object = new Bridge_Api_Dailymotion_Element($this->test, 'blabla'); + $this->assertEquals($this->test['id'], $this->object->get_id()); + unset($this->test["id"]); + $this->object = new Bridge_Api_Dailymotion_Element($this->test, 'blabla'); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object->get_description()); + $this->assertEmpty($this->object->get_id()); + } + + public function testGet_thumbnail() + { + $this->object = new Bridge_Api_Dailymotion_Element($this->test, 'blabla'); + $this->assertEquals($this->test['thumbnail_medium_url'], $this->object->get_thumbnail()); + unset($this->test["thumbnail_medium_url"]); + $this->object = new Bridge_Api_Dailymotion_Element($this->test, 'blabla'); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object->get_thumbnail()); + $this->assertEmpty($this->object->get_thumbnail()); + } + + public function testGet_title() + { + $this->object = new Bridge_Api_Dailymotion_Element($this->test, 'blabla'); + $this->assertEquals($this->test['title'], $this->object->get_title()); + unset($this->test["title"]); + $this->object = new Bridge_Api_Dailymotion_Element($this->test, 'blabla'); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object->get_title()); + $this->assertEmpty($this->object->get_title()); + } + + public function testGet_type() + { + $type = 'kikoo'; + $this->object = new Bridge_Api_Dailymotion_Element($this->test, $type); + $this->assertEquals($type, $this->object->get_type()); + $type = 'kooki'; + $this->object = new Bridge_Api_Dailymotion_Element($this->test, $type); + $this->assertEquals($type, $this->object->get_type()); + } + + public function testGet_updated_on() + { + $this->object = new Bridge_Api_Dailymotion_Element($this->test, 'blabla'); + $this->assertEquals(DateTime::createFromFormat('U', $this->test['modified_time']), $this->object->get_updated_on()); + } + + public function testGet_url() + { + $this->object = new Bridge_Api_Dailymotion_Element($this->test, 'blabla'); + $this->assertEquals($this->test['url'], $this->object->get_url()); + unset($this->test["url"]); + $this->object = new Bridge_Api_Dailymotion_Element($this->test, 'blabla'); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object->get_url()); + $this->assertEmpty($this->object->get_url()); + } + + public function testIs_private() + { + $this->object = new Bridge_Api_Dailymotion_Element($this->test, 'blabla'); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_BOOL, $this->object->is_private()); + $this->assertTrue($this->object->is_private()); + unset($this->test["private"]); + $this->object = new Bridge_Api_Dailymotion_Element($this->test, 'blabla'); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_BOOL, $this->object->is_private()); + $this->assertFalse($this->object->is_private()); + } + + public function testGet_duration() + { + $this->object = new Bridge_Api_Dailymotion_Element($this->test, 'blabla'); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object->get_duration()); + $this->assertEquals("01:20", $this->object->get_duration()); + unset($this->test["duration"]); + $this->object = new Bridge_Api_Dailymotion_Element($this->test, 'blabla'); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object->get_duration()); + $this->assertEquals("00:00", $this->object->get_duration()); + } + + public function testGet_view_count() + { + $this->object = new Bridge_Api_Dailymotion_Element($this->test, 'blabla'); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_INT, $this->object->get_view_count()); + $this->assertEquals($this->test['views_total'], $this->object->get_view_count()); + unset($this->test["views_total"]); + $this->object = new Bridge_Api_Dailymotion_Element($this->test, 'blabla'); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_INT, $this->object->get_view_count()); + $this->assertEquals(0 , $this->object->get_view_count()); + } + + public function testGet_rating() + { + $this->object = new Bridge_Api_Dailymotion_Element($this->test, 'blabla'); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_INT, $this->object->get_rating()); + $this->assertEquals($this->test['ratings_total'], $this->object->get_rating()); + unset($this->test["ratings_total"]); + $this->object = new Bridge_Api_Dailymotion_Element($this->test, 'blabla'); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_INT, $this->object->get_rating()); + $this->assertEquals(0 , $this->object->get_rating()); + } + + public function testGet_category() + { + $this->object = new Bridge_Api_Dailymotion_Element($this->test, 'blabla'); + $this->assertEquals($this->test['channel'], $this->object->get_category()); + unset($this->test["channel"]); + $this->object = new Bridge_Api_Dailymotion_Element($this->test, 'blabla'); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object->get_category()); + $this->assertEmpty($this->object->get_category()); + } + +} + +?> diff --git a/tests/Bridge/Api/Flickr/Bridge_Api_Flickr_ContainerTest.php b/tests/Bridge/Api/Flickr/Bridge_Api_Flickr_ContainerTest.php new file mode 100644 index 0000000000..4c4103e036 --- /dev/null +++ b/tests/Bridge/Api/Flickr/Bridge_Api_Flickr_ContainerTest.php @@ -0,0 +1,86 @@ + + Avis Blanche + My Grandma\'s Recipe File. + + '; + $xml = simplexml_load_string($string); + $this->object = new Bridge_Api_Flickr_Container($xml, 'userid123', "photoset", "my_humbnail"); + } + + public function testGet_id() + { + $this->assertEquals("72157626216528324", $this->object->get_id()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object->get_id()); + } + + public function testGet_thumbnail() + { + $this->assertEquals("my_humbnail", $this->object->get_thumbnail()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object->get_thumbnail()); + } + + public function testGet_url() + { + $this->assertRegExp("/https:\/\/secure.flickr.com\/photos/", $this->object->get_url()); + $this->assertRegExp("/userid123/", $this->object->get_url()); + $this->assertRegExp("/72157626216528324/", $this->object->get_url()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object->get_thumbnail()); + } + + public function testGet_title() + { + $this->assertEquals("My Grandma's Recipe File.", $this->object->get_description()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object->get_description()); + } + + public function testGet_description() + { + $this->assertEquals("Avis Blanche", $this->object->get_title()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object->get_title()); + } + + public function testGet_updated_on() + { + $this->assertInstanceOf('DateTime', $this->object->get_updated_on()); + $this->assertEquals(DateTime::createFromFormat('U', '1300335009'), $this->object->get_updated_on()); + } + + public function testGet_created_on() + { + $this->assertInstanceOf('DateTime', $this->object->get_created_on()); + $this->assertEquals(DateTime::createFromFormat('U', '1299514498'), $this->object->get_created_on()); + } + + public function testGet_type() + { + $this->assertEquals("photoset", $this->object->get_type()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object->get_type()); + } + +} + +?> diff --git a/tests/Bridge/Api/Flickr/Bridge_Api_Flickr_ElementTest.php b/tests/Bridge/Api/Flickr/Bridge_Api_Flickr_ElementTest.php new file mode 100644 index 0000000000..04cf6613df --- /dev/null +++ b/tests/Bridge/Api/Flickr/Bridge_Api_Flickr_ElementTest.php @@ -0,0 +1,172 @@ + + une description + + '; + $string = ' + + + + Australia.gif + drapeau de l\'australie + + + + + + + 0 + + + + yo + + + http://www.flickr.com/photos/bees/2733/ + + + + '; + $this->xml_alone = simplexml_load_string($string); + $this->object_alone = new Bridge_Api_Flickr_Element($this->xml_alone, '12037949754@N01', 'album', false); + $this->xml_list = simplexml_load_string($str); + $this->object_list = new Bridge_Api_Flickr_Element($this->xml_list, '12037949754@N01', 'album', true); + } + + public function testGet_id() + { + $this->assertEquals("5930279108", $this->object_alone->get_id()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object_alone->get_id()); + $this->assertEquals("6263188755", $this->object_list->get_id()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object_list->get_id()); + } + + public function testGet_url() + { + $this->assertRegExp("/6263188755/", $this->object_list->get_url()); + $this->assertRegExp("/album/", $this->object_list->get_url()); + $this->assertRegExp("/60578095@N05/", $this->object_list->get_url()); + $this->assertRegExp("/http:\/\/www.flickr.com\//", $this->object_list->get_url()); + $this->assertEquals("http://www.flickr.com/photos/bees/2733/", $this->object_alone->get_url()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object_alone->get_url()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object_list->get_url()); + } + + public function testGet_thumbnail() + { + $this->assertEquals("http://farm7.static.flickr.com/6034/6263188755_2dca715798_t.jpg", $this->object_list->get_thumbnail()); + $this->assertRegExp("/https:\/\/farm7.static.flickr.com/", $this->object_alone->get_thumbnail()); + $this->assertRegExp("/6135/", $this->object_alone->get_thumbnail()); + $this->assertRegExp("/c06196fbd8/", $this->object_alone->get_thumbnail()); + $this->assertRegExp("/5930279108/", $this->object_alone->get_thumbnail()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object_alone->get_thumbnail()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object_list->get_thumbnail()); + } + + public function testGet_title() + { + $this->assertEquals("un titre", $this->object_list->get_title()); + $this->assertEquals("Australia.gif", $this->object_alone->get_title()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object_alone->get_title()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object_list->get_title()); + } + + public function testGet_description() + { + $this->assertEquals("une description", $this->object_list->get_description()); + $this->assertEquals("drapeau de l'australie", $this->object_alone->get_description()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object_alone->get_description()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object_list->get_description()); + } + + public function testGet_updated_on() + { + $this->assertInstanceOf('DateTime', $this->object_list->get_updated_on()); + $this->assertInstanceOf('DateTime', $this->object_alone->get_updated_on()); + $this->assertEquals(DateTime::createFromFormat('U', '1319126343'), $this->object_list->get_updated_on()); + $this->assertEquals(DateTime::createFromFormat('U', '1314975347'), $this->object_alone->get_updated_on()); + } + + public function testGet_category() + { + $this->assertEmpty($this->object_list->get_category()); + $this->assertEmpty($this->object_alone->get_category()); + } + + public function testGet_duration() + { + $this->assertEmpty($this->object_list->get_duration()); + $this->assertEmpty($this->object_alone->get_duration()); + } + + public function testGet_view_count() + { + $this->assertEquals(1, $this->object_list->get_view_count()); + $this->assertEquals(1, $this->object_alone->get_view_count()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_INT, $this->object_alone->get_view_count()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_INT, $this->object_list->get_view_count()); + } + + public function testGet_rating() + { + $this->assertNull($this->object_list->get_rating()); + $this->assertNull($this->object_alone->get_rating()); + } + + public function testGet_created_on() + { + $this->assertInstanceOf('DateTime', $this->object_list->get_created_on()); + $this->assertInstanceOf('DateTime', $this->object_alone->get_created_on()); + $this->assertEquals(DateTime::createFromFormat('U', '1319117962'), $this->object_list->get_created_on()); + $this->assertEquals(DateTime::createFromFormat('U', '1310477820'), $this->object_alone->get_created_on()); + } + + public function testIs_private() + { + $this->assertFalse($this->object_list->is_private()); + $this->assertTrue($this->object_alone->is_private()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_BOOL, $this->object_alone->is_private()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_BOOL, $this->object_list->is_private()); + } + + public function testGet_type() + { + $this->assertEquals('album', $this->object_list->get_type()); + $this->assertEquals('album', $this->object_alone->get_type()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object_alone->get_type()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object_list->get_type()); + } + +} + +?> diff --git a/tests/Bridge/Api/Youtube/Bridge_Api_Youtube_ContainerTest.php b/tests/Bridge/Api/Youtube/Bridge_Api_Youtube_ContainerTest.php new file mode 100644 index 0000000000..e1ceca4b0f --- /dev/null +++ b/tests/Bridge/Api/Youtube/Bridge_Api_Youtube_ContainerTest.php @@ -0,0 +1,91 @@ +setMajorProtocolVersion(2); + $entry->setId($id); + $entry->setTitle(new Zend_Gdata_App_Extension_Title("one title")); + $entry->setUpdated($updated); + $entry->setPublished($published); + $entry->setLink(array(new Zend_Gdata_App_Extension_link("one url", "alternate"))); + $entry->setDescription(new Zend_Gdata_App_Extension_Summary("one description")); + $this->object = new Bridge_Api_Youtube_Container($entry, 'playlist', 'my_thumbnail'); + } + + + + /** + * @todo fin a way to test getPlaylistId + */ + + + + + public function testGet_thumbnail() + { + $this->assertEquals("my_thumbnail", $this->object->get_thumbnail()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object->get_thumbnail()); + } + + public function testGet_url() + { + $this->assertEquals("one url", $this->object->get_url()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object->get_url()); + } + + public function testGet_title() + { + $this->assertEquals("one title", $this->object->get_title()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object->get_title()); + } + + public function testGet_description() + { + $this->assertEquals("one description", $this->object->get_description()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object->get_description()); + } + + public function testGet_updated_on() + { + $this->assertInstanceOf('DateTime', $this->object->get_updated_on()); + $this->assertEquals(new DateTime("2011-10-21 12:20:00"), $this->object->get_updated_on()); + } + + public function testGet_created_on() + { + $this->assertInstanceOf('DateTime', $this->object->get_created_on()); + $this->assertEquals(new DateTime("2011-10-21 12:00:00"), $this->object->get_created_on()); + } + + public function testGet_type() + { + $this->assertEquals("playlist", $this->object->get_type()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object->get_type()); + } + +} + +?> diff --git a/tests/Bridge/Api/Youtube/Bridge_Api_Youtube_ElementTest.php b/tests/Bridge/Api/Youtube/Bridge_Api_Youtube_ElementTest.php new file mode 100644 index 0000000000..49dcb38175 --- /dev/null +++ b/tests/Bridge/Api/Youtube/Bridge_Api_Youtube_ElementTest.php @@ -0,0 +1,134 @@ +setUrl("coucou"); + $stat = new Zend_Gdata_YouTube_Extension_Statistics(); + $stat->setViewCount("5"); + $thumb = new Zend_Gdata_Media_Extension_MediaThumbnail('une url', '120', '90'); + $media = new Zend_Gdata_YouTube_Extension_MediaGroup(); + $media->setPlayer(array($player)); + $media->setDuration($duration); + $media->setVideoId($id); + $media->setThumbnail(array($thumb)); + $entry = new Zend_Gdata_YouTube_VideoEntry(); + $entry->setMajorProtocolVersion(2); + $entry->setMediaGroup($media); + $entry->setStatistics($stat); + $entry->setRating($rating); + $entry->setVideoCategory("category"); + $entry->setVideoDescription("one description"); + $entry->setVideoPrivate(); + $entry->setVideoTags(array('tags')); + $entry->setVideoTitle("hellow"); + $entry->setUpdated($updated); + $entry->setPublished($published); + $this->object = new Bridge_Api_Youtube_Element($entry, 'video'); + } + + public function testGet_id() + { + $this->assertEquals("Az2cv12", $this->object->get_id()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object->get_id()); + } + + public function testGet_thumbnail() + { + $this->assertEquals("une url", $this->object->get_thumbnail()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object->get_thumbnail()); + } + + public function testGet_url() + { + $this->assertEquals("coucou", $this->object->get_url()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object->get_url()); + } + + public function testGet_title() + { + $this->assertEquals("hellow", $this->object->get_title()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object->get_title()); + } + + public function testGet_description() + { + $this->assertEquals("one description", $this->object->get_description()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object->get_description()); + } + + public function testGet_updated_on() + { + $this->assertInstanceOf('DateTime', $this->object->get_updated_on()); + $this->assertEquals(new DateTime("2011-10-21 12:20:00"), $this->object->get_updated_on()); + } + + public function testGet_category() + { + $this->assertEquals("category", $this->object->get_category()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object->get_category()); + } + + public function testGet_duration() + { + $this->assertEquals(p4string::format_seconds(80), $this->object->get_duration()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object->get_duration()); + } + + public function testGet_view_count() + { + $this->assertEquals(5, $this->object->get_view_count()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_INT, $this->object->get_view_count()); + } + + public function testGet_rating() + { + $this->assertEquals(200, $this->object->get_rating()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_INT, $this->object->get_rating()); + } + + public function testGet_created_on() + { + $this->assertInstanceOf('DateTime', $this->object->get_created_on()); + $this->assertEquals(new DateTime("2011-10-21 12:00:00"), $this->object->get_created_on()); + } + + public function testIs_private() + { + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_BOOL, $this->object->is_private()); + $this->assertTrue($this->object->is_private()); + } + + public function testGet_type() + { + $this->assertEquals("video", $this->object->get_type()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->object->get_type()); + } + +} + +?> diff --git a/tests/Bridge/Bridge_AccountSettingsTest.php b/tests/Bridge/Bridge_AccountSettingsTest.php new file mode 100644 index 0000000000..8ade4b8e2a --- /dev/null +++ b/tests/Bridge/Bridge_AccountSettingsTest.php @@ -0,0 +1,78 @@ +get_connection()->prepare($sql); + $stmt->execute(); + $stmt->closeCursor(); + $this->api = Bridge_Api::create($appbox, 'Apitest'); + $this->dist_id = 'EZ1565loPP'; + $this->named = 'Fête à pinpins'; + $this->account = Bridge_Account::create($appbox, $this->api, self::$user, $this->dist_id, $this->named); + + $this->object = new Bridge_AccountSettings($appbox, $this->account); + } + catch (Exception $e) + { + $this->fail($e->getMessage()); + } + } + + public function tearDown() + { + $this->api->delete(); + } + + public function testGet() + { + $this->assertNull($this->object->get('test')); + $this->assertEquals('caca', $this->object->get('test', 'caca')); + $obj = new DateTime(); + $this->assertEquals($obj, $this->object->get('test', $obj)); + } + + public function testSet() + { + $this->object->set('tip', 'top'); + $this->assertEquals('top', $this->object->get('tip')); + $this->object->set('tip', 'tap'); + $this->assertEquals('tap', $this->object->get('tip')); + $this->object->set('tip', null); + $this->assertEquals(null, $this->object->get('tip')); + } + + public function testDelete() + { + $this->object->set('tip', 'top'); + $this->assertEquals('top', $this->object->get('tip')); + $this->object->delete('tip'); + $this->assertEquals(null, $this->object->get('tip')); + $this->assertEquals('flop', $this->object->get('tip', 'flop')); + } + +} + diff --git a/tests/Bridge/Bridge_AccountTest.php b/tests/Bridge/Bridge_AccountTest.php new file mode 100644 index 0000000000..376e9963da --- /dev/null +++ b/tests/Bridge/Bridge_AccountTest.php @@ -0,0 +1,167 @@ +get_connection()->prepare($sql); + $stmt->execute(); + $stmt->closeCursor(); + + $this->api = Bridge_Api::create($appbox, 'Apitest'); + $this->dist_id = 'EZ1565loPP'; + $this->named = 'Fête à pinpins'; + $account = Bridge_Account::create($appbox, $this->api, self::$user, $this->dist_id, $this->named); + $this->id = $account->get_id(); + + $this->object = new Bridge_Account($appbox, $this->api, $this->id); + } + catch (Exception $e) + { + $this->fail($e->getMessage()); + } + } + + public function tearDown() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $this->object->delete(); + + try + { + new Bridge_Account($appbox, $this->api, $this->id); + $this->fail(); + } + catch (Bridge_Exception_AccountNotFound $e) + { + + } + + $this->api->delete(); + } + + public function testGet_id() + { + $this->assertTrue(is_int($this->object->get_id())); + $this->assertEquals($this->id, $this->object->get_id()); + } + + public function testGet_api() + { + $this->assertInstanceOf('Bridge_Api', $this->object->get_api()); + $this->assertEquals($this->api, $this->object->get_api()); + $this->assertEquals($this->api->get_id(), $this->object->get_api()->get_id()); + } + + public function testGet_dist_id() + { + $this->assertEquals($this->dist_id, $this->object->get_dist_id()); + } + + public function testGet_user() + { + $this->assertInstanceOf('User_Adapter', $this->object->get_user()); + $this->assertEquals(self::$user->get_id(), $this->object->get_user()->get_id()); + } + + public function testGet_name() + { + $this->assertEquals($this->named, $this->object->get_name()); + } + + public function testGet_created_on() + { + $this->assertInstanceOf('DateTime', $this->object->get_created_on()); + $this->assertTrue($this->object->get_created_on() <= new DateTime()); + } + + public function testGet_updated_on() + { + $this->assertInstanceOf('DateTime', $this->object->get_updated_on()); + $this->assertTrue($this->object->get_updated_on() <= new DateTime()); + $this->assertTrue($this->object->get_updated_on() >= $this->object->get_created_on()); + + $update1 = $this->object->get_updated_on(); + sleep(2); + $this->object->set_name('prout'); + + $update2 = $this->object->get_updated_on(); + $this->assertTrue($update2 > $update1); + } + + public function testSet_name() + { + $new_name = 'YODELALI &é"\'(-è_çà)'; + $this->object->set_name($new_name); + $this->assertEquals($new_name, $this->object->get_name()); + $new_name = 'BACHI BOUZOUKS'; + $this->object->set_name($new_name); + $this->assertEquals($new_name, $this->object->get_name()); + } + + public function testGet_accounts_by_api() + { + $accounts = Bridge_Account::get_accounts_by_api(appbox::get_instance(\bootstrap::getCore()), $this->api); + $this->assertTrue(is_array($accounts)); + + $this->assertGreaterThan(0, count($accounts)); + + foreach ($accounts as $account) + { + $this->assertInstanceOf('Bridge_Account', $account); + } + } + + public function testGet_settings() + { + $this->assertInstanceOf('Bridge_AccountSettings', $this->object->get_settings()); + } + + public function testGet_accounts_by_user() + { + $accounts = Bridge_Account::get_accounts_by_user(appbox::get_instance(\bootstrap::getCore()), self::$user); + + $this->assertTrue(is_array($accounts)); + $this->assertTrue(count($accounts) > 0); + + foreach ($accounts as $account) + { + $this->assertInstanceOf('Bridge_Account', $account); + } + } + + public function testLoad_account() + { + $account = Bridge_Account::load_account(appbox::get_instance(\bootstrap::getCore()), $this->object->get_id()); + $this->assertEquals($this->object->get_id(), $account->get_id()); + } + + public function testLoad_account_from_distant_id() + { + $this->markTestIncomplete(); + } + +} + diff --git a/tests/Bridge/Bridge_ApiTest.php b/tests/Bridge/Bridge_ApiTest.php new file mode 100644 index 0000000000..01e3f6d493 --- /dev/null +++ b/tests/Bridge/Bridge_ApiTest.php @@ -0,0 +1,198 @@ +get_connection()->prepare($sql); + $stmt->execute(); + $stmt->closeCursor(); + + $this->type = 'Apitest'; + $api = Bridge_Api::create($appbox, $this->type); + + $this->id = $api->get_id(); + $this->object = new Bridge_Api($appbox, $api->get_id()); + } + + public function tearDown() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $this->object->delete(); + + try + { + new Bridge_Api($appbox, $this->id); + $this->fail(); + } + catch (Bridge_Exception_ApiNotFound $e) + { + + } + } + + public function testGet_id() + { + $this->assertTrue(is_int($this->object->get_id())); + $this->assertTrue($this->object->get_id() > 0); + $this->assertEquals($this->id, $this->object->get_id()); + } + + public function testis_disabled() + { + $this->assertTrue(is_bool($this->object->is_disabled())); + $this->assertFalse($this->object->is_disabled()); + } + + public function testenable() + { + $this->assertTrue(is_bool($this->object->is_disabled())); + $this->assertFalse($this->object->is_disabled()); + sleep(1); + $update1 = $this->object->get_updated_on(); + + $this->object->disable(new DateTime('+2 seconds')); + $this->assertTrue($this->object->is_disabled()); + sleep(3); + $update2 = $this->object->get_updated_on(); + $this->assertTrue($update2 > $update1); + $this->assertFalse($this->object->is_disabled()); + $this->object->enable(); + $this->assertFalse($this->object->is_disabled()); + } + + public function testdisable() + { + $this->testenable(); + } + + public function testGet_created_on() + { + $this->assertInstanceOf('DateTime', $this->object->get_created_on()); + $this->assertTrue($this->object->get_created_on() <= new DateTime()); + } + + public function testGet_updated_on() + { + $this->assertInstanceOf('DateTime', $this->object->get_updated_on()); + $this->assertTrue($this->object->get_updated_on() <= new DateTime()); + $this->assertTrue($this->object->get_updated_on() >= $this->object->get_created_on()); + } + + public function testGet_connector() + { + $this->markTestIncomplete(); + } + + public function testlist_elements() + { + $this->markTestIncomplete(); + } + + public function testlist_containers() + { + $this->markTestIncomplete(); + } + + public function testupdate_element() + { + $this->markTestIncomplete(); + } + + public function testcreate_container() + { + $this->markTestIncomplete(); + } + + public function testadd_element_to_container() + { + $this->markTestIncomplete(); + } + + public function testdelete_object() + { + $this->markTestIncomplete(); + } + + public function testacceptable_records() + { + $this->markTestIncomplete(); + } + + public function testget_element_from_id() + { + $this->markTestIncomplete(); + } + + public function testget_container_from_id() + { + $this->markTestIncomplete(); + } + + public function testget_category_list() + { + $this->markTestIncomplete(); + } + + public function testget_element_status() + { + $this->markTestIncomplete(); + } + + public function testmap_connector_to_element_status() + { + $this->markTestIncomplete(); + } + + public function testupload() + { + $this->markTestIncomplete(); + } + + public function testgenerate_callback_url() + { + $this->markTestIncomplete(); + } + + public function testgenerate_login_url() + { + $this->markTestIncomplete(); + } + + public function testget_connector_by_name() + { + $this->markTestIncomplete(); + } + + public function testget_by_api_name() + { + $this->markTestIncomplete(); + } + + public function testget_availables() + { + $this->markTestIncomplete(); + } + +} + diff --git a/tests/Bridge/Bridge_ElementTest.php b/tests/Bridge/Bridge_ElementTest.php new file mode 100644 index 0000000000..e727ede56a --- /dev/null +++ b/tests/Bridge/Bridge_ElementTest.php @@ -0,0 +1,189 @@ +get_connection()->prepare($sql); + $stmt->execute(); + $stmt->closeCursor(); + + $this->api = Bridge_Api::create($appbox, 'Apitest'); + $this->dist_id = 'EZ1565loPP'; + $this->named = 'Fête à pinpins'; + $this->account = Bridge_Account::create($appbox, $this->api, self::$user, $this->dist_id, $this->named); + + $this->title = 'GOGACKO'; + $this->status = 'Processing'; + + $element = Bridge_Element::create($appbox, $this->account, self::$record_1, $this->title, $this->status, $this->account->get_api()->get_connector()->get_default_element_type()); + $this->id = $element->get_id(); + $this->object = new Bridge_Element($appbox, $this->account, $this->id); + } + + public function tearDown() + { + + $appbox = appbox::get_instance(\bootstrap::getCore()); + $this->object->delete(); + + try + { + new Bridge_Element($appbox, $this->account, $this->id); + $this->fail(); + } + catch (Bridge_Exception_ElementNotFound $e) + { + + } + + $this->api->delete(); + } + + public function testGet_account() + { + $this->assertInstanceOf('Bridge_Account', $this->object->get_account()); + $this->assertEquals($this->account, $this->object->get_account()); + $this->assertEquals($this->account->get_id(), $this->object->get_account()->get_id()); + } + + public function testGet_id() + { + + } + + public function testGet_record() + { + $this->assertInstanceOf('record_adapter', $this->object->get_record()); + $this->assertEquals(self::$record_1->get_sbas_id(), $this->object->get_record()->get_sbas_id()); + $this->assertEquals(self::$record_1->get_record_id(), $this->object->get_record()->get_record_id()); + } + + public function testGet_dist_id() + { + $this->assertNull($this->object->get_dist_id()); + } + + public function testGet_status() + { + $this->assertEquals($this->status, $this->object->get_status()); + } + + public function testSet_status() + { + $update1 = $this->object->get_updated_on(); + $new_status = '&é"\'(-è_çà)'; + $this->object->set_status($new_status); + $this->assertEquals($new_status, $this->object->get_status()); + sleep(1); + $new_status = '&é"0687345àç_)à)'; + $this->object->set_status($new_status); + $this->assertEquals($new_status, $this->object->get_status()); + $update2 = $this->object->get_updated_on(); + $this->assertTrue($update2 > $update1); + } + + public function testGet_title() + { + $this->assertEquals($this->title, $this->object->get_title()); + } + + public function testGet_type() + { + $this->markTestIncomplete(); + } + + public function testSet_title() + { + $update1 = $this->object->get_updated_on(); + sleep(1); + $new_title = 'Cigares du pharaon'; + $this->object->set_title($new_title); + $this->assertEquals($new_title, $this->object->get_title()); + $update2 = $this->object->get_updated_on(); + $this->assertTrue($update2 > $update1); + } + + public function testSet_distid() + { + $update1 = $this->object->get_updated_on(); + sleep(1); + $this->object->set_dist_id($this->dist_id); + $this->assertEquals($this->dist_id, $this->object->get_dist_id()); + $update2 = $this->object->get_updated_on(); + $this->assertTrue($update2 > $update1); + } + + public function testGet_created_on() + { + $this->assertInstanceOf('DateTime', $this->object->get_created_on()); + } + + public function testGet_updated_on() + { + $this->assertInstanceOf('DateTime', $this->object->get_updated_on()); + } + + public function testGet_elements_by_account() + { + $elements = Bridge_Element::get_elements_by_account(appbox::get_instance(\bootstrap::getCore()), $this->account); + $this->assertTrue(is_array($elements)); + $this->assertGreaterThan(0, count($elements)); + + foreach ($elements as $element) + { + $this->assertInstanceOf('Bridge_Element', $element); + } + } + + public function testGet_connector_status() + { + $this->markTestIncomplete(); + } + + public function testSet_connector_status() + { + $this->markTestIncomplete(); + } + + public function testGet_datas() + { + $this->markTestIncomplete(); + } + + public function testSet_datas() + { + $this->markTestIncomplete(); + } + + public function test() + { + $this->markTestIncomplete(); + } + +} + diff --git a/tests/Bridge/Bridge_datas.inc b/tests/Bridge/Bridge_datas.inc new file mode 100644 index 0000000000..32e86b4bd5 --- /dev/null +++ b/tests/Bridge/Bridge_datas.inc @@ -0,0 +1,547 @@ +id; + } + + public function get_thumbnail($width = 120, $height = 90) + { + + } + + public function get_title() + { + + } + + public function get_type() + { + return $this->type; + } + + public function get_updated_on() + { + + } + + public function get_url() + { + + } + + public function get_category() + { + + } + + public function is_private() + { + + } + + public function get_rating() + { + + } + +} + +class Bridge_Api_Apitest_Element implements Bridge_Api_ElementInterface +{ + + public $id; + public $type; + + public function __construct() + { + + } + + public function get_category() + { + + } + + public function get_created_on() + { + + } + + public function get_description() + { + + } + + public function get_duration() + { + + } + + public function get_id() + { + return $this->id; + } + + public function get_rating() + { + + } + + public function get_thumbnail() + { + + } + + public function get_title() + { + + } + + public function get_type() + { + return $this->type; + } + + public function get_updated_on() + { + + } + + public function get_url() + { + + } + + public function get_view_count() + { + + } + + public function is_private() + { + + } + +} + +class Bridge_Api_Apitest extends Bridge_Api_Abstract implements Bridge_Api_Interface +{ + + const AUTH_TYPE = 'None'; + + public static $hasError = false; + public static $hasException = false; + + public function __construct(registryInterface $registry, Bridge_Api_Auth_Interface $auth) + { + parent::__construct($registry, $auth); + } + + protected function initialize_transport() + { + + } + + protected function set_auth_params() + { + + } + + protected function set_transport_authentication_params() + { + + } + + public function get_terms_url() + { + return 'http://example.com/terms.html'; + } + + /** + * + * @return Array + */ + public function connect() + { + return array('auth_token' => 'kikoo', 'refresh_token' => 'kooki'); + } + + /** + * + * @return Bridge_Api_Interface + */ + public function reconnect() + { + return parent::reconnect(); + } + + /** + * + * @return Bridge_Api_Interface + */ + public function disconnect() + { + return parent::disconnect(); + } + + /** + * + * @return boolean + */ + public function is_connected() + { + return parent::is_connected(); + } + + /** + * + * @return Bridge_Api_Interface + */ + public function set_locale($locale) + { + + } + + /** + * + * @return string + */ + public function get_name() + { + return 'Youtube'; + } + + /** + * + * @return string + */ + public function get_icon_url() + { + + } + + /** + * + * @return string + */ + public function get_image_url() + { + + } + + /** + * + * @return string + */ + public function get_url() + { + + } + + /** + * + * @return string + */ + public function get_infos() + { + + } + + public function get_object_class_from_type($type) + { + switch ($type) + { + case $this->get_default_element_type() : + return Bridge_Api_Interface::OBJECT_CLASS_ELEMENT; + break; + case $this->get_default_container_type() : + return Bridge_Api_Interface::OBJECT_CLASS_CONTAINER; + break; + } + } + + public function get_default_element_type() + { + return 'video'; + } + + public function get_default_container_type() + { + return 'playlist'; + } + + public function get_element_types() + { + + } + + public function get_container_types() + { + + } + + public function get_element_from_id($element_id, $object) + { + $element = new Bridge_Api_Apitest_Element(); + $element->id = $element_id; + $element->type = $object; + + return $element; + } + + public function get_container_from_id($element_id, $object) + { + $container = new Bridge_Api_Apitest_Containers(); + $container->id = $element_id; + $container->type = $object; + + return $container; + } + + public function get_category_list() + { + + } + + public function get_user_name() + { + return "coucou"; + } + + public function get_user_id() + { + return 'kirikoo'; + } + + public function list_elements($type, $offset_start = 0, $quantity = 10) + { + $element_collection = new Bridge_Api_ElementCollection(); + $i = 0; + while ($i < 5) + { + $element_collection->add_element(new Bridge_Api_Apitest_Element()); + $i++; + } + + return $element_collection; + } + + public function list_containers($type, $offset_start = 0, $quantity = 10) + { + $container_collection = new Bridge_Api_ContainerCollection(); + $i = 0; + while ($i < 5) + { + $container_collection->add_element(new Bridge_Api_Apitest_Containers()); + $i++; + } + + return $container_collection; + } + + public function update_element($object, $object_id, Array $datas) + { + + } + + public function create_container($container_type, \Symfony\Component\HttpFoundation\Request $request) + { + if (self::$hasException) + { + self::$hasException = false; + throw new \Exception("une erreur"); + } + } + + public function add_element_to_container($element_type, $element_id, $destination, $container_id) + { + if (self::$hasException) + { + self::$hasException = false; + throw new \Exception("une erreur"); + } + } + + public function delete_object($object, $object_id) + { + if (self::$hasException) + { + self::$hasException = false; + throw new \Exception("une erreur"); + } + } + + /** + * + * @return Closure + */ + public function acceptable_records() + { + $func = function($record) + { + return true; + }; + + return $func; + } + + public function get_element_status(Bridge_Element $element) + { + + } + + public function map_connector_to_element_status($status) + { + + } + + public function get_error_message_from_status($connector_status) + { + + } + + public function upload(record_adapter &$record, array $options = array()) + { + + } + + public function is_valid_object_id($object_id) + { + + } + + public function is_configured() + { + return true; + } + + public function check_upload_constraints(array $datas, record_adapter $record) + { + if (self::$hasError) + { + self::$hasError = false; + + return array('title' => 'too long'); + } + + return array(); + } + + public function get_upload_datas(\Symfony\Component\HttpFoundation\Request $request, record_adapter $record) + { + return array(); + } + + public function is_multiple_upload() + { + + } + + public function check_update_constraints(Array $datas) + { + if (!self::$hasError) + { + if (self::$hasException) + { + self::$hasException = false; + throw new \Exception('une erreur'); + } + + return array(); + } + elseif (self::$hasError) + { + self::$hasError = false; + + return array('title' => 'too long'); + } + } + + public function get_update_datas(\Symfony\Component\HttpFoundation\Request $request) + { + return array(); + } + +} + +class Bridge_Api_Auth_None extends Bridge_Api_Auth_Abstract implements Bridge_Api_Auth_Interface +{ + + public function connect($param) + { + + } + + public function parse_request_token() + { + + } + + public function reconnect() + { + $this->settings->set('access_token', 'biloute'); + } + + public function disconnect() + { + $this->settings->set('auth_token', null); + } + + public function is_connected() + { + return $this->settings->get('auth_token', null) !== null; + } + + public function get_auth_url(Array $supp_parameters = array()) + { + return 'kameamea'; + } + + public function get_auth_signatures() + { + + } + + public function set_parameters(Array $parameters) + { + + } + +} diff --git a/tests/Doctrine/Entities/BasketTest.php b/tests/Doctrine/Entities/BasketTest.php new file mode 100644 index 0000000000..02945673fe --- /dev/null +++ b/tests/Doctrine/Entities/BasketTest.php @@ -0,0 +1,195 @@ +em = self::$core->getEntityManager(); + $this->basket = $this->insertOneBasket(); + } + + public function tearDown() + { + parent::tearDown(); + } + + public function testGetId() + { + $this->assertTrue(is_int($this->basket->getId())); + $otherBasket = $this->insertOneBasket(); + $this->assertGreaterThan($this->basket->getId(), $otherBasket->getId()); + } + + public function testGetName() + { + $this->basket->setName('one name'); + $this->em->persist($this->basket); + $this->em->flush(); + $this->assertEquals('one name', $this->basket->getName()); + } + + public function testGetDescription() + { + $this->basket->setDescription('une jolie description pour mon super panier'); + $this->em->persist($this->basket); + $this->em->flush(); + $this->assertEquals('une jolie description pour mon super panier', $this->basket->getDescription()); + } + + public function testGetUsrId() + { + $this->basket->setUsrId(1); + $this->em->persist($this->basket); + $this->em->flush(); + $this->assertEquals(1, $this->basket->getUsrId()); + } + + public function testGetPusherId() + { + $this->basket->setPusherId(1); + $this->em->persist($this->basket); + $this->em->flush(); + $this->assertEquals(1, $this->basket->getPusherId()); + } + + public function testGetArchived() + { + $this->basket->setArchived(true); + $this->em->persist($this->basket); + $this->em->flush(); + $this->assertTrue($this->basket->GetArchived()); + $this->basket->setArchived(false); + $this->em->persist($this->basket); + $this->em->flush(); + $this->assertFalse($this->basket->GetArchived()); + } + + public function testGetCreated() + { + $date = $this->basket->getCreated(); + $this->assertInstanceOf('\DateTime', $date); + } + + public function testGetUpdated() + { + $date = $this->basket->getUpdated(); + $this->assertInstanceOf('\DateTime', $date); + + } + + public function testGetElements() + { + $elements = $this->basket->getElements(); + + $this->assertInstanceOf('\Doctrine\ORM\PersistentCollection', $elements); + + $this->assertEquals(0, $elements->count()); + + $basketElement = new \Entities\BasketElement(); + + $basketElement->setRecord(self::$record_1); + + $basketElement->setBasket($this->basket); + + $this->em->persist($basketElement); + + $this->em->flush(); + + $this->em->refresh($this->basket); + + $this->assertEquals(1, $this->basket->getElements()->count()); + } + + public function testGetPusher() + { + $this->assertNull($this->basket->getPusher()); //no pusher + $this->basket->setPusherId(self::$user->get_id()); + $this->assertInstanceOf('\User_Adapter', $this->basket->getPusher()); + $this->assertEquals($this->basket->getPusher()->get_id(), self::$user->get_id()); + } + + public function testGetOwner() + { + $this->assertNotNull($this->basket->getOwner()); //no owner + $this->basket->setUsrId(self::$user->get_id()); + $this->assertInstanceOf('\User_Adapter', $this->basket->getOwner()); + $this->assertEquals($this->basket->getOwner()->get_id(), self::$user->get_id()); + } + + public function testGetValidation() + { + $this->assertNull($this->basket->getValidation()); + + $validationSession = new \Entities\ValidationSession(); + + $validationSession->setBasket($this->basket); + + $validationSession->setDescription('Une description au hasard'); + + $validationSession->setName('Un nom de validation'); + + $expires = new \DateTime(); + $expires->modify('+1 week'); + + $validationSession->setExpires($expires); + + $validationSession->setInitiator(self::$user); + + $this->em->persist($validationSession); + + $this->em->flush(); + + $this->em->refresh($this->basket); + + $this->assertInstanceOf('\Entities\ValidationSession', $this->basket->getValidation()); + } + + public function testGetIsRead() + { + $this->markTestIncomplete(); + } + + public function testGetSize() + { + $this->markTestIncomplete(); + } + + public function hasRecord() + { + $this->markTestIncomplete(); + } + +} diff --git a/tests/Feed/Entry/Feed_Entry_AdapterTest.php b/tests/Feed/Entry/Feed_Entry_AdapterTest.php new file mode 100644 index 0000000000..76d1b0cfcd --- /dev/null +++ b/tests/Feed/Entry/Feed_Entry_AdapterTest.php @@ -0,0 +1,160 @@ +get_session()->authenticate($auth); + + self::$feed = Feed_Adapter::create($appbox, self::$user, self::$feed_title, self::$feed_subtitle); + $publisher = Feed_Publisher_Adapter::getPublisher($appbox, self::$feed, self::$user); + self::$object = Feed_Entry_Adapter::create($appbox, self::$feed, $publisher, self::$title, self::$subtitle, self::$author_name, self::$author_email); + } + + public static function tearDownAfterClass() + { + self::$feed->delete(); + parent::tearDownAfterClass(); + } + + public function testGet_feed() + { + $this->assertInstanceOf('Feed_Adapter', self::$object->get_feed()); + $this->assertEquals(self::$feed->get_id(), self::$object->get_feed()->get_id()); + } + + public function testGet_id() + { + $this->assertTrue(is_int(self::$object->get_id())); + } + + public function testGet_title() + { + $this->assertEquals(self::$title, self::$object->get_title()); + } + + public function testGet_subtitle() + { + $this->assertEquals(self::$subtitle, self::$object->get_subtitle()); + } + + public function testSet_title() + { + $new_titre = 'UNnouveau titre'; + self::$object->set_title($new_titre); + $this->assertEquals($new_titre, self::$object->get_title()); + $new_titre = 'UNnouveau titre encore'; + self::$object->set_title($new_titre); + $this->assertNotEquals($new_titre, self::$object->get_title()); + $this->assertEquals(strip_tags($new_titre), self::$object->get_title()); + try + { + self::$object->set_title(''); + $this->fail(); + } + catch (Exception_InvalidArgument $e) + { + + } + } + + public function testSet_subtitle() + { + $new_subtitle = 'PROUT + ET PROUT'; + self::$object->set_subtitle($new_subtitle); + $this->assertEquals($new_subtitle, self::$object->get_subtitle()); + $new_subtitle = ''; + self::$object->set_subtitle($new_subtitle); + $this->assertEquals($new_subtitle, self::$object->get_subtitle()); + } + + public function testSet_author_name() + { + $new_author = 'Tintin et Milou'; + self::$object->set_author_name($new_author); + $this->assertEquals($new_author, self::$object->get_author_name()); + self::$object->set_author_name(self::$author_name); + $this->assertEquals(self::$author_name, self::$object->get_author_name()); + } + + public function testSet_author_email() + { + $new_email = 'Tintin@herge.be'; + self::$object->set_author_email($new_email); + $this->assertEquals($new_email, self::$object->get_author_email()); + self::$object->set_author_email(self::$author_email); + $this->assertEquals(self::$author_email, self::$object->get_author_email()); + } + + public function testGet_publisher() + { + $this->assertInstanceOf('Feed_Publisher_Adapter', self::$object->get_publisher()); + $this->assertEquals(self::$object->get_publisher()->get_user()->get_id(), self::$user->get_id()); + } + + public function testGet_created_on() + { + $this->assertInstanceOf('DateTime', self::$object->get_created_on()); + } + + public function testGet_updated_on() + { + $this->assertInstanceOf('DateTime', self::$object->get_updated_on()); + } + + public function testGet_author_name() + { + $this->assertEquals(self::$author_name, self::$object->get_author_name()); + } + + public function testGet_author_email() + { + $this->assertEquals(self::$author_email, self::$object->get_author_email()); + } + + public function testGet_content() + { + $this->assertTrue(is_array(self::$object->get_content())); + $this->assertEquals(0, count(self::$object->get_content())); + } + + public function testLoad_from_id() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $test_entry = Feed_Entry_Adapter::load_from_id($appbox, self::$object->get_id()); + + $this->assertInstanceOf('Feed_Entry_Adapter', $test_entry); + $this->assertEquals(self::$object->get_id(), $test_entry->get_id()); + + } + +} + diff --git a/tests/Feed/Entry/Feed_Entry_CollectionTest.php b/tests/Feed/Entry/Feed_Entry_CollectionTest.php new file mode 100644 index 0000000000..6b59ca4551 --- /dev/null +++ b/tests/Feed/Entry/Feed_Entry_CollectionTest.php @@ -0,0 +1,73 @@ +get_session()->authenticate($auth); + + self::$feed = Feed_Adapter::create($appbox, self::$user, self::$feed_title, self::$feed_subtitle); + $publisher = Feed_Publisher_Adapter::getPublisher($appbox, self::$feed, self::$user); + self::$entry = Feed_Entry_Adapter::create($appbox, self::$feed, $publisher, self::$title, self::$subtitle, self::$author_name, self::$author_email); + + self::$item = Feed_Entry_Item::create($appbox, self::$entry, self::$record_1); + + self::$object = new Feed_Entry_Collection(); + } + + public static function tearDownAfterClass() + { + self::$feed->delete(); + parent::tearDownAfterClass(); + } + + public function testAdd_entry() + { + self::$object->add_entry(self::$entry); + } + + public function testGet_entries() + { + $this->assertTrue(is_array(self::$object->get_entries())); + $this->assertTrue(count(self::$object->get_entries()) == 1); + } + +} + diff --git a/tests/Feed/Entry/Feed_Entry_ItemTest.php b/tests/Feed/Entry/Feed_Entry_ItemTest.php new file mode 100644 index 0000000000..f1763c7d53 --- /dev/null +++ b/tests/Feed/Entry/Feed_Entry_ItemTest.php @@ -0,0 +1,80 @@ +get_session()->authenticate($auth); + + self::$feed = Feed_Adapter::create($appbox, self::$user, self::$feed_title, self::$feed_subtitle); + $publisher = Feed_Publisher_Adapter::getPublisher($appbox, self::$feed, self::$user); + self::$entry = Feed_Entry_Adapter::create($appbox, self::$feed, $publisher, self::$title, self::$subtitle, self::$author_name, self::$author_email); + + self::$object = Feed_Entry_Item::create($appbox, self::$entry, self::$record_1); + } + + public static function tearDownAfterClass() + { + self::$feed->delete(); + parent::tearDownAfterClass(); + } + + public function testGet_id() + { + $this->assertTrue(is_int(self::$object->get_id())); + } + + public function testGet_record() + { + $this->assertInstanceOf('record_adapter',self::$object->get_record()); + $this->assertEquals(self::$record_1->get_record_id(), self::$object->get_record()->get_record_id()); + $this->assertEquals(self::$record_1->get_sbas_id(), self::$object->get_record()->get_sbas_id()); + $this->assertEquals(self::$record_1->get_base_id(), self::$object->get_record()->get_base_id()); + } + + public function testGet_ord() + { + $this->assertTrue(is_int(self::$object->get_ord())); + } + + public function testGet_entry() + { + $this->assertInstanceOf('Feed_Entry_Adapter', self::$object->get_entry()); + $this->assertEquals(self::$entry->get_id(),self::$object->get_entry()->get_id()); + } + +} + diff --git a/tests/Feed/Feed_AdapterTest.php b/tests/Feed/Feed_AdapterTest.php new file mode 100644 index 0000000000..08c2107f94 --- /dev/null +++ b/tests/Feed/Feed_AdapterTest.php @@ -0,0 +1,294 @@ +get_session()->authenticate($auth); + self::$object = Feed_Adapter::create($appbox, self::$user, self::$title, self::$subtitle); + } + + public static function tearDownAfterClass() + { + self::$object->delete(); + parent::tearDownAfterClass(); + } + + public function testGet_icon_url() + { + $this->assertEquals('/skins/icons/rss32.gif', self::$object->get_icon_url()); + } + + public function testSet_icon() + { + $this->assertEquals('/skins/icons/rss32.gif', self::$object->get_icon_url()); + $file = new system_file(__DIR__ . '/../testfiles/p4logo.jpg'); + self::$object->set_icon($file); + try + { + $file = new system_file(__DIR__ . '/../testfiles/iphone_pic.jpg'); + self::$object->set_icon($file); + $this->fail('Should fail'); + } + catch (Exception $e) + { + + } + $this->assertEquals('/custom/feed_' . self::$object->get_id() . '.jpg', self::$object->get_icon_url()); + } + + public function testIs_aggregated() + { + $this->assertFalse(self::$object->is_aggregated()); + } + + public function testIs_owner() + { + $this->assertTrue(self::$object->is_owner(self::$user)); + } + + public function testIs_publisher() + { + $this->assertTrue(self::$object->is_publisher(self::$user)); + } + + public function testIs_public() + { + $this->assertFalse(self::$object->is_public()); + self::$object->set_public(true); + $this->assertTrue(self::$object->is_public()); + + $coll = null; + $appbox = appbox::get_instance(\bootstrap::getCore()); + foreach ($appbox->get_databoxes() as $databox) + { + foreach ($databox->get_collections() as $collection) + { + $coll = $collection; + break; + } + if ($coll instanceof collection) + break; + } + + self::$object->set_collection($coll); + $this->assertFalse(self::$object->is_public()); + self::$object->set_collection(null); + $this->assertTrue(self::$object->is_public()); + self::$object->set_public(false); + $this->assertFalse(self::$object->is_public()); + } + + public function testGet_publishers() + { + $publishers = self::$object->get_publishers(); + $this->assertEquals(1, count($publishers)); + } + + public function testGet_collection() + { + $this->assertNull(self::$object->get_collection()); + } + + public function testAdd_publisher() + { + self::$object->add_publisher(self::$user); + $publishers = self::$object->get_publishers(); + $this->assertEquals(1, count($publishers)); + } + + public function testGet_id() + { + $this->assertTrue(is_int(self::$object->get_id())); + $this->assertTrue(self::$object->get_id() > 0); + } + + public function testSet_collection() + { + + $coll = null; + $appbox = appbox::get_instance(\bootstrap::getCore()); + foreach ($appbox->get_databoxes() as $databox) + { + foreach ($databox->get_collections() as $collection) + { + $coll = $collection; + break; + } + if ($coll instanceof collection) + break; + } + + self::$object->set_collection($coll); + $this->assertInstanceOf('collection', self::$object->get_collection()); + self::$object->set_collection(null); + $this->assertNull(self::$object->get_collection()); + } + + public function testSet_public() + { + self::$object->set_collection(null); + self::$object->set_public(true); + $this->assertTrue(self::$object->is_public()); + self::$object->set_public(false); + $this->assertFalse(self::$object->is_public()); + } + + public function testSet_title() + { + $this->assertEquals(self::$title, self::$object->get_title()); + $title = 'GROS NIchONS'; + self::$object->set_title($title); + $this->assertEquals($title, self::$object->get_title()); + $title = 'GROS NIchONS'; + self::$object->set_title($title); + $this->assertNotEquals($title, self::$object->get_title()); + $this->assertEquals(strip_tags($title), self::$object->get_title()); + + try + { + self::$object->set_title(' '); + $this->fail(); + } + catch (Exception $e) + { + + } + try + { + self::$object->set_title(' '); + $this->fail(); + } + catch (Exception $e) + { + + } + try + { + self::$object->set_title(''); + $this->fail(); + } + catch (Exception $e) + { + + } + + self::$object->set_title(self::$title); + } + + public function testSet_subtitle() + { + $this->assertEquals(self::$subtitle, self::$object->get_subtitle()); + self::$object->set_subtitle(''); + + $this->assertEquals('', self::$object->get_subtitle()); + self::$object->set_subtitle(' un sous titre '); + $this->assertEquals(' un sous titre ', self::$object->get_subtitle()); + self::$object->set_subtitle(self::$subtitle); + $this->assertEquals(self::$subtitle, self::$object->get_subtitle()); + } + + public function testLoad_with_user() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $this->assertEquals(Feed_Adapter::load_with_user($appbox, self::$user, self::$object->get_id())->get_id(), self::$object->get_id()); + } + + public function testGet_count_total_entries() + { + $n = self::$object->get_count_total_entries(); + $this->assertTrue($n === 0); + } + + public function testGet_entries() + { + $entries_coll = self::$object->get_entries(0, 5); + + $this->assertInstanceOf('Feed_Entry_Collection', $entries_coll); + $this->assertEquals(0, count($entries_coll->get_entries())); + } + + public function testGet_homepage_link() + { + self::$object->set_public(false); + $registry = registry::get_instance(); + $link = self::$object->get_homepage_link($registry, Feed_Adapter::FORMAT_ATOM); + $this->assertNull($link); + + self::$object->set_public(true); + $link = self::$object->get_homepage_link($registry, Feed_Adapter::FORMAT_ATOM); + $this->assertInstanceOf('Feed_Link', $link); + } + + public function testGet_user_link() + { + $registry = registry::get_instance(); + + $link = self::$object->get_user_link($registry, self::$user, Feed_Adapter::FORMAT_ATOM); + $supposed = '/feeds\/userfeed\/([a-zA-Z0-9]{12})\/' . self::$object->get_id() . '\/atom\//'; + + $atom = $link->get_href(); + + $this->assertRegExp($supposed, str_replace($registry->get('GV_ServerName'), '', $atom)); + $this->assertEquals($atom, self::$object->get_user_link($registry, self::$user, Feed_Adapter::FORMAT_ATOM)->get_href()); + $this->assertEquals($atom, self::$object->get_user_link($registry, self::$user, Feed_Adapter::FORMAT_ATOM)->get_href()); + + $this->assertNotEquals($atom, self::$object->get_user_link($registry, self::$user, Feed_Adapter::FORMAT_ATOM, null, true)->get_href()); + + $link = self::$object->get_user_link($registry, self::$user, Feed_Adapter::FORMAT_RSS); + $supposed = '/feeds\/userfeed\/([a-zA-Z0-9]{12})\/' . self::$object->get_id() . '\/rss\//'; + $this->assertRegExp($supposed, str_replace($registry->get('GV_ServerName'), '', $link->get_href())); + } + + public function testGet_title() + { + $this->assertEquals(self::$object->get_title(), self::$title); + try + { + self::$object->set_title(''); + $this->fail(); + } + catch (Exception $e) + { + + } + } + + public function testGet_subtitle() + { + $this->assertEquals(self::$object->get_subtitle(), self::$subtitle); + self::$object->set_subtitle(''); + $this->assertEquals(self::$object->get_subtitle(), ''); + self::$object->set_subtitle(self::$subtitle); + } + + public function testGet_created_on() + { + $this->assertInstanceOf('DateTime', self::$object->get_created_on()); + } + + public function testGet_updated_on() + { + $this->assertInstanceOf('DateTime', self::$object->get_updated_on()); + } + +} + diff --git a/tests/Feed/Feed_AggregateTest.php b/tests/Feed/Feed_AggregateTest.php new file mode 100644 index 0000000000..84c9a272b3 --- /dev/null +++ b/tests/Feed/Feed_AggregateTest.php @@ -0,0 +1,101 @@ +get_session()->authenticate($auth); + $objects[] = Feed_Adapter::create($appbox, self::$user, self::$title, self::$subtitle); + $objects[] = Feed_Adapter::create($appbox, self::$user, self::$title, self::$subtitle); + + self::$feeds = $objects; + self::$object = new Feed_Aggregate($appbox, self::$feeds); + } + + public static function tearDownAfterClass() + { + foreach (self::$feeds as $feed) + $feed->delete(); + parent::tearDownAfterClass(); + } + + public function testGet_icon_url() + { + $this->assertEquals('/skins/icons/rss32.gif', self::$object->get_icon_url()); + } + + public function testIs_aggregated() + { + $this->assertTrue(self::$object->is_aggregated()); + } + + public function testGet_entries() + { + $entries_coll = self::$object->get_entries(0, 5); + $this->assertInstanceOf('Feed_Entry_Collection', $entries_coll); + $this->assertEquals(0, count($entries_coll->get_entries())); + } + + public function testGet_count_total_entries() + { + $this->assertEquals(0, self::$object->get_count_total_entries()); + } + + public function testGet_homepage_link() + { + $registry = registry::get_instance(); + $link = self::$object->get_homepage_link($registry, Feed_Adapter::FORMAT_ATOM); + $this->assertInstanceOf('Feed_Link', $link); + $this->assertEquals($registry->get('GV_ServerName') . 'feeds/aggregated/atom/', $link->get_href()); + } + + public function testGet_user_link() + { + $registry = registry::get_instance(); + + $link = self::$object->get_user_link($registry, self::$user, Feed_Adapter::FORMAT_ATOM); + $supposed = '/feeds\/userfeed\/aggregated\/([a-zA-Z0-9]{12})\/atom\//'; + + $atom = $link->get_href(); + + $this->assertRegExp($supposed, str_replace($registry->get('GV_ServerName'), '', $atom)); + $this->assertEquals($atom, self::$object->get_user_link($registry, self::$user, Feed_Adapter::FORMAT_ATOM)->get_href()); + $this->assertEquals($atom, self::$object->get_user_link($registry, self::$user, Feed_Adapter::FORMAT_ATOM)->get_href()); + + $this->assertNotEquals($atom, self::$object->get_user_link($registry, self::$user, Feed_Adapter::FORMAT_ATOM, null, true)->get_href()); + + $link = self::$object->get_user_link($registry, self::$user, Feed_Adapter::FORMAT_RSS); + $supposed = '/feeds\/userfeed\/aggregated\/([a-zA-Z0-9]{12})\/rss\//'; + $this->assertRegExp($supposed, str_replace($registry->get('GV_ServerName'), '', $link->get_href())); + } + + public function testGet_created_on() + { + $this->assertInstanceOf('DateTime', self::$object->get_created_on()); + } + + public function testGet_updated_on() + { + $this->assertInstanceOf('DateTime', self::$object->get_updated_on()); + } + +} + diff --git a/tests/Feed/Feed_CollectionTest.php b/tests/Feed/Feed_CollectionTest.php new file mode 100644 index 0000000000..3851dce7a2 --- /dev/null +++ b/tests/Feed/Feed_CollectionTest.php @@ -0,0 +1,77 @@ +get_session()->authenticate($auth); + self::$object = Feed_Adapter::create($appbox, self::$user, self::$title, self::$subtitle); + self::$object->set_public(true); + } + + public static function tearDownAfterClass() + { + self::$object->delete(); + } + + public function testLoad_all() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $coll = Feed_Collection::load_all($appbox, self::$user); + + foreach ($coll->get_feeds() as $feed) + { + $this->assertInstanceOf('Feed_Adapter', $feed); + } + } + + public function testGet_feeds() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $coll = Feed_Collection::load_public_feeds($appbox); + + foreach ($coll->get_feeds() as $feed) + { + $this->assertInstanceOf('Feed_Adapter', $feed); + } + } + + public function testGet_aggregate() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $coll = Feed_Collection::load_public_feeds($appbox); + + $this->assertInstanceOf('Feed_Aggregate', $coll->get_aggregate()); + } + + public function testLoad_public_feeds() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $coll = Feed_Collection::load_public_feeds($appbox); + + foreach ($coll->get_feeds() as $feed) + { + $this->assertTrue($feed->is_public()); + } + } + +} + diff --git a/tests/Feed/Feed_LinkTest.php b/tests/Feed/Feed_LinkTest.php new file mode 100644 index 0000000000..aec7d164ae --- /dev/null +++ b/tests/Feed/Feed_LinkTest.php @@ -0,0 +1,40 @@ +object = new Feed_Link($this->href, $this->title, $this->mime); + } + + public function testGet_mimetype() + { + $this->assertEquals($this->mime, $this->object->get_mimetype()); + } + + public function testGet_title() + { + $this->assertEquals($this->title, $this->object->get_title()); + } + + public function testGet_href() + { + $this->assertEquals($this->href, $this->object->get_href()); + } + +} diff --git a/tests/Feed/Publisher/Feed_Publisher_AdapterTest.php b/tests/Feed/Publisher/Feed_Publisher_AdapterTest.php new file mode 100644 index 0000000000..e1df744e18 --- /dev/null +++ b/tests/Feed/Publisher/Feed_Publisher_AdapterTest.php @@ -0,0 +1,66 @@ +get_session()->authenticate($auth); + self::$feed = Feed_Adapter::create($appbox, self::$user, self::$title, self::$subtitle); + self::$object = array_pop(self::$feed->get_publishers()); + } + + public static function tearDownAfterClass() + { + self::$feed->delete(); + parent::tearDownAfterClass(); + } + + public function testGet_user() + { + $this->assertInstanceOf('user_Adapter', self::$object->get_user()); + $this->assertEquals(self::$user->get_id(), self::$object->get_user()->get_id()); + } + + public function testIs_owner() + { + $this->assertTrue(self::$object->is_owner()); + } + + public function testGet_created_on() + { + $this->assertInstanceOf('DateTime', self::$object->get_created_on()); + } + + public function testGet_added_by() + { + $this->assertInstanceOf('User_Adapter', self::$object->get_added_by()); + } + + public function testGet_id() + { + $this->assertTrue(is_int(self::$object->get_id())); + } + +} + diff --git a/tests/Feed/XML/Feed_XML_AtomTest.php b/tests/Feed/XML/Feed_XML_AtomTest.php new file mode 100644 index 0000000000..b5e3f8c1e1 --- /dev/null +++ b/tests/Feed/XML/Feed_XML_AtomTest.php @@ -0,0 +1,220 @@ +render()); + $this->assertInstanceOf('SimpleXMLElement', $sxe); + } + + public function testSet_author_name() + { + self::$atom->set_author_name('boulbil'); + $sxe = simplexml_load_string(self::$atom->render()); + $this->assertInstanceOf('SimpleXMLElement', $sxe); + $this->assertEquals('boulbil', (string) $sxe->author->name); + + self::$atom->set_author_name('boubi bouba'); + $sxe = simplexml_load_string(self::$atom->render()); + $this->assertInstanceOf('SimpleXMLElement', $sxe); + $this->assertEquals('boubi bouba', (string) $sxe->author->name); + } + + public function testSet_author_email() + { + self::$atom->set_author_email('bouba@example.net'); + $sxe = simplexml_load_string(self::$atom->render()); + $this->assertInstanceOf('SimpleXMLElement', $sxe); + $this->assertEquals('bouba@example.net', (string) $sxe->author->email); + + self::$atom->set_author_email('email+test@example.org'); + $sxe = simplexml_load_string(self::$atom->render()); + $this->assertInstanceOf('SimpleXMLElement', $sxe); + $this->assertEquals('email+test@example.org', (string) $sxe->author->email); + } + + public function testSet_author_url() + { + self::$atom->set_author_url('http://example.net/'); + $sxe = simplexml_load_string(self::$atom->render()); + $this->assertInstanceOf('SimpleXMLElement', $sxe); + $this->assertEquals('http://example.net/', (string) $sxe->author->uri); + } + + public function testSet_title() + { + $title = 'Un joli titre'; + self::$atom->set_title($title); + self::$atom->set_title($title); + + $xml = self::$atom->render(); + $sxe = simplexml_load_string($xml); + $namespaces = $sxe->getDocNamespaces(); + $sxe->registerXPathNamespace('__empty_ns', $namespaces['']); + $this->assertInstanceOf('SimpleXMLElement', $sxe); + $obj = $sxe->xpath('/__empty_ns:feed/__empty_ns:title'); + + $this->assertEquals(1, count($obj)); + + $this->assertEquals($title, (string) $obj[0]); + } + + public function testSet_updated_on() + { + $date_obj = new DateTime(); + self::$atom->set_updated_on($date_obj); + + $sxe = simplexml_load_string(self::$atom->render()); + $this->assertInstanceOf('SimpleXMLElement', $sxe); + $this->assertEquals($date_obj->format(DATE_ATOM), (string) $sxe->updated); + } + + public function testSet_subtitle() + { + $subtitle = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'; + self::$atom->set_subtitle($subtitle); + self::$atom->set_subtitle($subtitle); + + $xml = self::$atom->render(); + $sxe = simplexml_load_string($xml); + $namespaces = $sxe->getDocNamespaces(); + $sxe->registerXPathNamespace('__empty_ns', $namespaces['']); + $this->assertInstanceOf('SimpleXMLElement', $sxe); + $obj = $sxe->xpath('/__empty_ns:feed/__empty_ns:subtitle'); + + $this->assertEquals(1, count($obj)); + + $this->assertEquals($subtitle, (string) $obj[0]); + } + + public function testSet_link() + { + $href = 'http://www.example.org/?page=24'; + $current_page = new Feed_Link($href, 'example next page', 'application/atom+xml'); + self::$atom->set_link($current_page); + self::$atom->set_link($current_page); + + $xml = self::$atom->render(); + $sxe = simplexml_load_string($xml); + $namespaces = $sxe->getDocNamespaces(); + $sxe->registerXPathNamespace('__empty_ns', $namespaces['']); + $this->assertInstanceOf('SimpleXMLElement', $sxe); + $obj = $sxe->xpath('/__empty_ns:feed/__empty_ns:link[@rel="self"]'); + + $this->assertEquals(1, count($obj)); + $found = false; + + foreach ($obj[0]->attributes() as $attr => $attr_value) + { + if ($attr != 'href') + continue; + $found = true; + $this->assertEquals($href, (string) $attr_value); + } + + if (!$found) + $this->fail(); + } + + public function testSet_next_page() + { + $href = 'http://www.example.org/?page=23'; + $next_page = new Feed_Link($href, 'example next page', 'application/atom+xml'); + self::$atom->set_next_page($next_page); + self::$atom->set_next_page($next_page); + + $xml = self::$atom->render(); + $sxe = simplexml_load_string($xml); + $namespaces = $sxe->getDocNamespaces(); + $sxe->registerXPathNamespace('__empty_ns', $namespaces['']); + $this->assertInstanceOf('SimpleXMLElement', $sxe); + $obj = $sxe->xpath('/__empty_ns:feed/__empty_ns:link[@rel="next"]'); + + $this->assertEquals(1, count($obj)); + $found = false; + + foreach ($obj[0]->attributes() as $attr => $attr_value) + { + if ($attr != 'href') + continue; + $found = true; + $this->assertEquals($href, (string) $attr_value); + } + + if (!$found) + $this->fail(); + } + + public function testSet_previous_page() + { + $href = 'http://www.example.org/?page=25'; + $prev_page = new Feed_Link($href, 'example next page', 'application/atom+xml'); + self::$atom->set_previous_page($prev_page); + self::$atom->set_previous_page($prev_page); + + $xml = self::$atom->render(); + $sxe = simplexml_load_string($xml); + $namespaces = $sxe->getDocNamespaces(); + $sxe->registerXPathNamespace('__empty_ns', $namespaces['']); + $this->assertInstanceOf('SimpleXMLElement', $sxe); + $obj = $sxe->xpath('/__empty_ns:feed/__empty_ns:link[@rel="previous"]'); + + $this->assertEquals(1, count($obj)); + $found = false; + + foreach ($obj[0]->attributes() as $attr => $attr_value) + { + if ($attr != 'href') + continue; + $found = true; + $this->assertEquals($href, (string) $attr_value); + } + + if (!$found) + $this->fail(); + } + + public function testSet_generator() + { + $generator = 'Arnold Schwarzenegger'; + self::$atom->set_generator($generator); + self::$atom->set_generator($generator); + + $xml = self::$atom->render(); + $sxe = simplexml_load_string($xml); + $namespaces = $sxe->getDocNamespaces(); + $sxe->registerXPathNamespace('__empty_ns', $namespaces['']); + $this->assertInstanceOf('SimpleXMLElement', $sxe); + $obj = $sxe->xpath('/__empty_ns:feed/__empty_ns:generator'); + + $this->assertEquals(1, count($obj)); + + $this->assertEquals($generator, (string) $obj[0]); + } + + public function testGet_mimetype() + { + $this->assertEquals('application/atom+xml', self::$atom->get_mimetype()); + } + +} diff --git a/tests/Feed/XML/Feed_XML_RSSTest.php b/tests/Feed/XML/Feed_XML_RSSTest.php new file mode 100644 index 0000000000..a57be50dea --- /dev/null +++ b/tests/Feed/XML/Feed_XML_RSSTest.php @@ -0,0 +1,207 @@ +set_language($language); + + $xml = self::$rss->render(); + $sxe = simplexml_load_string($xml); + $this->assertInstanceOf('SimpleXMLElement', $sxe); + $this->assertEquals($language, (string) $sxe->channel->language); + } + + public function testSet_copyright() + { + $copyright = '2006-2014 No copyright'; + self::$rss->set_copyright($copyright); + + $xml = self::$rss->render(); + $sxe = simplexml_load_string($xml); + $this->assertInstanceOf('SimpleXMLElement', $sxe); + $this->assertEquals($copyright, (string) $sxe->channel->copyright); + } + + public function testSet_managingEditor() + { + $email = 'manager@example.org'; + self::$rss->set_managingEditor($email); + + $xml = self::$rss->render(); + $sxe = simplexml_load_string($xml); + $this->assertInstanceOf('SimpleXMLElement', $sxe); + $this->assertEquals($email, (string) $sxe->channel->managingEditor); + } + + public function testSet_webMaster() + { + $email = 'webmaster@example.org'; + self::$rss->set_webMaster($email); + + $xml = self::$rss->render(); + $sxe = simplexml_load_string($xml); + $this->assertInstanceOf('SimpleXMLElement', $sxe); + $this->assertEquals($email, (string) $sxe->channel->webMaster); + } + + public function testSet_lastBuildDate() + { + $last_build = new DateTime('-2 hours'); + self::$rss->set_lastBuildDate($last_build); + + + $xml = self::$rss->render(); + $sxe = simplexml_load_string($xml); + $this->assertInstanceOf('SimpleXMLElement', $sxe); + $this->assertEquals($last_build->format(DATE_RFC2822), (string) $sxe->channel->lastBuildDate); + } + + public function testSet_category() + { + $categories = array('banana' => 'banana', 'prout' => 'prout'); + foreach ($categories as $category) + { + self::$rss->set_category($category); + } + + $xml = self::$rss->render(); + $sxe = simplexml_load_string($xml); + $this->assertInstanceOf('SimpleXMLElement', $sxe); + + $cat_objs = $sxe->xpath('//channel/category'); + $this->assertEquals(count($cat_objs), count($categories)); + + foreach ($cat_objs as $cat_obj) + { + $str_cat = (string) $cat_obj; + $this->assertArrayHasKey($str_cat, $categories); + unset($categories[$str_cat]); + } + + $this->assertTrue((count($categories) === 0)); + } + + public function testSet_docs() + { + $xml = self::$rss->render(); + $sxe = simplexml_load_string($xml); + $this->assertInstanceOf('SimpleXMLElement', $sxe); + + $this->assertEquals('http://blogs.law.harvard.edu/tech/rss', (string) $sxe->channel->docs); + + self::$rss->set_docs('http://www.example.org'); + $xml = self::$rss->render(); + $sxe = simplexml_load_string($xml); + $this->assertInstanceOf('SimpleXMLElement', $sxe); + + $this->assertEquals('http://www.example.org', (string) $sxe->channel->docs); + } + + public function testSet_ttl() + { + self::$rss->set_ttl(240); + $xml = self::$rss->render(); + $sxe = simplexml_load_string($xml); + $this->assertInstanceOf('SimpleXMLElement', $sxe); + + $this->assertEquals(240, (string) $sxe->channel->ttl); + } + + public function testSet_image() + { + $link = 'http://www.example.org'; + $title = 'Un beau titre'; + $url = 'http://www.example.org/image.jpg'; + $image = new Feed_XML_RSS_Image($url, $title, $link); + $width = 42; + $height = 30; + $description = 'KIKOO'; + $image->set_width($width); + $image->set_height($height); + $image->set_description($description); + + + self::$rss->set_image($image); + $xml = self::$rss->render(); + $sxe = simplexml_load_string($xml); + $this->assertInstanceOf('SimpleXMLElement', $sxe); + + $this->assertEquals($title, (string) $sxe->channel->image->title); + $this->assertEquals($link, (string) $sxe->channel->image->link); + $this->assertEquals($url, (string) $sxe->channel->image->url); + $this->assertEquals($height, (string) $sxe->channel->image->height); + $this->assertEquals($width, (string) $sxe->channel->image->width); + $this->assertEquals($description, (string) $sxe->channel->image->description); + + } + + public function testSet_skipHours() + { + $hours = array('4' => '4', '8' => '8', '12' => '12'); + foreach ($hours as $hour) + { + self::$rss->set_skipHour($hour); + } + $xml = self::$rss->render(); + $sxe = simplexml_load_string($xml); + $this->assertInstanceOf('SimpleXMLElement', $sxe); + + foreach ($sxe->channel->skipHours->hour as $sx_hour) + { + $sx_hour = (string) $sx_hour; + $this->assertArrayHasKey($sx_hour, $hours); + unset($hours[$sx_hour]); + } + + $this->assertTrue(empty($hours)); + } + + public function testSet_skipDays() + { + $days = array('sunday' => 'sunday', 'saturday' => 'saturday', 'monday' => 'monday'); + foreach ($days as $day) + { + self::$rss->set_skipDays($day); + } + $xml = self::$rss->render(); + $sxe = simplexml_load_string($xml); + $this->assertInstanceOf('SimpleXMLElement', $sxe); + + foreach ($sxe->channel->skipDays->day as $sx_day) + { + $sx_day = (string) $sx_day; + $this->assertArrayHasKey($sx_day, $days); + unset($days[$sx_day]); + } + + $this->assertTrue(empty($days)); + } + + public function testRender() + { + $sxe = simplexml_load_string(self::$rss->render()); + $this->assertInstanceOf('SimpleXMLElement', $sxe); + } + +} + diff --git a/tests/Feed/XML/RSS/Feed_XML_RSS_ImageTest.php b/tests/Feed/XML/RSS/Feed_XML_RSS_ImageTest.php new file mode 100644 index 0000000000..359df0b135 --- /dev/null +++ b/tests/Feed/XML/RSS/Feed_XML_RSS_ImageTest.php @@ -0,0 +1,91 @@ +this->link = 'http://www.example.org'; + $this->title = 'Un beau titre'; + $this->url = 'http://www.example.org/image.jpg'; + $this->width = 42; + $this->height = 30; + $this->description = 'KIKOO'; + $this->object = new Feed_XML_RSS_Image($this->url, $this->title, $this->link); + $this->object->set_width($this->width); + $this->object->set_height($this->height); + $this->object->set_description($this->description); + + } + + public function testGet_url() + { + $this->assertEquals($this->url, $this->object->get_url()); + } + + public function testGet_title() + { + $this->assertEquals($this->title, $this->object->get_title()); + } + + public function testGet_link() + { + $this->assertEquals($this->link, $this->object->get_link()); + } + + public function testGet_description() + { + $this->assertEquals($this->description, $this->object->get_description()); + } + + public function testGet_height() + { + $this->assertEquals($this->height, $this->object->get_height()); + } + + public function testGet_width() + { + $this->assertEquals($this->width, $this->object->get_width()); + } + + public function testSet_description() + { + $new_desc = 'une nouvelle'; + $this->object->set_description($new_desc); + $this->assertEquals($new_desc, $this->object->get_description()); + } + + public function testSet_height() + { + $new_height = 27; + $this->object->set_height($new_height); + $this->assertEquals($new_height, $this->object->get_height()); + } + + public function testSet_width() + { + $new_width = 14; + $this->object->set_width($new_width); + $this->assertEquals($new_width, $this->object->get_width()); + } + +} + diff --git a/tests/FeedValidator.inc b/tests/FeedValidator.inc new file mode 100644 index 0000000000..04a371dd88 --- /dev/null +++ b/tests/FeedValidator.inc @@ -0,0 +1,377 @@ +isValidUrl($url)) + throw new W3CFeedValidatorException(sprintf("Invalid url %s", $url)); + + $this->url = $url; + } + + /** + * Validate the xml feed located at the current url + * @param string $feed_url + * @return W3CFeedValidatorResponse + * @throws W3CFeedValidatorException if the request failed + */ + public function validate() + { + $url_validator = sprintf("%s?url=%s&output=%s", self::VALIDATOR_ENDPOINT, $this->url, self::OUTPOUT); + sleep(self::TIME_BETWEEN_REQUEST); + $response = @file_get_contents($url_validator); + if ( ! $response) + { + throw new W3CFeedValidatorException("Unable to request W3C API"); + } + + return new W3CFeedValidatorResponse($response); + } + + /** + * Check if an url is valid + * @param string $url + * @return boolean + */ + private function isValidUrl($url) + { + return filter_var($url, FILTER_VALIDATE_URL); + } + +} + +/** + * + * @example + * + * try + * { + * $validator = new W3CFeedRawValidator($xml); + * $response = $validator->validate(); + * + * if($response->isValid()) + * { + * //do something + * } + * + * $validator = new W3CFeedUrlValidator($url); + * $response = $validator->validate(); + * + * if($response->isValid()) + * { + * //do something + * } + * } + * catch (W3CFeedValidatorException $e) + * { + * print "\n" . $e->getMessage(); + * } + * + * Use W3C API to valide your RSS FEED + * @see http://validator.w3.org/feed/docs/soap.html + */ +class W3CFeedRawValidator extends W3CFeedValidator +{ + + /** + * The source of the document to validate + * @var string + */ + private $rawData; + + /** + * Constructor + * Init instance which the specified data to validate + * @param string $rawData + */ + public function __construct($rawData) + { + $this->rawData = utf8_encode(trim($rawData)); + } + + /** + * Validate the rawData xml + * @param string $feed_url + * @return W3CFeedValidatorResponse + * @throws W3CFeedValidatorException if the request failed + */ + public function validate() + { + $context = stream_context_create(array( + 'http' => array( + 'method' => 'POST', + 'header' => 'Content-type: application/x-www-form-urlencoded', + 'content' => http_build_query(array( + 'rawdata' => $this->rawData, + 'manual' => 1, + 'output' => self::OUTPOUT)), + 'timeout' => 5, + ), + )); + sleep(self::TIME_BETWEEN_REQUEST); + $response = @file_get_contents(self::VALIDATOR_ENDPOINT, false, $context); + if ( ! $response) + { + throw new W3CFeedValidatorException("Unable to request W3C API"); + } + + return new W3CFeedValidatorResponse($response); + } + +} + +/** + * An object that handle the W3C FEED VALIDATOR response + */ +class W3CFeedValidatorResponse +{ + + /** + * XML W3C response + * @var string + */ + private $xml; + + /** + * @var DOMDocument + */ + private $DOMDocument; + + /** + * @var DOMXPath + */ + private $DOMXpath; + + /** + * Constructor + * Init instance with the W3C FEED VALIDATOR xml response + * @param string $xml + * @throws W3CFeedValidatorException if xml can't be loaded + */ + public function __construct($xml) + { + $this->DOMDocument = new DOMDocument(); + if ( ! $this->DOMDocument->loadXML($xml)) + { + throw new W3CFeedValidatorException("Unable to parse XML"); + } + $this->DOMXpath = new DOMXPath($this->DOMDocument); + $this->DOMXpath->registerNamespace("env", "http://www.w3.org/2003/05/soap-envelope"); + $this->DOMXpath->registerNamespace("m", "http://www.w3.org/2005/10/feed-validator"); + + $this->xml = $xml; + } + + public function __toString() + { + $string = ""; + foreach ($this->getErrors() as $error) + { + $string .= "\nERROR\n"; + foreach ($error as $name => $detail) + { + $string .= $name . "=>" . $detail . "\n"; + } + } + + return $string; + } + + /** + * Check if the XML is valid + * @return boolean + */ + public function isValid() + { + $xPathQuery = "/env:Envelope/env:Body/m:feedvalidationresponse/m:validity"; + + return $this->DOMXpath->query($xPathQuery)->item(0)->nodeValue !== "false"; + } + + /** + * Check for errors + * @return boolean + */ + public function hasError() + { + $xPathQuery = "/env:Envelope/env:Body/m:feedvalidationresponse/m:errors/m:errorcount"; + + return (int) $this->DOMXpath->query($xPathQuery)->item(0)->nodeValue !== 0; + } + + /** + * check fro warnings + * @return boolean + */ + public function hasWarning() + { + $xPathQuery = "/env:Envelope/env:Body/m:feedvalidationresponse/m:warnings/m:warningcount"; + + return (int) $this->DOMXpath->query($xPathQuery)->item(0)->nodeValue !== 0; + } + + /** + * Get an array with all warnings as follow + * array{ + * 0 => array { + * array(10) { + * ["level"]=> + * string(7) "warning" + * ["type"]=> + * string(23) "SelfDoesntMatchLocation" + * ["line"]=> + * string(2) "11" + * ["column"]=> + * string(2) "91" + * ["text"]=> + * string(46) "Self reference doesn't match document location" + * ["msgcount"]=> + * string(1) "1" + * ["backupcolumn"]=> + * string(2) "91" + * ["backupline"]=> + * string(2) "11" + * ["element"]=> + * string(4) "href" + * ["parent"]=> + * string(7) "channel" + * } + * 1 => etc .. + * } + * } + * + * @return array + */ + public function getWarnings() + { + $warnings = array(); + $xPathQuery = "/env:Envelope/env:Body/m:feedvalidationresponse/m:warnings/m:warninglist/*"; + $warningList = $this->DOMXpath->query($xPathQuery); + if ($warningList->length > 0) + { + foreach ($warningList as $warning) + { + $warnings[] = $this->domNodeToArray($warning); + } + } + + return $warnings; + } + + /** + * Get an array with all errors as follow + * @return array + */ + public function getErrors() + { + $errors = array(); + $xPathQuery = "/env:Envelope/env:Body/m:feedvalidationresponse/m:errors/m:errorlist/*"; + $errorList = $this->DOMXpath->query($xPathQuery); + if ($errorList->length > 0) + { + foreach ($errorList as $error) + { + $errors[] = $this->domNodeToArray($error); + } + } + + return $errors; + } + + /** + * @source http://www.ermshaus.org/2010/12/php-transform-domnode-to-array + * + * Transforms the contents of a DOMNode to an associative array + * + * XML node names become array keys, attributes will be discarded. If $node + * doesn't contain child nodes, a string will be returned. + * + * @param DOMNode $node DOMDocument node + * @return string|array Associative array or string with node content + */ + private function domNodeToArray(DOMNode $node) + { + $ret = ''; + + if ($node->hasChildNodes()) + { + if ($node->firstChild === $node->lastChild && $node->firstChild->nodeType === XML_TEXT_NODE) + { + // Node contains nothing but a text node, return its value + $ret = trim($node->nodeValue); + } + else + { + // Otherwise, do recursion + $ret = array(); + foreach ($node->childNodes as $child) + { + if ($child->nodeType !== XML_TEXT_NODE) + { + // If there's more than one node with this node name on the + // current level, create an array + if (isset($ret[$child->nodeName])) + { + if ( ! is_array($ret[$child->nodeName]) || ! isset($ret[$child->nodeName][0])) + { + $tmp = $ret[$child->nodeName]; + $ret[$child->nodeName] = array(); + $ret[$child->nodeName][] = $tmp; + } + + $ret[$child->nodeName][] = $this->domNodeToArray($child); + } + else + { + $ret[$child->nodeName] = $this->domNodeToArray($child); + } + } + } + } + } + + return $ret; + } + +} diff --git a/tests/PhraseanetPHPUnitAbstract.class.inc b/tests/PhraseanetPHPUnitAbstract.class.inc new file mode 100644 index 0000000000..bab5caaa4c --- /dev/null +++ b/tests/PhraseanetPHPUnitAbstract.class.inc @@ -0,0 +1,1459 @@ +classesMetatdas + * Load DoctrineTestServices + * + * @return + */ + public function __construct() + { + //check if app is set up + if (!setup::is_installed()) + { + exit("\033[0;31mPhraseanet is not set up\033[0;37m\r\n"); + } + + //init core + if (null === self::$core) + { + self::$core = \bootstrap::getCore(); + } + } + + /** + * Delete all ressources created during the test + */ + public function __destruct() + { + self::deleteRessources(); + + if (self::$time_start) + { + $stop = microtime(true); + self::$time_start = null; + } + } + + /** + * Implements abstract method from webtestcase + */ + public function createApplication() + { + return; + } + + public function setUp() + { + parent::setUp(); + //create a new core instance loaded with the test environment for each test + self::$core = \bootstrap::execute('test'); + //$this->app['EM'] = self::$core->getEntityManager(); + //set Mozilla user agent as default + $browser = Browser::getInstance(); + $browser->setUserAgent(self::USER_AGENT_FIREFOX8MAC); + + $this->purgeDatabase(); + + self::$user->ACL()->revoke_access_from_bases(array(self::$collection_no_access->get_base_id())); + } + + public function tearDown() + { + //unset static::$core + self::$core = null; + parent::tearDown(); + } + + /** + * Insert fixture contained in the specified fixtureLoader + * into sqlLite test temporary database + * + * @param Doctrine\Common\DataFixtures\Loader $fixtureLoader + */ + public function insertFixtureInDatabase(Doctrine\Common\DataFixtures\Loader $fixtureLoader, $append = true) + { + $purger = new Doctrine\Common\DataFixtures\Purger\ORMPurger(); + $executor = new Doctrine\Common\DataFixtures\Executor\ORMExecutor(self::$core->getEntityManager(), $purger); + $executor->execute($fixtureLoader->getFixtures(), $append); + } + + /** + * Purge sqlLite test temporary database by truncate all existing tables + */ + protected function purgeDatabase() + { + $purger = new Doctrine\Common\DataFixtures\Purger\ORMPurger(); + $executor = new Doctrine\Common\DataFixtures\Executor\ORMExecutor(self::$core->getEntityManager(), $purger); + $executor->execute(array()); + self::$core["CacheService"]->flushAll(); + } + + protected function assertDateAtom($date) + { + return $this->assertRegExp('/\d{4}[-]\d{2}[-]\d{2}[T]\d{2}[:]\d{2}[:]\d{2}[+]\d{2}[:]\d{2}/', $date); + } + + protected function set_user_agent($user_agent) + { + $browser = Browser::getInstance(); + $browser->setUserAgent($user_agent); + //reset twg instance + $this->resetTwig(); + } + + /** + * Insert one basket entry ans set current authenticated user as owner + * + * @return \Entities\Basket + */ + protected function insertOneBasket() + { + try + { + $basketFixture = new PhraseaFixture\Basket\LoadOneBasket(); + + $basketFixture->setUser(self::$user); + + $loader = new Loader(); + $loader->addFixture($basketFixture); + + $this->insertFixtureInDatabase($loader); + + return $basketFixture->basket; + } + catch (\Exception $e) + { + $this->fail('Fail load one Basket : ' . $e->getMessage()); + } + } + + + protected function insertOneUsrList(\User_Adapter $user) + { + try + { + $loader = new Loader(); + + $UsrOwner = new PhraseaFixture\UsrLists\UsrListOwner(); + $UsrOwner->setUser($user); + + $loader->addFixture($UsrOwner); + + $UsrList = new PhraseaFixture\UsrLists\UsrList(); + + $loader->addFixture($UsrList); + + + $this->insertFixtureInDatabase($loader); + + return $UsrList->list; + } + catch (\Exception $e) + { + $this->fail('Fail load one UsrList : ' . $e->getMessage()); + } + } + + /** + * + * @param \Entities\UsrList $UsrList + * @return \Entities\UsrListEntry + */ + protected function insertOneUsrListEntry(\User_adapter $owner, \User_adapter $user) + { + try + { + $loader = new Loader(); + + $UsrOwner = new PhraseaFixture\UsrLists\UsrListOwner(); + $UsrOwner->setUser($owner); + + $loader->addFixture($UsrOwner); + + $UsrList = new PhraseaFixture\UsrLists\UsrList(); + + $loader->addFixture($UsrList); + + $UsrEntry = new PhraseaFixture\UsrLists\UsrListEntry(); +// $UsrEntry->setList($UsrList); + $UsrEntry->setUser($user); + + $loader->addFixture($UsrEntry); + + + $this->insertFixtureInDatabase($loader); + + return $UsrEntry->entry; + } + catch (\Exception $e) + { + $this->fail('Fail load one UsrListEntry : ' . $e->getMessage()); + } + } + + /** + * Insert five baskets and set current authenticated user as owner + * + * @return \Entities\Basket + */ + protected function insertFiveBasket() + { + try + { + $basketFixture = new PhraseaFixture\Basket\LoadFiveBaskets(); + + $basketFixture->setUser(self::$user); + + $loader = new Loader(); + $loader->addFixture($basketFixture); + + $this->insertFixtureInDatabase($loader); + + return $basketFixture->baskets; + } + catch (\Exception $e) + { + $this->fail('Fail load five Basket : ' . $e->getMessage()); + } + } + + /** + * + * @return \Entities\BasketElement + */ + protected function insertOneBasketElement() + { + $basket = $this->insertOneBasket(); + + $basketElement = new \Entities\BasketElement(); + $basketElement->setRecord(self::$record_1); + $basketElement->setBasket($basket); + + $basket->addBasketElement($basketElement); + + $em = self::$core->getEntityManager(); + + $em->persist($basketElement); + + $em->merge($basket); + + $em->flush(); + + return $basketElement; + } + + /** + * + * @return \Entities\Basket + */ + protected function insertOneValidationBasket() + { + $em = self::$core->getEntityManager(); + + $basketElement = $this->insertOneBasketElement(); + $basket = $basketElement->getBasket(); + + $Validation = new Entities\ValidationSession(); + $Validation->setBasket($basket); + $Validation->setInitiator(self::$user); + + $basket->setValidation($Validation); + + $em->persist($Validation); + $em->merge($basket); + + $Participant = new Entities\ValidationParticipant(); + $Participant->setUser(self::$user); + $Participant->setCanAgree(true); + $Participant->setCanSeeOthers(true); + + $Validation->addValidationParticipant($Participant); + $Participant->setSession($Validation); + + $em->persist($Participant); + $em->merge($Validation); + + $Data = new Entities\ValidationData(); + $Data->setBasketElement($basketElement); + $Data->setParticipant($Participant); + $basketElement->addValidationData($Data); + + $em->persist($Data); + $em->merge($basketElement); + + $em->flush(); + + return $basket; + } + + /** + * Create a new basket with current auhtenticated user as owner + * Create a new sessionValidation with the newly created basket + * Set current authenticated user as sessionValidation initiator + * Add 2 records as elments of the newly created basket + * Add 2 participants to the newly created sessionValidation + * + * @return \Entities\Basket + */ + protected function insertOneBasketEnv() + { + try + { + $em = self::$core->getEntityManager(); + /* @var $em \Doctrine\ORM\EntityManager */ + + $basketFixture = new PhraseaFixture\Basket\LoadOneBasketEnv(); + + $basketFixture->setUser(self::$user); + + $basketFixture->addParticipant(self::$user_alt1); + $basketFixture->addParticipant(self::$user_alt2); + + $basketFixture->addBasketElement(self::$record_1); + $basketFixture->addBasketElement(self::$record_2); + + $loader = new Loader(); + $loader->addFixture($basketFixture); + + $this->insertFixtureInDatabase($loader); + + return $basketFixture->basket; + } + catch (\Exception $e) + { + $this->fail('Fail load one Basket context : ' . $e->getMessage()); + } + } + + /** + * Load One WZ with + * One basket + * One story + * One ValidationSession & one participant + * @return + */ + protected function insertOneWZ() + { + try + { + $currentUser = self::$user; + $altUser = self::$user_alt1; + //add one basket + $basket = new PhraseaFixture\Basket\LoadOneBasket(); + $basket->setUser($currentUser); + //add one story + $story = new PhraseaFixture\Story\LoadOneStory(); + $story->setUser($currentUser); + $story->setRecord(self::$record_1); + //add a validation session initiated by alt user + $validationSession = new PhraseaFixture\ValidationSession\LoadOneValidationSession(); + $validationSession->setUser($altUser); + + $loader = new Loader(); + $loader->addFixture($basket); + $loader->addFixture($story); + $loader->addFixture($validationSession); + + $this->insertFixtureInDatabase($loader); + + //add current user as participant + $validationParticipant = new PhraseaFixture\ValidationParticipant\LoadParticipantWithSession(); + $validationParticipant->setSession($validationSession->validationSession); + $validationParticipant->setUser($currentUser); + + $loader = new Loader(); + $loader->addFixture($validationParticipant); + $this->insertFixtureInDatabase($loader); + } + catch (\Exception $e) + { + $this->fail('Fail load one WorkingZone : ' . $e->getMessage()); + } + + return; + } + + /** + * Create a new instance of Twig service + * @return void + */ + protected function resetTwig() + { + $configuration = self::$core->getConfiguration(); + + $serviceName = $configuration->getTemplating(); + $confService = $configuration->getService($serviceName); + + $templateService = \Alchemy\Phrasea\Core\Service\Builder::create( + self::$core + , $confService + ); + + $this->app['Core']["Twig"] = $templateService->getDriver(); + } + + /** + * Update the sql tables with the current schema + * @return void + */ + private static function updateTablesSchema() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + if (!self::$updated) + { + + if (file_exists(Setup_Upgrade::get_lock_file())) + { + unlink(Setup_Upgrade::get_lock_file()); + } + + if (null !== self::$core) + { + /* @var $em \Doctrine\ORM\EntityManager */ + $em = self::$core->getEntityManager(); + + //get sqlite filePath + $params = $em->getConnection()->getParams(); + + //remove if exists + if (is_file($params["path"])) + { + unlink($params["path"]); + } + + //create schema + $tool = new \Doctrine\ORM\Tools\SchemaTool($em); + $metas = $em->getMetadataFactory()->getAllMetadata(); + $tool->createSchema($metas); + } + + $upgrader = new Setup_Upgrade($appbox); + $appbox->forceUpgrade($upgrader); + unset($upgrader); + + self::$updated = true; + } + + set_time_limit(3600); + + return; + } + + /** + * Create a set of users for the test suite + * self::$user + * self::$user_alt1 + * self::$user_alt2 + * + * @return void; + */ + private static function createSetOfUserTests() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $usr_id = User_Adapter::get_usr_id_from_login('test_phpunit'); + $usr_alt1_id = User_Adapter::get_usr_id_from_login('test_phpunit_alt1'); + $usr_alt2_id = User_Adapter::get_usr_id_from_login('test_phpunit_alt2'); + + if (!$usr_id) + { + $user = User_Adapter::create($appbox, 'test_phpunit', random::generatePassword(), 'noone@example.com', false); + $usr_id = $user->get_id(); + } + if (!$usr_alt1_id) + { + $user = User_Adapter::create($appbox, 'test_phpunit_alt1', random::generatePassword(), 'noonealt1@example.com', false); + $usr_alt1_id = $user->get_id(); + } + if (!$usr_alt2_id) + { + $user = User_Adapter::create($appbox, 'test_phpunit_alt2', random::generatePassword(), 'noonealt2@example.com', false); + $usr_alt2_id = $user->get_id(); + } + + self::$user = User_Adapter::getInstance($usr_id, $appbox); + self::$user_alt1 = User_Adapter::getInstance($usr_alt2_id, $appbox); + self::$user_alt2 = User_Adapter::getInstance($usr_alt1_id, $appbox); + + return; + } + + /** + * Give Bases Rights to User + * + * @param \User_AAdapter $user + */ + private static function giveRightsToUser(\User_Adapter $user) + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $user->ACL()->give_access_to_sbas(array_keys($appbox->get_databoxes())); + + foreach ($appbox->get_databoxes() as $databox) + { + + $rights = array( + 'bas_manage' => '1' + , 'bas_modify_struct' => '1' + , 'bas_modif_th' => '1' + , 'bas_chupub' => '1' + ); + + $user->ACL()->update_rights_to_sbas($databox->get_sbas_id(), $rights); + + foreach ($databox->get_collections() as $collection) + { + $base_id = $collection->get_base_id(); + $user->ACL()->give_access_to_base(array($base_id)); + + $rights = array( + 'canputinalbum' => '1' + , 'candwnldhd' => '1' + , 'candwnldsubdef' => '1' + , 'nowatermark' => '1' + , 'candwnldpreview' => '1' + , 'cancmd' => '1' + , 'canadmin' => '1' + , 'canreport' => '1' + , 'canpush' => '1' + , 'creationdate' => '1' + , 'canaddrecord' => '1' + , 'canmodifrecord' => '1' + , 'candeleterecord' => '1' + , 'chgstatus' => '1' + , 'imgtools' => '1' + , 'manage' => '1' + , 'modify_struct' => '1' + , 'manage' => '1' + , 'bas_modify_struct' => '1' + ); + + $user->ACL()->update_rights_to_base($collection->get_base_id(), $rights); + } + } + } + + /** + * Set self::$collection + * @return void + */ + private static function setCollection() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $coll = $collection_no_acces = null; + + foreach ($appbox->get_databoxes() as $databox) + { + foreach ($databox->get_collections() as $collection) + { + if($coll instanceof collection && !$collection_no_acces) + $collection_no_acces = $collection; + if(!$coll) + $coll = $collection; + if($coll instanceof collection && $collection_no_acces instanceof collection) + break; + } + + if ($coll instanceof collection && $collection_no_acces instanceof collection) + { + break; + } + } + + if (!$coll instanceof collection) + { + self::fail('Unable to find a collection'); + } + if (!$collection_no_acces instanceof collection) + { + $collection_no_acces = collection::create($databox, $appbox, 'BIBOO', self::$user); +// self::fail('Unable to find a second collection'); + } + + self::$collection = $coll; + + self::$collection_no_access = $collection_no_acces; + + return; + } + + /** + * Generate a set of stories for the current test suites + * + * @return void + */ + private static function generateStories() + { + if (static::$need_story && !self::$story_1 instanceof record_adapter) + { + self::$story_1 = \record_adapter::create( + self::$collection + , new system_file(__DIR__ . '/testfiles/test001.CR2') + , false + , true + ); + } + + if (static::$need_story && !self::$story_2 instanceof record_adapter) + { + self::$story_2 = \record_adapter::create( + self::$collection + , new system_file(__DIR__ . '/testfiles/test001.CR2') + , false + , true + ); + } + + return; + } + + /** + * Generate a set subdef according to the records previously created + * + * @return void + */ + private static function generateSubdefs() + { + if (static::$need_records && static::$need_subdefs) + { + if (self::$record_1 instanceof record_adapter && !isset(self::$generated_subdefs['a1'])) + { + self::$record_1->generate_subdefs(self::$collection->get_databox()); + self::$generated_subdefs['a1'] = true; + } + if (self::$record_2 instanceof record_adapter && !isset(self::$generated_subdefs['a2'])) + { + self::$record_2->generate_subdefs(self::$collection->get_databox()); + self::$generated_subdefs['a2'] = true; + } + if (self::$record_3 instanceof record_adapter && !isset(self::$generated_subdefs['a3'])) + { + self::$record_3->generate_subdefs(self::$collection->get_databox()); + self::$generated_subdefs['a3'] = true; + } + if (self::$record_4 instanceof record_adapter && !isset(self::$generated_subdefs['a4'])) + { + self::$record_4->generate_subdefs(self::$collection->get_databox()); + self::$generated_subdefs['a4'] = true; + } + if (self::$record_5 instanceof record_adapter && !isset(self::$generated_subdefs['a5'])) + { + self::$record_5->generate_subdefs(self::$collection->get_databox()); + self::$generated_subdefs['a5'] = true; + } + if (self::$record_6 instanceof record_adapter && !isset(self::$generated_subdefs['a6'])) + { + self::$record_6->generate_subdefs(self::$collection->get_databox()); + self::$generated_subdefs['a6'] = true; + } + if (self::$record_7 instanceof record_adapter && !isset(self::$generated_subdefs['a7'])) + { + self::$record_7->generate_subdefs(self::$collection->get_databox()); + self::$generated_subdefs['a7'] = true; + } + if (self::$record_8 instanceof record_adapter && !isset(self::$generated_subdefs['a8'])) + { + self::$record_8->generate_subdefs(self::$collection->get_databox()); + self::$generated_subdefs['a8'] = true; + } + if (self::$record_9 instanceof record_adapter && !isset(self::$generated_subdefs['a9'])) + { + self::$record_9->generate_subdefs(self::$collection->get_databox()); + self::$generated_subdefs['a9'] = true; + } + if (self::$record_10 instanceof record_adapter && !isset(self::$generated_subdefs['a10'])) + { + self::$record_10->generate_subdefs(self::$collection->get_databox()); + self::$generated_subdefs['a10'] = true; + } + if (self::$record_11 instanceof record_adapter && !isset(self::$generated_subdefs['a11'])) + { + self::$record_11->generate_subdefs(self::$collection->get_databox()); + self::$generated_subdefs['a11'] = true; + } + if (self::$record_12 instanceof record_adapter && !isset(self::$generated_subdefs['a12'])) + { + self::$record_12->generate_subdefs(self::$collection->get_databox()); + self::$generated_subdefs['a12'] = true; + } + if (self::$record_13 instanceof record_adapter && !isset(self::$generated_subdefs['a13'])) + { + self::$record_13->generate_subdefs(self::$collection->get_databox()); + self::$generated_subdefs['a13'] = true; + } + if (self::$record_14 instanceof record_adapter && !isset(self::$generated_subdefs['a14'])) + { + self::$record_14->generate_subdefs(self::$collection->get_databox()); + self::$generated_subdefs['a14'] = true; + } + if (self::$record_15 instanceof record_adapter && !isset(self::$generated_subdefs['a15'])) + { + self::$record_15->generate_subdefs(self::$collection->get_databox()); + self::$generated_subdefs['a15'] = true; + } + if (self::$record_16 instanceof record_adapter && !isset(self::$generated_subdefs['a16'])) + { + self::$record_16->generate_subdefs(self::$collection->get_databox()); + self::$generated_subdefs['a16'] = true; + } + if (self::$record_17 instanceof record_adapter && !isset(self::$generated_subdefs['a17'])) + { + self::$record_17->generate_subdefs(self::$collection->get_databox()); + self::$generated_subdefs['a17'] = true; + } + if (self::$record_18 instanceof record_adapter && !isset(self::$generated_subdefs['a18'])) + { + self::$record_18->generate_subdefs(self::$collection->get_databox()); + self::$generated_subdefs['a18'] = true; + } + if (self::$record_19 instanceof record_adapter && !isset(self::$generated_subdefs['a19'])) + { + self::$record_19->generate_subdefs(self::$collection->get_databox()); + self::$generated_subdefs['a19'] = true; + } + if (self::$record_20 instanceof record_adapter && !isset(self::$generated_subdefs['a20'])) + { + self::$record_20->generate_subdefs(self::$collection->get_databox()); + self::$generated_subdefs['a20'] = true; + } + if (self::$record_21 instanceof record_adapter && !isset(self::$generated_subdefs['a21'])) + { + self::$record_21->generate_subdefs(self::$collection->get_databox()); + self::$generated_subdefs['a21'] = true; + } + if (self::$record_22 instanceof record_adapter && !isset(self::$generated_subdefs['a22'])) + { + self::$record_22->generate_subdefs(self::$collection->get_databox()); + self::$generated_subdefs['a22'] = true; + } + if (self::$record_23 instanceof record_adapter && !isset(self::$generated_subdefs['a23'])) + { + self::$record_23->generate_subdefs(self::$collection->get_databox()); + self::$generated_subdefs['a23'] = true; + } + } + + return; + } + + /** + * Generate a set of records for the current tests suites + * @return void + */ + private static function generateRecords() + { + if (static::$need_records) + { + if ((static::$need_records === true || static::$need_records >= 1) && !self::$record_1 instanceof record_adapter) + { + self::$record_sf_1 = new system_file(__DIR__ . '/testfiles/test001.CR2'); + self::$record_1 = record_adapter::create(self::$collection, self::$record_sf_1); + } + if ((static::$need_records === true || static::$need_records >= 1) && !self::$record_no_access instanceof record_adapter) + { + $file = new system_file(__DIR__ . '/testfiles/test001.CR2'); + self::$record_no_access = record_adapter::create(self::$collection_no_access, $file); + } + if ((static::$need_records === true || static::$need_records >= 2) && !self::$record_2 instanceof record_adapter) + { + self::$record_sf_2 = new system_file(__DIR__ . '/testfiles/test002.CR2'); + self::$record_2 = record_adapter::create(self::$collection, self::$record_sf_2); + } + if ((static::$need_records === true || static::$need_records >= 3) && !self::$record_3 instanceof record_adapter) + { + self::$record_sf_3 = new system_file(__DIR__ . '/testfiles/test003.CR2'); + self::$record_3 = record_adapter::create(self::$collection, self::$record_sf_3); + } + if ((static::$need_records === true || static::$need_records >= 4) && !self::$record_4 instanceof record_adapter) + { + self::$record_sf_4 = new system_file(__DIR__ . '/testfiles/test004.CR2'); + self::$record_4 = record_adapter::create(self::$collection, self::$record_sf_4); + } + if ((static::$need_records === true || static::$need_records >= 5) && !self::$record_5 instanceof record_adapter) + { + self::$record_sf_5 = new system_file(__DIR__ . '/testfiles/test005.CR2'); + self::$record_5 = record_adapter::create(self::$collection, self::$record_sf_5); + } + if ((static::$need_records === true || static::$need_records >= 6) && !self::$record_6 instanceof record_adapter) + { + self::$record_sf_6 = new system_file(__DIR__ . '/testfiles/test006.wav'); + self::$record_6 = record_adapter::create(self::$collection, self::$record_sf_6); + } + if ((static::$need_records === true || static::$need_records >= 7) && !self::$record_7 instanceof record_adapter) + { + self::$record_sf_7 = new system_file(__DIR__ . '/testfiles/test007.ppt'); + self::$record_7 = record_adapter::create(self::$collection, self::$record_sf_7); + } + if ((static::$need_records === true || static::$need_records >= 8) && !self::$record_8 instanceof record_adapter) + { + self::$record_sf_8 = new system_file(__DIR__ . '/testfiles/test008.ai'); + self::$record_8 = record_adapter::create(self::$collection, self::$record_sf_8); + } + if ((static::$need_records === true || static::$need_records >= 9) && !self::$record_9 instanceof record_adapter) + { + self::$record_sf_9 = new system_file(__DIR__ . '/testfiles/test009.TIFF'); + self::$record_9 = record_adapter::create(self::$collection, self::$record_sf_9); + } + if ((static::$need_records === true || static::$need_records >= 10) && !self::$record_10 instanceof record_adapter) + { + self::$record_sf_10 = new system_file(__DIR__ . '/testfiles/test010.fla'); + self::$record_10 = record_adapter::create(self::$collection, self::$record_sf_10); + } + if ((static::$need_records === true || static::$need_records >= 11) && !self::$record_11 instanceof record_adapter) + { + self::$record_sf_11 = new system_file(__DIR__ . '/testfiles/test011.swf'); + self::$record_11 = record_adapter::create(self::$collection, self::$record_sf_11); + } + if ((static::$need_records === true || static::$need_records >= 12) && !self::$record_12 instanceof record_adapter) + { + self::$record_sf_12 = new system_file(__DIR__ . '/testfiles/test012.wav'); + self::$record_12 = record_adapter::create(self::$collection, self::$record_sf_12); + } + if ((static::$need_records === true || static::$need_records >= 13) && !self::$record_13 instanceof record_adapter) + { + self::$record_sf_13 = new system_file(__DIR__ . '/testfiles/test013.ai'); + self::$record_13 = record_adapter::create(self::$collection, self::$record_sf_13); + } + if ((static::$need_records === true || static::$need_records >= 14) && !self::$record_14 instanceof record_adapter) + { + self::$record_sf_14 = new system_file(__DIR__ . '/testfiles/test014.swf'); + self::$record_14 = record_adapter::create(self::$collection, self::$record_sf_14); + } + if ((static::$need_records === true || static::$need_records >= 15) && !self::$record_15 instanceof record_adapter) + { + self::$record_sf_15 = new system_file(__DIR__ . '/testfiles/test015.eps'); + self::$record_15 = record_adapter::create(self::$collection, self::$record_sf_15); + } + if ((static::$need_records === true || static::$need_records >= 16) && !self::$record_16 instanceof record_adapter) + { + self::$record_sf_16 = new system_file(__DIR__ . '/testfiles/test016.ai'); + self::$record_16 = record_adapter::create(self::$collection, self::$record_sf_16); + } + if ((static::$need_records === true || static::$need_records >= 17) && !self::$record_17 instanceof record_adapter) + { + self::$record_sf_17 = new system_file(__DIR__ . '/testfiles/test017.wav'); + self::$record_17 = record_adapter::create(self::$collection, self::$record_sf_17); + } + if ((static::$need_records === true || static::$need_records >= 18) && !self::$record_18 instanceof record_adapter) + { + self::$record_sf_18 = new system_file(__DIR__ . '/testfiles/test018.TIFF'); + self::$record_18 = record_adapter::create(self::$collection, self::$record_sf_18); + } + if ((static::$need_records === true || static::$need_records >= 19) && !self::$record_19 instanceof record_adapter) + { + self::$record_sf_19 = new system_file(__DIR__ . '/testfiles/test019.mp3'); + self::$record_19 = record_adapter::create(self::$collection, self::$record_sf_19); + } + if ((static::$need_records === true || static::$need_records >= 20) && !self::$record_20 instanceof record_adapter) + { + self::$record_sf_20 = new system_file(__DIR__ . '/testfiles/test020.mp3'); + self::$record_20 = record_adapter::create(self::$collection, self::$record_sf_20); + } + if ((static::$need_records === true || static::$need_records >= 21) && !self::$record_21 instanceof record_adapter) + { + self::$record_sf_21 = new system_file(__DIR__ . '/testfiles/test021.fla'); + self::$record_21 = record_adapter::create(self::$collection, self::$record_sf_21); + } + if ((static::$need_records === true || static::$need_records >= 22) && !self::$record_22 instanceof record_adapter) + { + self::$record_sf_22 = new system_file(__DIR__ . '/testfiles/test022.swf'); + self::$record_22 = record_adapter::create(self::$collection, self::$record_sf_22); + } + if ((static::$need_records === true || static::$need_records >= 23) && !self::$record_23 instanceof record_adapter) + { + self::$record_sf_23 = new system_file(__DIR__ . '/testfiles/test023.mp4'); + self::$record_23 = record_adapter::create(self::$collection, self::$record_sf_23); + } + } + + return; + } + + /** + * Delete previously created Ressources + * + * @return void + */ + private static function deleteRessources() + { + $skipped = \PhraseanetPHPUnitListener::getSkipped(); + + if($skipped) + { + echo "\nSkipped test : \n\n"; + foreach($skipped as $skipped_test) + { + echo $skipped_test . "\n"; + } + echo "\n"; + } + + \PhraseanetPHPUnitListener::resetSkipped(); + + if (self::$story_1 instanceof record_adapter) + { + self::$story_1->delete(); + self::$story_1 = null; + } + if (self::$story_2 instanceof record_adapter) + { + self::$story_2->delete(); + self::$story_2 = null; + } + if (self::$record_1 instanceof record_adapter) + { + self::$record_1->delete(); + self::$record_1 = null; + } + if (self::$record_2 instanceof record_adapter) + { + self::$record_2->delete(); + self::$record_2 = null; + } + if (self::$record_3 instanceof record_adapter) + { + self::$record_3->delete(); + self::$record_3 = null; + } + if (self::$record_4 instanceof record_adapter) + { + self::$record_4->delete(); + self::$record_4 = null; + } + if (self::$record_5 instanceof record_adapter) + { + self::$record_5->delete(); + self::$record_5 = null; + } + if (self::$record_6 instanceof record_adapter) + { + self::$record_6->delete(); + self::$record_6 = null; + } + if (self::$record_7 instanceof record_adapter) + { + self::$record_7->delete(); + self::$record_7 = null; + } + if (self::$record_8 instanceof record_adapter) + { + self::$record_8->delete(); + self::$record_8 = null; + } + if (self::$record_9 instanceof record_adapter) + { + self::$record_9->delete(); + self::$record_9 = null; + } + if (self::$record_10 instanceof record_adapter) + { + self::$record_10->delete(); + self::$record_10 = null; + } + if (self::$record_11 instanceof record_adapter) + { + self::$record_11->delete(); + self::$record_11 = null; + } + if (self::$record_12 instanceof record_adapter) + { + self::$record_12->delete(); + self::$record_12 = null; + } + if (self::$record_13 instanceof record_adapter) + { + self::$record_13->delete(); + self::$record_13 = null; + } + if (self::$record_14 instanceof record_adapter) + { + self::$record_14->delete(); + self::$record_14 = null; + } + if (self::$record_15 instanceof record_adapter) + { + self::$record_15->delete(); + self::$record_15 = null; + } + if (self::$record_16 instanceof record_adapter) + { + self::$record_16->delete(); + self::$record_16 = null; + } + if (self::$record_17 instanceof record_adapter) + { + self::$record_17->delete(); + self::$record_17 = null; + } + if (self::$record_18 instanceof record_adapter) + { + self::$record_18->delete(); + self::$record_18 = null; + } + if (self::$record_19 instanceof record_adapter) + { + self::$record_19->delete(); + self::$record_19 = null; + } + if (self::$record_20 instanceof record_adapter) + { + self::$record_20->delete(); + self::$record_20 = null; + } + if (self::$record_21 instanceof record_adapter) + { + self::$record_21->delete(); + self::$record_21 = null; + } + if (self::$record_22 instanceof record_adapter) + { + self::$record_22->delete(); + self::$record_22 = null; + } + if (self::$record_23 instanceof record_adapter) + { + self::$record_23->delete(); + self::$record_23 = null; + } + + return; + } + +} diff --git a/tests/PhraseanetPHPUnitAuthenticatedAbstract.class.inc b/tests/PhraseanetPHPUnitAuthenticatedAbstract.class.inc new file mode 100644 index 0000000000..740afc2a77 --- /dev/null +++ b/tests/PhraseanetPHPUnitAuthenticatedAbstract.class.inc @@ -0,0 +1,44 @@ +get_session(); + $auth = new Session_Authentication_None(self::$user); + $session->authenticate($auth); + } + + public function tearDown() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $session = $appbox->get_session(); + $session->logout(); + parent::tearDown(); + } + +} diff --git a/tests/PhraseanetPHPUnitListener.class.inc b/tests/PhraseanetPHPUnitListener.class.inc new file mode 100644 index 0000000000..4533141386 --- /dev/null +++ b/tests/PhraseanetPHPUnitListener.class.inc @@ -0,0 +1,62 @@ +getName() . ' - ' . $e->getMessage(); + + return; + } + + public function getSkipped() + { + return static::$skipped; + } + + public function resetSkipped() + { + static::$skipped = array(); + + return; + } + + public function startTest(PHPUnit_Framework_Test $test) + { + return; + } + + public function endTest(PHPUnit_Framework_Test $test, $time) + { + return; + } + + public function startTestSuite(PHPUnit_Framework_TestSuite $suite) + { + return; + } + + public function endTestSuite(PHPUnit_Framework_TestSuite $suite) + { + return; + } + +} diff --git a/tests/PhraseanetWebTestCaseAbstract.class.inc b/tests/PhraseanetWebTestCaseAbstract.class.inc new file mode 100644 index 0000000000..7e1c48964d --- /dev/null +++ b/tests/PhraseanetWebTestCaseAbstract.class.inc @@ -0,0 +1,27 @@ +get_session(); + $auth = new Session_Authentication_None(self::$user); + $session->authenticate($auth); + } + + public static function tearDownAfterClass() + { + parent::tearDownAfterClass(); + $appbox = appbox::get_instance(\bootstrap::getCore()); + $session = $appbox->get_session(); + $session->logout(); + } + +} diff --git a/tests/Session/Authentication/Session_Authentication_GuestTest.php b/tests/Session/Authentication/Session_Authentication_GuestTest.php new file mode 100644 index 0000000000..6c7aec17c8 --- /dev/null +++ b/tests/Session/Authentication/Session_Authentication_GuestTest.php @@ -0,0 +1,48 @@ +object = new Session_Authentication_Guest(appbox::get_instance(\bootstrap::getCore())); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + public function tearDown() + { + + } + + /** + * @todo Implement testSignOn(). + */ + public function testSignOn() + { + $user = $this->object->signOn(); + $this->assertInstanceOf('User_Adapter', $user); + $this->assertTrue($user->is_guest()); + } + + +} + +?> diff --git a/tests/Session/Authentication/Session_Authentication_NativeTest.php b/tests/Session/Authentication/Session_Authentication_NativeTest.php new file mode 100644 index 0000000000..06bb049160 --- /dev/null +++ b/tests/Session/Authentication/Session_Authentication_NativeTest.php @@ -0,0 +1,58 @@ +local_user = User_Adapter::getInstance($usr_id, appbox::get_instance(\bootstrap::getCore())); + } + else + { + $this->local_user = User_Adapter::create(appbox::get_instance(\bootstrap::getCore()), $login, $password, null, false); + } + $this->object = new Session_Authentication_Native(appbox::get_instance(\bootstrap::getCore()), $login, $password); + } + + public function tearDown() + { + $this->local_user->delete(); + } + + public function testSet_captcha_challenge() + { + $this->object->set_captcha_challenge(false); + } + + + public function testSignOn() + { + $user = $this->object->signOn(); + $this->assertInstanceOf('User_Adapter', $user); + } + + +} + +?> diff --git a/tests/Session/Authentication/Session_Authentication_PersistentCookieTest.php b/tests/Session/Authentication/Session_Authentication_PersistentCookieTest.php new file mode 100644 index 0000000000..088ba71998 --- /dev/null +++ b/tests/Session/Authentication/Session_Authentication_PersistentCookieTest.php @@ -0,0 +1,70 @@ +object = new Session_Authentication_PersistentCookie; + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + public function tearDown() + { + + } + + /** + * @todo Implement testPrelog(). + */ + public function testPrelog() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testSignOn(). + */ + public function testSignOn() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testPostlog(). + */ + public function testPostlog() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/Session/Authentication/Session_Authentication_TokenTest.php b/tests/Session/Authentication/Session_Authentication_TokenTest.php new file mode 100644 index 0000000000..ad60574965 --- /dev/null +++ b/tests/Session/Authentication/Session_Authentication_TokenTest.php @@ -0,0 +1,70 @@ +object = new Session_Authentication_Token; + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + public function tearDown() + { + + } + + /** + * @todo Implement testPrelog(). + */ + public function testPrelog() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testSignOn(). + */ + public function testSignOn() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testPostlog(). + */ + public function testPostlog() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/Session/Session_HandlerTest.php b/tests/Session/Session_HandlerTest.php new file mode 100644 index 0000000000..98e7a1177d --- /dev/null +++ b/tests/Session/Session_HandlerTest.php @@ -0,0 +1,450 @@ +object = Session_Handler::getInstance(appbox::get_instance(\bootstrap::getCore())); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + public function tearDown() + { + if ($this->object->is_authenticated()) + $this->object->logout(); + } + + public function testGetInstance() + { + $this->assertInstanceOf('Session_Handler', $this->object); + } + + /** + * @todo Implement testLogout(). + */ + public function testLogout() + { + $user = self::$user; + $auth = new Session_Authentication_None($user); + $databoxes = $user->ACL()->get_granted_sbas(); + + $this->assertFalse($this->object->is_authenticated()); + $this->object->authenticate($auth); + $this->assertTrue($this->object->is_authenticated()); + $this->assertTrue(is_int($this->object->get_ses_id())); + + $ses_id = $this->object->get_ses_id(); + + $conn = connection::getPDOConnection(); + $sql = 'SELECT session_id FROM cache WHERE session_id = :ses_id'; + $params = array(':ses_id' => $ses_id); + $stmt = $conn->prepare($sql); + $stmt->execute($params); + $this->assertEquals(1, $stmt->rowCount()); + + $loggers = array(); + + foreach ($databoxes as $databox) + { + $logger = $this->object->get_logger($databox); + $this->assertInstanceOf('Session_Logger', $logger); + $loggers[$databox->get_sbas_id()] = $logger; + } + + $this->object->logout(); + $this->assertFalse($this->object->is_authenticated()); + + $stmt->execute($params); + $this->assertEquals(0, $stmt->rowCount()); + $stmt->closeCursor(); + + + foreach ($databoxes as $databox) + { + + $logger = $this->object->get_logger($databox); + $this->assertInstanceOf('Session_Logger', $logger); + $this->assertNotEquals($loggers[$databox->get_sbas_id()]->get_id(), $logger->get_id()); + } + } + + /** + * @todo Implement testStorage(). + */ + public function testStorage() + { + $this->assertInstanceOf('Session_Storage_Interface', $this->object->storage()); + } + + /** + * @todo Implement testClose_storage(). + */ + public function testClose_storage() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + + /** + * @todo Implement testGet_locale(). + */ + public function testGet_locale() + { + $this->assertRegExp('/[a-z]{2}_[A-Z]{2}/', Session_Handler::get_locale()); + } + + /** + * @todo Implement testSet_locale(). + */ + public function testSet_locale() + { + Session_Handler::set_locale('fr_FR'); + $this->assertEquals('fr_FR', Session_Handler::get_locale()); + Session_Handler::set_locale('en_GB'); + $this->assertEquals('en_GB', Session_Handler::get_locale()); + } + + /** + * @todo Implement testGet_l10n(). + */ + public function testGet_l10n() + { + Session_Handler::set_locale('fr_FR'); + $this->assertEquals('FR', $this->object->get_l10n()); + Session_Handler::set_locale('en_GB'); + $this->assertEquals('GB', $this->object->get_l10n()); + } + + /** + * @todo Implement testGet_I18n(). + */ + public function testGet_I18n() + { + Session_Handler::set_locale('fr_FR'); + $this->assertEquals('fr', $this->object->get_I18n()); + Session_Handler::set_locale('en_GB'); + $this->assertEquals('en', $this->object->get_I18n()); + } + + /** + * @todo Implement testIs_authenticated(). + */ + public function testIs_authenticated() + { + $user = self::$user; + $auth = new Session_Authentication_None($user); + $this->assertFalse($this->object->is_authenticated()); + $this->object->authenticate($auth); + $this->assertTrue($this->object->is_authenticated()); + $this->object->logout(); + $this->assertFalse($this->object->is_authenticated()); + } + + /** + * @todo Implement testGet_usr_id(). + */ + public function testGet_usr_id() + { + $this->assertNull($this->object->get_usr_id()); + + $user = self::$user; + $auth = new Session_Authentication_None($user); + $this->object->authenticate($auth); + + $this->assertTrue(is_int($this->object->get_usr_id())); + $this->assertEquals($user->get_id(), $this->object->get_usr_id()); + + $this->object->logout(); + $this->assertNull($this->object->get_usr_id()); + } + + /** + * @todo Implement testGet_ses_id(). + */ + public function testGet_ses_id() + { + $this->assertNull($this->object->get_ses_id()); + + $user = self::$user; + $auth = new Session_Authentication_None($user); + $this->object->authenticate($auth); + + $this->assertTrue(is_int($this->object->get_ses_id())); + + $this->object->logout(); + $this->assertNull($this->object->get_ses_id()); + } + + /** + * @todo Implement testIsset_postlog(). + */ + public function testIsset_postlog() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testSet_postlog(). + */ + public function testSet_postlog() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_postlog(). + */ + public function testGet_postlog() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testDelete_postlog(). + */ + public function testDelete_postlog() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testSet_session_prefs(). + */ + public function testSet_session_prefs() + { + $datas = array('bla' => 1, 2 => 'boum'); + $this->object->set_session_prefs('test', $datas); + $this->assertEquals($datas, $this->object->get_session_prefs('test')); + } + + /** + * @todo Implement testGet_session_prefs(). + */ + public function testGet_session_prefs() + { + $this->testSet_session_prefs(); + } + + /** + * @todo Implement testGet_cookie(). + */ + public function testGet_cookie() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testSet_cookie(). + */ + public function testSet_cookie() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testIsset_cookie(). + */ + public function testIsset_cookie() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testOpen_phrasea_session(). + */ + public function testOpen_phrasea_session() + { + try + { + $this->object->open_phrasea_session(); + $this->fail(); + } + catch (Exception_Session_Closed $e) + { + + } + + $user = self::$user; + $auth = new Session_Authentication_None($user); + $this->object->authenticate($auth); + + $this->object->open_phrasea_session(); + + $this->object->logout(); + + try + { + $this->object->open_phrasea_session(); + $this->fail(); + } + catch (Exception_Session_Closed $e) + { + + } + } + + /** + * @todo Implement testRestore(). + */ + public function testRestore() + { + $user = self::$user; + $auth = new Session_Authentication_None($user); + $this->object->authenticate($auth); + + $ses_id = $this->object->get_ses_id(); + $this->object->storage()->reset(); + + $this->assertFalse($this->object->is_authenticated()); + + $this->object->restore($user, $ses_id); + + $this->assertTrue($this->object->is_authenticated()); + + $databoxes = $user->ACL()->get_granted_sbas(); + foreach ($databoxes as $databox) + { + $this->assertInstanceOf('Session_Logger', $this->object->get_logger($databox)); + } + + } + + /** + * @todo Implement testAuthenticate(). + */ + public function testAuthenticate() + { + $user = self::$user; + $auth = new Session_Authentication_None($user); + $this->object->authenticate($auth); + + $registry = registry::get_instance(); + + + foreach($user->ACL()->get_granted_sbas() as $databox) + { + $sql = 'SELECT usr_id FROM collusr WHERE site = :site AND usr_id = :usr_id AND coll_id = :coll_id'; + $stmt = $databox->get_connection()->prepare($sql); + + foreach($user->ACL()->get_granted_base(array(), array($databox->get_sbas_id())) as $collection) + { + $stmt->execute(array(':site'=>$registry->get('GV_sit'),':usr_id'=>$user->get_id(), ':coll_id'=>$collection->get_coll_id())); + $this->assertEquals(1, $stmt->rowCount()); + } + + $stmt->closeCursor(); + } + + $this->object->logout(); + + } + + /** + * @todo Implement testAdd_persistent_cookie(). + */ + public function testAdd_persistent_cookie() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_logger(). + */ + public function testGet_logger() + { + $user = self::$user; + $auth = new Session_Authentication_None($user); + $databoxes = $user->ACL()->get_granted_sbas(); + + $this->object->authenticate($auth); + $this->assertTrue($this->object->is_authenticated()); + + foreach ($databoxes as $databox) + { + $this->assertInstanceOf('Session_Logger', $this->object->get_logger($databox)); + } + $this->assertTrue(is_int($this->object->get_logger($databox)->get_id())); + } + + /** + * @todo Implement testGet_my_sessions(). + */ + public function testGet_my_sessions() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testSet_event_module(). + */ + public function testSet_event_module() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_active_sessions(). + */ + public function testGet_active_sessions() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/Session/Session_LoggerTest.php b/tests/Session/Session_LoggerTest.php new file mode 100644 index 0000000000..3b3c6e736f --- /dev/null +++ b/tests/Session/Session_LoggerTest.php @@ -0,0 +1,110 @@ +session = $appbox->get_session(); + $user = self::$user; + $auth = new Session_Authentication_None($user); + + + + $this->session->authenticate($auth); + + foreach ($user->ACL()->get_granted_sbas() as $databox) + { + $this->object = $this->session->get_logger($databox); + $this->databox = $databox; + break; + } + if (!$this->object instanceof Session_Logger) + $this->fail('Not enough datas to test'); + } + + public function testGet_id() + { + $this->feed_datas(); + $log_id = $this->object->get_id(); + $this->assertTrue(is_int($log_id)); + + $registry = registry::get_instance(); + + $sql = 'SELECT id FROM log + WHERE sit_session = :ses_id AND usrid = :usr_id AND site = :site'; + $params = array( + ':ses_id' => $this->session->get_ses_id() + , ':usr_id' => $this->session->get_usr_id() + , ':site' => $registry->get('GV_sit') + ); + + $stmt = $this->databox->get_connection()->prepare($sql); + $stmt->execute($params); + $this->assertEquals(1, $stmt->rowCount()); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + $this->assertEquals($this->object->get_id(), $row['id']); + $log_id = $this->object->get_id(); + $ses_id = $this->session->get_ses_id(); + $usr_id = $this->session->get_usr_id(); + + $this->session->logout(); + + $sql = 'SELECT id FROM log + WHERE sit_session = :ses_id AND usrid = :usr_id AND site = :site'; + $params = array( + ':ses_id' => $ses_id + , ':usr_id' => $usr_id + , ':site' => $registry->get('GV_sit') + ); + + $stmt = $this->databox->get_connection()->prepare($sql); + $stmt->execute($params); + $this->assertEquals(1, $stmt->rowCount()); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + $this->assertEquals($log_id, $row['id']); + + } + +} + diff --git a/tests/Session/Session_PhraseaTest.php b/tests/Session/Session_PhraseaTest.php new file mode 100644 index 0000000000..8bb6cfae85 --- /dev/null +++ b/tests/Session/Session_PhraseaTest.php @@ -0,0 +1,115 @@ +object = new Session_Phrasea; + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + public function tearDown() + { + + } + + /** + * @todo Implement testGet_id(). + */ + public function testGet_id() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testCreate(). + */ + public function testCreate() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + + /** + * @todo Implement testOpen(). + */ + public function testOpen() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testRestore(). + */ + public function testRestore() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testClose(). + */ + public function testClose() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_actives_by_usr_id(). + */ + public function testGet_actives_by_usr_id() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_actives(). + */ + public function testGet_actives() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/Session/Storage/Session_Storage_CommandLineTest.php b/tests/Session/Storage/Session_Storage_CommandLineTest.php new file mode 100644 index 0000000000..321c09ba59 --- /dev/null +++ b/tests/Session/Storage/Session_Storage_CommandLineTest.php @@ -0,0 +1,125 @@ +object = new Session_Storage_CommandLine; + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + public function tearDown() + { + + } + + /** + * @todo Implement testGetInstance(). + */ + public function testGetInstance() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet(). + */ + public function testGet() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testHas(). + */ + public function testHas() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testSet(). + */ + public function testSet() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testRemove(). + */ + public function testRemove() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGetName(). + */ + public function testGetName() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGetId(). + */ + public function testGetId() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testDestroy(). + */ + public function testDestroy() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/Session/Storage/Session_Storage_PHPSessionTest.php b/tests/Session/Storage/Session_Storage_PHPSessionTest.php new file mode 100644 index 0000000000..d3e3bdb421 --- /dev/null +++ b/tests/Session/Storage/Session_Storage_PHPSessionTest.php @@ -0,0 +1,136 @@ +object = new Session_Storage_PHPSession; + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + public function tearDown() + { + + } + + /** + * @todo Implement testGetInstance(). + */ + public function testGetInstance() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testClose(). + */ + public function testClose() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testHas(). + */ + public function testHas() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet(). + */ + public function testGet() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testSet(). + */ + public function testSet() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testRemove(). + */ + public function testRemove() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGetName(). + */ + public function testGetName() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGetId(). + */ + public function testGetId() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testDestroy(). + */ + public function testDestroy() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/Setup/Setup_ConstraintTest.php b/tests/Setup/Setup_ConstraintTest.php new file mode 100644 index 0000000000..c7c6decd17 --- /dev/null +++ b/tests/Setup/Setup_ConstraintTest.php @@ -0,0 +1,48 @@ +object_non_blocker = new Setup_Constraint($this->name, true, $this->message, false); + $this->object_blocker = new Setup_Constraint($this->name, false, $this->message, true); + } + + public function testGet_name() + { + $this->assertEquals($this->name, $this->object_blocker->get_name()); + $this->assertEquals($this->name, $this->object_non_blocker->get_name()); + } + + public function testIs_ok() + { + $this->assertFalse($this->object_blocker->is_ok()); + $this->assertTrue($this->object_non_blocker->is_ok()); + } + + public function testIs_blocker() + { + $this->assertTrue($this->object_blocker->is_blocker()); + $this->assertFalse($this->object_non_blocker->is_blocker()); + } + + public function testGet_message() + { + $this->assertEquals($this->message, $this->object_blocker->get_message()); + $this->assertEquals($this->message, $this->object_non_blocker->get_message()); + } + +} + diff --git a/tests/Setup/Setup_UpgradeTest.php b/tests/Setup/Setup_UpgradeTest.php new file mode 100644 index 0000000000..ac9cd1e29b --- /dev/null +++ b/tests/Setup/Setup_UpgradeTest.php @@ -0,0 +1,97 @@ +object = new Setup_Upgrade($appbox); + } + + public function tearDown() + { + unset($this->object); + } + + public function test__destruct() + { + $this->assertFileExists(Setup_Upgrade::get_lock_file()); + unset($this->object); + $this->assertFileNotExists(Setup_Upgrade::get_lock_file()); + } + + public function testAdd_steps() + { + $this->check_percentage(1, 0, 0); + $this->object->add_steps(1); + $this->check_percentage(0, 1, 0); + $this->object->add_steps('lsdf'); + $this->check_percentage(0, 1, 0); + $this->object->add_steps(20); + $this->check_percentage(0, 21, 0); + $this->object->add_steps(-5); + $this->check_percentage(0, 16, 0); + } + + protected function check_percentage($percent, $total, $complete) + { + $datas = $this->object->get_status(); + $this->assertArrayHasKey('completed_steps', $datas); + $this->assertArrayHasKey('total_steps', $datas); + $this->assertArrayHasKey('percentage', $datas); + $this->assertArrayHasKey('last_update', $datas); + $this->assertDateAtom($datas['last_update']); + $this->assertEquals($percent, $datas['percentage']); + $this->assertEquals($total, $datas['total_steps']); + $this->assertEquals($complete, $datas['completed_steps']); + } + + public function testAdd_steps_complete() + { + $this->check_percentage(1, 0, 0); + $this->object->add_steps(1)->add_steps_complete(1); + $this->check_percentage(1, 1, 1); + $this->object->add_steps(20)->add_steps_complete(20); + $this->check_percentage(1, 21, 21); + $this->object->add_steps(20); + $this->check_percentage(round(21 / 41, 2), 41, 21); + $this->object->add_steps_complete(40); + $this->check_percentage(1, 41, 61); + } + + public function testSet_current_message() + { + $message = 'ZOubid èèè\\'; + $this->object->set_current_message($message); + + $datas = $this->object->get_status(); + $this->assertArrayHasKey('message', $datas); + $this->assertEquals($message, $datas['message']); + } + + public function testGet_status() + { + $datas = $this->object->get_status(); + $this->assertTrue(is_array($datas)); + $this->assertArrayHasKey('active', $datas); + $this->assertArrayHasKey('percentage', $datas); + $this->assertArrayHasKey('total_steps', $datas); + $this->assertArrayHasKey('completed_steps', $datas); + $this->assertArrayHasKey('message', $datas); + $this->assertArrayHasKey('last_update', $datas); + $this->assertDateAtom($datas['last_update']); + } + +} diff --git a/tests/api/oauthv2/API_OAuth2_AuthCodeTest.php b/tests/api/oauthv2/API_OAuth2_AuthCodeTest.php new file mode 100644 index 0000000000..dde8e57327 --- /dev/null +++ b/tests/api/oauthv2/API_OAuth2_AuthCodeTest.php @@ -0,0 +1,80 @@ +application = API_OAuth2_Application::create($appbox, self::$user, 'test app'); + $this->account = API_OAuth2_Account::load_with_user($appbox, $this->application, self::$user); + + $expires = time() + 100; + $this->code = random::generatePassword(8); + + $this->object = API_OAuth2_AuthCode::create($appbox, $this->account, $this->code, $expires); + } + + public function tearDown() + { + $this->application->delete(); + } + + public function testGet_code() + { + $this->assertEquals($this->code, $this->object->get_code()); + } + + public function testGet_account() + { + $this->assertInstanceOf('API_OAuth2_Account', $this->object->get_account()); + } + + public function testGet_redirect_uri() + { + $this->assertEquals('', $this->object->get_redirect_uri()); + } + + public function testSet_redirect_uri() + { + $redirect_uri = 'https://www.google.com'; + $this->assertEquals('', $this->object->get_redirect_uri()); + $this->object->set_redirect_uri($redirect_uri); + $this->assertEquals($redirect_uri, $this->object->get_redirect_uri()); + } + + public function testGet_expires() + { + $this->assertInternalType('string', $this->object->get_expires()); + } + + public function testGet_scope() + { + $this->assertEquals('', $this->object->get_scope()); + } + + public function testSet_scope() + { + $scope = 'prout'; + $this->assertEquals('', $this->object->get_scope()); + $this->object->set_scope($scope); + $this->assertEquals($scope, $this->object->get_scope()); + } + + public function testLoad_codes_by_account() + { + $this->assertTrue(is_array(API_OAuth2_AuthCode::load_codes_by_account(appbox::get_instance(\bootstrap::getCore()), $this->account))); + $this->assertTrue(count(API_OAuth2_AuthCode::load_codes_by_account(appbox::get_instance(\bootstrap::getCore()), $this->account)) > 0); + } + +} diff --git a/tests/api/oauthv2/API_OAuth2_RefreshTokenTest.php b/tests/api/oauthv2/API_OAuth2_RefreshTokenTest.php new file mode 100644 index 0000000000..2c604dec03 --- /dev/null +++ b/tests/api/oauthv2/API_OAuth2_RefreshTokenTest.php @@ -0,0 +1,108 @@ +application = API_OAuth2_Application::create($appbox, self::$user, 'test app'); + $this->account = API_OAuth2_Account::load_with_user($appbox, $this->application, self::$user); + + $expires = time() + 100; + $this->token = random::generatePassword(8); + $this->scope = 'scopidou'; + + $this->object = API_OAuth2_RefreshToken::create($appbox, $this->account, $expires, $this->token, $this->scope); + } + + public function tearDown() + { + $this->application->delete(); + } + + public function testGet_value() + { + $this->assertEquals($this->token, $this->object->get_value()); + } + + /** + * @todo Implement testGet_account(). + */ + public function testGet_account() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_expires(). + */ + public function testGet_expires() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_scope(). + */ + public function testGet_scope() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testDelete(). + */ + public function testDelete() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testLoad_by_account(). + */ + public function testLoad_by_account() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testCreate(). + */ + public function testCreate() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/api/oauthv2/api_oauthv2_AccountTest.php b/tests/api/oauthv2/api_oauthv2_AccountTest.php new file mode 100644 index 0000000000..8d8f194a8a --- /dev/null +++ b/tests/api/oauthv2/api_oauthv2_AccountTest.php @@ -0,0 +1,84 @@ +application = API_OAuth2_Application::create($appbox, self::$user, 'test app'); + $this->object = API_OAuth2_Account::load_with_user($appbox, $this->application, self::$user); + } + + public function tearDown() + { + $this->application->delete(); + } + + public function testGet_id() + { + $this->assertTrue(is_int($this->object->get_id())); + } + + public function testGet_user() + { + $this->assertInstanceOf('User_Adapter', $this->object->get_user()); + $this->assertEquals(self::$user, $this->object->get_user()); + } + + public function testGet_api_version() + { + $this->assertEquals('1.0', $this->object->get_api_version()); + } + + public function testIs_revoked() + { + $this->assertTrue(is_bool($this->object->is_revoked())); + $this->assertFalse($this->object->is_revoked()); + } + + public function testSet_revoked() + { + $this->object->set_revoked(true); + $this->assertTrue($this->object->is_revoked()); + $this->object->set_revoked(false); + $this->assertFalse($this->object->is_revoked()); + } + + public function testGet_created_on() + { + $this->assertInstanceOf('DateTime', $this->object->get_created_on()); + } + + public function testGet_token() + { + $this->assertInstanceOf('API_OAuth2_Token', $this->object->get_token()); + } + + public function testGet_application() + { + $this->assertInstanceOf('API_OAuth2_Application', $this->object->get_application()); + $this->assertEquals($this->application, $this->object->get_application()); + } + + public function testLoad_with_user() + { + $loaded = API_OAuth2_Account::load_with_user(appbox::get_instance(\bootstrap::getCore()), $this->application, self::$user); + $this->assertInstanceOf('API_OAuth2_Account', $loaded); + $this->assertEquals($this->object, $loaded); + } + +} + diff --git a/tests/api/oauthv2/api_oauthv2_ApplicationTest.php b/tests/api/oauthv2/api_oauthv2_ApplicationTest.php new file mode 100644 index 0000000000..f90a522627 --- /dev/null +++ b/tests/api/oauthv2/api_oauthv2_ApplicationTest.php @@ -0,0 +1,202 @@ +object = API_OAuth2_Application::create($appbox, self::$user, 'test app'); + } + + public function tearDown() + { + $this->object->delete(); + } + + public function testLoad_from_client_id() + { + $client_id = $this->object->get_client_id(); + $loaded = API_OAuth2_Application::load_from_client_id(appbox::get_instance(\bootstrap::getCore()), $client_id); + $this->assertInstanceOf('API_OAuth2_Application', $loaded); + $this->assertEquals($this->object, $loaded); + } + + public function testLoad_dev_app_by_user() + { + $apps = API_OAuth2_Application::load_dev_app_by_user(appbox::get_instance(\bootstrap::getCore()), self::$user); + $this->assertTrue(is_array($apps)); + $this->assertTrue(count($apps) > 0); + $found = false; + foreach ($apps as $app) + { + if ($app->get_id() === $this->object->get_id()) + $found = true; + $this->assertInstanceOf('API_OAuth2_Application', $app); + } + + if (!$found) + $this->fail(); + } + + public function testLoad_app_by_user() + { + $apps = API_OAuth2_Application::load_app_by_user(appbox::get_instance(\bootstrap::getCore()), self::$user); + $this->assertTrue(is_array($apps)); + $this->assertTrue(count($apps) > 0); + $found = false; + + foreach ($apps as $app) + { + if ($app->get_id() === $this->object->get_id()) + $found = true; + $this->assertInstanceOf('API_OAuth2_Application', $app); + } + + if (!$found) + $this->fail(); + } + + public function testGet_id() + { + $this->assertTrue(is_int($this->object->get_id())); + } + + public function testGet_creator() + { + $this->assertInstanceOf('User_Adapter', $this->object->get_creator()); + } + + public function testGet_type() + { + $this->assertTrue(in_array($this->object->get_type(), array(API_OAuth2_Application::DESKTOP_TYPE, API_OAuth2_Application::WEB_TYPE))); + } + + public function testGet_nonce() + { + $this->assertTrue(is_string($this->object->get_nonce())); + $this->assertTrue(strlen($this->object->get_nonce()) === 6); + } + + public function testSet_type() + { + try + { + $this->object->set_type('prout'); + $this->fail(); + } + catch (Exception_InvalidArgument $e) + { + + } + + $this->object->set_type(API_OAuth2_Application::WEB_TYPE); + $this->assertEquals(API_OAuth2_Application::WEB_TYPE, $this->object->get_type()); + $this->object->set_type(API_OAuth2_Application::DESKTOP_TYPE); + $this->assertEquals(API_OAuth2_Application::DESKTOP_TYPE, $this->object->get_type()); + $this->assertEquals(API_OAuth2_Application::NATIVE_APP_REDIRECT_URI, $this->object->get_redirect_uri()); + } + + public function testGet_name() + { + $this->assertEquals('test app', $this->object->get_name()); + } + + public function testSet_name() + { + $this->object->set_name('prout'); + $this->assertEquals('prout', $this->object->get_name()); + } + + public function testGet_description() + { + $this->assertEquals('', $this->object->get_description()); + } + + public function testSet_description() + { + $desc = 'prouti prouto prout prout'; + $this->object->set_description($desc); + $this->assertEquals($desc, $this->object->get_description()); + } + + public function testGet_website() + { + $this->assertEquals('', $this->object->get_website()); + } + + public function testSet_website() + { + $site = 'http://www.example.com/'; + $this->object->set_website($site); + $this->assertEquals($site, $this->object->get_website()); + } + + public function testGet_created_on() + { + $this->assertInstanceOf('DateTime', $this->object->get_created_on()); + } + + public function testGet_last_modified() + { + $this->assertInstanceOf('DateTime', $this->object->get_last_modified()); + } + + protected function assertmd5($md5) + { + $this->assertTrue((count(preg_match('/[a-z0-9]{32}/', $md5)) === 1)); + } + + public function testGet_client_id() + { + $this->assertMd5($this->object->get_client_id()); + } + + public function testSet_client_id() + { + $client_id = md5('prouto'); + $this->object->set_client_id($client_id); + $this->assertEquals($client_id, $this->object->get_client_id()); + $this->assertMd5($this->object->get_client_id()); + } + + public function testGet_client_secret() + { + $this->assertMd5($this->object->get_client_secret()); + } + + public function testSet_client_secret() + { + $client_secret = md5('prouto'); + $this->object->set_client_secret($client_secret); + $this->assertEquals($client_secret, $this->object->get_client_secret()); + $this->assertMd5($this->object->get_client_secret()); + } + + public function testGet_redirect_uri() + { + $this->assertEquals('', $this->object->get_redirect_uri()); + } + + public function testSet_redirect_uri() + { + $uri = 'http://www.example.com/callback/'; + $this->object->set_redirect_uri($uri); + $this->assertEquals($uri, $this->object->get_redirect_uri()); + } + + public function testGet_user_account() + { + $this->assertInstanceOf('API_OAuth2_Account', $this->object->get_user_account(self::$user)); + } + + +} + diff --git a/tests/api/oauthv2/api_oauthv2_TokenTest.php b/tests/api/oauthv2/api_oauthv2_TokenTest.php new file mode 100644 index 0000000000..24e6691306 --- /dev/null +++ b/tests/api/oauthv2/api_oauthv2_TokenTest.php @@ -0,0 +1,127 @@ +application = API_OAuth2_Application::create($appbox, self::$user, 'test app'); + $account = API_OAuth2_Account::load_with_user($appbox, $this->application, self::$user); + + try + { + new API_OAuth2_Token($appbox, $account); + $this->fail(); + } + catch (Exception $e) + { + + } + + $this->object = API_OAuth2_Token::create($appbox, $account); + } + + public function tearDown() + { + $this->application->delete(); + } + + protected function assertmd5($md5) + { + $this->assertTrue((count(preg_match('/[a-z0-9]{32}/', $md5)) === 1)); + } + + public function testGet_value() + { + $this->assertmd5($this->object->get_value()); + } + + public function testSet_value() + { + $value = md5('prout'); + $this->object->set_value($value); + $this->assertEquals($value, $this->object->get_value()); + } + + public function testGet_session_id() + { + $this->assertNull($this->object->get_session_id()); + } + + public function testSet_session_id() + { + $this->object->set_session_id(458); + $this->assertEquals(458, $this->object->get_session_id()); + } + + public function testGet_expires() + { + $this->assertInternalType('string', $this->object->get_expires()); + } + + public function testSet_expires() + { + $date = time() + 7200; + $this->object->set_expires($date); + $this->assertEquals($date, $this->object->get_expires()); + } + + public function testGet_scope() + { + $this->assertNull($this->object->get_scope()); + } + + public function testset_scope() + { + $this->assertNull($this->object->get_scope()); + $scope = "prout"; + $this->object->set_scope($scope); + $this->assertEquals($scope, $this->object->get_scope()); + } + + public function testGet_account() + { + $this->assertInstanceOf('API_OAuth2_Account', $this->object->get_account()); + } + + public function testRenew() + { + $first = $this->object->get_value(); + $this->assertMd5($first); + $this->object->renew(); + $second = $this->object->get_value(); + $this->assertMd5($second); + $this->assertNotEquals($second, $first); + } + + public function testLoad_by_oauth_token() + { + $token = $this->object->get_value(); + $loaded = API_OAuth2_Token::load_by_oauth_token(appbox::get_instance(\bootstrap::getCore()), $token); + $this->assertInstanceOf('API_OAuth2_Token', $loaded); + $this->assertEquals($this->object, $loaded); + } + + public function testGenerate_token() + { + for ($i = 0; $i < 100; $i++) + { + $this->assertMd5(API_OAuth2_Token::generate_token()); + } + } + +} + diff --git a/tests/api/v1/api_v1_adapterTest.php b/tests/api/v1/api_v1_adapterTest.php new file mode 100644 index 0000000000..9422dabf74 --- /dev/null +++ b/tests/api/v1/api_v1_adapterTest.php @@ -0,0 +1,563 @@ +object = new API_V1_adapter(FALSE, $appbox, self::$core); + } + + public function testGet_error_code() + { + $request = new Request(array(), array(), array(), array(), array(), array('HTTP_Accept' => 'application/json')); + $result = $this->object->get_error_code($request, 400); + $this->assertTrue(is_object(json_decode($result->format()))); + $this->assertEquals(400, $result->get_http_code()); + $this->assertEquals('application/json', $result->get_content_type()); + + $result = $this->object->get_error_code($request, 403); + $this->assertTrue(is_object(json_decode($result->format()))); + $this->assertEquals(403, $result->get_http_code()); + $this->assertEquals('application/json', $result->get_content_type()); + + $result = $this->object->get_error_code($request, 500); + $this->assertTrue(is_object(json_decode($result->format()))); + $this->assertEquals(500, $result->get_http_code()); + $this->assertEquals('application/json', $result->get_content_type()); + + $result = $this->object->get_error_code($request, 405); + $this->assertTrue(is_object(json_decode($result->format()))); + $this->assertEquals(405, $result->get_http_code()); + $this->assertEquals('application/json', $result->get_content_type()); + + $result = $this->object->get_error_code($request, 404); + $this->assertTrue(is_object(json_decode($result->format()))); + $this->assertEquals(404, $result->get_http_code()); + $this->assertEquals('application/json', $result->get_content_type()); + + $result = $this->object->get_error_code($request, 401); + $this->assertTrue(is_object(json_decode($result->format()))); + $this->assertEquals(401, $result->get_http_code()); + $this->assertEquals('application/json', $result->get_content_type()); + } + + public function testGet_error_message() + { + $request = new Request(array(), array(), array(), array(), array(), array('HTTP_Accept' => 'application/json')); + $result = $this->object->get_error_message($request, API_V1_result::ERROR_BAD_REQUEST); + $this->assertTrue(is_object(json_decode($result->format()))); + $this->assertEquals(400, $result->get_http_code()); + $this->assertEquals('application/json', $result->get_content_type()); + + $result = $this->object->get_error_message($request, API_V1_result::ERROR_FORBIDDEN); + $this->assertTrue(is_object(json_decode($result->format()))); + $this->assertEquals(403, $result->get_http_code()); + $this->assertEquals('application/json', $result->get_content_type()); + + $result = $this->object->get_error_message($request, API_V1_result::ERROR_INTERNALSERVERERROR); + $this->assertTrue(is_object(json_decode($result->format()))); + $this->assertEquals(500, $result->get_http_code()); + $this->assertEquals('application/json', $result->get_content_type()); + + $result = $this->object->get_error_message($request, API_V1_result::ERROR_METHODNOTALLOWED); + $this->assertTrue(is_object(json_decode($result->format()))); + $this->assertEquals(405, $result->get_http_code()); + $this->assertEquals('application/json', $result->get_content_type()); + + $result = $this->object->get_error_message($request, API_V1_result::ERROR_NOTFOUND); + $this->assertTrue(is_object(json_decode($result->format()))); + $this->assertEquals(404, $result->get_http_code()); + $this->assertEquals('application/json', $result->get_content_type()); + + $result = $this->object->get_error_message($request, API_V1_result::ERROR_UNAUTHORIZED); + $this->assertTrue(is_object(json_decode($result->format()))); + $this->assertEquals(401, $result->get_http_code()); + $this->assertEquals('application/json', $result->get_content_type()); + } + + public function testGet_version() + { + $this->assertEquals('1.1', $this->object->get_version()); + } + + public function testGet_databoxes() + { + $request = new Request(array(), array(), array(), array(), array(), array('HTTP_Accept' => 'application/json')); + $result = $this->object->get_databoxes($request); + $this->assertEquals(200, $result->get_http_code()); + $this->assertEquals('application/json', $result->get_content_type()); + $this->assertTrue(is_object(json_decode($result->format()))); + } + + public function testGet_databox_collections() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $request = new Request(); + foreach ($appbox->get_databoxes() as $databox) + { + $result = $this->object->get_databox_collections($request, $databox->get_sbas_id()); + $this->assertEquals(200, $result->get_http_code()); + $this->assertEquals('application/json', $result->get_content_type()); + $this->assertTrue(is_object(json_decode($result->format()))); + } + } + + public function testGet_record() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $request = new Request(array(), array(), array(), array(), array(), array('HTTP_Accept' => 'application/json')); + $result = $this->object->get_record($request, self::$record_1->get_sbas_id(), "-40"); + $this->assertEquals(400, $result->get_http_code()); + + $request = new Request(array(), array(), array(), array(), array(), array('HTTP_Accept' => 'application/json')); + $result = $this->object->get_record($request, self::$record_1->get_sbas_id(), self::$record_1->get_record_id()); + $this->assertEquals(200, $result->get_http_code()); + $this->assertEquals('application/json', $result->get_content_type()); + $this->assertTrue(is_object(json_decode($result->format()))); + } + + public function testGet_databox_status() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $request = new Request(array(), array(), array(), array(), array(), array('HTTP_Accept' => 'application/json')); + foreach ($appbox->get_databoxes() as $databox) + { + $result = $this->object->get_databox_status($request, $databox->get_sbas_id()); + $this->assertEquals(200, $result->get_http_code()); + $this->assertEquals('application/json', $result->get_content_type()); + $this->assertTrue(is_object(json_decode($result->format()))); + } + } + + public function testGet_databox_metadatas() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $request = new Request(array(), array(), array(), array(), array(), array('HTTP_Accept' => 'application/json')); + foreach ($appbox->get_databoxes() as $databox) + { + $result = $this->object->get_databox_metadatas($request, $databox->get_sbas_id()); + $this->assertEquals(200, $result->get_http_code()); + $this->assertEquals('application/json', $result->get_content_type()); + $this->assertTrue(is_object(json_decode($result->format()))); + } + } + + public function testGet_databox_terms() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $request = new Request(array(), array(), array(), array(), array(), array('HTTP_Accept' => 'application/json')); + foreach ($appbox->get_databoxes() as $databox) + { + $result = $this->object->get_databox_terms($request, $databox->get_sbas_id()); + $this->assertEquals(200, $result->get_http_code()); + $this->assertEquals('application/json', $result->get_content_type()); + $this->assertTrue(is_object(json_decode($result->format()))); + } + } + + public function testSearch_records() + { + $request = new Request(array('record_type' => "image"), array(), array(), array(), array(), array('HTTP_Accept' => 'application/json')); + $result = $this->object->search_records($request); + $this->assertEquals(200, $result->get_http_code()); + $this->assertEquals('application/json', $result->get_content_type()); + $this->assertTrue(is_object(json_decode($result->format()))); + } + + public function testGet_record_related() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $request = new Request(array(), array(), array(), array(), array(), array('HTTP_Accept' => 'application/json')); + $result = $this->object->get_record_related($request, self::$record_1->get_sbas_id(), self::$record_1->get_record_id()); + $this->assertEquals(200, $result->get_http_code()); + $this->assertEquals('application/json', $result->get_content_type()); + $this->assertTrue(is_object(json_decode($result->format()))); + } + + public function testGet_record_metadatas() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $request = new Request(array(), array(), array(), array(), array(), array('HTTP_Accept' => 'application/json')); + $result = $this->object->get_record_metadatas($request, self::$record_1->get_sbas_id(), self::$record_1->get_record_id()); + $this->assertEquals(200, $result->get_http_code()); + $this->assertEquals('application/json', $result->get_content_type()); + $this->assertTrue(is_object(json_decode($result->format()))); + } + + public function testGet_record_status() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $request = new Request(); + $request = new Request(array(), array(), array(), array(), array(), array('HTTP_Accept' => 'application/json')); + $result = $this->object->get_record_status($request, self::$record_1->get_sbas_id(), self::$record_1->get_record_id()); + $this->assertEquals(200, $result->get_http_code()); + $this->assertEquals('application/json', $result->get_content_type()); + $this->assertTrue(is_object(json_decode($result->format()))); + } + + public function testGet_record_embed() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $request = new Request(array(), array(), array(), array(), array(), array('HTTP_Accept' => 'application/json')); + $result = $this->object->get_record_embed($request, self::$record_1->get_sbas_id(), self::$record_1->get_record_id()); + $this->assertEquals(200, $result->get_http_code()); + $this->assertEquals('application/json', $result->get_content_type()); + $this->assertTrue(is_object(json_decode($result->format()))); + } + + public function testSet_record_metadatas() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $databox = self::$record_1->get_databox(); + $request = new Request(array("salut" => "salut c'est la fete"), array(), array(), array(), array(), array('HTTP_Accept' => 'application/json')); + $result = $this->object->set_record_metadatas($request, self::$record_1->get_sbas_id(), self::$record_1->get_record_id()); + $this->assertEquals(400, $result->get_http_code()); + + $request = new Request(array("metadatas" => "salut c'est la fete"), array(), array(), array(), array(), array('HTTP_Accept' => 'application/json')); + $this->object->set_record_metadatas($request, self::$record_1->get_sbas_id(), self::$record_1->get_record_id()); + $this->assertEquals(400, $result->get_http_code()); + + if (sizeof(self::$record_1->get_caption()->get_fields()) == 0) + { + $caption_field_value = caption_Field_Value::create(databox_field::get_instance($databox, 1), self::$record_1, 'my value'); + } + +//valide metas + $metadatas = array(); + + foreach (self::$record_1->get_databox()->get_meta_structure()->get_elements() as $field) + { + try + { + $values = self::$record_1->get_caption()->get_field($field->get_name())->get_values(); + $value = array_pop($values); + $meta_id = $value->getId(); + } + catch (\Exception $e) + { + $meta_id = null; + } + + $metadatas[] = array( + 'meta_id' => $meta_id + , 'meta_struct_id' => $field->get_id() + , 'value' => 'poOM POOM TCHOK ' . $field->get_id() + ); + } + + $request = new Request(array("metadatas" => $metadatas), array(), array(), array(), array(), array('HTTP_Accept' => 'application/json')); + + $result = $this->object->set_record_metadatas($request, self::$record_1->get_sbas_id(), self::$record_1->get_record_id()); + + $response = json_decode($result->format()); + + $this->assertEquals($response->meta->http_code, 200); + + $this->checkResponseField($result, "metadatas", PHPUnit_Framework_Constraint_IsType::TYPE_OBJECT); + } + + /** + * @todo Implement testSet_record_status(). + */ + public function testSet_record_status() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $stub = $this->getMock("API_V1_adapter", array("list_record_status"), array(false, &$appbox, bootstrap::getCore())); + $appbox = appbox::get_instance(\bootstrap::getCore()); + $databox = self::$record_1->get_databox(); + + $statusbit = null; + foreach ($databox->get_statusbits() as $key => $value) + { + $statusbit = $key; + break; + } + + $request = new Request(array("salut" => "salut c'est la fete"), array(), array(), array(), array(), array('HTTP_Accept' => 'application/json')); + $result = $stub->set_record_status($request, self::$record_1->get_sbas_id(), self::$record_1->get_record_id()); + $this->assertEquals(400, $result->get_http_code()); + + $request = new Request(array("status" => "salut c'est la fete"), array(), array(), array(), array(), array('HTTP_Accept' => 'application/json')); + $stub->set_record_status($request, self::$record_1->get_sbas_id(), self::$record_1->get_record_id()); + $this->assertEquals(400, $result->get_http_code()); + + $status = array($statusbit => '1'); + + $request = new Request(array("status" => $status), array(), array(), array(), array(), array('HTTP_Accept' => 'application/json')); + //check method use record->get_caption + $stub->expects($this->once()) + ->method("list_record_status") + ->will($this->returnValue(new stdClass())); + //check for metadas fiels in response + $result = $stub->set_record_status($request, self::$record_1->get_sbas_id(), self::$record_1->get_record_id()); + $this->checkResponseField($result, "status", PHPUnit_Framework_Constraint_IsType::TYPE_OBJECT); + } + + /** + * @todo Implement testSet_record_collection(). + */ + public function testSet_record_collection() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $stub = $this->getMock("API_V1_adapter", array("list_record"), array(false, &$appbox, bootstrap::getCore())); + $databox = self::$record_1->get_databox(); + + $request = new Request(array("salut" => "salut c'est la fete"), array(), array(), array(), array(), array('HTTP_Accept' => 'application/json')); + $result = $stub->set_record_collection($request, self::$record_1->get_sbas_id(), self::$record_1->get_record_id()); + $this->assertEquals(400, $result->get_http_code()); + + foreach ($appbox->get_databoxes() as $databox) + { + $collections = $databox->get_collections(); + break; + } + + $collection = array_shift($collections); + + $request = new Request(array("base_id" => $collection->get_base_id()), array(), array(), array(), array(), array('HTTP_Accept' => 'application/json')); + //check method use record->get_caption + $stub->expects($this->once()) + ->method("list_record") + ->will($this->returnValue(new stdClass())); + //check for metadas fiels in response + $result = $stub->set_record_collection($request, self::$record_1->get_sbas_id(), self::$record_1->get_record_id()); + $this->checkResponseField($result, "record", PHPUnit_Framework_Constraint_IsType::TYPE_OBJECT); + } + + /** + * @todo Implement testAdd_record_tobasket(). + */ + public function testAdd_record_tobasket() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testSearch_baskets() + { + $request = new Request(array(), array(), array(), array(), array(), array('HTTP_Accept' => 'application/json')); + $result = $this->object->search_baskets($request); + $this->assertEquals(200, $result->get_http_code()); + $this->assertEquals('application/json', $result->get_content_type()); + $this->assertTrue(is_object(json_decode($result->format()))); + } + + public function testCreate_basket() + { + $request = new Request(array(), array(), array('name' => 'BIG BASKET'), array(), array(), array('HTTP_Accept' => 'application/json')); + $result = $this->object->create_basket($request); + $this->assertEquals(200, $result->get_http_code()); + $this->assertEquals('application/json', $result->get_content_type()); + $this->assertTrue(is_object(json_decode($result->format()))); + + $n = 0; + foreach (json_decode($result->format())->response->basket as $ssel_id => $basket) + { + $n++; + } + + $this->assertEquals(1, $n); + $appbox = appbox::get_instance(\bootstrap::getCore()); + $session = $appbox->get_session(); + $usr_id = $session->get_usr_id(); + + $em = self::$core->getEntityManager(); + $repo = $em->getRepository('\Entities\Basket'); + + /* @var $repo \Repositories\BasketRepository */ + $basket = $repo->findUserBasket($ssel_id, self::$core->getAuthenticatedUser(), true); + + $this->assertTrue($basket instanceof \Entities\Basket); + $em->remove($basket); + $em->flush(); + } + + public function testDelete_basket() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $usr_id = $appbox->get_session()->get_usr_id(); + $user = User_Adapter::getInstance($usr_id, $appbox); + + $em = self::$core->getEntityManager(); + + $Basket = new Entities\Basket(); + $Basket->setName('Delete test'); + $Basket->setOwner($user); + + $em->persist($Basket); + $em->flush(); + + $ssel_id = $Basket->getId(); + + $request = new Request(array(), array(), array(), array(), array(), array('HTTP_Accept' => 'application/json')); + $result = $this->object->delete_basket($request, $ssel_id); + $this->assertEquals(200, $result->get_http_code()); + $this->assertEquals('application/json', $result->get_content_type()); + $this->assertTrue(is_object(json_decode($result->format()))); + + $repo = $em->getRepository('\Entities\Basket'); + + try + { + $repo->findUserBasket($ssel_id, $user, true); + $this->fail('An exception should have been raised'); + } + catch (Exception_NotFound $e) + { + + } + } + + public function testGet_basket() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $usr_id = $appbox->get_session()->get_usr_id(); + + $basket = $this->insertOneBasket(); + + $request = new Request(array(), array(), array(), array(), array(), array('HTTP_Accept' => 'application/json')); + $result = $this->object->get_basket($request, $basket->getId()); + $this->assertEquals(200, $result->get_http_code()); + $this->assertEquals('application/json', $result->get_content_type()); + $this->assertTrue(is_object(json_decode($result->format()))); + } + + public function testSet_basket_title() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $usr_id = $appbox->get_session()->get_usr_id(); + + $basket = $this->insertOneBasket(); + + $request = new Request(array(), array(), array('name' => 'PROUTO'), array(), array(), array('HTTP_Accept' => 'application/json')); + $result = $this->object->set_basket_title($request, $basket->getId()); + $this->assertEquals(200, $result->get_http_code()); + $this->assertEquals('application/json', $result->get_content_type()); + $this->assertTrue(is_object(json_decode($result->format()))); + + $repository = self::$core->getEntityManager()->getRepository('\Entities\Basket'); + + $ret_bask = $repository->find($basket->getId()); + + $this->assertEquals('PROUTO', $ret_bask->getName()); + } + + public function testSet_basket_description() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $usr_id = $appbox->get_session()->get_usr_id(); + + $basket = $this->insertOneBasket(); + + $request = new Request(array(), array(), array('description' => 'une belle description'), array(), array(), array('HTTP_Accept' => 'application/json')); + $result = $this->object->set_basket_description($request, $basket->getId()); + $this->assertEquals(200, $result->get_http_code()); + $this->assertEquals('application/json', $result->get_content_type()); + $this->assertTrue(is_object(json_decode($result->format()))); + + $repository = self::$core->getEntityManager()->getRepository('\Entities\Basket'); + + $ret_bask = $repository->find($basket->getId()); + + $this->assertEquals('une belle description', $ret_bask->getDescription()); + } + + public function testSearch_publications() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $stub = $this->getMock("API_V1_adapter", array("list_publication"), array(false, &$appbox, bootstrap::getCore())); + $request = new Request(array(), array(), array(), array(), array(), array('HTTP_Accept' => 'application/json')); + $feed = Feed_Adapter::create($appbox, self::$user, "hello", "salut"); + $result = $this->object->search_publications($request, self::$user); + $this->checkResponseField($result, "feeds", PHPUnit_Framework_Constraint_IsType::TYPE_OBJECT); + $feed->delete(); + } + + public function testRemove_publications() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testGet_publication() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $date = new DateTime(); + $request = new Request(array(), array(), array(), array(), array(), array('HTTP_Accept' => 'application/json')); + $feed = Feed_Adapter::create($appbox, self::$user, "hello", "salut"); + $feed_publisher = Feed_Publisher_Adapter::getPublisher($appbox, $feed, self::$user); + $feed_entry = Feed_Entry_Adapter::create($appbox, $feed, $feed_publisher, "coucou", "hello", "me", "my@email.com"); + $feed_entry_item = Feed_Entry_Item::create($appbox, $feed_entry, self::$record_1); + $coll = Feed_Collection::load_all($appbox, self::$user); + foreach ($coll->get_feeds() as $feed) + { + $result = $this->object->get_publication($request, $feed->get_id(), self::$user); + $this->checkResponseField($result, "feed", PHPUnit_Framework_Constraint_IsType::TYPE_OBJECT); + $this->checkResponseField($result, "entries", PHPUnit_Framework_Constraint_IsType::TYPE_OBJECT); + $this->checkResponseField($result, "offset_start", PHPUnit_Framework_Constraint_IsType::TYPE_INT); + $this->checkResponseField($result, "per_page", PHPUnit_Framework_Constraint_IsType::TYPE_INT); + } + $feed->delete(); + } + + public function testSearch_users() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testGet_user_acces() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testAdd_user() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + protected function checkResponseField(API_V1_result $result, $field, $type) + { + $response = json_decode($result->format()); + $this->assertObjectHasAttribute($field, $response->response); + $this->assertInternalType($type, $response->response->$field); + } + +} + diff --git a/tests/api/v1/api_v1_resultTest.php b/tests/api/v1/api_v1_resultTest.php new file mode 100644 index 0000000000..5cb6f0edd1 --- /dev/null +++ b/tests/api/v1/api_v1_resultTest.php @@ -0,0 +1,355 @@ +api = $this->getMock("API_V1_adapter", array("get_version"), array(), "", false); + $this->api->expects($this->any())->method("get_version")->will($this->returnValue("my_super_version1.0")); + } + + public function testFormat() + { + $server = array( + "HTTP_ACCEPT" => "application/json" + , 'REQUEST_METHOD' => 'GET' + , 'SCRIPT_FILENAME' => 'my/base/path/my/request/uri/filename' + , "REQUEST_URI" => "my/base/path/my/request/uri" + , 'PHP_SELF' => 'my/base/path' + ); + $request = new Request(array("callback" => ""), array(), array(), array(), array(), $server); + + $api_result = new API_V1_result($request, $this->api); + $return = $api_result->format(); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $return); + $response = json_decode($return); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_OBJECT, $response); + $this->assertObjectHasAttribute("meta", $response); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_OBJECT, $response->meta); + $this->assertObjectHasAttribute("response", $response); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_OBJECT, $response->response); + $this->assertEquals(0, sizeof(get_object_vars($response->response))); + $this->assertEquals(0, sizeof(get_class_methods($response->response))); + $this->checkResponseFieldMeta($response, "api_version", "my_super_version1.0", PHPUnit_Framework_Constraint_IsType::TYPE_STRING); + $this->checkResponseFieldMeta($response, "request", "GET my/base/path/my/request/uri", PHPUnit_Framework_Constraint_IsType::TYPE_STRING); +// $this->checkResponseFieldMeta($response, "response_time", $date->format(DATE_ATOM), PHPUnit_Framework_Constraint_IsType::TYPE_STRING); + + $this->assertDateAtom($response->meta->response_time); + $date = new DateTime(); + $now_U = $date->format('U'); + $date_resp = DateTime::createFromFormat(DATE_ATOM,$response->meta->response_time); + $resp_U = $date_resp->format('U'); + + $this->assertLessThan(3, abs($resp_U - $now_U), 'No more than 3sec between now and the query'); + + $this->checkResponseFieldMeta($response, "http_code", 200, PHPUnit_Framework_Constraint_IsType::TYPE_INT); + $this->checkResponseFieldMeta($response, "charset", "UTF-8", PHPUnit_Framework_Constraint_IsType::TYPE_STRING); + $this->assertObjectHasAttribute("error_message", $response->meta); + $this->assertNull($response->meta->error_message); + $this->assertObjectHasAttribute("error_details", $response->meta); + $this->assertNull($response->meta->error_details); + + $server = array( + "HTTP_ACCEPT" => "application/yaml" + , 'REQUEST_METHOD' => 'GET' + , 'SCRIPT_FILENAME' => 'my/base/path/my/request/uri/filename' + , "REQUEST_URI" => "my/base/path/my/request/uri" + , 'PHP_SELF' => 'my/base/path' + ); + $request = new Request(array("callback" => ""), array(), array(), array(), array(), $server); + + $api_result = new API_V1_result($request, $this->api); + $return = $api_result->format(); + $sfYaml = new Symfony\Component\Yaml\Parser(); + $response = $sfYaml->parse($return); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $response); + $this->assertArrayHasKey("meta", $response); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $response["meta"]); + $this->assertArrayHasKey("response", $response); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $response["response"]); + $this->assertEquals(0, count($response["response"])); + $this->assertArrayHasKey("api_version", $response["meta"]); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $response["meta"]["api_version"]); + $this->assertEquals("my_super_version1.0", $response["meta"]["api_version"]); + $this->assertArrayHasKey("request", $response["meta"]); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $response["meta"]["request"]); + $this->assertEquals("GET my/base/path/my/request/uri", $response["meta"]["request"]); + $this->assertArrayHasKey("response_time", $response["meta"]); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $response["meta"]["response_time"]); + + $this->assertDateAtom($response["meta"]["response_time"]); + $date_obj1 = DateTime::createFromFormat(DATE_ATOM, $response["meta"]["response_time"]); + $date_obj2 = new DateTime(); + $this->assertLessThan(3, abs($date_obj1->format('U') - $date_obj2->format('U')), 'No more than 3sec between now and the query'); + + + $this->assertArrayHasKey("http_code", $response["meta"]); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_INT, $response["meta"]["http_code"]); + $this->assertEquals(200, $response["meta"]["http_code"]); + $this->assertArrayHasKey("error_message", $response["meta"]); + $this->assertNull($response["meta"]["error_message"]); + $this->assertArrayHasKey("error_details", $response["meta"]); + $this->assertNull($response["meta"]["error_details"]); + $this->assertArrayHasKey("charset", $response["meta"]); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $response["meta"]["charset"]); + $this->assertEquals("UTF-8", $response["meta"]["charset"]); + + + $server = array( + "HTTP_ACCEPT" => "application/yaml" + , 'REQUEST_METHOD' => 'GET' + , 'SCRIPT_FILENAME' => 'my/base/path/my/request/uri/filename' + , "REQUEST_URI" => "my/base/path/my/request/uri" + , 'PHP_SELF' => 'my/base/path' + ); + $request = new Request(array("callback" => "my_callback_function"), array(), array(), array(), array(), $server); + + $api_result = new API_V1_result($request, $this->api); + $return = $api_result->format(); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $return); + $this->assertRegexp("/my_callback_function\(\{.+\}\)/", $return); + } + + /** + * @depends testFormat + */ + public function testSet_datas() + { + $server = array( + "HTTP_ACCEPT" => "application/json" + , 'REQUEST_METHOD' => 'GET' + , 'SCRIPT_FILENAME' => 'my/base/path/my/request/uri/filename' + , "REQUEST_URI" => "my/base/path/my/request/uri" + , 'PHP_SELF' => 'my/base/path' + ); + $request = new Request(array("callback" => ""), array(), array(), array(), array(), $server); + + $api_result = new API_V1_result($request, $this->api); + $api_result->set_datas(array("pirouette" => "cacahuete", "black" => true, "bob" => array("bob"))); + $response = json_decode($api_result->format()); + $this->checkResponseFieldResponse($response, "pirouette", "cacahuete", PHPUnit_Framework_Constraint_IsType::TYPE_STRING); + $this->checkResponseFieldResponse($response, "black", true, PHPUnit_Framework_Constraint_IsType::TYPE_BOOL); + $this->checkResponseFieldResponse($response, "bob", array("bob"), PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY); + } + + protected function checkResponseFieldMeta(stdClass $response, $field, $expected_value, $type) + { + $this->assertObjectHasAttribute($field, $response->meta); + $this->assertInternalType($type, $response->meta->$field); + $this->assertEquals($expected_value, $response->meta->$field); + } + + protected function checkResponseFieldResponse(stdClass $response, $field, $expected_value, $type) + { + $this->assertObjectHasAttribute($field, $response->response); + $this->assertInternalType($type, $response->response->$field); + $this->assertEquals($expected_value, $response->response->$field); + } + + public function testGet_content_type() + { + $server = array("HTTP_ACCEPT" => "application/json"); + $request = new Request(array("callback" => ""), array(), array(), array(), array(), $server); + $api_result = new API_V1_result($request, $this->api); + $this->assertEquals("application/json", $api_result->get_content_type()); + + $server = array("HTTP_ACCEPT" => "application/yaml"); + $request = new Request(array("callback" => ""), array(), array(), array(), array(), $server); + $api_result = new API_V1_result($request, $this->api); + $this->assertEquals('application/yaml', $api_result->get_content_type()); + + $server = array("HTTP_ACCEPT" => "text/yaml"); + $request = new Request(array("callback" => ""), array(), array(), array(), array(), $server); + $api_result = new API_V1_result($request, $this->api); + $this->assertEquals('application/yaml', $api_result->get_content_type()); + + $server = array("HTTP_ACCEPT" => ""); + $request = new Request(array("callback" => "hello"), array(), array(), array(), array(), $server); + $api_result = new API_V1_result($request, $this->api); + $this->assertEquals('text/javascript', $api_result->get_content_type()); + + $server = array("HTTP_ACCEPT" => "unknow"); + $request = new Request(array("callback" => ""), array(), array(), array(), array(), $server); + $api_result = new API_V1_result($request, $this->api); + $this->assertEquals("application/json", $api_result->get_content_type()); + } + + /** + * @depends testFormat + */ + public function testSet_error_message() + { + $api_result = new API_V1_result(new Request(), $this->api); + $api_result->set_error_message(API_V1_result::ERROR_BAD_REQUEST); + $this->assertErrorMessage($api_result, 400, API_V1_result::ERROR_BAD_REQUEST, API_V1_exception_badrequest::get_details()); + + $api_result = new API_V1_result(new Request(), $this->api); + $api_result->set_error_message(API_V1_result::ERROR_UNAUTHORIZED); + $this->assertErrorMessage($api_result, 401, API_V1_result::ERROR_UNAUTHORIZED, API_V1_exception_unauthorized::get_details()); + + $api_result = new API_V1_result(new Request(), $this->api); + $api_result->set_error_message(API_V1_result::ERROR_FORBIDDEN); + $this->assertErrorMessage($api_result, 403, API_V1_result::ERROR_FORBIDDEN, API_V1_exception_forbidden::get_details()); + + $api_result = new API_V1_result(new Request(), $this->api); + $api_result->set_error_message(API_V1_result::ERROR_NOTFOUND); + $this->assertErrorMessage($api_result, 404, API_V1_result::ERROR_NOTFOUND, API_V1_exception_notfound::get_details()); + + $api_result = new API_V1_result(new Request(), $this->api); + $api_result->set_error_message(API_V1_result::ERROR_METHODNOTALLOWED); + $this->assertErrorMessage($api_result, 405, API_V1_result::ERROR_METHODNOTALLOWED, API_V1_exception_methodnotallowed::get_details()); + + $api_result = new API_V1_result(new Request(), $this->api); + $api_result->set_error_message(API_V1_result::ERROR_INTERNALSERVERERROR); + $this->assertErrorMessage($api_result, 500, API_V1_result::ERROR_INTERNALSERVERERROR, API_V1_exception_internalservererror::get_details()); + + $api_result = new API_V1_result(new Request(), $this->api); + $api_result->set_error_message(OAUTH2_ERROR_INVALID_REQUEST); + $this->assertErrorMessage($api_result, 200, OAUTH2_ERROR_INVALID_REQUEST, NULL); + } + + protected function assertErrorMessage(API_V1_result $api_result, $code, $message, $detail) + { + $response = json_decode($api_result->format()); + $this->checkResponseFieldMeta($response, 'http_code', $code, PHPUnit_Framework_Constraint_IsType::TYPE_INT); + + if (is_null($message)) + { + $this->assertObjectHasAttribute('error_message', $response->meta); + $this->assertNull($response->meta->error_message); + } + else + { + $this->checkResponseFieldMeta($response, 'error_message', $message, PHPUnit_Framework_Constraint_IsType::TYPE_STRING); + } + + if (is_null($detail)) + { + $this->assertObjectHasAttribute('error_details', $response->meta); + $this->assertNull($response->meta->error_details); + } + else + { + $this->checkResponseFieldMeta($response, 'error_details', $detail, PHPUnit_Framework_Constraint_IsType::TYPE_STRING); + } + } + + /** + * @depends testFormat + */ + public function testSet_error_code() + { + $api_result = new API_V1_result(new Request(), $this->api); + $api_result->set_error_code(400); + $this->assertErrorMessage($api_result, 400, API_V1_result::ERROR_BAD_REQUEST, API_V1_exception_badrequest::get_details()); + + $api_result = new API_V1_result(new Request(), $this->api); + $api_result->set_error_code(401); + $this->assertErrorMessage($api_result, 401, API_V1_result::ERROR_UNAUTHORIZED, API_V1_exception_unauthorized::get_details()); + + $api_result = new API_V1_result(new Request(), $this->api); + $api_result->set_error_code(403); + $this->assertErrorMessage($api_result, 403, API_V1_result::ERROR_FORBIDDEN, API_V1_exception_forbidden::get_details()); + + $api_result = new API_V1_result(new Request(), $this->api); + $api_result->set_error_code(404); + $this->assertErrorMessage($api_result, 404, API_V1_result::ERROR_NOTFOUND, API_V1_exception_notfound::get_details()); + + $api_result = new API_V1_result(new Request(), $this->api); + $api_result->set_error_code(405); + $this->assertErrorMessage($api_result, 405, API_V1_result::ERROR_METHODNOTALLOWED, API_V1_exception_methodnotallowed::get_details()); + + $api_result = new API_V1_result(new Request(), $this->api); + $api_result->set_error_code(500); + $this->assertErrorMessage($api_result, 500, API_V1_result::ERROR_INTERNALSERVERERROR, API_V1_exception_internalservererror::get_details()); + } + + public function testGet_http_code() + { + $api_result = new API_V1_result(new Request(), $this->api); + $api_result->set_error_code(400); + $this->assertEquals(400, $api_result->get_http_code()); + + $api_result = new API_V1_result(new Request(), $this->api); + $api_result->set_error_code(401); + $this->assertEquals(401, $api_result->get_http_code()); + + $api_result = new API_V1_result(new Request(), $this->api); + $api_result->set_error_code(403); + $this->assertEquals(403, $api_result->get_http_code()); + + $api_result = new API_V1_result(new Request(), $this->api); + $api_result->set_error_code(404); + $this->assertEquals(404, $api_result->get_http_code()); + + $api_result = new API_V1_result(new Request(), $this->api); + $api_result->set_error_code(405); + $this->assertEquals(405, $api_result->get_http_code()); + + $api_result = new API_V1_result(new Request(), $this->api); + $api_result->set_error_code(500); + $this->assertEquals(500, $api_result->get_http_code()); + + $api_result = new API_V1_result(new Request(array("callback" => "my_callback")), $this->api); + $api_result->set_error_code(400); + $this->assertEquals(200, $api_result->get_http_code()); + + $api_result = new API_V1_result(new Request(array("callback" => "my_callback")), $this->api); + $api_result->set_error_code(500); + $this->assertEquals(500, $api_result->get_http_code()); + } + + public function testSet_http_code() + { + $api_result = new API_V1_result(new Request(), $this->api); + $api_result->set_http_code(500); + $this->assertEquals(500, $api_result->get_http_code()); + + $api_result->set_http_code(400); + $this->assertEquals(400, $api_result->get_http_code()); + $api_result->set_http_code(401); + $this->assertEquals(401, $api_result->get_http_code()); + $api_result->set_http_code(403); + $this->assertEquals(403, $api_result->get_http_code()); + + $api_result = new API_V1_result(new Request(array("callback" => "my_callback")), $this->api); + $api_result->set_http_code(500); + $this->assertEquals(500, $api_result->get_http_code()); + + $api_result->set_http_code(400); + $this->assertEquals(200, $api_result->get_http_code()); + + $api_result->set_http_code(401); + $this->assertEquals(200, $api_result->get_http_code()); + + $api_result->set_http_code(403); + $this->assertEquals(200, $api_result->get_http_code()); + + $api_result->set_http_code(404); + $this->assertEquals(200, $api_result->get_http_code()); + + $api_result->set_http_code(405); + $this->assertEquals(200, $api_result->get_http_code()); + } + +} + +?> diff --git a/tests/bootstrap.inc b/tests/bootstrap.inc new file mode 100644 index 0000000000..277b8a8742 --- /dev/null +++ b/tests/bootstrap.inc @@ -0,0 +1,20 @@ +object = new caption_record(self::$record_1, self::$record_1->get_databox()); + } + + /** + * @covers \caption_record::serializeXML + */ + public function testSerializeXML() + { + + foreach (self::$record_1->get_databox()->get_meta_structure() as $databox_field) + { + $n = $databox_field->is_multi() ? 3 : 1; + + for ($i = 0; $i < $n; $i++) + { + \caption_Field_Value::create($databox_field, self::$record_1, \random::generatePassword()); + } + } + + $xml = $this->object->serialize(\caption_record::SERIALIZE_XML); + + $sxe = simplexml_load_string($xml); + $this->assertInstanceOf('SimpleXMLElement', $sxe); + + foreach (self::$record_1->get_caption()->get_fields() as $field) + { + if($field->get_databox_field()->is_multi()) + { + $tagname = $field->get_name(); + $retrieved = array(); + foreach($sxe->description->$tagname as $value) + { + $retrieved[] = (string) $value; + } + + $values = $field->get_values(); + $this->assertEquals(count($values), count($retrieved)); + foreach($values as $val) + { + $this->assertTrue(in_array($val->getValue(), $retrieved)); + } + } + else + { + $tagname = $field->get_name(); + $value = array_pop($field->get_values()); + $this->assertEquals($value->getValue(), (string) $sxe->description->$tagname); + } + } + } + + /** + * @covers \caption_record::serializeYAML + */ + public function testSerializeYAML() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers \caption_record::get_fields + * @todo Implement testGet_fields(). + */ + public function testGet_fields() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers \caption_record::get_field + * @todo Implement testGet_field(). + */ + public function testGet_field() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers \caption_record::get_dc_field + * @todo Implement testGet_dc_field(). + */ + public function testGet_dc_field() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers \caption_record::get_highlight_fields + * @todo Implement testGet_highlight_fields(). + */ + public function testGet_highlight_fields() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers \caption_record::get_cache_key + * @todo Implement testGet_cache_key(). + */ + public function testGet_cache_key() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers \caption_record::get_data_from_cache + * @todo Implement testGet_data_from_cache(). + */ + public function testGet_data_from_cache() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers \caption_record::set_data_to_cache + * @todo Implement testSet_data_to_cache(). + */ + public function testSet_data_to_cache() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers \caption_record::delete_data_from_cache + * @todo Implement testDelete_data_from_cache(). + */ + public function testDelete_data_from_cache() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} diff --git a/tests/collectionTest.php b/tests/collectionTest.php new file mode 100644 index 0000000000..ba4d83906b --- /dev/null +++ b/tests/collectionTest.php @@ -0,0 +1,360 @@ +get_session()->authenticate($auth); + + $found = false; + foreach ($appbox->get_databoxes() as $databox) + { + $found = true; + break; + } + + if (!$found) + self::fail('No databox found for collection test'); + + self::$object = collection::create($databox, $appbox, 'test_collection', self::$user); + + if (!self::$object instanceof collection) + self::fail('Unable to create collection'); + } + + public static function tearDownAfterClass() + { + + self::$object->delete(); + parent::tearDownAfterClass(); + } + + public function testEnable() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $base_id = self::$object->get_base_id(); + $coll_id = self::$object->get_coll_id(); + self::$object->disable($appbox); + $this->assertTrue(is_int(self::$object->get_base_id())); + $this->assertTrue(is_int(self::$object->get_coll_id())); + $this->assertFalse(self::$object->is_active()); + + $sbas_id = self::$object->get_databox()->get_sbas_id(); + $databox = databox::get_instance($sbas_id); + + foreach ($databox->get_collections() as $collection) + { + $this->assertTrue($collection->get_base_id() !== $base_id); + $this->assertTrue($collection->get_coll_id() !== $coll_id); + } + + self::$object->enable($appbox); + $this->assertTrue(is_int(self::$object->get_base_id())); + $this->assertTrue(is_int(self::$object->get_coll_id())); + $this->assertTrue(self::$object->is_active()); + + $databox = databox::get_instance($sbas_id); + + $n = $m = 0; + foreach ($databox->get_collections() as $collection) + { + if ($collection->get_base_id() === $base_id) + $n++; + if ($collection->get_coll_id() === $coll_id) + $m++; + } + $this->assertEquals($n, 1); + $this->assertEquals($m, 1); + } + + public function testDisable() + { + $this->testEnable(); + } + + public function testEmpty_collection() + { + $record = record_adapter::create(self::$object, new system_file(__DIR__ . '/testfiles/cestlafete.jpg')); + $this->assertTrue(self::$object->get_record_amount() > 0); + self::$object->empty_collection(); + $this->assertTrue(self::$object->get_record_amount() === 0); + } + + public function testIs_active() + { + $this->assertTrue(is_bool(self::$object->is_active())); + } + + public function testGet_databox() + { + $this->assertInstanceOf('databox', self::$object->get_databox()); + } + + public function testGet_connection() + { + $this->assertInstanceOf('connection_pdo', self::$object->get_connection()); + } + + /** + * @todo Implement testSet_public_presentation(). + */ + public function testSet_public_presentation() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testSet_name() + { + self::$object->set_name('babababe bi bo bu'); + $this->assertEquals('babababe bi bo bu', self::$object->get_name()); + self::$object->set_name('babaé&\'" bi bo bu'); + $this->assertEquals('babaé&\'" bi bo bu', self::$object->get_name()); + self::$object->set_name('babababe bi bo bu'); + $this->assertEquals('babababe bi bo bu', self::$object->get_name()); + self::$object->set_name('babababe bi bo bu'); + $this->assertEquals('babababe bi bo bu', self::$object->get_name()); + } + + public function testGet_record_amount() + { + self::$object->empty_collection(); + $record = record_adapter::create(self::$object, new system_file(__DIR__ . '/testfiles/cestlafete.jpg')); + $this->assertTrue(self::$object->get_record_amount() === 1); + self::$object->empty_collection(); + $this->assertTrue(self::$object->get_record_amount() === 0); + } + + public function testGet_record_details() + { + $record = record_adapter::create(self::$object, new system_file(__DIR__ . '/testfiles/cestlafete.jpg')); + $details = self::$object->get_record_details(); + + $this->assertTrue(is_array($details)); + foreach ($details as $detail) + { + $this->assertTrue(is_array($detail)); + $this->assertArrayHasKey('coll_id', $detail); + $this->asserttrue(is_int($detail['coll_id'])); + $this->assertArrayHasKey('name', $detail); + $this->asserttrue(is_string($detail['name'])); + $this->assertArrayHasKey('amount', $detail); + $this->asserttrue(is_int($detail['amount'])); + $this->assertArrayHasKey('size', $detail); + $this->asserttrue(is_int($detail['size'])); + } + } + + public function testUpdate_logo() + { + $pathfile = new system_file(__DIR__ . '/testfiles/logocoll.gif'); + self::$object->update_logo($pathfile); + $this->assertEquals(file_get_contents($pathfile->getPathname()), self::$object->get_binary_minilogos()); + } + + public function testReset_watermark() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testDelete() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testGet_binary_minilogos() + { + $this->testUpdate_logo(); + } + + public function testGet_from_base_id() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testGet_from_coll_id() + { + $temp_coll = collection::get_from_coll_id(self::$object->get_databox(), self::$object->get_coll_id()); + $this->assertEquals(self::$object->get_coll_id(), $temp_coll->get_coll_id()); + $this->assertEquals(self::$object->get_base_id(), $temp_coll->get_base_id()); + } + + public function testGet_base_id() + { + $this->assertTrue(is_int(self::$object->get_base_id())); + $this->assertTrue(self::$object->get_base_id() > 0); + } + + public function testGet_sbas_id() + { + $this->assertTrue(is_int(self::$object->get_sbas_id())); + $this->assertEquals(self::$object->get_sbas_id(), self::$object->get_databox()->get_sbas_id()); + } + + public function testGet_coll_id() + { + $this->assertTrue(is_int(self::$object->get_coll_id())); + $this->assertTrue(self::$object->get_coll_id() > 0); + } + + /** + * @todo Implement testGet_prefs(). + */ + public function testGet_prefs() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testSet_prefs(). + */ + public function testSet_prefs() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_name(). + */ + public function testGet_name() + { + $this->assertTrue(is_string(self::$object->get_name())); + $this->assertTrue(trim(strip_tags(self::$object->get_name())) === self::$object->get_name()); + } + + /** + * @todo Implement testGet_pub_wm(). + */ + public function testGet_pub_wm() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testIs_available(). + */ + public function testIs_available() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testUnmount_collection(). + */ + public function testUnmount_collection() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testCreate(). + */ + public function testCreate() + { + + } + + /** + * @todo Implement testSet_admin(). + */ + public function testSet_admin() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testMount_collection(). + */ + public function testMount_collection() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGetLogo(). + */ + public function testGetLogo() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGetWatermark(). + */ + public function testGetWatermark() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGetPresentation(). + */ + public function testGetPresentation() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGetStamp(). + */ + public function testGetStamp() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} diff --git a/tests/databox/Field/DCES/databox_Field_DCES_ContributorTest.php b/tests/databox/Field/DCES/databox_Field_DCES_ContributorTest.php new file mode 100644 index 0000000000..46f3cba24c --- /dev/null +++ b/tests/databox/Field/DCES/databox_Field_DCES_ContributorTest.php @@ -0,0 +1,40 @@ +object = new databox_Field_DCES_Contributor; + } + + public function testGet_label() + { + $name = str_replace('Test', '', array_pop(explode('_', __CLASS__))); + $this->assertEquals($name, $this->object->get_label()); + } + + public function testGet_definition() + { + $this->assertTrue(is_string($this->object->get_definition())); + $this->assertTrue(strlen($this->object->get_definition()) > 20); + } + + public function testGet_documentation_link() + { + $this->assertRegExp('/^http:\/\/dublincore\.org\/documents\/dces\/#[a-z]+$/', $this->object->get_documentation_link()); + } + +} + diff --git a/tests/databox/Field/DCES/databox_Field_DCES_CoverageTest.php b/tests/databox/Field/DCES/databox_Field_DCES_CoverageTest.php new file mode 100644 index 0000000000..1a8c961e39 --- /dev/null +++ b/tests/databox/Field/DCES/databox_Field_DCES_CoverageTest.php @@ -0,0 +1,40 @@ +object = new databox_Field_DCES_Coverage; + } + + public function testGet_label() + { + $name = str_replace('Test', '', array_pop(explode('_', __CLASS__))); + $this->assertEquals($name, $this->object->get_label()); + } + + public function testGet_definition() + { + $this->assertTrue(is_string($this->object->get_definition())); + $this->assertTrue(strlen($this->object->get_definition()) > 20); + } + + public function testGet_documentation_link() + { + $this->assertRegExp('/^http:\/\/dublincore\.org\/documents\/dces\/#[a-z]+$/', $this->object->get_documentation_link()); + } + +} + diff --git a/tests/databox/Field/DCES/databox_Field_DCES_CreatorTest.php b/tests/databox/Field/DCES/databox_Field_DCES_CreatorTest.php new file mode 100644 index 0000000000..d5e0a18481 --- /dev/null +++ b/tests/databox/Field/DCES/databox_Field_DCES_CreatorTest.php @@ -0,0 +1,40 @@ +object = new databox_Field_DCES_Creator; + } + + public function testGet_label() + { + $name = str_replace('Test', '', array_pop(explode('_', __CLASS__))); + $this->assertEquals($name, $this->object->get_label()); + } + + public function testGet_definition() + { + $this->assertTrue(is_string($this->object->get_definition())); + $this->assertTrue(strlen($this->object->get_definition()) > 20); + } + + public function testGet_documentation_link() + { + $this->assertRegExp('/^http:\/\/dublincore\.org\/documents\/dces\/#[a-z]+$/', $this->object->get_documentation_link()); + } + +} + diff --git a/tests/databox/Field/DCES/databox_Field_DCES_DateTest.php b/tests/databox/Field/DCES/databox_Field_DCES_DateTest.php new file mode 100644 index 0000000000..81f80da3a2 --- /dev/null +++ b/tests/databox/Field/DCES/databox_Field_DCES_DateTest.php @@ -0,0 +1,40 @@ +object = new databox_Field_DCES_Date; + } + + public function testGet_label() + { + $name = str_replace('Test', '', array_pop(explode('_', __CLASS__))); + $this->assertEquals($name, $this->object->get_label()); + } + + public function testGet_definition() + { + $this->assertTrue(is_string($this->object->get_definition())); + $this->assertTrue(strlen($this->object->get_definition()) > 20); + } + + public function testGet_documentation_link() + { + $this->assertRegExp('/^http:\/\/dublincore\.org\/documents\/dces\/#[a-z]+$/', $this->object->get_documentation_link()); + } + +} + diff --git a/tests/databox/Field/DCES/databox_Field_DCES_DescriptionTest.php b/tests/databox/Field/DCES/databox_Field_DCES_DescriptionTest.php new file mode 100644 index 0000000000..d9e19df155 --- /dev/null +++ b/tests/databox/Field/DCES/databox_Field_DCES_DescriptionTest.php @@ -0,0 +1,40 @@ +object = new databox_Field_DCES_Description; + } + + public function testGet_label() + { + $name = str_replace('Test', '', array_pop(explode('_', __CLASS__))); + $this->assertEquals($name, $this->object->get_label()); + } + + public function testGet_definition() + { + $this->assertTrue(is_string($this->object->get_definition())); + $this->assertTrue(strlen($this->object->get_definition()) > 20); + } + + public function testGet_documentation_link() + { + $this->assertRegExp('/^http:\/\/dublincore\.org\/documents\/dces\/#[a-z]+$/', $this->object->get_documentation_link()); + } + +} + diff --git a/tests/databox/Field/DCES/databox_Field_DCES_FormatTest.php b/tests/databox/Field/DCES/databox_Field_DCES_FormatTest.php new file mode 100644 index 0000000000..e58b82b815 --- /dev/null +++ b/tests/databox/Field/DCES/databox_Field_DCES_FormatTest.php @@ -0,0 +1,40 @@ +object = new databox_Field_DCES_Format; + } + + public function testGet_label() + { + $name = str_replace('Test', '', array_pop(explode('_', __CLASS__))); + $this->assertEquals($name, $this->object->get_label()); + } + + public function testGet_definition() + { + $this->assertTrue(is_string($this->object->get_definition())); + $this->assertTrue(strlen($this->object->get_definition()) > 20); + } + + public function testGet_documentation_link() + { + $this->assertRegExp('/^http:\/\/dublincore\.org\/documents\/dces\/#[a-z]+$/', $this->object->get_documentation_link()); + } + +} + diff --git a/tests/databox/Field/DCES/databox_Field_DCES_IdentifierTest.php b/tests/databox/Field/DCES/databox_Field_DCES_IdentifierTest.php new file mode 100644 index 0000000000..943401ecf8 --- /dev/null +++ b/tests/databox/Field/DCES/databox_Field_DCES_IdentifierTest.php @@ -0,0 +1,40 @@ +object = new databox_Field_DCES_Identifier; + } + + public function testGet_label() + { + $name = str_replace('Test', '', array_pop(explode('_', __CLASS__))); + $this->assertEquals($name, $this->object->get_label()); + } + + public function testGet_definition() + { + $this->assertTrue(is_string($this->object->get_definition())); + $this->assertTrue(strlen($this->object->get_definition()) > 20); + } + + public function testGet_documentation_link() + { + $this->assertRegExp('/^http:\/\/dublincore\.org\/documents\/dces\/#[a-z]+$/', $this->object->get_documentation_link()); + } + +} + diff --git a/tests/databox/Field/DCES/databox_Field_DCES_LanguageTest.php b/tests/databox/Field/DCES/databox_Field_DCES_LanguageTest.php new file mode 100644 index 0000000000..84982dc675 --- /dev/null +++ b/tests/databox/Field/DCES/databox_Field_DCES_LanguageTest.php @@ -0,0 +1,40 @@ +object = new databox_Field_DCES_Language; + } + + public function testGet_label() + { + $name = str_replace('Test', '', array_pop(explode('_', __CLASS__))); + $this->assertEquals($name, $this->object->get_label()); + } + + public function testGet_definition() + { + $this->assertTrue(is_string($this->object->get_definition())); + $this->assertTrue(strlen($this->object->get_definition()) > 20); + } + + public function testGet_documentation_link() + { + $this->assertRegExp('/^http:\/\/dublincore\.org\/documents\/dces\/#[a-z]+$/', $this->object->get_documentation_link()); + } + +} + diff --git a/tests/databox/Field/DCES/databox_Field_DCES_PublisherTest.php b/tests/databox/Field/DCES/databox_Field_DCES_PublisherTest.php new file mode 100644 index 0000000000..1adf98734c --- /dev/null +++ b/tests/databox/Field/DCES/databox_Field_DCES_PublisherTest.php @@ -0,0 +1,40 @@ +object = new databox_Field_DCES_Publisher; + } + + public function testGet_label() + { + $name = str_replace('Test', '', array_pop(explode('_', __CLASS__))); + $this->assertEquals($name, $this->object->get_label()); + } + + public function testGet_definition() + { + $this->assertTrue(is_string($this->object->get_definition())); + $this->assertTrue(strlen($this->object->get_definition()) > 20); + } + + public function testGet_documentation_link() + { + $this->assertRegExp('/^http:\/\/dublincore\.org\/documents\/dces\/#[a-z]+$/', $this->object->get_documentation_link()); + } + +} + diff --git a/tests/databox/Field/DCES/databox_Field_DCES_RelationTest.php b/tests/databox/Field/DCES/databox_Field_DCES_RelationTest.php new file mode 100644 index 0000000000..47e27db0b4 --- /dev/null +++ b/tests/databox/Field/DCES/databox_Field_DCES_RelationTest.php @@ -0,0 +1,39 @@ +object = new databox_Field_DCES_Relation; + } + + public function testGet_label() + { + $name = str_replace('Test', '', array_pop(explode('_', __CLASS__))); + $this->assertEquals($name, $this->object->get_label()); + } + + public function testGet_definition() + { + $this->assertTrue(is_string($this->object->get_definition())); + $this->assertTrue(strlen($this->object->get_definition()) > 18); + } + + public function testGet_documentation_link() + { + $this->assertRegExp('/^http:\/\/dublincore\.org\/documents\/dces\/#[a-z]+$/', $this->object->get_documentation_link()); + } + +} diff --git a/tests/databox/Field/DCES/databox_Field_DCES_RightsTest.php b/tests/databox/Field/DCES/databox_Field_DCES_RightsTest.php new file mode 100644 index 0000000000..dd38d38fee --- /dev/null +++ b/tests/databox/Field/DCES/databox_Field_DCES_RightsTest.php @@ -0,0 +1,40 @@ +object = new databox_Field_DCES_Rights; + } + + public function testGet_label() + { + $name = str_replace('Test', '', array_pop(explode('_', __CLASS__))); + $this->assertEquals($name, $this->object->get_label()); + } + + public function testGet_definition() + { + $this->assertTrue(is_string($this->object->get_definition())); + $this->assertTrue(strlen($this->object->get_definition()) > 20); + } + + public function testGet_documentation_link() + { + $this->assertRegExp('/^http:\/\/dublincore\.org\/documents\/dces\/#[a-z]+$/', $this->object->get_documentation_link()); + } + +} + diff --git a/tests/databox/Field/DCES/databox_Field_DCES_SourceTest.php b/tests/databox/Field/DCES/databox_Field_DCES_SourceTest.php new file mode 100644 index 0000000000..33e0ab8a6a --- /dev/null +++ b/tests/databox/Field/DCES/databox_Field_DCES_SourceTest.php @@ -0,0 +1,39 @@ +object = new databox_Field_DCES_Source; + } + + public function testGet_label() + { + $name = str_replace('Test', '', array_pop(explode('_', __CLASS__))); + $this->assertEquals($name, $this->object->get_label()); + } + + public function testGet_definition() + { + $this->assertTrue(is_string($this->object->get_definition())); + $this->assertTrue(strlen($this->object->get_definition()) > 20); + } + + public function testGet_documentation_link() + { + $this->assertRegExp('/^http:\/\/dublincore\.org\/documents\/dces\/#[a-z]+$/', $this->object->get_documentation_link()); + } + +} diff --git a/tests/databox/Field/DCES/databox_Field_DCES_SubjectTest.php b/tests/databox/Field/DCES/databox_Field_DCES_SubjectTest.php new file mode 100644 index 0000000000..0e03b3b20d --- /dev/null +++ b/tests/databox/Field/DCES/databox_Field_DCES_SubjectTest.php @@ -0,0 +1,40 @@ +object = new databox_Field_DCES_Subject; + } + + public function testGet_label() + { + $name = str_replace('Test', '', array_pop(explode('_', __CLASS__))); + $this->assertEquals($name, $this->object->get_label()); + } + + public function testGet_definition() + { + $this->assertTrue(is_string($this->object->get_definition())); + $this->assertTrue(strlen($this->object->get_definition()) > 20); + } + + public function testGet_documentation_link() + { + $this->assertRegExp('/^http:\/\/dublincore\.org\/documents\/dces\/#[a-z]+$/', $this->object->get_documentation_link()); + } + +} + diff --git a/tests/databox/Field/DCES/databox_Field_DCES_TitleTest.php b/tests/databox/Field/DCES/databox_Field_DCES_TitleTest.php new file mode 100644 index 0000000000..25d4935905 --- /dev/null +++ b/tests/databox/Field/DCES/databox_Field_DCES_TitleTest.php @@ -0,0 +1,40 @@ +object = new databox_Field_DCES_Title; + } + + public function testGet_label() + { + $name = str_replace('Test', '', array_pop(explode('_', __CLASS__))); + $this->assertEquals($name, $this->object->get_label()); + } + + public function testGet_definition() + { + $this->assertTrue(is_string($this->object->get_definition())); + $this->assertTrue(strlen($this->object->get_definition()) > 20); + } + + public function testGet_documentation_link() + { + $this->assertRegExp('/^http:\/\/dublincore\.org\/documents\/dces\/#[a-z]+$/', $this->object->get_documentation_link()); + } + +} + diff --git a/tests/databox/Field/DCES/databox_Field_DCES_TypeTest.php b/tests/databox/Field/DCES/databox_Field_DCES_TypeTest.php new file mode 100644 index 0000000000..6b463d6d27 --- /dev/null +++ b/tests/databox/Field/DCES/databox_Field_DCES_TypeTest.php @@ -0,0 +1,40 @@ +object = new databox_Field_DCES_Type; + } + + public function testGet_label() + { + $name = str_replace('Test', '', array_pop(explode('_', __CLASS__))); + $this->assertEquals($name, $this->object->get_label()); + } + + public function testGet_definition() + { + $this->assertTrue(is_string($this->object->get_definition())); + $this->assertTrue(strlen($this->object->get_definition()) > 20); + } + + public function testGet_documentation_link() + { + $this->assertRegExp('/^http:\/\/dublincore\.org\/documents\/dces\/#[a-z]+$/', $this->object->get_documentation_link()); + } + +} + diff --git a/tests/databox/Field/databox_Field_DCESAbstractTest.php b/tests/databox/Field/databox_Field_DCESAbstractTest.php new file mode 100644 index 0000000000..68799ee526 --- /dev/null +++ b/tests/databox/Field/databox_Field_DCESAbstractTest.php @@ -0,0 +1,70 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_definition(). + */ + public function testGet_definition() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_documentation_link(). + */ + public function testGet_documentation_link() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/databox/databox_cguTest.php b/tests/databox/databox_cguTest.php new file mode 100644 index 0000000000..f9d7d0b90a --- /dev/null +++ b/tests/databox/databox_cguTest.php @@ -0,0 +1,70 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testDenyCgus(). + */ + public function testDenyCgus() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGetHome(). + */ + public function testGetHome() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/databox/databox_descriptionStructureTest.php b/tests/databox/databox_descriptionStructureTest.php new file mode 100644 index 0000000000..426f9efde1 --- /dev/null +++ b/tests/databox/databox_descriptionStructureTest.php @@ -0,0 +1,114 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testAdd_element(). + */ + public function testAdd_element() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testRemove_element(). + */ + public function testRemove_element() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_elements(). + */ + public function testGet_elements() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_element(). + */ + public function testGet_element() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_element_by_name(). + */ + public function testGet_element_by_name() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testIsset_element(). + */ + public function testIsset_element() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/databox/databox_fieldTest.php b/tests/databox/databox_fieldTest.php new file mode 100644 index 0000000000..f6e93dd923 --- /dev/null +++ b/tests/databox/databox_fieldTest.php @@ -0,0 +1,529 @@ +databox = self::$record_1->get_databox(); + $this->name_mono = 'Field Test Mono'; + $this->name_multi = 'Field Test Multi'; + + $this->object_mono = $this->databox->get_meta_structure()->get_element_by_name($this->name_mono); + + $this->object_multi = $this->databox->get_meta_structure()->get_element_by_name($this->name_multi); + + if(!$this->object_mono instanceof databox_field) + $this->object_mono = databox_field::create($this->databox, $this->name_mono); + if(!$this->object_multi instanceof databox_field) + { + $this->object_multi = databox_field::create($this->databox, $this->name_multi); + $this->object_multi->set_multi(true)->save(); + } + } + + public function tearDown() + { + if($this->object_mono instanceof databox_field) + $this->object_mono->delete(); + if($this->object_multi instanceof databox_field) + $this->object_multi->delete(); + + $extra = $this->databox->get_meta_structure()->get_element_by_name('Bonoboyoyo'); + if($extra instanceof databox_field) + $extra->delete(); + } + + public function testGet_instance() + { + $instance = databox_field::get_instance($this->databox, $this->object_mono->get_id()); + $this->assertEquals($this->object_mono->get_id(), $instance->get_id()); + + $instance = databox_field::get_instance($this->databox, $this->object_multi->get_id()); + $this->assertEquals($this->object_multi->get_id(), $instance->get_id()); + } + + /** + * @todo Implement testSet_databox(). + */ + public function testSet_databox() + { + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testGet_connection() + { + $this->assertInstanceOf('\connection_pdo', $this->object_mono->get_connection()); + $this->assertInstanceOf('\connection_pdo', $this->object_multi->get_connection()); + } + + public function testGet_databox() + { + $this->assertInstanceOf('\databox', $this->object_mono->get_databox()); + $this->assertEquals(self::$record_1->get_databox()->get_sbas_id(), $this->object_mono->get_databox()->get_sbas_id()); + $this->assertInstanceOf('\databox', $this->object_multi->get_databox()); + $this->assertEquals(self::$record_1->get_databox()->get_sbas_id(), $this->object_multi->get_databox()->get_sbas_id()); + } + + /** + * @todo Implement testDelete(). + */ + public function testDelete() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testSave(). + */ + public function testSave() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testSet_name() + { + $name = 'Eléphant'; + $this->object_mono->set_name($name); + $this->assertEquals('Elephant', $this->object_mono->get_name()); + + $name = '0!èEléphant '; + $this->object_mono->set_name($name); + $this->assertEquals('eElephant', $this->object_mono->get_name()); + + $name = 'Gaston'; + $this->object_mono->set_name($name); + $this->assertEquals('Gaston', $this->object_mono->get_name()); + + try + { + $this->object_mono->set_name(''); + $this->fail(); + } + catch (Exception $e) + { + $this->assertTrue(true, 'test passed'); + } + + try + { + $this->object_mono->set_name('éà'); + $this->assertEquals('ea', $this->object_mono->get_name()); + } + catch (Exception $e) + { + + } + } + + /** + * @todo Implement testLoad_class_from_xpath(). + */ + public function testLoad_class_from_xpath() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testSet_source() + { + $source = '/rdf:RDF/rdf:Description/PHRASEANET:tf-filename'; + + $this->object_mono->set_source($source); + $this->object_multi->set_source($source); + + $this->assertEquals($source, $this->object_mono->get_source()->get_source()); + $this->assertEquals($source, $this->object_multi->get_source()->get_source()); + + $this->object_mono->set_source(null); + $this->object_multi->set_source(null); + + $this->assertInstanceOf('\metadata_Interface', $this->object_mono->get_source()); + $this->assertInstanceOf('\metadata_Interface', $this->object_multi->get_source()); + $this->assertEquals('', $this->object_mono->get_source()->get_source()); + $this->assertEquals('', $this->object_multi->get_source()->get_source()); + } + + public function testGet_source() + { + $this->assertInstanceOf('\metadata_Interface', $this->object_mono->get_source()); + $this->assertInstanceOf('\metadata_Interface', $this->object_multi->get_source()); + } + + /** + * @todo Implement testGet_dces_element(). + */ + public function testGet_dces_element() + { + $this->assertNull($this->object_mono->get_dces_element()); + $this->assertNull($this->object_multi->get_dces_element()); + } + + public function testSet_dces_element() + { + $this->object_mono->set_dces_element(new \databox_Field_DCES_Contributor()); + $this->object_multi->set_dces_element(new \databox_Field_DCES_Format()); + + $this->assertInstanceOf('\databox_Field_DCESAbstract', $this->object_mono->get_dces_element()); + $this->assertInstanceOf('\databox_Field_DCESAbstract', $this->object_multi->get_dces_element()); + + $this->object_multi->set_dces_element(null); + $this->assertNull($this->object_multi->get_dces_element()); + } + + public function testSet_indexable() + { + $this->object_mono->set_indexable(false); + $this->assertFalse($this->object_mono->is_indexable()); + $this->object_mono->set_indexable(true); + $this->assertTrue($this->object_mono->is_indexable()); + } + + public function testSet_readonly() + { + $this->object_mono->set_readonly(false); + $this->assertFalse($this->object_mono->is_readonly()); + $this->object_mono->set_readonly(true); + $this->assertTrue($this->object_mono->is_readonly()); + } + + public function testSet_required() + { + $this->object_mono->set_required(false); + $this->assertFalse($this->object_mono->is_required()); + $this->object_mono->set_required(true); + $this->assertTrue($this->object_mono->is_required()); + } + + public function testSet_business() + { + $this->object_mono->set_business(false); + $this->assertFalse($this->object_mono->isBusiness()); + $this->object_mono->set_business(true); + $this->assertTrue($this->object_mono->isBusiness()); + } + + public function testSet_multi() + { + $this->object_mono->set_multi(false); + $this->assertFalse($this->object_mono->is_multi()); + $this->object_mono->set_multi(true); + $this->assertTrue($this->object_mono->is_multi()); + } + + public function testSet_report() + { + $this->object_mono->set_report(false); + $this->assertFalse($this->object_mono->is_report()); + $this->object_mono->set_report(true); + $this->assertTrue($this->object_mono->is_report()); + } + + public function testSet_type() + { + $this->object_mono->set_type('date'); + $this->assertEquals('date', $this->object_mono->get_type()); + $this->object_mono->set_type('text'); + $this->assertEquals('text', $this->object_mono->get_type()); + } + + public function testSet_tbranch() + { + $this->object_mono->set_tbranch('newBranche'); + $this->assertEquals('newBranche', $this->object_mono->get_tbranch()); + $this->object_mono->set_tbranch(null); + $this->assertNull($this->object_mono->get_tbranch()); + } + + public function testSet_separator() + { + $this->assertEquals('', $this->object_mono->get_separator()); + $this->assertEquals(';', $this->object_multi->get_separator()); + + $this->object_mono->set_separator(';.:'); + $this->object_multi->set_separator(';.:'); + + $this->assertEquals('', $this->object_mono->get_separator()); + $this->assertEquals(';.:', $this->object_multi->get_separator()); + + $this->object_multi->set_separator('.:-'); + $this->assertEquals('.:-;', $this->object_multi->get_separator()); + } + + public function testSet_thumbtitle() + { + $this->object_mono->set_thumbtitle(true); + $this->assertTrue($this->object_mono->get_thumbtitle()); + $this->object_mono->set_thumbtitle('fr'); + $this->assertEquals('fr', $this->object_mono->get_thumbtitle()); + $this->object_mono->set_thumbtitle(false); + $this->assertFalse($this->object_mono->get_thumbtitle()); + } + + public function testGet_thumbtitle() + { + $this->assertNull($this->object_mono->get_thumbtitle()); + $this->assertNull($this->object_multi->get_thumbtitle()); + } + + public function testGet_id() + { + $this->assertTrue(is_int($this->object_mono->get_id())); + $this->assertTrue(is_int($this->object_multi->get_id())); + } + + public function testGet_type() + { + $this->assertEquals('string', $this->object_mono->get_type()); + $this->assertEquals('string', $this->object_multi->get_type()); + } + + public function testGet_tbranch() + { + $this->assertEquals('', $this->object_mono->get_tbranch()); + $this->assertEquals('', $this->object_multi->get_tbranch()); + } + + public function testGet_separator() + { + $this->assertEquals('', $this->object_mono->get_separator()); + $this->assertEquals(';', $this->object_multi->get_separator()); + } + + public function testIs_indexable() + { + $this->assertTrue($this->object_mono->is_indexable()); + $this->assertTrue($this->object_multi->is_indexable()); + } + + public function testIs_readonly() + { + $this->assertFalse($this->object_mono->is_readonly()); + $this->assertFalse($this->object_multi->is_readonly()); + } + + public function testIs_required() + { + $this->assertFalse($this->object_mono->is_required()); + $this->assertFalse($this->object_multi->is_required()); + } + + public function testIs_multi() + { + $this->assertFalse($this->object_mono->is_multi()); + $this->assertTrue($this->object_multi->is_multi()); + } + + public function testIs_report() + { + $this->assertTrue($this->object_mono->is_report()); + $this->assertTrue($this->object_multi->is_report()); + } + + public function testGet_name() + { + $this->assertEquals(str_replace(' ', '', $this->name_mono), $this->object_mono->get_name()); + $this->assertEquals(str_replace(' ', '', $this->name_multi), $this->object_multi->get_name()); + } + + public function testGet_metadata_source() + { + $this->assertEquals('', $this->object_mono->get_metadata_source()); + $this->assertEquals('', $this->object_multi->get_metadata_source()); + + $source = '/rdf:RDF/rdf:Description/PHRASEANET:tf-filename'; + + $this->object_mono->set_source($source); + $this->object_multi->set_source($source); + + $this->assertEquals($source, $this->object_mono->get_metadata_source()); + $this->assertEquals($source, $this->object_multi->get_metadata_source()); + } + + public function testGet_metadata_namespace() + { + $this->assertEquals('NoSource', $this->object_mono->get_metadata_namespace()); + $this->assertEquals('NoSource', $this->object_multi->get_metadata_namespace()); + + $source = '/rdf:RDF/rdf:Description/PHRASEANET:tf-filename'; + + $this->object_mono->set_source($source); + $this->object_multi->set_source($source); + + $this->assertEquals('PHRASEANET', $this->object_mono->get_metadata_namespace()); + $this->assertEquals('PHRASEANET', $this->object_multi->get_metadata_namespace()); + } + + public function testGet_metadata_tagname() + { + $this->assertEquals('NoSource', $this->object_mono->get_metadata_tagname()); + $this->assertEquals('NoSource', $this->object_multi->get_metadata_tagname()); + + $source = '/rdf:RDF/rdf:Description/PHRASEANET:tf-filename'; + + $this->object_mono->set_source($source); + $this->object_multi->set_source($source); + + $this->assertEquals('tf-filename', $this->object_mono->get_metadata_tagname()); + $this->assertEquals('tf-filename', $this->object_multi->get_metadata_tagname()); + } + + public function testIs_on_error() + { + $this->assertFalse($this->object_mono->is_on_error()); + $this->assertFalse($this->object_multi->is_on_error()); + } + + public function testRenameField() + { + $AddedValue = 'scalar value'; + + self::$record_1->set_metadatas(array( + array( + 'meta_id' => null, + 'meta_struct_id' => $this->object_mono->get_id(), + 'value'=> $AddedValue + ) + )); + + $this->object_mono->set_name('Bonobo yoyo')->save(); + + $value = array_pop(self::$record_1->get_caption()->get_field('Bonoboyoyo')->get_values()); + $this->assertEquals($value->getValue(), $AddedValue); + } + + public function testChangeMulti() + { + $AddedValue_1 = 'scalar value 1'; + $AddedValue_2 = 'scalar value 2'; + + self::$record_1->set_metadatas(array( + array( + 'meta_id' => null, + 'meta_struct_id' => $this->object_multi->get_id(), + 'value'=> $AddedValue_1 + ), + array( + 'meta_id' => null, + 'meta_struct_id' => $this->object_multi->get_id(), + 'value'=> $AddedValue_2 + ) + )); + + $this->assertEquals(2, count(self::$record_1->get_caption()->get_field(str_replace(' ', '', $this->name_multi))->get_values())); + + $this->object_multi->set_multi(false)->save(); + + $this->assertEquals(1, count(self::$record_1->get_caption()->get_field(str_replace(' ', '', $this->name_multi))->get_values())); + } + + /** + * @todo Implement testCreate(). + */ + public function testCreate() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement test__sleep(). + */ + public function test__sleep() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement test__wakeup(). + */ + public function test__wakeup() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_cache_key(). + */ + public function testGet_cache_key() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_data_from_cache(). + */ + public function testGet_data_from_cache() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testSet_data_to_cache(). + */ + public function testSet_data_to_cache() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testDelete_data_from_cache(). + */ + public function testDelete_data_from_cache() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/databox/databox_fieldUnknownTest.php b/tests/databox/databox_fieldUnknownTest.php new file mode 100644 index 0000000000..9b12aea31e --- /dev/null +++ b/tests/databox/databox_fieldUnknownTest.php @@ -0,0 +1,92 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_metadata_source(). + */ + public function testGet_metadata_source() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_metadata_namespace(). + */ + public function testGet_metadata_namespace() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_metadata_tagname(). + */ + public function testGet_metadata_tagname() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testIs_on_error(). + */ + public function testIs_on_error() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/databox/databox_statusTest.php b/tests/databox/databox_statusTest.php new file mode 100644 index 0000000000..57c62f164f --- /dev/null +++ b/tests/databox/databox_statusTest.php @@ -0,0 +1,184 @@ +databox = self::$record_1->get_databox(); + $this->object = $this->databox->get_statusbits(); + } + + public function testGetStatus() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testGetDisplayStatus() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGetSearchStatus(). + */ + public function testGetSearchStatus() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGetPath(). + */ + public function testGetPath() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGetUrl(). + */ + public function testGetUrl() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testDeleteStatus(). + */ + public function testDeleteStatus() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testUpdateStatus(). + */ + public function testUpdateStatus() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testDeleteIcon(). + */ + public function testDeleteIcon() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testUpdateIcon(). + */ + public function testUpdateIcon() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testOperation_and(). + */ + public function testOperation_and() + { + $this->assertEquals('0', databox_status::operation_and('0x001','0x010')); + $this->assertEquals('1', databox_status::operation_and('01','11')); + $this->assertEquals('0', databox_status::operation_and('01','10')); + $this->assertEquals('10', databox_status::operation_and('11','10')); + } + + /** + * @todo Implement testOperation_and_not(). + */ + public function testOperation_and_not() + { + $this->assertEquals('0', databox_status::operation_and_not('0x001','0x011')); + $this->assertEquals('0', databox_status::operation_and_not('01','11')); + $this->assertEquals('1', databox_status::operation_and_not('01','10')); + $this->assertEquals('1', databox_status::operation_and_not('11','10')); + $this->assertEquals('10', databox_status::operation_and_not('10','01')); + } + + /** + * @todo Implement testOperation_or(). + */ + public function testOperation_or() + { + $this->assertEquals('10001', databox_status::operation_or('0x001','0x011')); + $this->assertEquals('11', databox_status::operation_or('01','11')); + } + + /** + * @todo Implement testDec2bin(). + */ + public function testDec2bin() + { + $this->assertEquals('1010', databox_status::dec2bin('10')); + + try + { + } + catch(Exception $e) + { + + } + } + + public function testHex2bin() + { + $this->assertEquals('10100001', databox_status::hex2bin('0x0A1')); + $this->assertEquals('10100001', databox_status::hex2bin('0A1')); + + try + { + databox_status::hex2bin('G1'); + $this->fail('Should raise an exception'); + } + catch(Exception $e) + { + + } + } + +} + +?> diff --git a/tests/databox/databox_subdefAbstractTest.php b/tests/databox/databox_subdefAbstractTest.php new file mode 100644 index 0000000000..d43deecb2d --- /dev/null +++ b/tests/databox/databox_subdefAbstractTest.php @@ -0,0 +1,169 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_path(). + */ + public function testGet_path() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_baseurl(). + */ + public function testGet_baseurl() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_mediatype(). + */ + public function testGet_mediatype() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_labels(). + */ + public function testGet_labels() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testIs_downloadable(). + */ + public function testIs_downloadable() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_mediatype_options(). + */ + public function testGet_mediatype_options() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testMeta_writeable(). + */ + public function testMeta_writeable() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testLog(). + */ + public function testLog() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_name(). + */ + public function testGet_name() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGenerate(). + */ + public function testGenerate() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_options(). + */ + public function testGet_options() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/databox/databox_subdefsStructureTest.php b/tests/databox/databox_subdefsStructureTest.php new file mode 100644 index 0000000000..eef3364fd8 --- /dev/null +++ b/tests/databox/databox_subdefsStructureTest.php @@ -0,0 +1,92 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_subdef(). + */ + public function testGet_subdef() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testDelete_subdef(). + */ + public function testDelete_subdef() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testAdd_subdef(). + */ + public function testAdd_subdef() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testSet_subdef(). + */ + public function testSet_subdef() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/databox/subdef/databox_subdef_audioTest.php b/tests/databox/subdef/databox_subdef_audioTest.php new file mode 100644 index 0000000000..6397fb8afc --- /dev/null +++ b/tests/databox/subdef/databox_subdef_audioTest.php @@ -0,0 +1,42 @@ +markTestIncomplete(); + } + +} + +?> diff --git a/tests/databox/subdef/databox_subdef_documentTest.php b/tests/databox/subdef/databox_subdef_documentTest.php new file mode 100644 index 0000000000..25d81fb766 --- /dev/null +++ b/tests/databox/subdef/databox_subdef_documentTest.php @@ -0,0 +1,41 @@ +markTestIncomplete(); + } +} + +?> diff --git a/tests/databox/subdef/databox_subdef_flashTest.php b/tests/databox/subdef/databox_subdef_flashTest.php new file mode 100644 index 0000000000..2231ac3636 --- /dev/null +++ b/tests/databox/subdef/databox_subdef_flashTest.php @@ -0,0 +1,41 @@ +markTestIncomplete(); + } +} + +?> diff --git a/tests/databox/subdef/databox_subdef_imageTest.php b/tests/databox/subdef/databox_subdef_imageTest.php new file mode 100644 index 0000000000..e7164fe6ac --- /dev/null +++ b/tests/databox/subdef/databox_subdef_imageTest.php @@ -0,0 +1,41 @@ +markTestIncomplete(); + } +} + +?> diff --git a/tests/databox/subdef/databox_subdef_mediatypeAbstractTest.php b/tests/databox/subdef/databox_subdef_mediatypeAbstractTest.php new file mode 100644 index 0000000000..341882513e --- /dev/null +++ b/tests/databox/subdef/databox_subdef_mediatypeAbstractTest.php @@ -0,0 +1,48 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/databox/subdef/databox_subdef_videoTest.php b/tests/databox/subdef/databox_subdef_videoTest.php new file mode 100644 index 0000000000..6a29159937 --- /dev/null +++ b/tests/databox/subdef/databox_subdef_videoTest.php @@ -0,0 +1,41 @@ +markTestIncomplete(); + } +} + +?> diff --git a/tests/databox/subdef/mediatype/databox_subdef_mediatype_audioTest.php b/tests/databox/subdef/mediatype/databox_subdef_mediatype_audioTest.php new file mode 100644 index 0000000000..9c6939d54e --- /dev/null +++ b/tests/databox/subdef/mediatype/databox_subdef_mediatype_audioTest.php @@ -0,0 +1,48 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/databox/subdef/mediatype/databox_subdef_mediatype_flexpaperTest.php b/tests/databox/subdef/mediatype/databox_subdef_mediatype_flexpaperTest.php new file mode 100644 index 0000000000..fcea86ab0d --- /dev/null +++ b/tests/databox/subdef/mediatype/databox_subdef_mediatype_flexpaperTest.php @@ -0,0 +1,48 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/databox/subdef/mediatype/databox_subdef_mediatype_gifTest.php b/tests/databox/subdef/mediatype/databox_subdef_mediatype_gifTest.php new file mode 100644 index 0000000000..6ae94e8635 --- /dev/null +++ b/tests/databox/subdef/mediatype/databox_subdef_mediatype_gifTest.php @@ -0,0 +1,48 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/databox/subdef/mediatype/databox_subdef_mediatype_imageTest.php b/tests/databox/subdef/mediatype/databox_subdef_mediatype_imageTest.php new file mode 100644 index 0000000000..6aa098a624 --- /dev/null +++ b/tests/databox/subdef/mediatype/databox_subdef_mediatype_imageTest.php @@ -0,0 +1,48 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/databox/subdef/mediatype/databox_subdef_mediatype_videoTest.php b/tests/databox/subdef/mediatype/databox_subdef_mediatype_videoTest.php new file mode 100644 index 0000000000..cbec52a13b --- /dev/null +++ b/tests/databox/subdef/mediatype/databox_subdef_mediatype_videoTest.php @@ -0,0 +1,48 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/formatTest.php b/tests/formatTest.php new file mode 100644 index 0000000000..a0b0b2e9ce --- /dev/null +++ b/tests/formatTest.php @@ -0,0 +1,25 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testArr_to_csv() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + diff --git a/tests/geonamesTest.php b/tests/geonamesTest.php new file mode 100644 index 0000000000..5f18d335a7 --- /dev/null +++ b/tests/geonamesTest.php @@ -0,0 +1,86 @@ +object = new geonames(); + } + + public function testName_from_id() + { + $result = $this->object->name_from_id(2989317); + $this->assertEquals("Orléans, France", $result); + } + + public function testGet_country() + { + $orleans = $this->object->find_city('orléans, france'); + $this->assertTrue(is_array($orleans)); + $this->assertTrue(count($orleans) === 1); + $orleans = array_pop($orleans); + + $found = $this->object->get_country($orleans['geoname_id']); + + $this->assertEquals($found, $orleans['country']); + $this->assertEquals($found, 'France'); + } + + public function testGet_country_code() + { + $this->assertEquals('FR', $this->object->get_country_code(2989317)); + $this->assertEquals('', $this->object->get_country_code(298945135163153317)); + $this->assertEquals('', $this->object->get_country_code('29894513516315331dsfsd7')); + $this->assertEquals('', $this->object->get_country_code('dsfsd')); + } + + public function testFind_city() + { + $orleans = $this->object->find_city('orléa'); + $this->assertTrue(is_array($orleans)); + foreach ($orleans as $potential) + { + $this->assertArrayHasKey('region', $potential); + $this->assertArrayHasKey('title_highlighted', $potential); + $this->assertArrayHasKey('country', $potential); + $this->assertArrayHasKey('title', $potential); + $this->assertArrayHasKey('country_highlighted', $potential); + $this->assertArrayHasKey('geoname_id', $potential); + $this->assertTrue(is_int($potential['geoname_id'])); + $this->assertTrue(is_string($potential['country_highlighted'])); + $this->assertTrue(is_string($potential['title'])); + $this->assertTrue(is_string($potential['country'])); + $this->assertTrue(is_string($potential['title_highlighted'])); + $this->assertTrue(is_string($potential['region'])); + } + } + + public function testFind_geoname_from_ip() + { + $result = $this->object->find_geoname_from_ip('80.12.81.18'); + $this->assertArrayHasKey('city', $result); + $this->assertArrayHasKey('country_code', $result); + $this->assertArrayHasKey('country', $result); + $this->assertArrayHasKey('fips', $result); + $this->assertArrayHasKey('longitude', $result); + $this->assertArrayHasKey('latitude', $result); + $this->assertEquals("Paris", $result['city']); + $this->assertEquals("FR", $result['country_code']); + $this->assertEquals("France", $result['country']); + } + +} + diff --git a/tests/http/http_requestTest.php b/tests/http/http_requestTest.php new file mode 100644 index 0000000000..08347215f9 --- /dev/null +++ b/tests/http/http_requestTest.php @@ -0,0 +1,219 @@ +object = new http_request(); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + + } + + /** + * @todo Implement testGetInstance(). + */ + public function testGetInstance() + { + $this->assertInstanceOf('http_request', http_request::getInstance()); + } + + /** + * @todo Implement testIs_ajax(). + */ + public function testIs_ajax() + { + $this->assertFalse($this->object->is_ajax()); + $_SERVER['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'; + $this->assertTrue($this->object->is_ajax()); + } + + /** + * @todo Implement testComes_from_flash(). + */ + public function testComes_from_flash() + { + $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null; + $this->assertFalse($this->object->comes_from_flash()); + $_SERVER['HTTP_USER_AGENT'] = 'Shockwave Flash'; + $this->assertTrue($this->object->comes_from_flash()); + $_SERVER['HTTP_USER_AGENT'] = 'Shockwave Flash Player'; + $this->assertTrue($this->object->comes_from_flash()); + $_SERVER['HTTP_USER_AGENT'] = 'Adobe Flash Player'; + $this->assertTrue($this->object->comes_from_flash()); + $_SERVER['HTTP_USER_AGENT'] = 'Adobe Flash Player 10'; + $this->assertTrue($this->object->comes_from_flash()); + $_SERVER['HTTP_USER_AGENT'] = 'Flash'; + $this->assertTrue($this->object->comes_from_flash()); + $_SERVER['HTTP_USER_AGENT'] = 'Flash Player'; + $this->assertTrue($this->object->comes_from_flash()); + $_SERVER['HTTP_USER_AGENT'] = 'Flash '; + $this->assertTrue($this->object->comes_from_flash()); + $_SERVER['HTTP_USER_AGENT'] = 'Flashs '; + $this->assertFalse($this->object->comes_from_flash()); + $_SERVER['HTTP_USER_AGENT'] = $user_agent; + $this->assertFalse($this->object->comes_from_flash()); + } + + /** + * @todo Implement testGet_code(). + */ + public function testGet_code() + { + $this->assertNull($this->object->get_code()); + $_SERVER['REDIRECT_STATUS'] = 301; + $this->assertEquals(301, $this->object->get_code()); + $this->object->set_code(580); + $this->assertEquals(580, $this->object->get_code()); + $this->object->set_code('a'); + $this->assertEquals(0, $this->object->get_code()); + $this->object->set_code('a'); + $this->assertEquals(0, $this->object->get_code()); + } + + /** + * @todo Implement testSet_code(). + */ + public function testSet_code() + { + $this->object->set_code(302); + $this->assertEquals(302, $this->object->get_code()); + } + + /** + * @todo Implement testGet_parms(). + */ + public function testGet_parms() + { + $_GET = array('lili' => '25', 'popo' => array('tip', 'top')); + $_POST = array('Plili' => '25', 'Gpopo' => array('mtip', 'btop')); + $parm = $this->object->get_parms('lili', 'Plili', 'popo', 'Gpopo', 'notexists'); + $this->assertEquals($_GET['lili'], $parm['lili']); + $this->assertEquals($_POST['Plili'], $parm['Plili']); + $this->assertEquals($_GET['popo'], $parm['popo']); + $this->assertEquals($_POST['Gpopo'], $parm['Gpopo']); + $this->assertNull($parm['notexists']); + + $parm = $this->object->get_parms( + array( + 'lili' => http_request::SANITIZE_NUMBER_INT + , 'Plili' + , 'popo' + , 'Gpopo' => http_request::SANITIZE_STRING + , 'notexists' => http_request::SANITIZE_STRING + ) + ); + + $this->assertEquals((int)$_GET['lili'], $parm['lili']); + $this->assertTrue(is_int($parm['lili'])); + $this->assertEquals($_POST['Plili'], $parm['Plili']); + $this->assertEquals($_GET['popo'], $parm['popo']); + $this->assertEquals('Array', $parm['Gpopo']); + $this->assertEquals('',$parm['notexists']); + + $_GET = $_POST = array(); + } + + /** + * @todo Implement testGet_parms_from_serialized_datas(). + */ + public function testGet_parms_from_serialized_datas() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testHas_post_datas(). + */ + public function testHas_post_datas() + { + $this->assertFalse($this->object->has_post_datas()); + $_POST = array('TOPPy'=>null); + $this->assertTrue($this->object->has_post_datas()); + } + + /** + * @todo Implement testGet_post_datas(). + */ + public function testGet_post_datas() + { + $post = $_POST = array('Plili' => '25', 'Gpopo' => array('mtip', 'btop')); + $this->assertEquals($post, $this->object->get_post_datas()); + } + + /** + * @todo Implement testHas_get_datas(). + */ + public function testHas_get_datas() + { + $this->assertFalse($this->object->has_get_datas()); + $_GET = array('TOPPy'=>null); + $this->assertTrue($this->object->has_get_datas()); + } + + /** + * @todo Implement testHas_datas(). + */ + public function testHas_datas() + { + $_POST = $_GET = array(); + $this->assertFalse($this->object->has_datas()); + $_POST = array('malal'=>true); + $this->assertTrue($this->object->has_datas()); + $_GET = array('malal'=>true); + $_POST = array(); + $this->assertTrue($this->object->has_datas()); + $_GET = array('malal'=>true); + $_POST = array('malal'=>true); + $this->assertTrue($this->object->has_datas()); + $_POST = $_GET = array(); + $this->assertFalse($this->object->has_datas()); + } + + /** + * @todo Implement testFilter(). + */ + public function testFilter() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testIs_command_line(). + */ + public function testIs_command_line() + { + $this->assertTrue($this->object->is_command_line()); + } + +} + +?> diff --git a/tests/lazaretFileTest.php b/tests/lazaretFileTest.php new file mode 100644 index 0000000000..6e4fcc4785 --- /dev/null +++ b/tests/lazaretFileTest.php @@ -0,0 +1,74 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testDelete(). + */ + public function testDelete() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testSubstitute(). + */ + public function testSubstitute() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testMove_uploaded_to_lazaret(). + */ + public function testMove_uploaded_to_lazaret() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testStream_thumbnail(). + */ + public function testStream_thumbnail() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/lazaretTest.php b/tests/lazaretTest.php new file mode 100644 index 0000000000..5635db9dc9 --- /dev/null +++ b/tests/lazaretTest.php @@ -0,0 +1,33 @@ +object = new lazaret; + } + + public function testIsOk() + { + $this->markTestIncomplete(); + foreach ($this->object->get_elements() as $element) + { + + } + } + +} + diff --git a/tests/listeTest.php b/tests/listeTest.php new file mode 100644 index 0000000000..0e1a7dd4a3 --- /dev/null +++ b/tests/listeTest.php @@ -0,0 +1,30 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/loginTest.php b/tests/loginTest.php new file mode 100644 index 0000000000..37509f5c9c --- /dev/null +++ b/tests/loginTest.php @@ -0,0 +1,85 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testRegister_enabled(). + */ + public function testRegister_enabled() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_language_selector(). + */ + public function testGet_language_selector() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_password_link(). + */ + public function testGet_password_link() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_register_link(). + */ + public function testGet_register_link() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_guest_link(). + */ + public function testGet_guest_link() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/logsTest.php b/tests/logsTest.php new file mode 100644 index 0000000000..d519c68d93 --- /dev/null +++ b/tests/logsTest.php @@ -0,0 +1,30 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/mailTest.php b/tests/mailTest.php new file mode 100644 index 0000000000..00aa21f48b --- /dev/null +++ b/tests/mailTest.php @@ -0,0 +1,173 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testSend_validation_results(). + */ + public function testSend_validation_results() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testHack_alert(). + */ + public function testHack_alert() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testFtp_sent(). + */ + public function testFtp_sent() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testFtp_receive(). + */ + public function testFtp_receive() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testSend_documents(). + */ + public function testSend_documents() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testForgot_passord(). + */ + public function testForgot_passord() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testRegister_confirm(). + */ + public function testRegister_confirm() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testRegister_user(). + */ + public function testRegister_user() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testReset_email(). + */ + public function testReset_email() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testMail_confirm_registered(). + */ + public function testMail_confirm_registered() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testMail_confirm_unregistered(). + */ + public function testMail_confirm_unregistered() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testMail_confirmation(). + */ + public function testMail_confirmation() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testSend_mail(). + */ + public function testSend_mail() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/media/Permalink/media_Permalink_AdapterTest.php b/tests/media/Permalink/media_Permalink_AdapterTest.php new file mode 100644 index 0000000000..d3535c3d5b --- /dev/null +++ b/tests/media/Permalink/media_Permalink_AdapterTest.php @@ -0,0 +1,155 @@ +get_databox(); + static::$object = media_Permalink_Adapter::getPermalink($databox, self::$record_1->get_subdef('document')); + } + + public static function tearDownAfterClass() + { + parent::tearDownAfterClass(); + } + + public function testGetPermalink() + { + $this->assertTrue((static::$object instanceof media_Permalink_Adapter)); + } + + public function testSet_is_activated() + { + static::$object->set_is_activated(true); + $this->assertTrue(static::$object->get_is_activated()); + static::$object->set_is_activated(false); + $this->assertFalse(static::$object->get_is_activated()); + static::$object->set_is_activated(true); + $this->assertTrue(static::$object->get_is_activated()); + } + + public function testSet_label() + { + static::$object->set_label('coucou les chicos'); + $this->assertEquals('coucou-les-chicos', static::$object->get_label()); + static::$object->set_label(''); + $this->assertEquals('', static::$object->get_label()); + static::$object->set_label('JE ANp ra&é"\/,;:!§/.?%µ*ù$]@^\[{#~234567890°+\'(-è_çà'); + $this->assertEquals('JE-ANp-raeu234567890-e_ca', static::$object->get_label()); + } + + public function testGet_url() + { + $registry = registry::get_instance(); + $url = $registry->get('GV_ServerName') . 'permalink/v1/' . static::$object->get_label() . '/' . self::$record_1->get_sbas_id() . '/' . self::$record_1->get_record_id() . '/' . + static::$object->get_token() . '/document/'; + + $this->assertEquals($url, static::$object->get_url($registry)); + } + + /** + * @todo Implement testGet_page(). + */ + public function testGet_page() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_id(). + */ + public function testGet_id() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_token(). + */ + public function testGet_token() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_is_activated(). + */ + public function testGet_is_activated() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + $this->assertTrue(is_bool(static::$object->get_is_activated)); + } + + /** + * @todo Implement testGet_created_on(). + */ + public function testGet_created_on() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_last_modified(). + */ + public function testGet_last_modified() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_label(). + */ + public function testGet_label() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testCreate(). + */ + public function testCreate() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/metadata/description/descriptionTest.php b/tests/metadata/description/descriptionTest.php new file mode 100644 index 0000000000..6264355446 --- /dev/null +++ b/tests/metadata/description/descriptionTest.php @@ -0,0 +1,81 @@ +metadatas = databox::get_available_metadatas(); + } + + public function testMetadatas() + { + foreach($this->metadatas as $metadata) + { + $this->assertInstanceOf('metadata_Interface', $metadata); + $this->assertInstanceOf('metadata_Abstract', $metadata); + + $this->assertTrue(is_bool($metadata::is_multi())); + $this->assertEquals($metadata::MULTI, $metadata::is_multi()); + + $this->assertTrue(is_bool($metadata::is_deprecated())); + $this->assertEquals($metadata::DEPRECATED, $metadata::is_deprecated()); + + $this->assertTrue(is_bool($metadata::is_readonly())); + $this->assertEquals($metadata::READONLY, $metadata::is_readonly()); + + $this->assertTrue(is_bool($metadata:: is_mandatory())); + $this->assertEquals($metadata::MANDATORY, $metadata::is_mandatory()); + + $this->assertTrue(is_array($metadata::available_values())); + foreach($metadata::available_values() as $value) + { + $this->assertTrue(is_string($value)); + } + + $this->assertTrue(is_string($metadata::get_tagname())); + $this->assertEquals($metadata::TAGNAME, $metadata::get_tagname()); + + $this->assertTrue(is_string($metadata::get_type())); + $this->assertEquals($metadata::TYPE, $metadata::get_type()); + + $this->assertTrue(is_string($metadata::get_namespace())); + $this->assertEquals($metadata::NAME_SPACE, $metadata::get_namespace()); + + $this->assertEquals($metadata::SOURCE, $metadata::get_source()); + if($metadata instanceof metadata_description_nosource) + { + $this->assertNull ($metadata::get_source()); + } + else + { + $this->assertTrue(is_string($metadata::get_source())); + $this->assertTrue(strpos($metadata::get_source(), '/rdf:RDF/rdf:Description/') === 0, get_class($metadata)); + } + + $this->assertEquals($metadata::MIN_LENGTH, $metadata::minlength()); + if(!is_null($metadata::minlength())) + { + $this->assertTrue(is_int($metadata::minlength())); + $this->assertTrue($metadata::minlength()>=0); + } + + $this->assertEquals($metadata::MAX_LENGTH, $metadata::maxlength()); + if(!is_null($metadata::maxlength())) + { + $this->assertTrue(is_int($metadata::maxlength())); + $this->assertTrue($metadata::maxlength()>=0); + } + } + } + +} + diff --git a/tests/module/console/module_console_aboutAuthorsTest.php b/tests/module/console/module_console_aboutAuthorsTest.php new file mode 100644 index 0000000000..1baa6c13b0 --- /dev/null +++ b/tests/module/console/module_console_aboutAuthorsTest.php @@ -0,0 +1,43 @@ +add(new module_console_aboutAuthors('about:authors')); + + $command = $application->find('about:authors'); + $commandTester = new CommandTester($command); + $commandTester->execute(array('command' => $command->getName())); + + $this->assertEquals( + trim(file_get_contents(__DIR__ . '/../../../AUTHORS')) + , trim($commandTester->getDisplay()) + ); + } + +} + +?> diff --git a/tests/module/console/module_console_aboutLicenseTest.php b/tests/module/console/module_console_aboutLicenseTest.php new file mode 100644 index 0000000000..5e6f743c82 --- /dev/null +++ b/tests/module/console/module_console_aboutLicenseTest.php @@ -0,0 +1,40 @@ +add(new module_console_aboutLicense('about:license')); + + $command = $application->find('about:license'); + $commandTester = new CommandTester($command); + $commandTester->execute(array('command' => $command->getName())); + + $this->assertEquals( + trim(file_get_contents(__DIR__ . '/../../../LICENSE')) + , trim($commandTester->getDisplay()) + ); + } + +} + +?> diff --git a/tests/module/console/module_console_schedulerStartTest.php b/tests/module/console/module_console_schedulerStartTest.php new file mode 100644 index 0000000000..e116e13552 --- /dev/null +++ b/tests/module/console/module_console_schedulerStartTest.php @@ -0,0 +1,50 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/module/console/module_console_schedulerStateTest.php b/tests/module/console/module_console_schedulerStateTest.php new file mode 100644 index 0000000000..6e69506805 --- /dev/null +++ b/tests/module/console/module_console_schedulerStateTest.php @@ -0,0 +1,35 @@ +add(new module_console_schedulerState('system:schedulerState')); + + $command = $application->find('system:schedulerState'); + $commandTester = new CommandTester($command); + $commandTester->execute(array('command' => $command->getName())); + + $task_manager = new task_manager(appbox::get_instance(\bootstrap::getCore())); + $state = $task_manager->get_scheduler_state(); + + $sentence = sprintf('Scheduler is %s', $state['status']); + $this->assertTrue(strpos($commandTester->getDisplay(), $sentence) !== false); + + } + + +} + +?> diff --git a/tests/module/console/module_console_systemTemplateGeneratorTest.php b/tests/module/console/module_console_systemTemplateGeneratorTest.php new file mode 100644 index 0000000000..c327213ddc --- /dev/null +++ b/tests/module/console/module_console_systemTemplateGeneratorTest.php @@ -0,0 +1,32 @@ +add(new module_console_systemTemplateGenerator('system:templateGenerator')); + + $command = $application->find('system:templateGenerator'); + $commandTester = new CommandTester($command); + $commandTester->execute(array('command' => $command->getName())); + + $last_line = array_pop(explode("\n", trim($commandTester->getDisplay()))); + + $this->assertTrue(strpos($last_line, 'templates failed') === false, 'Some templates failed'); + $this->assertTrue(strpos($last_line, 'templates generated') !== true, 'Some templates have been generated'); + } + + +} + +?> diff --git a/tests/module/console/module_console_systemUpgradeTest.php b/tests/module/console/module_console_systemUpgradeTest.php new file mode 100644 index 0000000000..5822ae2361 --- /dev/null +++ b/tests/module/console/module_console_systemUpgradeTest.php @@ -0,0 +1,50 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/module/console/module_console_tasklistTest.php b/tests/module/console/module_console_tasklistTest.php new file mode 100644 index 0000000000..fd0291834a --- /dev/null +++ b/tests/module/console/module_console_tasklistTest.php @@ -0,0 +1,47 @@ +add(new module_console_tasklist('task:list')); + + $command = $application->find('task:list'); + $commandTester = new CommandTester($command); + $commandTester->execute(array('command' => $command->getName())); + + $task_manager = new task_manager(appbox::get_instance(\bootstrap::getCore())); + $lines = explode("\n", trim($commandTester->getDisplay())); + + if(count($task_manager->get_tasks()) > 0) + { + $this->assertEquals(count($task_manager->get_tasks()), count($lines)); + foreach($task_manager->get_tasks() as $task) + { + $this->assertTrue(strpos($commandTester->getDisplay(), $task->get_title()) !== false); + } + } + else + { + $this->assertEquals(1, $n_lines); + } + } + +} + +?> diff --git a/tests/module/console/module_console_taskrunTest.php b/tests/module/console/module_console_taskrunTest.php new file mode 100644 index 0000000000..710f49cc2e --- /dev/null +++ b/tests/module/console/module_console_taskrunTest.php @@ -0,0 +1,50 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/p4fieldTest.php b/tests/p4fieldTest.php new file mode 100644 index 0000000000..522f9a2c25 --- /dev/null +++ b/tests/p4fieldTest.php @@ -0,0 +1,41 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testIsno(). + */ + public function testIsno() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/p4fileTest.php b/tests/p4fileTest.php new file mode 100644 index 0000000000..d94cbee1b0 --- /dev/null +++ b/tests/p4fileTest.php @@ -0,0 +1,63 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testArchiveFile(). + */ + public function testArchiveFile() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testCheck_file_error(). + */ + public function testCheck_file_error() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testSubstitute(). + */ + public function testSubstitute() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/p4stringTest.php b/tests/p4stringTest.php new file mode 100644 index 0000000000..fcaf1b9880 --- /dev/null +++ b/tests/p4stringTest.php @@ -0,0 +1,148 @@ +assertEquals('./', p4string::addFirstSlash($string)); + $string = '/'; + $this->assertEquals('/', p4string::addFirstSlash($string)); + $string = '//'; + $this->assertEquals('//', p4string::addFirstSlash($string)); + $string = '\\'; + $this->assertEquals('\\', p4string::addFirstSlash($string)); + $string = '\\\\'; + $this->assertEquals('\\\\', p4string::addFirstSlash($string)); + $string = 'alalal'; + $this->assertEquals('/alalal', p4string::addFirstSlash($string)); + } + + public function testDelFirstSlash() + { + $string = ''; + $this->assertEquals('./', p4string::delFirstSlash($string)); + $string = '/'; + $this->assertEquals('', p4string::delFirstSlash($string)); + $string = '//'; + $this->assertEquals('/', p4string::delFirstSlash($string)); + $string = '\\'; + $this->assertEquals('', p4string::delFirstSlash($string)); + $string = '\\\\'; + $this->assertEquals('\\', p4string::delFirstSlash($string)); + $string = '/alalal/'; + $this->assertEquals('alalal/', p4string::delFirstSlash($string)); + } + + public function testAddEndSlash() + { + $string = ''; + $this->assertEquals('./', p4string::addEndSlash($string)); + $string = '/'; + $this->assertEquals('/', p4string::addEndSlash($string)); + $string = '//'; + $this->assertEquals('//', p4string::addEndSlash($string)); + $string = '\\'; + $this->assertEquals('\\', p4string::addEndSlash($string)); + $string = '\\\\'; + $this->assertEquals('\\\\', p4string::addEndSlash($string)); + $string = '/alalal/'; + $this->assertEquals('/alalal/', p4string::addEndSlash($string)); + } + + public function testDelEndSlash() + { + $string = ''; + $this->assertEquals('.', p4string::delEndSlash($string)); + $string = '/'; + $this->assertEquals('', p4string::delEndSlash($string)); + $string = '//'; + $this->assertEquals('/', p4string::delEndSlash($string)); + $string = '\\'; + $this->assertEquals('', p4string::delEndSlash($string)); + $string = '\\\\'; + $this->assertEquals('\\', p4string::delEndSlash($string)); + $string = '/alalal/'; + $this->assertEquals('/alalal', p4string::delEndSlash($string)); + } + + public function testCleanTags() + { + $string = ' yuh i jkn lkk jk '; + $this->assertEquals($string, p4string::cleanTags($string)); + $stringb = ' yuh i jkn lkk jk '; + $this->assertEquals($string, p4string::cleanTags($stringb)); + } + + public function testJSstring() + { + $this->assertEquals('babébibobu & marcel \'\"', p4string::JSstring('babébibobu & marcel \'"')); + } + + public function testMakeString() + { + /** + * @deprecated + */ + } + + public function testHasAccent() + { + $this->assertTrue(p4string::hasAccent('azertyuéjn')); + $this->assertFalse(p4string::hasAccent('azertyujn')); + $this->assertFalse(p4string::hasAccent('')); + $this->assertTrue(p4string::hasAccent('é')); + } + + public function testJsonencode() + { + $a = new stdClass(); + $a->prout = 'pue'; + $a->couleur = array('marron', 'jaune'); + $a->compteur = array('incrémental' => true, 'fiente' => 'vrai'); + $this->assertEquals('{"prout":"pue","couleur":["marron","jaune"],"compteur":{"incr\u00e9mental":true,"fiente":"vrai"}}', p4string::jsonencode($a)); + + $b = array('un', 'petit' => 'tout petit', 'cul', 1 => 'qui', 10 => 'roule'); + $this->assertEquals('{"0":"un","petit":"tout petit","1":"qui","10":"roule"}', p4string::jsonencode($b)); + + $c = array('gros', 'chien'); + $this->assertEquals('["gros","chien"]', p4string::jsonencode($c)); + } + + public function testFormat_octets() + { + $size = 1024; + $this->assertEquals('1 ko', p4string::format_octets($size)); + $size = 824; + $this->assertEquals('824 o', p4string::format_octets($size)); + $size = 102; + $this->assertEquals('102 o', p4string::format_octets($size)); + $size = 10245335; + $this->assertRegExp('/^9[,\.]{1}77 Mo$/', p4string::format_octets($size)); + $size = 10245335; + $this->assertRegExp('/^9[,\.]{1}771 Mo$/', p4string::format_octets($size, 3)); + $this->assertEquals('10 Mo', p4string::format_octets($size, 0)); + $size = 9990245335123153; + $this->assertRegexp('/^9086[,\.]{1}08 To$/', p4string::format_octets($size)); + $size = 2048; + $this->assertEquals('2 ko', p4string::format_octets($size)); + } + + public function testFormat_seconds() + { + $this->assertEquals('07:38', p4string::format_seconds(458)); + $this->assertEquals('15:46:31', p4string::format_seconds(56791)); + $this->assertEquals('2737:59:51', p4string::format_seconds(9856791)); + $this->assertEquals('00:00', p4string::format_seconds(0)); + $this->assertEquals('', p4string::format_seconds(-15)); + } + +} + diff --git a/tests/phraseaTest.php b/tests/phraseaTest.php new file mode 100644 index 0000000000..089c736ae2 --- /dev/null +++ b/tests/phraseaTest.php @@ -0,0 +1,216 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testIs_scheduler_started(). + */ + public function testIs_scheduler_started() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testStart(). + */ + public function testStart() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGetHome(). + */ + public function testGetHome() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testClear_sbas_params(). + */ + public function testClear_sbas_params() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testSbas_params(). + */ + public function testSbas_params() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGuest_allowed(). + */ + public function testGuest_allowed() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testLoad_events(). + */ + public function testLoad_events() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testUse_i18n(). + */ + public function testUse_i18n() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testModulesName(). + */ + public function testModulesName() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testSbasFromBas(). + */ + public function testSbasFromBas() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testBaseFromColl(). + */ + public function testBaseFromColl() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testCollFromBas(). + */ + public function testCollFromBas() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testSbas_names(). + */ + public function testSbas_names() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testBas_names(). + */ + public function testBas_names() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testRedirect(). + */ + public function testRedirect() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testHeaders(). + */ + public function testHeaders() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testScheduler_key(). + */ + public function testScheduler_key() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + diff --git a/tests/phraseadateTest.php b/tests/phraseadateTest.php new file mode 100644 index 0000000000..b3f0d26453 --- /dev/null +++ b/tests/phraseadateTest.php @@ -0,0 +1,86 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGetDate(). + */ + public function testGetDate() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGetPrettyString(). + */ + public function testGetPrettyString() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testFormat_mysql(). + */ + public function testFormat_mysql() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testIsodateToDate(). + */ + public function testIsodateToDate() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testDateToIsodate(). + */ + public function testDateToIsodate() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/queriesTest.php b/tests/queriesTest.php new file mode 100644 index 0000000000..4643d28c34 --- /dev/null +++ b/tests/queriesTest.php @@ -0,0 +1,63 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testTopics_exists(). + */ + public function testTopics_exists() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testDropdown_topics(). + */ + public function testDropdown_topics() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testHistory(). + */ + public function testHistory() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/randomTest.php b/tests/randomTest.php new file mode 100644 index 0000000000..16c82a4e5a --- /dev/null +++ b/tests/randomTest.php @@ -0,0 +1,168 @@ +get_id(); + $token = random::getUrlToken(\random::TYPE_PASSWORD, $usr_id, $expires_on, 'some nice datas'); + random::cleanTokens(); + + try + { + random::helloToken($token); + $this->fail(); + } + catch (Exception_NotFound $e) + { + + } + } + + public function testGeneratePassword() + { + $this->assertRegExp('/[a-zA-Z]{4}/', random::generatePassword(4, random::LETTERS)); + $this->assertRegExp('/[a-zA-Z]{8}/', random::generatePassword(8, random::LETTERS)); + $this->assertRegExp('/[a-zA-Z]{16}/', random::generatePassword(16, random::LETTERS)); + $this->assertRegExp('/[a-zA-Z]{32}/', random::generatePassword(32, random::LETTERS)); + $this->assertRegExp('/[a-zA-Z]{64}/', random::generatePassword(64, random::LETTERS)); + $this->assertRegExp('/[a-zA-Z0-9]{4}/', random::generatePassword(4, random::LETTERS_AND_NUMBERS)); + $this->assertRegExp('/[a-zA-Z0-9]{8}/', random::generatePassword(8, random::LETTERS_AND_NUMBERS)); + $this->assertRegExp('/[a-zA-Z0-9]{16}/', random::generatePassword(16, random::LETTERS_AND_NUMBERS)); + $this->assertRegExp('/[a-zA-Z0-9]{32}/', random::generatePassword(32, random::LETTERS_AND_NUMBERS)); + $this->assertRegExp('/[a-zA-Z0-9]{64}/', random::generatePassword(64, random::LETTERS_AND_NUMBERS)); + $this->assertRegExp('/[0-9]{4}/', random::generatePassword(4, random::NUMBERS)); + $this->assertRegExp('/[0-9]{8}/', random::generatePassword(8, random::NUMBERS)); + $this->assertRegExp('/[0-9]{16}/', random::generatePassword(16, random::NUMBERS)); + $this->assertRegExp('/[0-9]{32}/', random::generatePassword(32, random::NUMBERS)); + $this->assertRegExp('/[0-9]{64}/', random::generatePassword(64, random::NUMBERS)); + try + { + random::generatePassword('gros caca', random::NUMBERS); + $this->fail('An invalid argument exception should have been triggered'); + } + catch (Exception_InvalidArgument $e) + { + + } + try + { + random::generatePassword('012', random::NUMBERS); + $this->fail('An invalid argument exception should have been triggered'); + } + catch (Exception_InvalidArgument $e) + { + + } + try + { + random::generatePassword('caca007', random::NUMBERS); + $this->fail('An invalid argument exception should have been triggered'); + } + catch (Exception_InvalidArgument $e) + { + + } + } + + public function testGetUrlToken() + { + $usr_id = self::$user->get_id(); + $token = random::getUrlToken(\random::TYPE_PASSWORD, $usr_id, null, 'some nice datas'); + $datas = random::helloToken($token); + $this->assertEquals('some nice datas', $datas['datas']); + random::updateToken($token, 'some very nice datas'); + $datas = random::helloToken($token); + $this->assertEquals('some very nice datas', $datas['datas']); + random::removeToken($token); + } + + public function testRemoveToken() + { + $this->testGetUrlToken(); + } + + /** + * @todo Implement testUpdateToken(). + */ + public function testUpdateToken() + { + $this->testGetUrlToken(); + } + + public function testHelloToken() + { + $usr_id = self::$user->get_id(); + $token = random::getUrlToken(\random::TYPE_PASSWORD, $usr_id, null, 'some nice datas'); + $datas = random::helloToken($token); + $this->assertEquals('some nice datas', $datas['datas']); + $this->assertNull($datas['expire_on']); + $created_on = new DateTime($datas['created_on']); + $date = new DateTime('-3 seconds'); + $this->assertTrue($date < $created_on); + $date = new DateTime(); + $this->assertTrue($date >= $created_on); + $this->assertEquals('password', $datas['type']); + + random::removeToken($token); + try + { + random::helloToken($token); + $this->fail(); + } + catch (Exception_NotFound $e) + { + + } + + $expires_on = new DateTime('+5 minutes'); + $usr_id = self::$user->get_id(); + $token = random::getUrlToken(\random::TYPE_PASSWORD, $usr_id, $expires_on, 'some nice datas'); + $datas = random::helloToken($token); + $this->assertEquals('some nice datas', $datas['datas']); + $sql_expires = new DateTime($datas['expire_on']); + $this->assertTrue($sql_expires == $expires_on); + $created_on = new DateTime($datas['created_on']); + $date = new DateTime('-3 seconds'); + $this->assertTrue($date < $created_on); + $date = new DateTime(); + $this->assertTrue($date >= $created_on); + $this->assertEquals('password', $datas['type']); + + random::removeToken($token); + try + { + random::helloToken($token); + $this->fail(); + } + catch (Exception_NotFound $e) + { + + } + + + $expires_on = new DateTime('-5 minutes'); + $usr_id = self::$user->get_id(); + $token = random::getUrlToken(\random::TYPE_PASSWORD, $usr_id, $expires_on, 'some nice datas'); + + try + { + random::helloToken($token); + $this->fail(); + } + catch (Exception_NotFound $e) + { + + } + } + +} + diff --git a/tests/record/adapterTest.php b/tests/record/adapterTest.php new file mode 100644 index 0000000000..cf2558dfb8 --- /dev/null +++ b/tests/record/adapterTest.php @@ -0,0 +1,580 @@ +get_hd_file(); + $databox = self::$record_1->get_databox(); + $metadatas = $system_file->extract_metadatas($databox->get_meta_structure()); + static::$record_1->set_metadatas($metadatas['metadatas']); + + $databox = self::$record_23->get_databox(); + $system_file = self::$record_23->get_hd_file(); + $metadatas = $system_file->extract_metadatas($databox->get_meta_structure()); + static::$record_23->set_metadatas($metadatas['metadatas']); + + + /** + * Reset thumbtitle in order to have consistent tests (testGet_title) + */ + foreach(static::$record_1->get_databox()->get_meta_structure() as $databox_field) + { + + /* @var $databox_field \databox_field */ + $databox_field->set_thumbtitle(false)->save(); + } + + + $system_file = new system_file(__DIR__ . '/../testfiles/cestlafete.jpg'); + } + + public static function tearDownAfterClass() + { + parent::tearDownAfterClass(); + } + + public function testGet_creation_date() + { + $date_obj = new DateTime(); + $this->assertTrue((static::$record_1->get_creation_date() instanceof DateTime)); + $this->assertTrue((static::$record_1->get_creation_date() <= $date_obj)); + } + + protected function assertDateAtom($date) + { + return $this->assertRegExp('/\d{4}[-]\d{2}[-]\d{2}[T]\d{2}[:]\d{2}[:]\d{2}[+]\d{2}[:]\d{2}/', $date); + } + + public function testGet_uuid() + { + $this->assertTrue(uuid::is_valid(static::$record_1->get_uuid())); + } + + public function testGet_modification_date() + { + $date_obj = new DateTime(); + $this->assertTrue((static::$record_1->get_creation_date() instanceof DateTime)); + $this->assertTrue((static::$record_1->get_creation_date() <= $date_obj)); + } + + public function testGet_number() + { + self::$record_1->set_number(24); + $this->assertEquals(24, self::$record_1->get_number()); + self::$record_1->set_number(42); + $this->assertEquals(42, self::$record_1->get_number()); + self::$record_1->set_number(0); + $this->assertEquals(0, self::$record_1->get_number()); + self::$record_1->set_number(null); + $this->assertEquals(0, self::$record_1->get_number()); + } + + public function testSet_number() + { + $this->testGet_number(); + } + + public function testSet_type() + { + try + { + self::$record_1->set_type('jambon'); + $this->fail(); + } + catch (Exception $e) + { + + } + $old_type = self::$record_1->get_type(); + self::$record_1->set_type('video'); + $this->assertEquals('video', self::$record_1->get_type()); + self::$record_1->set_type($old_type); + $this->assertEquals($old_type, self::$record_1->get_type()); + } + + public function testIs_grouping() + { + $this->assertFalse(self::$record_1->is_grouping()); + $this->assertTrue(self::$story_1->is_grouping()); + } + + public function testGet_base_id() + { + $this->assertTrue(is_int(static::$record_1->get_base_id())); + $this->assertEquals(self::$collection->get_base_id(), static::$record_1->get_base_id()); + $this->assertTrue(is_int(self::$story_1->get_base_id())); + $this->assertEquals(self::$collection->get_base_id(), self::$story_1->get_base_id()); + } + + public function testGet_record_id() + { + $this->assertTrue(is_int(static::$record_1->get_record_id())); + $this->assertTrue(is_int(self::$story_1->get_record_id())); + } + + public function testGet_thumbnail() + { + $this->assertTrue((static::$record_1->get_thumbnail() instanceof media_subdef)); + } + + public function testGet_embedable_medias() + { + $embeddables = self::$record_1->get_embedable_medias(); + $this->assertTrue(is_array($embeddables)); + foreach ($embeddables as $subdef) + { + $this->assertInstanceOf('media_subdef', $subdef); + } + } + + public function testGet_status_icons() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testGet_type() + { + $this->assertTrue(in_array(static::$record_1->get_type(), array('video', 'audio', 'image', 'document', 'flash', 'unknown'))); + } + + public function testGet_formated_duration() + { + $this->assertEquals('00:17', self::$record_23->get_formated_duration()); + $this->assertEquals('', self::$record_1->get_formated_duration()); + } + + public function testGet_duration() + { + $this->assertEquals(17, self::$record_23->get_duration()); + $this->assertEquals(false, self::$record_1->get_duration()); + } + + public function testGet_rollover_thumbnail() + { + $this->assertInstanceOf('media_subdef', self::$record_23->get_rollover_thumbnail()); + $this->assertNull(self::$record_1->get_rollover_thumbnail()); + } + + public function testGenerate_subdefs() + { + + } + + public function testGet_sha256() + { + $this->assertNotNull(static::$record_1->get_sha256()); + $this->assertRegExp('/[a-zA-Z0-9]{64}/', static::$record_1->get_sha256()); + $this->assertNull(self::$story_1->get_sha256()); + } + + public function testGet_mime() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $found = $coll = false; + foreach ($appbox->get_databoxes() as $databox) + { + foreach ($databox->get_collections() as $collection) + { + $found = true; + $coll = $collection; + break; + } + if ($found) + break; + } + if (!($coll instanceof collection)) + $this->fail('Unable to find a collection'); + + $record = record_adapter::create($coll, new system_file(__DIR__ . '/../testfiles/cestlafete.jpg')); + + $this->assertEquals('image/jpeg', $record->get_mime()); + $record->delete(); + } + + public function testGet_status() + { + $this->assertRegExp('/[01]{64}/', static::$record_1->get_status()); + } + + public function testGet_subdef() + { + $this->assertInstanceOf('media_subdef', self::$record_1->get_subdef('document')); + $this->assertInstanceOf('media_subdef', self::$record_1->get_subdef('preview')); + $this->assertInstanceOf('media_subdef', self::$record_1->get_subdef('thumbnail')); + $this->assertInstanceOf('media_subdef', self::$record_23->get_subdef('document')); + $this->assertInstanceOf('media_subdef', self::$record_23->get_subdef('preview')); + $this->assertInstanceOf('media_subdef', self::$record_23->get_subdef('thumbnail')); + $this->assertInstanceOf('media_subdef', self::$record_23->get_subdef('thumbnailGIF')); + } + + public function testGet_subdefs() + { + $subdefs = static::$record_1->get_subdefs(); + $this->assertTrue(is_array($subdefs)); + foreach ($subdefs as $subdef) + { + $this->assertInstanceOf('media_subdef', $subdef); + } + } + + /** + * @todo Implement testGet_collection_logo(). + */ + public function testGet_collection_logo() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testGet_technical_infos() + { + $this->assertTrue(is_array(static::$record_1->get_technical_infos())); + } + + public function testGet_caption() + { + $this->assertTrue((static::$record_1->get_caption() instanceof caption_record)); + } + + public function testGet_original_name() + { + $this->assertTrue(static::$record_1->get_original_name() === self::$record_sf_1->getFilename()); + } + + public function testGet_title() + { + $this->assertEquals(static::$record_sf_1->getFilename(), static::$record_1->get_title()); + $this->assertEquals(static::$record_sf_23->getFilename(), static::$record_23->get_title()); + } + + public function testGet_preview() + { + $this->assertTrue((static::$record_1->get_preview() instanceof media_subdef)); + } + + public function testHas_preview() + { + $this->assertTrue(self::$record_1->has_preview()); + $this->assertTrue(self::$record_23->has_preview()); + } + + public function testGet_serialize_key() + { + $this->assertTrue(static::$record_1->get_serialize_key() == static::$record_1->get_sbas_id() . '_' . static::$record_1->get_record_id()); + } + + public function testGet_sbas_id() + { + $this->assertTrue(is_int(static::$record_1->get_sbas_id())); + } + + public function testSubstitute_subdef() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testSet_metadatas() + { + + $meta_structure_el = self::$collection->get_databox()->get_meta_structure()->get_elements(); + + $current_caption = self::$record_1->get_caption(); + + $metadatas = array(); + + foreach ($meta_structure_el as $meta_el) + { + $current_fields = $current_caption->get_fields(array($meta_el->get_name())); + + $field = null; + + if (count($current_fields) > 0) + { + $field = array_pop($current_fields); + } + + if($meta_el->is_multi()) + { + if($field) + { + foreach($field->get_values() as $value) + { + $metadatas[] = array( + 'meta_struct_id' => $meta_el->get_id() + , 'meta_id' => $value->getId() + , 'value' => '' + ); + } + } + + $metadatas[] = array( + 'meta_struct_id' => $meta_el->get_id() + , 'meta_id' => null + , 'value' => 'un' + ); + $metadatas[] = array( + 'meta_struct_id' => $meta_el->get_id() + , 'meta_id' => null + , 'value' => 'jeu' + ); + $metadatas[] = array( + 'meta_struct_id' => $meta_el->get_id() + , 'meta_id' => null + , 'value' => 'de' + ); + $metadatas[] = array( + 'meta_struct_id' => $meta_el->get_id() + , 'meta_id' => null + , 'value' => 'test' + ); + } + else + { + $meta_id = null; + + if($field) + { + $meta_id = array_pop($field->get_values())->getId(); + } + + $metadatas[] = array( + 'meta_struct_id' => $meta_el->get_id() + , 'meta_id' => $meta_id + , 'value' => 'un premier jeu de test' + ); + + $metadatas[] = array( + 'meta_struct_id' => $meta_el->get_id() + , 'meta_id' => $meta_id + , 'value' => 'un second jeu de test' + ); + } + } + + self::$record_1->set_metadatas($metadatas, true); + + $caption = self::$record_1->get_caption(); + + + + foreach ($meta_structure_el as $meta_el) + { + $current_fields = $caption->get_fields(array($meta_el->get_name())); + + $this->assertEquals(1, count($current_fields)); + $field = $current_fields[0]; + + $separator = $meta_el->get_separator(); + + if(strlen($separator) > 0) + { + $separator = $separator[0]; + } + else + { + $separator = ''; + } + + $multi_imploded = implode(' ' . $separator . ' ', array('un', 'jeu', 'de', 'test')); + + if ($meta_el->is_multi()) + { + $initial_values = array(); + foreach($field->get_values() as $value) + { + $initial_values[] = $value->getValue(); + } + + $this->assertEquals($multi_imploded, implode(' ' . $meta_el->get_separator() . ' ', $initial_values)); + $this->assertEquals($multi_imploded, $field->get_serialized_values()); + } + else + $this->assertEquals('un second jeu de test', $field->get_serialized_values()); + } + } + + public function testReindex() + { + self::$record_1->reindex(); + $sql = 'SELECT record_id FROM record + WHERE (status & 7) IN (4,5,6) AND record_id = :record_id'; + $stmt = self::$record_1->get_databox()->get_connection()->prepare($sql); + + $stmt->execute(array(':record_id' => self::$record_1->get_record_id())); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + $stmt->closeCursor(); + + if (!$row) + $this->fail(); + if ($row['record_id'] != self::$record_1->get_record_id()) + $this->fail(); + } + + public function testRebuild_subdefs() + { + + self::$record_1->rebuild_subdefs(); + $sql = 'SELECT record_id + FROM record + WHERE jeton & ' . JETON_MAKE_SUBDEF . ' > 0 + AND record_id = :record_id'; + $stmt = self::$record_1->get_databox()->get_connection()->prepare($sql); + + $stmt->execute(array(':record_id' => self::$record_1->get_record_id())); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + $stmt->closeCursor(); + + if (!$row) + $this->fail(); + if ($row['record_id'] != self::$record_1->get_record_id()) + $this->fail(); + } + + public function testWrite_metas() + { + self::$record_1->write_metas(); + $sql = 'SELECT record_id, coll_id, jeton + FROM record WHERE (jeton & ' . JETON_WRITE_META . ' > 0) + AND record_id = :record_id'; + $stmt = self::$record_1->get_databox()->get_connection()->prepare($sql); + + $stmt->execute(array(':record_id' => self::$record_1->get_record_id())); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + $stmt->closeCursor(); + + if (!$row) + $this->fail(); + if ($row['record_id'] != self::$record_1->get_record_id()) + $this->fail(); + } + + /** + * @todo Implement testSet_binary_status(). + */ + public function testSet_binary_status() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testGet_record_by_sha() + { + $tmp_records = record_adapter::get_record_by_sha(self::$record_1->get_sbas_id(), self::$record_1->get_sha256()); + $this->assertTrue(is_array($tmp_records)); + + foreach ($tmp_records as $tmp_record) + { + $this->assertInstanceOf('record_adapter', $tmp_record); + $this->assertEquals(self::$record_1->get_sha256(), $tmp_record->get_sha256()); + } + + $tmp_records = record_adapter::get_record_by_sha(self::$record_1->get_sbas_id(), self::$record_1->get_sha256(), self::$record_1->get_record_id()); + $this->assertTrue(is_array($tmp_records)); + $this->assertTrue(count($tmp_records) === 1); + + foreach ($tmp_records as $tmp_record) + { + $this->assertInstanceOf('record_adapter', $tmp_record); + $this->assertEquals(self::$record_1->get_sha256(), $tmp_record->get_sha256()); + $this->assertEquals(self::$record_1->get_record_id(), $tmp_record->get_record_id()); + } + } + + public function testGet_hd_file() + { + $this->assertInstanceOf('system_file', self::$record_1->get_hd_file()); + } + + /** + * @todo Implement testLog_view(). + */ + public function testLog_view() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testRotate_subdefs() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet_container_baskets(). + */ + public function testGet_container_baskets() + { + $em = self::$core->getEntityManager(); + + $basket = $this->insertOneBasket(); + $this->assertInstanceOf('\Entities\Basket', $basket); + + /* @var $basket \Entities\Basket */ + $basket_element = new \Entities\BasketElement(); + $basket_element->setRecord(self::$record_1); + $basket_element->setBasket($basket); + + $em->persist($basket_element); + + $basket->addBasketElement($basket_element); + $basket = $em->merge($basket); + + $em->flush(); + + $found = $sselcont_id = false; + + $sbas_id = self::$record_1->get_sbas_id(); + $record_id = self::$record_1->get_record_id(); + + foreach (self::$record_1->get_container_baskets() as $c_basket) + { + if ($c_basket->getId() == $basket->getId()) + { + $found = true; + foreach ($c_basket->getElements() as $b_el) + { + if ($b_el->getRecord()->get_record_id() == $record_id && $b_el->getRecord()->get_sbas_id() == $sbas_id) + $sselcont_id = $b_el->getId(); + } + } + } + + + if (!$found) + $this->fail(); + } + +} + diff --git a/tests/recordutilsTest.php b/tests/recordutilsTest.php new file mode 100644 index 0000000000..501f48e203 --- /dev/null +++ b/tests/recordutilsTest.php @@ -0,0 +1,69 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testWatermark(). + */ + public function testWatermark() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testStamp(). + */ + public function testStamp() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testEmbed_preview(). + */ + public function testEmbed_preview() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testBinary_datas(). + */ + public function testBinary_datas() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/registryTest.php b/tests/registryTest.php new file mode 100644 index 0000000000..24849ba1d9 --- /dev/null +++ b/tests/registryTest.php @@ -0,0 +1,173 @@ +object = registry::get_instance(); + } + + public function testGet() + { + $this->testSet(); + } + + public function testSet() + { + /** + * Set value with default type (string) + */ + $this->object->set('key_test', 'value1', registry::TYPE_STRING); + $this->assertTrue($this->object->get('key_test') === 'value1'); + + $this->object->set('key_test', 1, registry::TYPE_STRING); + $this->assertTrue($this->object->get('key_test') === '1'); + + $this->object->set('key_test', '1', registry::TYPE_STRING); + $this->assertTrue($this->object->get('key_test') === '1'); + + $this->object->set('key_test', array('caca'), registry::TYPE_STRING); + $this->assertTrue($this->object->get('key_test') === 'Array'); + + + /** + * Set value with type (string) + */ + $this->object->set('key_test', 'value1', registry::TYPE_STRING); + $this->assertTrue($this->object->get('key_test') === 'value1'); + + $this->object->set('key_test', 1, registry::TYPE_STRING); + $this->assertTrue($this->object->get('key_test') === '1'); + + $this->object->set('key_test', '1', registry::TYPE_STRING); + $this->assertTrue($this->object->get('key_test') === '1'); + + $this->object->set('key_test', array('caca'), registry::TYPE_STRING); + $this->assertTrue($this->object->get('key_test') === 'Array'); + + /** + * Set value with type (int) + */ + $this->object->set('key_test', 'value1', registry::TYPE_INTEGER); + $this->assertTrue($this->object->get('key_test') === 0); + + $this->object->set('key_test', 1, registry::TYPE_INTEGER); + $this->assertTrue($this->object->get('key_test') === 1); + + $this->object->set('key_test', '1', registry::TYPE_INTEGER); + $this->assertTrue($this->object->get('key_test') === 1); + + $this->object->set('key_test', array('caca'), registry::TYPE_INTEGER); + $this->assertTrue($this->object->get('key_test') === 1); + + /** + * Set value with type boolean + */ + $this->object->set('key_test', 'value1', registry::TYPE_BOOLEAN); + $this->assertTrue($this->object->get('key_test') === true); + + $this->object->set('key_test', 1, registry::TYPE_BOOLEAN); + $this->assertTrue($this->object->get('key_test') === true); + + $this->object->set('key_test', '1', registry::TYPE_BOOLEAN); + $this->assertTrue($this->object->get('key_test') === true); + + $this->object->set('key_test', array('caca'), registry::TYPE_BOOLEAN); + $this->assertTrue($this->object->get('key_test') === true); + + $this->object->set('key_test', '0', registry::TYPE_BOOLEAN); + $this->assertTrue($this->object->get('key_test') === false); + + $this->object->set('key_test', 0, registry::TYPE_BOOLEAN); + $this->assertTrue($this->object->get('key_test') === false); + + $this->object->set('key_test', false, registry::TYPE_BOOLEAN); + $this->assertTrue($this->object->get('key_test') === false); + + $this->object->set('key_test', true, registry::TYPE_BOOLEAN); + $this->assertTrue($this->object->get('key_test') === true); + + /** + * Set value with type array + */ + $this->object->set('key_test', 'value1', registry::TYPE_ARRAY); + $this->assertTrue($this->object->get('key_test') === array('value1')); + + $this->object->set('key_test', 1, registry::TYPE_ARRAY); + $this->assertTrue($this->object->get('key_test') === array(1)); + + $this->object->set('key_test', '1', registry::TYPE_ARRAY); + $this->assertTrue($this->object->get('key_test') === array('1')); + + $this->object->set('key_test', array('caca'), registry::TYPE_ARRAY); + $this->assertTrue($this->object->get('key_test') === array('caca')); + + $this->object->set('key_test', '0', registry::TYPE_ARRAY); + $this->assertTrue($this->object->get('key_test') === array('0')); + + $this->object->set('key_test', 0, registry::TYPE_ARRAY); + $this->assertTrue($this->object->get('key_test') === array(0)); + + $this->object->set('key_test', false, registry::TYPE_ARRAY); + $this->assertTrue($this->object->get('key_test') === array(false)); + + $this->object->set('key_test', true, registry::TYPE_ARRAY); + $this->assertTrue($this->object->get('key_test') === array(true)); + + /** + * Set value with type enum_multi + */ + $this->object->set('key_test', 'value1', registry::TYPE_ENUM_MULTI); + $this->assertTrue($this->object->get('key_test') === array('value1')); + + $this->object->set('key_test', 1, registry::TYPE_ENUM_MULTI); + $this->assertTrue($this->object->get('key_test') === array(1)); + + $this->object->set('key_test', '1', registry::TYPE_ENUM_MULTI); + $this->assertTrue($this->object->get('key_test') === array('1')); + + $this->object->set('key_test', array('caca'), registry::TYPE_ENUM_MULTI); + $this->assertTrue($this->object->get('key_test') === array('caca')); + + $this->object->set('key_test', '0', registry::TYPE_ENUM_MULTI); + $this->assertTrue($this->object->get('key_test') === array('0')); + + $this->object->set('key_test', 0, registry::TYPE_ENUM_MULTI); + $this->assertTrue($this->object->get('key_test') === array(0)); + + $this->object->set('key_test', false, registry::TYPE_ENUM_MULTI); + $this->assertTrue($this->object->get('key_test') === array(false)); + + $this->object->set('key_test', true, registry::TYPE_ENUM_MULTI); + $this->assertTrue($this->object->get('key_test') === array(true)); + } + + public function testIs_set() + { + $this->object->set('key_test', 'value', registry::TYPE_STRING); + $this->assertTrue($this->object->is_set('key_test')); + $this->assertFalse($this->object->is_set('keifgjkqskodfqsflqkspfoqsfp')); + } + + public function testUn_set() + { + $this->testIs_set(); + $this->object->un_set('key_test'); + $this->assertFalse($this->object->is_set('key_test')); + } + +} + diff --git a/tests/registry_setupTest.php b/tests/registry_setupTest.php new file mode 100644 index 0000000000..67adc2e832 --- /dev/null +++ b/tests/registry_setupTest.php @@ -0,0 +1,68 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGet(). + */ + public function testGet() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testSet(). + */ + public function testSet() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testIs_set(). + */ + public function testIs_set() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testUn_set(). + */ + public function testUn_set() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + diff --git a/tests/report/activityTest.php b/tests/report/activityTest.php new file mode 100644 index 0000000000..38f56c0d6e --- /dev/null +++ b/tests/report/activityTest.php @@ -0,0 +1,200 @@ +dmax = $date->format("Y-m-d H:i:s"); + $date->modify('-6 month'); + $this->dmin = $date->format("Y-m-d H:i:s"); + $appbox = appbox::get_instance(\bootstrap::getCore()); + $databoxes = $appbox->get_databoxes(); + $this->ret = array(); + foreach ($databoxes as $databox) + { + $colls = $databox->get_collections(); + $rett = array(); + foreach ($colls as $coll) + { + $rett[$coll->get_coll_id()] = $coll->get_coll_id(); + } + $this->ret[$databox->get_sbas_id()] = implode(',', $rett); + } + } + + public function testBuildReport() + { + $conf = array( + 'user' => array("", 1, 0, 1, 1), + 'date' => array("", 1, 0, 1, 1), + 'record_id' => array("", 1, 1, 1, 1), + 'file' => array("", 1, 0, 1, 1), + 'mime' => array("", 1, 0, 1, 1), + 'size' => array("", 1, 0, 1, 1) + ); + + foreach ($this->ret as $sbasid => $colllist) + { + $report = new module_report_activity( + $this->dmin, + $this->dmax, + $sbasid, + $colllist + ); + $report->setUser_id(self::$user->get_id()); + $this->activerPerHours($report); + $this->ConnexionBase($report); + $this->activiteAddedDocument($report, $sbasid, $colllist); + $this->activiteAddedTopTenUser($report, $sbasid, $colllist); + $this->activiteEditedDocument($report, $sbasid, $colllist); + $this->activiteTopTenSiteView($report, $sbasid, $colllist); + $this->activity($report, $sbasid, $colllist); + $this->activityDay($report, $sbasid, $colllist); + $this->activityQuestion($report, $sbasid, $colllist); + $this->allDownloadByUserBase($report); + $this->allQuestion($report); + $this->detailDownload($report); + $this->downloadByBaseByDay($report); + $this->otherTest($report); + $this->push($report); + $this->topQuestion($report); + $this->topTenUser($report, $sbasid, $colllist); + } + } + + + public function otherTest($report) + { + $report->setTop(15); + $this->assertEquals(15, $report->getTop()); + } + + public function activerPerHours($report) + { + $activityHours = $report->getActivityPerHours(); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $activityHours); + } + + public function allQuestion($report) + { + $allQuestion = $report->getAllQuestionByUser(self::$user->get_id(), 'usrid'); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $allQuestion); + } + + + public function topQuestion($report) + { + $topQuestion = $report->getTopQuestion(); + $topQuestion2 = $report->getTopQuestion(false, true); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $topQuestion); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $topQuestion2); + } + + public function allDownloadByUserBase($report) + { + $allDownload = $report->getAllDownloadByUserBase(self::$user->get_id()); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $allDownload); + } + + public function downloadByBaseByDay($report) + { + $dlBaseDay = $report->getDownloadByBaseByDay(); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $dlBaseDay); + } + + public function ConnexionBase($report) + { + $connexionBase = $report->getConnexionBase(); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $connexionBase); + } + + public function detailDownload($report) + { + $detailDl = $report->getDetailDownload(); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $detailDl); + } + + public function push($report) + { + $push = $report->getPush(); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $push); + } + + public function topTenUser($report, $sbasid, $colllist) + { + $result = $report->topTenUser($this->dmin, $this->dmax, $sbasid, $colllist); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $result); + } + + public function activity($report, $sbasid, $colllist) + { + $result = $report->activity($this->dmin, $this->dmax, $sbasid, $colllist); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $result); + } + + public function activityDay($report, $sbasid, $colllist) + { + $result = $report->activityDay($this->dmin, $this->dmax, $sbasid, $colllist); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $result); + } + + public function activityQuestion($report, $sbasid, $colllist) + { + $result = $report->activityQuestion($this->dmin, $this->dmax, $sbasid, $colllist); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $result); + } + + public function activiteTopTenSiteView($report, $sbasid, $colllist) + { + $result = $report->activiteTopTenSiteView($this->dmin, $this->dmax, $sbasid, $colllist); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $result); + } + + public function activiteAddedDocument($report, $sbasid, $colllist) + { + $result = $report->activiteAddedDocument($this->dmin, $this->dmax, $sbasid, $colllist); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $result); + } + + public function activiteEditedDocument($report, $sbasid, $colllist) + { + $result = $report->activiteEditedDocument($this->dmin, $this->dmax, $sbasid, $colllist); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $result); + } + + public function activiteAddedTopTenUser($report, $sbasid, $colllist) + { + $result = $report->activiteAddedTopTenUser($this->dmin, $this->dmax, $sbasid, $colllist); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $result); + } + + +} diff --git a/tests/report/addTest.php b/tests/report/addTest.php new file mode 100644 index 0000000000..6e7e671b74 --- /dev/null +++ b/tests/report/addTest.php @@ -0,0 +1,185 @@ +dmax = $date->format("Y-m-d H:i:s"); + $date->modify('-6 month'); + $this->dmin = $date->format("Y-m-d H:i:s"); + $appbox = appbox::get_instance(\bootstrap::getCore()); + $databoxes = $appbox->get_databoxes(); + $this->ret = array(); + foreach ($databoxes as $databox) + { + $colls = $databox->get_collections(); + $rett = array(); + foreach ($colls as $coll) + { + $rett[$coll->get_coll_id()] = $coll->get_coll_id(); + } + $this->ret[$databox->get_sbas_id()] = implode(',', $rett); + } + } + + public function ColFilter() + { + $ret = $this->report->colFilter('user'); + + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $ret); + foreach ($ret as $result) + { + $this->assertArrayHasKey('val', $result); + $this->assertArrayHasKey('value', $result); + } + } + + public function testBuildReport() + { + $conf = array( + 'user' => array("", 1, 0, 1, 1), + 'date' => array("", 1, 0, 1, 1), + 'record_id' => array("", 1, 1, 1, 1), + 'file' => array("", 1, 0, 1, 1), + 'mime' => array("", 1, 0, 1, 1), + 'size' => array("", 1, 0, 1, 1) + ); + + foreach ($this->ret as $sbasid => $collections) + { + $this->report = new module_report_add( + $this->dmin, + $this->dmax, + $sbasid, + $collections + ); + + $result = $this->report->buildReport($conf); + + $this->reporttestPage($result); + if (count($result['result']) > 0)$this->reporttestConf($conf); + if (count($result['result']) > 0) $this->reporttestResult($result, $conf); + + $this->ColFilter(); + } + + + foreach ($this->ret as $sbasid => $collections) + { + $this->report = new module_report_add( + $this->dmin, + $this->dmax, + $sbasid, + $collections + ); + + $this->ColFilter(); + + $result = $this->report->buildReport(false, 'user'); + $this->reporttestPage($result); + if (count($result['result']) > 0) $this->reporttestConf($conf, 'user'); + if (count($result['result']) > 0) $this->reporttestResult($result, $conf, 'user'); + } + } + + public function reporttestPage($report) + { + $this->assertLessThanOrEqual($this->report->getNbRecord(), count($report['result'])); + + $nbPage = $this->report->getTotal() / $this->report->getNbRecord(); + + if ($this->report->getTotal() > $this->report->getNbRecord()) $this->assertTrue($report['display_nav']); + else $this->assertFalse($report['display_nav']); + + if ($report['page'] == 1) $this->assertFalse($report['previous_page']); + else $this->assertEquals($report['page'] - 1, $report['previous_page']); + + + if (intval(ceil($nbPage)) == $report['page'] || intval(ceil($nbPage)) == 0) $this->assertFalse($report['next_page']); + else $this->assertEquals($report['page'] + 1, $report['next_page']); + } + + public function reporttestConf($conf, $groupby = false) + { + if ($groupby) $this->assertEquals(count($this->report->getDisplay()), 2); + else $this->assertEquals(count($this->report->getDisplay()), count($conf)); + + if (!$groupby) + { + foreach ($this->report->getDisplay() as $col => $colconf) + { + $this->assertArrayHaskey($col, $conf); + $this->assertTrue(is_array($colconf)); + $this->assertArrayHasKey('title', $colconf); + $this->assertArrayHasKey('sort', $colconf); + $this->assertArrayHasKey('bound', $colconf); + $this->assertArrayHasKey('filter', $colconf); + $this->assertArrayHasKey('groupby', $colconf); + $i = 0; + foreach ($colconf as $key => $value) + { + if ($i == 1) $this->assertEquals($conf[$col][$i], $value); + elseif ($i == 2) $this->assertEquals($conf[$col][$i], $value); + elseif ($i == 3) $this->assertEquals($conf[$col][$i], $value); + elseif ($i == 4) $this->assertEquals($conf[$col][$i], $value); + $i++; + } + } + } + else + { + $this->assertArrayHasKey($groupby, $this->report->getDisplay()); + $this->assertArrayHasKey('nombre', $this->report->getDisplay()); + } + } + + public function reporttestResult($report, $conf, $groupby = false) + { + if (!$groupby) + { + foreach ($report['result'] as $row) + { + foreach ($conf as $key => $value) + { + + $this->assertArrayHasKey($key, $row); + $condition = is_string($row[$key]) || is_int($row[$key]); + $this->assertTrue($condition); + } + } + } + else + { + foreach ($report['result'] as $row) + { + $this->assertArrayHasKey($groupby, $row); + $this->assertArrayHasKey('nombre', $row); + } + } + } + +} diff --git a/tests/report/connexionReportTest.php b/tests/report/connexionReportTest.php new file mode 100644 index 0000000000..9a10a1f03f --- /dev/null +++ b/tests/report/connexionReportTest.php @@ -0,0 +1,215 @@ +dmax = $date->format("Y-m-d H:i:s"); + $date->modify('-6 month'); + $this->dmin = $date->format("Y-m-d H:i:s"); + $appbox = appbox::get_instance(\bootstrap::getCore()); + $databoxes = $appbox->get_databoxes(); + $this->ret = array(); + foreach ($databoxes as $databox) + { + $colls = $databox->get_collections(); + $rett = array(); + foreach ($colls as $coll) + { + $rett[$coll->get_coll_id()] = $coll->get_coll_id(); + } + $this->ret[$databox->get_sbas_id()] = implode(',', $rett); + } + } + + public function ColFilter() + { + $ret = $this->report->colFilter('user'); + + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, + $ret); + foreach ($ret as $result) + { + $this->assertArrayHasKey('val', $result); + $this->assertArrayHasKey('value', $result); + } + } + + public function testBuildReport() + { + $conf = array( + 'user' => array(_('phraseanet::utilisateurs'), 1, 1, 1, 1), + 'ddate' => array(_('report:: date'), 1, 0, 1, 1), + 'ip' => array(_('report:: IP'), 1, 0, 0, 0), + 'appli' => array(_('report:: modules'), 1, 0, 0, 0), + 'fonction' => array(_('report::fonction'), 1, 1, 1, 1), + 'activite' => array(_('report::activite'), 1, 1, 1, 1), + 'pays' => array(_('report::pays'), 1, 1, 1, 1), + 'societe' => array(_('report::societe'), 1, 1, 1, 1) + ); + + $nbResult = 0; + foreach ($this->ret as $sbasid => $collections) + { + $this->report = new module_report_connexion( + $this->dmin, + $this->dmax, + $sbasid, + $collections + ); + $this->ColFilter(); + $result = $this->report->buildReport($conf); + + if(count($result)>$nbResult) + $this->save_report = $this->report; + + $nbResult = count($result); + $this->reporttestPage($result); + if (count($result['result']) > 0) $this->reporttestConf($conf); + if (count($result['result']) > 0) $this->reporttestResult($result, $conf); + } + + foreach ($this->ret as $sbasid => $collections) + { + $this->report = new module_report_connexion( + $this->dmin, + $this->dmax, + $sbasid, + $collections + ); + + $this->ColFilter(); + + $result = $this->report->buildReport(false, 'user'); + $this->reporttestPage($result); + if (count($result['result']) > 0) $this->reporttestConf($conf, 'user'); + if (count($result['result']) > 0) $this->reporttestResult($result, $conf, 'user'); + + } + + $result = $this->save_report->buildReport(false, 'user'); + } + + + public function reporttestPage($report) + { + $this->assertLessThanOrEqual($this->report->getNbRecord(), count($report['result'])); + + $nbPage = $this->report->getTotal() / $this->report->getNbRecord(); + + if ($this->report->getTotal() > $this->report->getNbRecord()) + $this->assertTrue($report['display_nav']); + else $this->assertFalse($report['display_nav']); + + if ($report['page'] == 1) $this->assertFalse($report['previous_page']); + else $this->assertEquals($report['page'] - 1, $report['previous_page']); + + if (intval(ceil($nbPage)) == $report['page']) + $this->assertFalse($report['next_page']); + else $this->assertEquals($report['page'] + 1, $report['next_page']); + } + + public function reporttestConf($conf, $groupby = false) + { + if($groupby) + $this->assertEquals(count($this->report->getDisplay()), 2); + else + $this->assertEquals(count($this->report->getDisplay()), count($conf)); + + if(!$groupby) + { + foreach($this->report->getDisplay() as $col => $colconf) + { + $this->assertArrayHaskey($col, $conf); + $this->assertTrue(is_array($colconf)); + $this->assertArrayHasKey('title', $colconf); + $this->assertArrayHasKey('sort', $colconf); + $this->assertArrayHasKey('bound', $colconf); + $this->assertArrayHasKey('filter', $colconf); + $this->assertArrayHasKey('groupby', $colconf); + $i = 0; + foreach($colconf as $key => $value) + { + if($i == 1) + $this->assertEquals($conf[$col][$i], $value); + elseif($i == 2) + $this->assertEquals($conf[$col][$i], $value); + elseif($i == 3) + $this->assertEquals($conf[$col][$i], $value); + elseif($i == 4) + $this->assertEquals($conf[$col][$i], $value); + $i++; + } + } + } + else + { + $this->assertArrayHasKey($groupby, $this->report->getDisplay()); + $this->assertArrayHasKey('nb', $this->report->getDisplay()); + } + } + + public function reporttestResult($report, $conf, $groupby = false) + { + if(!$groupby) + { + foreach ($report['result'] as $row) + { + foreach ($conf as $key => $value) + { + + $this->assertArrayHasKey($key, $row); + $condition = is_string($row[$key]) || is_int($row[$key]); + $this->assertTrue($condition); + } + } + } + else + { + foreach ($report['result'] as $row) + { + $this->assertArrayHasKey($groupby, $row); + $this->assertArrayHasKey('nb', $row); + } + } + } + + public function reporttestResultWithChamp($report, $conf) + { + foreach ($report['result'] as $row) + { + foreach ($conf as $key => $value) + { + $this->assertArrayHasKey($value, $row); + $condition = is_string($row[$value]); + $this->assertTrue($condition); + } + } + } + +} diff --git a/tests/report/dashboardTest.php b/tests/report/dashboardTest.php new file mode 100644 index 0000000000..b90949669b --- /dev/null +++ b/tests/report/dashboardTest.php @@ -0,0 +1,136 @@ +dashboard = new module_report_dashboard(self::$user); + $this->dashboard->setDate('-2 month', 'now'); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $this->dashboard->legendDay); + $this->assertNotNull($this->dashboard->dmin); + $this->assertNotNull($this->dashboard->dmax); + $this->assertGreaterThanOrEqual(1, count($this->dashboard->authorizedCollection)); + $this->assertEquals($this->dashboard->authorizedCollection, $this->dashboard->authorizedCollection()); + + + foreach ($this->dashboard->authorizedCollection as $coll) + { + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $coll); + $this->assertArrayHasKey('sbas_id', $coll); + $this->assertArrayHasKey('coll', $coll); + $this->assertArrayHasKey('name', $coll); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_INT, $coll['sbas_id']); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $coll['name']); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $coll['coll']); + } + } + + public function testValid() + { +// $this->assertFalse($this->dashboard->isValid()); +// $this->dashboard->execute(); +// $this->assertTrue($this->dashboard->isValid()); + } + + public function testExecute() + { + $this->dashboard->execute(); + $dashboard = $this->dashboard->getDash(); + $auth = $this->dashboard->authorizedCollection; + + $this->verify($dashboard); + } + + public function verify($dashboard) + { + $date1 = new DateTime($this->dashboard->dmin); + $date2 = new DateTime($this->dashboard->dmax); + $interval = $date1->diff($date2); + $nbDay = $interval->format("%a"); + $int = array('nb_dl', 'nb_conn'); + $top = array('top_ten_user_doc', 'top_ten_user_poiddoc', 'top_dl_document', 'top_ten_question', 'ask', 'top_ten_added'); + $activity = array('activity', 'activity_day', 'activity_added'); + foreach ($dashboard as $key => $dash) + { + if (count($dash) == 0) + continue; + + if (in_array($key, $int)) + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_INT, $dash); + elseif (in_array($key, $top)) + { + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $dash); + $this->assertLessThanOrEqual($this->dashboard->nbtop, count($dash)); + $lastvalue = null; + foreach ($dash as $value) + { + if (is_null($lastvalue)) + $lastvalue = $value['nb']; + $this->assertLessThanOrEqual($lastvalue, $value['nb']); + $lastvalue = $value['nb']; + } + } + elseif (in_array($key, $activity)) + { + if ($key == 'activity') + { + $this->assertEquals(24, count($dash)); + } + else + { + if ($key == 'activity_added') + { + + } + $this->assertLessThanOrEqual($nbDay, count($dash)); + } + } + } + } + + public function testGetTitleDate() + { + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->dashboard->getTitleDate('dmax')); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->dashboard->getTitleDate('dmin')); + try + { + $this->dashboard->getTitleDate('none'); + $this->fail('must throw an axception right here'); + } + catch (Exception $e) + { + + } + } + + public function testGetListeBase() + { + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $this->dashboard->getListeBase(' ')); + } + + public function testGroup() + { + $this->assertInstanceOf('module_report_dashboard_group', $this->dashboard->group()); + } + +} diff --git a/tests/report/downloadReportTest.php b/tests/report/downloadReportTest.php new file mode 100644 index 0000000000..c68adae19f --- /dev/null +++ b/tests/report/downloadReportTest.php @@ -0,0 +1,251 @@ +dmax = $date->format("Y-m-d H:i:s"); + $date->modify('-6 month'); + $this->dmin = $date->format("Y-m-d H:i:s"); + $appbox = appbox::get_instance(\bootstrap::getCore()); + $databoxes = $appbox->get_databoxes(); + $this->ret = array(); + foreach ($databoxes as $databox) + { + $colls = $databox->get_collections(); + $rett = array(); + foreach ($colls as $coll) + { + $rett[$coll->get_coll_id()] = $coll->get_coll_id(); + } + $this->ret[$databox->get_sbas_id()] = implode(',', $rett); + } + } + + public function ColFilter() + { + $ret = $this->report->colFilter('user'); + $this->manyCol($ret); + $ret = $this->report->colFilter('ddate'); + $this->manyCol($ret); + $ret = $this->report->colFilter('coll_id'); + $this->manyCol($ret); + } + + public function manyCol($ret) + { + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $ret); + foreach ($ret as $result) + { + $this->assertArrayHasKey('val', $result); + $this->assertArrayHasKey('value', $result); + } + } + + public function testBuildReport() + { + $conf = array( + 'user' => array(_('report:: utilisateurs'), 1, 1, 1, 1), + 'ddate' => array(_('report:: date'), 1, 0, 1, 1), + 'record_id' => array(_('report:: record id'), 1, 1, 1, 1), + 'final' => array(_('phrseanet:: sous definition'), 1, 0, 1, 1), + 'coll_id' => array(_('report:: collections'), 1, 0, 1, 1), + 'comment' => array(_('report:: commentaire'), 1, 0, 0, 0), + 'fonction' => array(_('report:: fonction'), 1, 1, 1, 1), + 'activite' => array(_('report:: activite'), 1, 1, 1, 1), + 'pays' => array(_('report:: pays'), 1, 1, 1, 1), + 'societe' => array(_('report:: societe'), 1, 1, 1, 1) + ); + + foreach ($this->ret as $sbasid => $collections) + { + $this->report = new module_report_download( + $this->dmin, + $this->dmax, + $sbasid, + $collections + ); + + $this->ColFilter(); + $result = $this->report->buildReport($conf); + + $this->reporttestPage($result); + if (count($result['result']) > 0) + $this->reporttestConf($conf); + if (count($result['result']) > 0) + $this->reporttestResult($result, $conf); + } + + foreach ($this->ret as $sbasid => $collections) + { + $this->report = new module_report_download( + $this->dmin, + $this->dmax, + $sbasid, + $collections + ); + + $this->ColFilter(); + + $result = $this->report->buildReport(false, 'fonction'); + + $this->reporttestPage($result); + if (count($result['result']) > 0) + $this->reporttestConf($conf, 'fonction'); + if (count($result['result']) > 0) + $this->reporttestResult($result, $conf, 'fonction'); + } + + foreach ($this->ret as $sbasid => $collections) + { + $this->report = new module_report_download( + $this->dmin, + $this->dmax, + $sbasid, + $collections + ); + + $this->ColFilter(); + + $result = $this->report->buildReport(false, 'record_id', 'DOC'); + $this->reporttestPage($result); + if (count($result['result']) > 0) + $this->reporttestConf($conf, 'record_id'); + if (count($result['result']) > 0) + $this->reporttestResult($result, $conf, 'record_id'); + } + + foreach ($this->ret as $sbasid => $collections) + { + $this->report = new module_report_download( + $this->dmin, + $this->dmax, + $sbasid, + $collections + ); + + $this->ColFilter(); + + $result = $this->report->buildReport(false, 'user', 'DOC'); + $this->reporttestPage($result); + if (count($result['result']) > 0) + $this->reporttestConf($conf, 'user'); + if (count($result['result']) > 0) + $this->reporttestResult($result, $conf, 'user'); + } + } + + public function reporttestPage($report) + { + $this->assertLessThanOrEqual($this->report->getNbRecord(), count($report['result'])); + + $nbPage = $this->report->getTotal() / $this->report->getNbRecord(); + + if ($this->report->getTotal() > $this->report->getNbRecord()) + $this->assertTrue($report['display_nav']); + else + $this->assertFalse($report['display_nav']); + + if ($report['page'] == 1) + $this->assertFalse($report['previous_page']); + else + $this->assertEquals($report['page'] - 1, $report['previous_page']); + + if (intval(ceil($nbPage)) == $report['page'] || intval(ceil($nbPage)) == 0) + $this->assertFalse($report['next_page']); + else + $this->assertEquals($report['page'] + 1, $report['next_page']); + } + + public function reporttestConf($conf, $groupby = false) + { + if ($groupby) + { + if ($groupby != 'record_id') + $this->assertEquals(count($this->report->getDisplay()), 2); + } + else + $this->assertEquals(count($this->report->getDisplay()), count($conf)); + + if (!$groupby) + { + foreach ($this->report->getDisplay() as $col => $colconf) + { + $this->assertArrayHaskey($col, $conf); + $this->assertTrue(is_array($colconf)); + $this->assertArrayHasKey('title', $colconf); + $this->assertArrayHasKey('sort', $colconf); + $this->assertArrayHasKey('bound', $colconf); + $this->assertArrayHasKey('filter', $colconf); + $this->assertArrayHasKey('groupby', $colconf); + $i = 0; + foreach ($colconf as $key => $value) + { + if ($i == 1) + $this->assertEquals($conf[$col][$i], $value); + elseif ($i == 2) + $this->assertEquals($conf[$col][$i], $value); + elseif ($i == 3) + $this->assertEquals($conf[$col][$i], $value); + elseif ($i == 4) + $this->assertEquals($conf[$col][$i], $value); + $i++; + } + } + } + else + { + + $this->assertArrayHasKey($groupby, $this->report->getDisplay()); + } + } + + public function reporttestResult($report, $conf, $groupby = false) + { + if (!$groupby) + { + foreach ($report['result'] as $row) + { + foreach ($conf as $key => $value) + { + + $this->assertArrayHasKey($key, $row); + $condition = is_string($row[$key]) || is_int($row[$key]); + $this->assertTrue($condition); + } + } + } + else + { + foreach ($report['result'] as $row) + { + $this->assertArrayHasKey($groupby, $row); + } + } + } + +} diff --git a/tests/report/editTest.php b/tests/report/editTest.php new file mode 100644 index 0000000000..fb0869b205 --- /dev/null +++ b/tests/report/editTest.php @@ -0,0 +1,196 @@ +dmax = $date->format("Y-m-d H:i:s"); + $date->modify('-6 month'); + $this->dmin = $date->format("Y-m-d H:i:s"); + $appbox = appbox::get_instance(\bootstrap::getCore()); + $databoxes = $appbox->get_databoxes(); + $this->ret = array(); + foreach ($databoxes as $databox) + { + $colls = $databox->get_collections(); + $rett = array(); + foreach ($colls as $coll) + { + $rett[$coll->get_coll_id()] = $coll->get_coll_id(); + } + $this->ret[$databox->get_sbas_id()] = implode(',', $rett); + } + } + + + public function testBuildReport() + { + $conf = array( + 'user' => array("", 1, 0, 1, 1), + 'date' => array("", 1, 0, 1, 1), + 'record_id' => array("", 1, 1, 1, 1), + 'file' => array("", 1, 0, 1, 1), + 'mime' => array("", 1, 0, 1, 1), + 'size' => array("", 1, 0, 1, 1) + ); + + foreach ($this->ret as $sbasid => $collections) + { + $this->report = new module_report_edit( + $this->dmin, + $this->dmax, + $sbasid, + $collections + ); + + $result = $this->report->buildReport($conf); + + $this->reporttestPage($result); + if (count($result['result']) > 0) $this->reporttestConf($conf); + if (count($result['result']) > 0) $this->reporttestResult($result, $conf); + + $this->ColFilter(); + } + + foreach ($this->ret as $sbasid => $collections) + { + $this->report = new module_report_edit( + $this->dmin, + $this->dmax, + $sbasid, + $collections + ); + + $this->ColFilter(); + + $result = $this->report->buildReport(false, 'user'); + $this->reporttestPage($result); + if (count($result['result']) > 0) $this->reporttestConf($conf, 'user'); + if (count($result['result']) > 0) $this->reporttestResult($result, $conf, 'user'); + } + } + + public function ColFilter() + { + $ret = $this->report->colFilter('user'); + $this->manyCol($ret); + $ret = $this->report->colFilter('getter'); + $this->manyCol($ret); + $ret = $this->report->colFilter('date'); + $this->manyCol($ret); + $ret = $this->report->colFilter('size'); + $this->manyCol($ret); + + } + + public function manyCol($ret) + { + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, + $ret); + foreach ($ret as $result) + { + $this->assertArrayHasKey('val', $result); + $this->assertArrayHasKey('value', $result); + } + } + + public function reporttestPage($report) + { + $this->assertLessThanOrEqual($this->report->getNbRecord(), count($report['result'])); + + $nbPage = $this->report->getTotal() / $this->report->getNbRecord(); + + if ($this->report->getTotal() > $this->report->getNbRecord()) $this->assertTrue($report['display_nav']); + else $this->assertFalse($report['display_nav']); + + if ($report['page'] == 1) $this->assertFalse($report['previous_page']); + else $this->assertEquals($report['page'] - 1, $report['previous_page']); + + if (intval(ceil($nbPage)) == $report['page'] || intval(ceil($nbPage)) == 0) $this->assertFalse($report['next_page']); + else $this->assertEquals($report['page'] + 1, $report['next_page']); + } + + public function reporttestConf($conf, $groupby = false) + { + if ($groupby) $this->assertEquals(count($this->report->getDisplay()), 2); + else $this->assertEquals(count($this->report->getDisplay()), count($conf)); + + if (!$groupby) + { + foreach ($this->report->getDisplay() as $col => $colconf) + { + $this->assertArrayHaskey($col, $conf); + $this->assertTrue(is_array($colconf)); + $this->assertArrayHasKey('title', $colconf); + $this->assertArrayHasKey('sort', $colconf); + $this->assertArrayHasKey('bound', $colconf); + $this->assertArrayHasKey('filter', $colconf); + $this->assertArrayHasKey('groupby', $colconf); + $i = 0; + foreach ($colconf as $key => $value) + { + if ($i == 1) $this->assertEquals($conf[$col][$i], $value); + elseif ($i == 2) $this->assertEquals($conf[$col][$i], $value); + elseif ($i == 3) $this->assertEquals($conf[$col][$i], $value); + elseif ($i == 4) $this->assertEquals($conf[$col][$i], $value); + $i++; + } + } + } + else + { + $this->assertArrayHasKey($groupby, $this->report->getDisplay()); + $this->assertArrayHasKey('nombre', $this->report->getDisplay()); + } + } + + public function reporttestResult($report, $conf, $groupby = false) + { + if (!$groupby) + { + foreach ($report['result'] as $row) + { + foreach ($conf as $key => $value) + { + + $this->assertArrayHasKey($key, $row); + $condition = is_string($row[$key]) || is_int($row[$key]); + $this->assertTrue($condition); + } + } + } + else + { + foreach ($report['result'] as $row) + { + $this->assertArrayHasKey($groupby, $row); + $this->assertArrayHasKey('nombre', $row); + } + } + } + +} diff --git a/tests/report/filterTest.php b/tests/report/filterTest.php new file mode 100644 index 0000000000..0b55ef55b6 --- /dev/null +++ b/tests/report/filterTest.php @@ -0,0 +1,113 @@ +dmax = $date->format("Y-m-d H:i:s"); + $date->modify('-6 month'); + $this->dmin = $date->format("Y-m-d H:i:s"); + $appbox = appbox::get_instance(\bootstrap::getCore()); + $databoxes = $appbox->get_databoxes(); + $this->ret = array(); + foreach ($databoxes as $databox) + { + $colls = $databox->get_collections(); + $rett = array(); + foreach ($colls as $coll) + { + $rett[$coll->get_coll_id()] = $coll->get_coll_id(); + } + $this->ret[$databox->get_sbas_id()] = implode(',', $rett); + } + $this->initFilter(); + } + + public function initFilter() + { + $conf = array( + 'user' => array(_('phraseanet::utilisateurs'), 1, 1, 1, 1), + 'ddate' => array(_('report:: date'), 1, 0, 1, 1), + 'ip' => array(_('report:: IP'), 1, 0, 0, 0), + 'appli' => array(_('report:: modules'), 1, 0, 0, 0), + 'fonction' => array(_('report::fonction'), 1, 1, 1, 1), + 'activite' => array(_('report::activite'), 1, 1, 1, 1), + 'pays' => array(_('report::pays'), 1, 1, 1, 1), + 'societe' => array(_('report::societe'), 1, 1, 1, 1) + ); + + foreach ($this->ret as $sbasid => $collections) + { + $this->report = new module_report_connexion( + $this->dmin, + $this->dmax, + $sbasid, + $collections + ); + break; + } + } + + public function testFilter() + { + $filter = new module_report_filter(array(), $this->report->getTransQueryString()); + $this->assertEquals(array(), $filter->getTabFilter()); + $filter->addFilter('x', 'LIKE', 'y'); + $filter->addFilter('x', 'LIKE', 'z'); + $filter->addFilter('1', '=', '1'); + $filter->addFilter('1', '=', '1'); + $tabfilter = $filter->getTabFilter(); + $this->assertEquals(2, count($tabfilter)); + $added_filter = $tabfilter[0]; + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY,$added_filter); + $this->assertArrayHasKey('f', $added_filter); + $this->assertArrayHasKey('o', $added_filter); + $this->assertArrayHasKey('v', $added_filter); + $this->assertEquals('x', $added_filter['f']); + $this->assertEquals('LIKE', $added_filter['o']); + $this->assertEquals('y', $added_filter['v']); + $active_column = $filter->getActiveColumn(); + $this->assertEquals('x', $active_column[0]); + + $tabfilter = $filter->getTabFilter(); + $this->assertEquals(2, count($tabfilter)); + $filter->addFilter('y', '=', 'z'); + $tabfilter = $filter->getTabFilter(); + $this->assertEquals(3, count($tabfilter)); + $filter->addFilter('user', '=', 'o'); + $tabfilter = $filter->getTabFilter(); + $this->assertEquals(4, count($tabfilter)); + $filter->addFilter('a', 'OR', ''); + $filter->addFilter('appli', '=', 'a:1:{i:0;i:1;}'); + $filter->addFilter('ddate', '=', 'o'); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $filter->getPostingFilter()); + $nbBefore = count($filter->getTabFilter()); + $filter->removeFilter('ddate'); + $nbAfter = count($filter->getTabFilter()); + $this->assertEquals($nbBefore - 1, $nbAfter); + } +} diff --git a/tests/report/pushTest.php b/tests/report/pushTest.php new file mode 100644 index 0000000000..eec715b609 --- /dev/null +++ b/tests/report/pushTest.php @@ -0,0 +1,185 @@ +dmax = $date->format("Y-m-d H:i:s"); + $date->modify('-6 month'); + $this->dmin = $date->format("Y-m-d H:i:s"); + $appbox = appbox::get_instance(\bootstrap::getCore()); + $databoxes = $appbox->get_databoxes(); + $this->ret = array(); + foreach ($databoxes as $databox) + { + $colls = $databox->get_collections(); + $rett = array(); + foreach ($colls as $coll) + { + $rett[$coll->get_coll_id()] = $coll->get_coll_id(); + } + $this->ret[$databox->get_sbas_id()] = implode(',', $rett); + } + } + + public function ColFilter() + { + $ret = $this->report->colFilter('user'); + + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $ret); + foreach ($ret as $result) + { + $this->assertArrayHasKey('val', $result); + $this->assertArrayHasKey('value', $result); + } + } + + public function testBuildReport() + { + $conf = array( + 'user' => array("", 1, 0, 1, 1), + 'getter' => array("Destinataire", 1, 0, 1, 1), + 'date' => array("", 1, 0, 1, 1), + 'record_id' => array("", 1, 1, 1, 1), + 'file' => array("", 1, 0, 1, 1), + 'mime' => array("", 1, 0, 1, 1), + 'size' => array("", 1, 0, 1, 1) + ); + + foreach ($this->ret as $sbasid => $collections) + { + $this->report = new module_report_push( + $this->dmin, + $this->dmax, + $sbasid, + $collections + ); + + $result = $this->report->buildReport($conf); + + $this->reporttestPage($result); + if (count($result['result']) > 0) $this->reporttestConf($conf); + if (count($result['result']) > 0) $this->reporttestResult($result, $conf); + + $this->ColFilter(); + } + + foreach ($this->ret as $sbasid => $collections) + { + $this->report = new module_report_push( + $this->dmin, + $this->dmax, + $sbasid, + $collections + ); + + $this->ColFilter(); + + $result = $this->report->buildReport(false, 'user'); + + $this->reporttestPage($result); + if (count($result['result']) > 0) $this->reporttestConf($conf, 'user'); + if (count($result['result']) > 0) $this->reporttestResult($result, $conf, 'user'); + } + } + + public function reporttestPage($report) + { + $this->assertLessThanOrEqual($this->report->getNbRecord(), count($report['result'])); + + $nbPage = $this->report->getTotal() / $this->report->getNbRecord(); + + if ($this->report->getTotal() > $this->report->getNbRecord()) $this->assertTrue($report['display_nav']); + else $this->assertFalse($report['display_nav']); + + if ($report['page'] == 1) $this->assertFalse($report['previous_page']); + else $this->assertEquals($report['page'] - 1, $report['previous_page']); + + if (intval(ceil($nbPage)) == $report['page'] || intval(ceil($nbPage)) == 0) $this->assertFalse($report['next_page']); + else $this->assertEquals($report['page'] + 1, $report['next_page']); + } + + public function reporttestConf($conf, $groupby = false) + { + if ($groupby) $this->assertEquals(count($this->report->getDisplay()), 2); + else $this->assertEquals(count($this->report->getDisplay()), count($conf)); + + if (!$groupby) + { + foreach ($this->report->getDisplay() as $col => $colconf) + { + $this->assertArrayHaskey($col, $conf); + $this->assertTrue(is_array($colconf)); + $this->assertArrayHasKey('title', $colconf); + $this->assertArrayHasKey('sort', $colconf); + $this->assertArrayHasKey('bound', $colconf); + $this->assertArrayHasKey('filter', $colconf); + $this->assertArrayHasKey('groupby', $colconf); + $i = 0; + foreach ($colconf as $key => $value) + { + if ($i == 1) $this->assertEquals($conf[$col][$i], $value); + elseif ($i == 2) $this->assertEquals($conf[$col][$i], $value); + elseif ($i == 3) $this->assertEquals($conf[$col][$i], $value); + elseif ($i == 4) $this->assertEquals($conf[$col][$i], $value); + $i++; + } + } + } + else + { + $this->assertArrayHasKey($groupby, $this->report->getDisplay()); + $this->assertArrayHasKey('nombre', $this->report->getDisplay()); + } + } + + public function reporttestResult($report, $conf, $groupby = false) + { + if (!$groupby) + { + foreach ($report['result'] as $row) + { + foreach ($conf as $key => $value) + { + + $this->assertArrayHasKey($key, $row); + $condition = is_string($row[$key]) || is_int($row[$key]); + $this->assertTrue($condition); + } + } + } + else + { + foreach ($report['result'] as $row) + { + $this->assertArrayHasKey($groupby, $row); + $this->assertArrayHasKey('nombre', $row); + } + } + } + +} diff --git a/tests/report/questionReportTest.php b/tests/report/questionReportTest.php new file mode 100644 index 0000000000..3b5ddffebb --- /dev/null +++ b/tests/report/questionReportTest.php @@ -0,0 +1,205 @@ +dmax = $date->format("Y-m-d H:i:s"); + $date->modify('-6 month'); + $this->dmin = $date->format("Y-m-d H:i:s"); + $appbox = appbox::get_instance(\bootstrap::getCore()); + $databoxes = $appbox->get_databoxes(); + $this->ret = array(); + foreach ($databoxes as $databox) + { + $colls = $databox->get_collections(); + $rett = array(); + foreach ($colls as $coll) + { + $rett[$coll->get_coll_id()] = $coll->get_coll_id(); + } + $this->ret[$databox->get_sbas_id()] = implode(',', $rett); + } + } + + public function ColFilter() + { + $ret = $this->report->colFilter('user'); + $this->moreFilter($ret); + $ret = $this->report->colFilter('ddate'); + $this->moreFilter($ret); + } + + public function moreFilter($ret) + { + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $ret); + foreach ($ret as $result) + { + $this->assertArrayHasKey('val', $result); + $this->assertArrayHasKey('value', $result); + } + } + + public function testBuildReport() + { + $conf = array( + 'user' => array(_('report:: utilisateur'), 1, 1, 1, 1), + 'search' => array(_('report:: question'), 1, 0, 1, 1), + 'ddate' => array(_('report:: date'), 1, 0, 1, 1), + 'fonction' => array(_('report:: fonction'), 1, 1, 1, 1), + 'activite' => array(_('report:: activite'), 1, 1, 1, 1), + 'pays' => array(_('report:: pays'), 1, 1, 1, 1), + 'societe' => array(_('report:: societe'), 1, 1, 1, 1) + ); + + foreach ($this->ret as $sbasid => $collections) + { + $this->report = new module_report_question( + $this->dmin, + $this->dmax, + $sbasid, + $collections + ); + + $this->ColFilter(); + + $result = $this->report->buildReport($conf); + $this->reporttestPage($result); + if (count($result['result']) > 0) + $this->reporttestConf($conf); + if (count($result['result']) > 0) + $this->reporttestResult($result, $conf); + } + + foreach ($this->ret as $sbasid => $collections) + { + $this->report = new module_report_question( + $this->dmin, + $this->dmax, + $sbasid, + $collections + ); + + $this->ColFilter(); + + $result = $this->report->buildReport(false, 'user'); + $this->reporttestPage($result); + if (count($result['result']) > 0) + $this->reporttestConf($conf, 'user'); + if (count($result['result']) > 0) + $this->reporttestResult($result, $conf, 'user'); + } + } + + public function reporttestPage($report) + { + $this->assertLessThanOrEqual($this->report->getNbRecord(), count($report['result'])); + + $nbPage = ceil($this->report->getTotal() / $this->report->getNbRecord()); + + if ($this->report->getTotal() > $this->report->getNbRecord()) + $this->assertTrue($report['display_nav']); + else + $this->assertFalse($report['display_nav']); + + if ($report['page'] == 1) + $this->assertFalse($report['previous_page']); + else + $this->assertEquals($report['page'] - 1, $report['previous_page']); + + if (intval($nbPage) > $report['page']) + $this->assertEquals($report['page'] + 1, $report['next_page']); + else + $this->assertFalse($report['next_page']); + } + + public function reporttestConf($conf, $groupby = false) + { + if ($groupby) + $this->assertEquals(count($this->report->getDisplay()), 2); + else + $this->assertEquals(count($this->report->getDisplay()), count($conf)); + + if (!$groupby) + { + foreach ($this->report->getDisplay() as $col => $colconf) + { + $this->assertArrayHaskey($col, $conf); + $this->assertTrue(is_array($colconf)); + $this->assertArrayHasKey('title', $colconf); + $this->assertArrayHasKey('sort', $colconf); + $this->assertArrayHasKey('bound', $colconf); + $this->assertArrayHasKey('filter', $colconf); + $this->assertArrayHasKey('groupby', $colconf); + $i = 0; + foreach ($colconf as $key => $value) + { + if ($i == 1) + $this->assertEquals($conf[$col][$i], $value); + elseif ($i == 2) + $this->assertEquals($conf[$col][$i], $value); + elseif ($i == 3) + $this->assertEquals($conf[$col][$i], $value); + elseif ($i == 4) + $this->assertEquals($conf[$col][$i], $value); + $i++; + } + } + } + else + { + $this->assertArrayHasKey($groupby, $this->report->getDisplay()); + $this->assertArrayHasKey('nb', $this->report->getDisplay()); + } + } + + public function reporttestResult($report, $conf, $groupby = false) + { + if (!$groupby) + { + foreach ($report['result'] as $row) + { + foreach ($conf as $key => $value) + { + + $this->assertArrayHasKey($key, $row); + $condition = is_string($row[$key]) || is_int($row[$key]); + $this->assertTrue($condition); + } + } + } + else + { + foreach ($report['result'] as $row) + { + $this->assertArrayHasKey($groupby, $row); + $this->assertArrayHasKey('nb', $row); + } + } + } + +} diff --git a/tests/report/reportTest.php b/tests/report/reportTest.php new file mode 100644 index 0000000000..85378456b2 --- /dev/null +++ b/tests/report/reportTest.php @@ -0,0 +1,199 @@ +xml = ' + + + hello + + '; + $date = new Datetime(); + $thid->dmax = $date->format("Y-m-d H:i:s"); + $date->modify('-6 month'); + $this->dmin = $date->format("Y-m-d H:i:s"); + $appbox = appbox::get_instance(\bootstrap::getCore()); + $databoxes = $appbox->get_databoxes(); + $this->ret = array(); + foreach ($databoxes as $databox) + { + $colls = $databox->get_collections(); + $rett = array(); + foreach ($colls as $coll) + { + $rett[$coll->get_coll_id()] = $coll->get_coll_id(); + } + $this->ret[$databox->get_sbas_id()] = implode(',', $rett); + } + } + public function testReport() + { + foreach ($this->ret as $sbasid => $collections) + { + $this->report = new module_report($this->dmin, $this->dmax, $sbasid, $collections); + $this->report->setUser_id(self::$user->get_id()); + $this->assertEquals($collections, $this->report->getListCollId()); + $this->host($this->report); + } + } + + public function host($report) + { + $host ='http://www.google.fr/search?q=helloworld&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:fr:official&client=firefox-a#pq=fake%20url%20constructor&hl=fr&sugexp=gsnos%2Cn%3D2&cp=8&gs_id=y&xhr=t&q=hello+world&pf=p&sclient=psy&client=firefox-a&hs=mIa&rls=org.mozilla:fr%3Aofficial&source=hp&pbx=1&oq=hello+wo&aq=0&aqi=g2&aql=t&gs_sm=&gs_upl=&bav=on.2,or.r_gc.r_pw.&fp=ab54cb1d4456efee&biw=1152&bih=712'; + $host = $report->getHost($host); + $this->assertEquals('www.google.fr', $host); + $host ='http://localhost.phr4/login'; + $host = $report->getHost($host); + $this->assertEquals('localhost.phr4', $host); + } + + /** + * @todo refactor + */ +// public function SqlBuilder() +// { +// $domain = $this->report->sqlBuilder("connexion"); +// $this->assertInstanceOf('module_report_sqlconnexion', $domain); +// $domain = $this->report->sqlBuilder("download"); +// $this->assertInstanceOf('module_report_sqldownload', $domain); +// $domain = $this->report->sqlBuilder("question"); +// $this->assertInstanceOf('module_report_sqlquestion', $domain); +// $domain = $this->report->sqlBuilder("action"); +// $this->assertInstanceOf('module_report_sqlaction', $domain); +// $domain = $this->report->sqlBuilder("unknow"); +// $this->assertEquals($this->report->getReq(), $domain); +// } + + public function SetBound() + { + $this->report->setBound('test', true); + $bound = $this->report->getBound(); + $this->assertArrayhaskey('test', $bound); + $this->assertEquals(1, $bound['test']); + $this->report->setBound('test', false); + $bound = $this->report->getBound(); + $this->assertArrayhaskey('test', $bound); + $this->assertEquals(0, $bound['test']); + } + + public function SetOrder() + { + $this->report->setOrder('champs', 'order'); + $order = $this->report->getOrder(); + $this->assertEquals('champs', $order['champ']); + $this->assertEquals('order', $order['order']); + $this->assertEquals('champs', $this->report->getOrder('champ')); + } + + public function testGetterSetter() + { + + + $report = new module_report($this->dmin, $this->dmax, 1 , ''); + $bool = true; + $report->setPrettyString($bool); + $this->assertEquals($bool, $report->getPrettyString()); + $title = 'test'; + $report->setTitle($title); + $this->assertEquals($title, $report->getTitle()); + $bool = false; + $report->setCsv($bool); + $this->assertEquals($bool, $report->getCsv()); + $filter = array('test', 'array'); + $report->setFilter($filter); + $this->assertEquals($filter, $report->getTabFilter()); + $periode = "2 years"; + $report->setPeriode($periode); + $this->assertEquals($periode, $report->getPeriode()); + $postingFilter = 'my posting filter !'; + $report->setpostingFilter($postingFilter); + $this->assertEquals($postingFilter, $report->getPostingFilter()); + $page = 223; + $limit = 125; + $report->setLimit($page, $limit); + $this->assertEquals($page, $report->getNbPage()); + $this->assertEquals($limit, $report->getNbRecord()); + $report->setGroupBy($bool); + $this->assertEquals($bool, $report->getGroupBy()); + $column = array('col1', 'col2'); + $report->setActiveColumn($column); + $this->assertEquals($column, $report->getActiveColumn()); + $report->setConfig($bool); + $report->setPrint($bool); + $report->setHasLimit($bool); + $this->assertFalse($report->getConfig()); + $this->assertFalse($report->getPrint()); + $this->assertFalse($report->getHasLimit()); + $result = array('result', 'result'); + $report->setResult($result); + $this->assertEquals($result, $report->getResult()); + $total = 3200; + $report->setTotal($total); + $this->assertEquals($total, $report->getTotal()); + $default_display = array('a', 'b', 'c'); + $report->setDefault_display($default_display); + $this->assertEquals($default_display, $report->getDefault_display()); + } + + + public function testOther() + { + + foreach ($this->ret as $sbasid => $collections) + { + $report = $this->getMock('module_report', array('buildReq', 'buildResult'), array(), '', FALSE); + $report->setSbas_id($sbasid); + $this->assertEquals($sbasid, $report->getSbas_id()); + $report->setRequest('SELECT + user, + usrid, + log.date as ddate, + log.societe, + log.pays, + log.activite, + log.fonction, + site, + sit_session, + coll_list, + appli, + ip + FROM log '); + $report->expects($this->any())->method('buildReq')->will($this->returnValue('')); + $report->expects($this->any())->method('buildResult')->will($this->returnValue(array())); + $result = $report->buildReport(false, 'user'); +// $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $report->getChamps()); + } + } +} diff --git a/tests/report/sqlActionTest.php b/tests/report/sqlActionTest.php new file mode 100644 index 0000000000..35526bf1ec --- /dev/null +++ b/tests/report/sqlActionTest.php @@ -0,0 +1,39 @@ +action = new module_report_sqlaction($this->getMock('module_report', array(), array() , '', false)); + + } + + public function testGetAction() + { + $this->assertEquals('add', $this->action->getAction()); + $this->action->setAction('unknowAction'); + $this->assertEquals('add', $this->action->getAction()); + } +} diff --git a/tests/report/sqlFilterTest.php b/tests/report/sqlFilterTest.php new file mode 100644 index 0000000000..f74e031a48 --- /dev/null +++ b/tests/report/sqlFilterTest.php @@ -0,0 +1,187 @@ +format("Y-m-d H:i:s"); + $date->modify('-6 month'); + $dmin = $date->format("Y-m-d H:i:s"); + $appbox = appbox::get_instance(\bootstrap::getCore()); + $databoxes = $appbox->get_databoxes(); + $ret = array(); + foreach ($databoxes as $databox) + { + $colls = $databox->get_collections(); + $rett = array(); + foreach ($colls as $coll) + { + $rett[$coll->get_coll_id()] = $coll->get_coll_id(); + } + $ret[$databox->get_sbas_id()] = implode(',', $rett); + } + foreach ($ret as $sbasid => $collections) + { + $report = new module_report_connexion( + $dmin, + $dmax, + $sbasid, + $collections + ); + if(!$this->report instanceof module_report) + { + $this->report = $report; + } + elseif($report->getTotal() > $this->report->getTotal()) + { + $this->report = $report; + } + } + + $this->report->setFilter(array(array('f' => 'user', 'o' => '=', 'v'=>'admin'), array('f' => 'ddate', 'o' => 'LIKE', 'v'=>'*'), array('f' => '1', 'o' => 'OR', 'v'=>'1'))); + $this->report->setUser_id(self::$user->get_id()); + $this->report->setOrder('user', 'ASC'); + $this->filter = new module_report_sqlfilter($this->report); + } + + public function checkFilter($filter) + { + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $filter); + $this->assertArrayHasKey('params', $filter); + $this->assertArrayHasKey('sql', $filter); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY, $filter['params']); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $filter['sql']); + foreach($filter['params'] as $key => $value) + { + $this->assertRegExp('/'.$key.'/', $filter['sql']); + } + } + + public function testGvFilter() + { + $filter = $this->filter->getGvSitFilter(); + $this->checkFilter($filter); + } + + public function testUserIDFilter() + { + $filter = $this->filter->getUserIdFilter(2); + $this->assertTrue(in_array(2, $filter['params'])); + $this->checkFilter($filter); + } + + public function testDateFilter() + { + $filter = $this->filter->getDateFilter(); + $this->checkFilter($filter); + } + + public function testUserFilter() + { + $f = $this->report->getTabFilter(); + + if (sizeof($f) == 0) + { + $this->assertFalse($this->filter->getUserFilter()); + } + else + { + $filter = $this->filter->getUserFilter(); + $this->checkFilter($filter); + } + + } + + public function testCollectionFilter() + { + if($this->report->getUserId() == '') + { + $this->assertFalse($this->filter->getCollectionFilter()); + } + elseif(count(explode(",", $this->report->getListCollId()) > 0)) + { + $filter = $this->filter->getCollectionFilter(); + $this->checkFilter($filter); + } + else + { + $this->assertFalse($this->filter->getCollectionFilter()); + } + } + + public function testRecordFilter() + { + if($this->report->getUserId() == '') + { + $this->assertFalse($this->filter->getRecordFilter()); + } + else + { + $filter = $this->filter->getRecordFilter(); + $this->checkFilter($filter); + } + } + + public function testLimitFilter() + { + $p = $this->report->getNbPage(); + $r = $this->report->getNbRecord(); + + if ($p && $r) + { + $filter = $this->filter->getLimitFilter(); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $filter); + } + else + { + $this->assertFalse($this->filter->getLimitFilter()); + } + + } + + public function testOrderFilter() + { + if (sizeof($this->report->getOrder()) > 0) + { + $filter = $this->filter->getOrderFilter(); + $this->assertInternalType(PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $filter); + } + else + { + $this->assertFalse($this->filter->getOrderFilter()); + } + } + + public function testReportFilter() + { + $filter = $this->filter->getReportFilter(); + $this->checkFilter($filter); + } +} diff --git a/tests/report/sqlTest.php b/tests/report/sqlTest.php new file mode 100644 index 0000000000..b86f461a59 --- /dev/null +++ b/tests/report/sqlTest.php @@ -0,0 +1,43 @@ +getMock('module_report', array(), array(), '', false); + $this->sql = new module_report_sql($report); + } + + public function testSql() + { + $sqlFilter = $this->getMock('module_report_sqlfilter', array('getCorFilter'), array(), '', false); + $sqlFilter->expects($this->any())->method('getCorFilter')->will($this->onConsecutiveCalls(array(), array('hello'=>'world'))); + $this->sql->setFilter($sqlFilter); + $this->assertEquals('hello', $this->sql->getTransQuery('hello')); + $this->assertEquals('world', $this->sql->getTransQuery('hello')); + $this->sql->setGroupby('test'); + $this->assertEquals('test', $this->sql->getGroupBy()); + $this->sql->setOn('on'); + $this->assertEquals('on', $this->sql->getOn()); + } +} diff --git a/tests/searchEngine_optionsTest.php b/tests/searchEngine_optionsTest.php new file mode 100644 index 0000000000..e9301fa9d2 --- /dev/null +++ b/tests/searchEngine_optionsTest.php @@ -0,0 +1,259 @@ +object = new searchEngine_options(); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + public function tearDown() + { + + } + + public function testSet_locale() + { + $locale = 'BABA'; + $this->object->set_locale($locale); + $this->assertEquals($locale, $this->object->get_locale()); + } + + public function testGet_locale() + { + $locale = null; + $this->object->set_locale($locale); + $this->assertEquals($locale, $this->object->get_locale()); + } + + public function testSet_sort() + { + $by = 'NAME'; + $sort = 'ASC'; + $this->object->set_sort($by, $sort); + $this->assertEquals($by, $this->object->get_sortby()); + $this->assertEquals($sort, $this->object->get_sortord()); + $this->object->set_sort($by); + $this->assertEquals($by, $this->object->get_sortby()); + $this->assertEquals(searchEngine_options::SORT_MODE_DESC, $this->object->get_sortord()); + } + + public function testGet_sortby() + { + $by = 'NAME'; + $sort = 'DESC'; + $this->object->set_sort($by, $sort); + $this->assertEquals($by, $this->object->get_sortby()); + $this->assertEquals($sort, $this->object->get_sortord()); + } + + public function testGet_sortord() + { + $by = 'NAME'; + $sort = 'DESC'; + $this->object->set_sort($by, $sort); + $this->assertEquals($by, $this->object->get_sortby()); + $this->assertEquals($sort, $this->object->get_sortord()); + } + + public function testSet_use_stemming() + { + $bool = true; + $this->object->set_use_stemming($bool); + $this->assertEquals($bool, $this->object->get_use_stemming()); + $bool = false; + $this->object->set_use_stemming($bool); + $this->assertEquals($bool, $this->object->get_use_stemming()); + } + + public function testGet_use_stemming() + { + $bool = true; + $this->object->set_use_stemming($bool); + $this->assertEquals($bool, $this->object->get_use_stemming()); + $bool = false; + $this->object->set_use_stemming($bool); + $this->assertEquals($bool, $this->object->get_use_stemming()); + } + + public function testSet_search_type() + { + $type = "caca"; + $this->object->set_search_type($type); + $this->assertEquals(searchEngine_options::RECORD_RECORD, $this->object->get_search_type()); + $type = searchEngine_options::RECORD_RECORD; + $this->object->set_search_type($type); + $this->assertEquals(searchEngine_options::RECORD_RECORD, $this->object->get_search_type()); + $type = searchEngine_options::RECORD_GROUPING; + $this->object->set_search_type($type); + $this->assertEquals(searchEngine_options::RECORD_GROUPING, $this->object->get_search_type()); + } + + public function testGet_search_type() + { + $type = "caca"; + $this->object->set_search_type($type); + $this->assertEquals(searchEngine_options::RECORD_RECORD, $this->object->get_search_type()); + $type = searchEngine_options::RECORD_RECORD; + $this->object->set_search_type($type); + $this->assertEquals(searchEngine_options::RECORD_RECORD, $this->object->get_search_type()); + $type = searchEngine_options::RECORD_GROUPING; + $this->object->set_search_type($type); + $this->assertEquals(searchEngine_options::RECORD_GROUPING, $this->object->get_search_type()); + } + + public function testSet_bases() + { + $bases = array_keys(self::$user->ACL()->get_granted_base()); + $this->object->set_bases($bases, self::$user->ACL()); + $this->assertEquals(array_values($bases), array_values($this->object->get_bases())); + } + + public function testGet_bases() + { + $bases = array_keys(self::$user->ACL()->get_granted_base()); + $this->object->set_bases($bases, self::$user->ACL()); + $this->assertEquals(array_values($bases), array_values($this->object->get_bases())); + } + + public function testSet_fields() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testGet_fields() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testSet_status() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testGet_status() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testSet_record_type() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testGet_record_type() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testSet_min_date() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testGet_min_date() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testSet_max_date() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testGet_max_date() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testSet_date_fields() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testGet_date_fields() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testSerialize() + { + $bases = array_keys(self::$user->ACL()->get_granted_base()); + $this->object->set_bases($bases, self::$user->ACL()); + $this->object->set_date_fields(array()); + $this->object->set_locale('fr_FR'); + $this->object->set_max_date(null); + $this->object->set_min_date(null); + $this->object->set_record_type(searchEngine_options::TYPE_AUDIO); + $this->object->set_search_type(searchEngine_options::RECORD_RECORD); + $this->object->set_sort('Name','DESC'); + $this->object->set_status(array()); + $this->object->set_use_stemming(true); + $this->assertEquals($this->object, unserialize(serialize($this->object))); + } + + public function testUnserialize() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} diff --git a/tests/setupTest.php b/tests/setupTest.php new file mode 100644 index 0000000000..109841f393 --- /dev/null +++ b/tests/setupTest.php @@ -0,0 +1,203 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testCheck_binaries(). + */ + public function testCheck_binaries() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testCheck_mod_auth_token(). + */ + public function testCheck_mod_auth_token() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testCheck_apache(). + */ + public function testCheck_apache() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testCheck_phrasea(). + */ + public function testCheck_phrasea() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testCheck_writability(). + */ + public function testCheck_writability() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testCheck_mail_form(). + */ + public function testCheck_mail_form() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testCheck_php_version(). + */ + public function testCheck_php_version() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testCheck_php_extension(). + */ + public function testCheck_php_extension() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + + /** + * @todo Implement testCheck_cache_memcache(). + */ + public function testCheck_cache_memcache() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testCheck_cache_opcode(). + */ + public function testCheck_cache_opcode() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testCheck_php_configuration(). + */ + public function testCheck_php_configuration() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testCheck_sphinx_search(). + */ + public function testCheck_sphinx_search() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testCheck_system_locales(). + */ + public function testCheck_system_locales() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testCreateAdmin(). + */ + public function testCreateAdmin() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testWrite_config(). + */ + public function testWrite_config() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testCreateBase(). + */ + public function testCreateBase() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/sphinxrtTest.php b/tests/sphinxrtTest.php new file mode 100644 index 0000000000..3eedcf0278 --- /dev/null +++ b/tests/sphinxrtTest.php @@ -0,0 +1,69 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testDelete(). + */ + public function testDelete() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testUpdate_status(). + */ + public function testUpdate_status() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testReplace_in_metas(). + */ + public function testReplace_in_metas() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testReplace_in_documents(). + */ + public function testReplace_in_documents() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + +} + +?> diff --git a/tests/system/system_fileTest.php b/tests/system/system_fileTest.php new file mode 100644 index 0000000000..29e59682ec --- /dev/null +++ b/tests/system/system_fileTest.php @@ -0,0 +1,267 @@ +objects['indd'] = new system_file(__DIR__ . '/../../lib/vendor/exiftool/t/images/InDesign.indd'); + $this->objects['wav'] = new system_file(__DIR__ . '/../testfiles/test012.wav'); + $this->objects['jpg'] = new system_file(__DIR__ . '/../../lib/vendor/exiftool/t/images/Casio.jpg'); + } + + public function testGet_mime() + { + $this->assertEquals('application/octet-stream', $this->objects['indd']->get_mime()); + $this->assertEquals('audio/x-wav', $this->objects['wav']->get_mime()); + $this->assertEquals('image/jpeg', $this->objects['jpg']->get_mime()); + } + + public function testIs_raw_image() + { + $this->assertFalse($this->objects['indd']->is_raw_image()); + $this->assertFalse($this->objects['wav']->is_raw_image()); + $this->assertFalse($this->objects['jpg']->is_raw_image()); + } + + public function testGet_extension() + { + $this->assertEquals('indd', $this->objects['indd']->get_extension()); + $this->assertEquals('wav', $this->objects['wav']->get_extension()); + $this->assertEquals('jpg', $this->objects['jpg']->get_extension()); + } + + public function testGet_sha256() + { + $this->assertEquals('50a49257863f510b4e7f0de795884a89aeefdd701069db414a472f0ede7b0c98', $this->objects['indd']->get_sha256()); + $this->assertEquals('690e00c90a97ea24a2bd69abe410dc0fc3b6b69ddc20b1d572d0a953a4617eed', $this->objects['wav']->get_sha256()); + $this->assertEquals('c08fed0e193a1549c130d79814d30a3ac3fcd81980e261c4947ff45691628f92', $this->objects['jpg']->get_sha256()); + } + + public function testGet_technical_datas() + { + $technical_datas = $this->objects['jpg']->get_technical_datas(); + $this->assertArrayHasKey(system_file::TC_DATAS_WIDTH, $technical_datas); + $this->assertArrayHasKey(system_file::TC_DATAS_HEIGHT, $technical_datas); + $this->assertArrayHasKey(system_file::TC_DATAS_CHANNELS, $technical_datas); + $this->assertArrayHasKey(system_file::TC_DATAS_COLORDEPTH, $technical_datas); + $this->assertArrayHasKey(system_file::TC_DATAS_MIMETYPE, $technical_datas); + $this->assertArrayHasKey(system_file::TC_DATAS_FILESIZE, $technical_datas); + $technical_datas = $this->objects['indd']->get_technical_datas(); + $this->assertArrayHasKey(system_file::TC_DATAS_MIMETYPE, $technical_datas); + $this->assertArrayHasKey(system_file::TC_DATAS_FILESIZE, $technical_datas); + + $registry = registry::get_instance(); + if (!is_executable($registry->get('GV_mplayer'))) + $this->markTestSkipped('MPlayer is not configured'); + $technical_datas = $this->objects['wav']->get_technical_datas(); + $this->assertArrayHasKey(system_file::TC_DATAS_AUDIOBITRATE, $technical_datas); + $this->assertArrayHasKey(system_file::TC_DATAS_AUDIOSAMPLERATE, $technical_datas); + $this->assertArrayHasKey(system_file::TC_DATAS_AUDIOCODEC, $technical_datas); + $this->assertArrayHasKey(system_file::TC_DATAS_DURATION, $technical_datas); + $this->assertArrayHasKey(system_file::TC_DATAS_MIMETYPE, $technical_datas); + $this->assertArrayHasKey(system_file::TC_DATAS_FILESIZE, $technical_datas); + } + + public function testGet_phrasea_type() + { + $this->assertEquals('unknown', $this->objects['indd']->get_phrasea_type()); + $this->assertEquals('audio', $this->objects['wav']->get_phrasea_type()); + $this->assertEquals('image', $this->objects['jpg']->get_phrasea_type()); + } + + public function testGetPath() + { + $supposed = __DIR__ . '/../../lib/vendor/exiftool/t/images/'; + $this->assertEquals($supposed, $this->objects['indd']->getPath()); + } + + /** + * @todo Implement testHas_uuid(). + */ + public function testHas_uuid() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testRead_uuid(). + */ + public function testRead_uuid() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testWrite_uuid(). + */ + public function testWrite_uuid() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testIs_new_in_base(). + */ + public function testIs_new_in_base() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testGenerate_and_write(). + */ + public function testGenerate_and_write() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @todo Implement testWrite(). + */ + public function testWrite() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + public function testMkdir() + { + $path = __DIR__ . '/test/dir/to/create/'; + if (is_dir($path)) + { + $this->fail('unable to create directory : directory already extists'); + } + system_file::mkdir($path); + $this->assertTrue(is_dir($path)); + rmdir($path); + $path = dirname($path); + rmdir($path); + $path = dirname($path); + rmdir($path); + $path = dirname($path); + rmdir(__DIR__ . '/test'); + $path = dirname($path); + } + + public function testChmod() + { + $file = __DIR__ . '/../testfiles/cestlafete.jpg'; + $dir = __DIR__ . '/testchmod'; + system_file::mkdir($dir); + + chmod($file, 0700); + chmod($dir, 0700); + $system_file = new system_file($file); + $system_dir = new system_file($dir); + $system_file->chmod(); + $system_dir->chmod(); + clearstatcache(); + $this->assertEquals('0766', substr(sprintf('%o', fileperms($file)), -4)); + $this->assertEquals('0755', substr(sprintf('%o', fileperms($dir)), -4)); + rmdir($dir); + } + + public function testEmpty_directory() + { + $file = __DIR__ . '/../testfiles/cestlafete.jpg'; + $dir = __DIR__ . '/testchmod'; + system_file::mkdir($dir); + + copy($file, $dir . '/v1.test'); + copy($file, $dir . '/v2.test'); + copy($file, $dir . '/v3.test'); + + $system_file = new system_file($dir); + $system_file->empty_directory(); + + $this->assertFalse(is_file($dir . '/v1.test')); + $this->assertFalse(is_file($dir . '/v2.test')); + $this->assertFalse(is_file($dir . '/v3.test')); + + rmdir($dir); + } + + public function testSet_phrasea_tech_field() + { + $this->objects['wav']->set_phrasea_tech_field('un', '1'); + $this->objects['wav']->set_phrasea_tech_field('trois', '3'); + $this->objects['wav']->set_phrasea_tech_field('deux', '2'); + + try + { + $this->objects['wav']->set_phrasea_tech_field(' ', '2'); + $this->fail(); + } + catch (Exception_InvalidArgument $e) + { + + } + + $this->assertEquals('1', $this->objects['wav']->get_phrasea_tech_field('un')); + $this->assertEquals('2', $this->objects['wav']->get_phrasea_tech_field('deux')); + $this->assertEquals('3', $this->objects['wav']->get_phrasea_tech_field('trois')); + } + + public function testGet_phrasea_tech_field() + { + $this->assertNull($this->objects['wav']->get_phrasea_tech_field('qsdfqsdfsqd')); + } + + public function testExtract_metadatas() + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + $databox = null; + foreach ($appbox->get_databoxes() as $d) + { + $databox = $d; + break; + } + $this->assertInstanceOf('databox', $databox); + $metadatas = $this->objects['wav']->extract_metadatas($databox->get_meta_structure()); + + $this->assertTrue(is_array($metadatas)); + $this->assertArrayHasKey('metadatas', $metadatas); + $this->assertArrayHasKey('status', $metadatas); + foreach ($metadatas['metadatas'] as $metadata) + { + $this->assertTrue(is_array($metadata)); + $this->assertArrayHasKey('meta_struct_id', $metadata); + $this->assertArrayHasKey('meta_id', $metadata); + $this->assertArrayHasKey('value', $metadata); + $this->assertTrue(is_scalar($metadata['value'])); + $this->assertNull($metadata['meta_id']); + $this->assertTrue(is_int($metadata['meta_struct_id'])); + $this->assertTrue($metadata['meta_struct_id'] > 0); + } + } + +} diff --git a/tests/system/system_serverTest.php b/tests/system/system_serverTest.php new file mode 100644 index 0000000000..f45d6c05b2 --- /dev/null +++ b/tests/system/system_serverTest.php @@ -0,0 +1,55 @@ +objects['apache'] = new system_server; + $_SERVER['SERVER_SOFTWARE'] = 'nginx'; + $this->objects['nginx'] = new system_server; + $_SERVER['SERVER_SOFTWARE'] = 'lighttpd'; + $this->objects['lighttpd'] = new system_server; + } + + public function testIs_nginx() + { + $this->assertFalse($this->objects['apache']->is_nginx()); + $this->assertTrue($this->objects['nginx']->is_nginx()); + $this->assertFalse($this->objects['lighttpd']->is_nginx()); + } + + public function testIs_lighttpd() + { + $this->assertFalse($this->objects['apache']->is_lighttpd()); + $this->assertFalse($this->objects['nginx']->is_lighttpd()); + $this->assertTrue($this->objects['lighttpd']->is_lighttpd()); + } + + public function testIs_apache() + { + $this->assertTrue($this->objects['apache']->is_apache()); + $this->assertFalse($this->objects['nginx']->is_apache()); + $this->assertFalse($this->objects['lighttpd']->is_apache()); + } + + public function testGet_platform() + { + $platform = system_server::get_platform(); + $this->assertRegExp('/[A-Z]+/', $platform); + } + +} + diff --git a/tests/system/system_urlTest.php b/tests/system/system_urlTest.php new file mode 100644 index 0000000000..916b67df76 --- /dev/null +++ b/tests/system/system_urlTest.php @@ -0,0 +1,29 @@ +object = new system_url($this->url); + } + + public function testGet_url() + { + $this->assertEquals($this->url, $this->object->get_url()); + } + +} + diff --git a/tests/task/task_abstractTest.php b/tests/task/task_abstractTest.php new file mode 100644 index 0000000000..57d969d2a0 --- /dev/null +++ b/tests/task/task_abstractTest.php @@ -0,0 +1,21 @@ +delete(); + } + +} + diff --git a/tests/testfiles/14082008057.mp4 b/tests/testfiles/14082008057.mp4 new file mode 100644 index 0000000000..6d39b2a84b Binary files /dev/null and b/tests/testfiles/14082008057.mp4 differ diff --git a/tests/testfiles/cestlafete.jpg b/tests/testfiles/cestlafete.jpg new file mode 100755 index 0000000000..7e47016c34 Binary files /dev/null and b/tests/testfiles/cestlafete.jpg differ diff --git a/tests/testfiles/heavy500.jpg b/tests/testfiles/heavy500.jpg new file mode 100644 index 0000000000..f0136d52b7 Binary files /dev/null and b/tests/testfiles/heavy500.jpg differ diff --git a/tests/testfiles/iphone_pic.jpg b/tests/testfiles/iphone_pic.jpg new file mode 100644 index 0000000000..bd95fa052b Binary files /dev/null and b/tests/testfiles/iphone_pic.jpg differ diff --git a/tests/testfiles/logocoll.gif b/tests/testfiles/logocoll.gif new file mode 100644 index 0000000000..64b33c5415 Binary files /dev/null and b/tests/testfiles/logocoll.gif differ diff --git a/tests/testfiles/p4logo.jpg b/tests/testfiles/p4logo.jpg new file mode 100644 index 0000000000..61491c47ba Binary files /dev/null and b/tests/testfiles/p4logo.jpg differ diff --git a/tests/testfiles/recta_logo.gif b/tests/testfiles/recta_logo.gif new file mode 100644 index 0000000000..d9def6cce9 Binary files /dev/null and b/tests/testfiles/recta_logo.gif differ diff --git a/tests/testfiles/rectb_logo.gif b/tests/testfiles/rectb_logo.gif new file mode 100644 index 0000000000..e3f990b5b6 Binary files /dev/null and b/tests/testfiles/rectb_logo.gif differ diff --git a/tests/testfiles/test001.CR2 b/tests/testfiles/test001.CR2 new file mode 100644 index 0000000000..8f9223e20a Binary files /dev/null and b/tests/testfiles/test001.CR2 differ diff --git a/tests/testfiles/test002.CR2 b/tests/testfiles/test002.CR2 new file mode 100644 index 0000000000..465d60ee29 Binary files /dev/null and b/tests/testfiles/test002.CR2 differ diff --git a/tests/testfiles/test003.CR2 b/tests/testfiles/test003.CR2 new file mode 100644 index 0000000000..426db7e8bd Binary files /dev/null and b/tests/testfiles/test003.CR2 differ diff --git a/tests/testfiles/test004.CR2 b/tests/testfiles/test004.CR2 new file mode 100644 index 0000000000..ab7a341cb8 Binary files /dev/null and b/tests/testfiles/test004.CR2 differ diff --git a/tests/testfiles/test005.CR2 b/tests/testfiles/test005.CR2 new file mode 100644 index 0000000000..69fe8ed815 Binary files /dev/null and b/tests/testfiles/test005.CR2 differ diff --git a/tests/testfiles/test006.wav b/tests/testfiles/test006.wav new file mode 100644 index 0000000000..4ae0073f40 Binary files /dev/null and b/tests/testfiles/test006.wav differ diff --git a/tests/testfiles/test007.ppt b/tests/testfiles/test007.ppt new file mode 100644 index 0000000000..d6eda36808 Binary files /dev/null and b/tests/testfiles/test007.ppt differ diff --git a/tests/testfiles/test008.ai b/tests/testfiles/test008.ai new file mode 100644 index 0000000000..4823f37a01 --- /dev/null +++ b/tests/testfiles/test008.ai @@ -0,0 +1,4197 @@ +%PDF-1.5 % +1 0 obj <>/OCGs[5 0 R 6 0 R 7 0 R 8 0 R 40 0 R 41 0 R 42 0 R 43 0 R 44 0 R 78 0 R 79 0 R 80 0 R 81 0 R 82 0 R 116 0 R 117 0 R 118 0 R 119 0 R 120 0 R 121 0 R 248 0 R 249 0 R 250 0 R 251 0 R 252 0 R 253 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <>stream + + + + + application/pdf + + + Impression + + + + + 2009-11-27T16:58:06+01:00 + 2009-11-27T16:58:06+01:00 + 2009-09-03T12:21:23+02:00 + Adobe Illustrator CS4 + + + + 256 + 152 + JPEG + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAmAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9PtqlgGI9UMo2eRQzRqf BpFBRfpOKpfJEJ/9KnFvLE7PxmkjE6KgYhKEOvEFQCffqcVVhay2lGjZIENPjhWkNT05wkmn+srD 3xVH2tx68Ak48Wqyuta0ZGKsK9/iBxVVxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2K uxV2KuxV2KuxV2KuxV2KuxV2KoJGaxX0jG72q/3LxqXKL2RlWrGnRSB06+JVcts3Ez2bmB5CXZHQ 8GJNavGeLBvkQfGuKpeLVZrhrZXitWYH95BGsUpI60VnfkK/zoVNDtiqNsY306JLWZjLDyPC6PUs 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 + + + + + + uuid:92a05eaf-880e-4039-9d18-af54f0b22fb2 + xmp.did:EEB21D887398DE11A18DE4B176236835 + uuid:5D20892493BFDB11914A8590D31508C8 + proof:pdf + + uuid:68dafb6c-2481-0c47-a84f-3e384d157169 + xmp.did:264F4C2C7BBDD6119286AC08E167C812 + uuid:5D20892493BFDB11914A8590D31508C8 + proof:pdf + + + + + converted + from application/pdf to <unknown> + + + saved + xmp.iid:D27F11740720681191099C3B601C4548 + 2008-04-17T14:19:15+05:30 + Adobe Illustrator CS4 + + + / + + + + + converted + from application/pdf to <unknown> + + + converted + from application/pdf to <unknown> + + + saved + xmp.iid:F97F1174072068118D4ED246B3ADB1C6 + 2008-05-15T16:23:06-07:00 + Adobe Illustrator CS4 + + + / + + + + + saved + xmp.iid:FA7F1174072068118D4ED246B3ADB1C6 + 2008-05-15T17:10:45-07:00 + Adobe Illustrator CS4 + + + / + + + + + saved + xmp.iid:EF7F117407206811A46CA4519D24356B + 2008-05-15T22:53:33-07:00 + Adobe Illustrator CS4 + + + / + + + + + saved + xmp.iid:F07F117407206811A46CA4519D24356B + 2008-05-15T23:07:07-07:00 + Adobe Illustrator CS4 + + + / + + + + + saved + xmp.iid:F77F117407206811BDDDFD38D0CF24DD + 2008-05-16T10:35:43-07:00 + Adobe Illustrator CS4 + + + / + + + + + converted + from application/pdf to <unknown> + + + saved + xmp.iid:F97F117407206811BDDDFD38D0CF24DD + 2008-05-16T10:40:59-07:00 + Adobe Illustrator CS4 + + + / + + + + + converted + from application/vnd.adobe.illustrator to <unknown> + + + saved + xmp.iid:FA7F117407206811BDDDFD38D0CF24DD + 2008-05-16T11:26:55-07:00 + Adobe Illustrator CS4 + + + / + + + + + saved + xmp.iid:FB7F117407206811BDDDFD38D0CF24DD + 2008-05-16T11:29:01-07:00 + Adobe Illustrator CS4 + + + / + + + + + saved + xmp.iid:FC7F117407206811BDDDFD38D0CF24DD + 2008-05-16T11:29:20-07:00 + Adobe Illustrator CS4 + + + / + + + + + saved + xmp.iid:FD7F117407206811BDDDFD38D0CF24DD + 2008-05-16T11:30:54-07:00 + Adobe Illustrator CS4 + + + / + + + + + saved + xmp.iid:FE7F117407206811BDDDFD38D0CF24DD + 2008-05-16T11:31:22-07:00 + Adobe Illustrator CS4 + + + / + + + + + saved + xmp.iid:B233668C16206811BDDDFD38D0CF24DD + 2008-05-16T12:23:46-07:00 + Adobe Illustrator CS4 + + + / + + + + + saved + xmp.iid:B333668C16206811BDDDFD38D0CF24DD + 2008-05-16T13:27:54-07:00 + Adobe Illustrator CS4 + + + / + + + + + saved + xmp.iid:B433668C16206811BDDDFD38D0CF24DD + 2008-05-16T13:46:13-07:00 + Adobe Illustrator CS4 + + + / + + + + + saved + xmp.iid:F77F11740720681197C1BF14D1759E83 + 2008-05-16T15:47:57-07:00 + Adobe Illustrator CS4 + + + / + + + + + saved + xmp.iid:F87F11740720681197C1BF14D1759E83 + 2008-05-16T15:51:06-07:00 + Adobe Illustrator CS4 + + + / + + + + + saved + xmp.iid:F97F11740720681197C1BF14D1759E83 + 2008-05-16T15:52:22-07:00 + Adobe Illustrator CS4 + + + / + + + + + converted + from application/vnd.adobe.illustrator to application/vnd.adobe.illustrator + + + saved + xmp.iid:FA7F117407206811B628E3BF27C8C41B + 2008-05-22T13:28:01-07:00 + Adobe Illustrator CS4 + + + / + + + + + converted + from application/vnd.adobe.illustrator to application/vnd.adobe.illustrator + + + saved + xmp.iid:FF7F117407206811B628E3BF27C8C41B + 2008-05-22T16:23:53-07:00 + Adobe Illustrator CS4 + + + / + + + + + converted + from application/vnd.adobe.illustrator to application/vnd.adobe.illustrator + + + saved + xmp.iid:07C3BD25102DDD1181B594070CEB88D9 + 2008-05-28T16:45:26-07:00 + Adobe Illustrator CS4 + + + / + + + + + converted + from application/vnd.adobe.illustrator to application/vnd.adobe.illustrator + + + saved + xmp.iid:F87F1174072068119098B097FDA39BEF + 2008-06-02T13:25:25-07:00 + Adobe Illustrator CS4 + + + / + + + + + saved + xmp.iid:F77F117407206811BB1DBF8F242B6F84 + 2008-06-09T14:58:36-07:00 + Adobe Illustrator CS4 + + + / + + + + + saved + xmp.iid:F97F117407206811ACAFB8DA80854E76 + 2008-06-11T14:31:27-07:00 + Adobe Illustrator CS4 + + + / + + + + + saved + xmp.iid:0180117407206811834383CD3A8D2303 + 2008-06-11T22:37:35-07:00 + Adobe Illustrator CS4 + + + / + + + + + saved + xmp.iid:01E540664A3DDD11BD33D3EB8D3A1068 + 2008-06-18T22:24:01+07:00 + Adobe Illustrator CS4 + + + / + + + + + saved + xmp.iid:6B6AE2A5723EDD11A6F1BABF7C5A7A51 + 2008-06-19T20:30:34-07:00 + Adobe Illustrator CS4 + + + / + + + + + saved + xmp.iid:BEE4CD276D42DD11A0BEF9B17C50C624 + 2008-06-25T07:44:32+02:00 + Adobe Illustrator CS4 + + + / + + + + + saved + xmp.iid:BFE4CD276D42DD11A0BEF9B17C50C624 + 2008-06-25T07:47:41+02:00 + Adobe Illustrator CS4 + + + / + + + + + saved + xmp.iid:0730D8D3F647DD11AA8EC5F1D0957DC6 + 2008-07-02T12:22:13+07:00 + Adobe Illustrator CS4 + + + / + + + + + saved + xmp.iid:FA7F117407206811ACAF9F0F41229DDF + 2008-07-21T13:59:24+05:30 + Adobe Illustrator CS4 + + + / + + + + + saved + xmp.iid:254F4C2C7BBDD6119286AC08E167C812 + 2002-09-01T14:20:33+07:00 + Adobe Illustrator CS4 + / + + + saved + xmp.iid:264F4C2C7BBDD6119286AC08E167C812 + 2002-09-01T14:21:03+07:00 + Adobe Illustrator CS4 + / + + + saved + xmp.iid:EEB21D887398DE11A18DE4B176236835 + 2009-09-03T12:21:24+02:00 + Adobe Illustrator CS4 + / + + + + + + + EmbedByReference + + \\darkvador\VP_office\Ventes-VP\VENTES 2009\3-Jul-Sep\CHICCO17\Créations\1 Sources\Docs\2191.jpg + + + + + + + Print + Document + + + False + True + 1 + + 210.001652 + 296.999959 + Millimeters + + + + Cyan + Magenta + Yellow + Black + + + + + + Groupe de nuances par défaut + 0 + + + + Blanc + RGB + PROCESS + 255 + 255 + 255 + + + Noir + RGB + PROCESS + 26 + 23 + 27 + + + CMJN Rouge + RGB + PROCESS + 225 + 0 + 26 + + + CMJN Jaune + RGB + PROCESS + 255 + 236 + 0 + + + CMJN Vert + RGB + PROCESS + 0 + 143 + 54 + + + CMJN Cyan + RGB + PROCESS + 0 + 157 + 223 + + + CMJN Bleu + RGB + PROCESS + 23 + 41 + 130 + + + CMJN Magenta + RGB + PROCESS + 225 + 0 + 122 + + + C=15 M=100 J=90 N=10 + RGB + PROCESS + 189 + 9 + 38 + + + C=0 M=90 J=85 N=0 + RGB + PROCESS + 228 + 52 + 45 + + + C=0 M=80 J=95 N=0 + RGB + PROCESS + 230 + 81 + 30 + + + C=0 M=50 J=100 N=0 + RGB + PROCESS + 241 + 147 + 0 + + + C=0 M=35 J=85 N=0 + RGB + PROCESS + 247 + 178 + 52 + + + C=5 M=0 J=90 N=0 + RGB + PROCESS + 252 + 233 + 10 + + + C=20 M=0 J=100 N=0 + RGB + PROCESS + 222 + 218 + 0 + + + C=50 M=0 J=100 N=0 + RGB + PROCESS + 150 + 189 + 13 + + + C=75 M=0 J=100 N=0 + RGB + PROCESS + 64 + 165 + 43 + + + C=85 M=10 J=100 N=10 + RGB + PROCESS + 0 + 137 + 46 + + + C=90 M=30 J=95 N=30 + RGB + PROCESS + 0 + 99 + 46 + + + C=75 M=0 J=75 N=0 + RGB + PROCESS + 54 + 168 + 98 + + + C=80 M=10 J=45 N=0 + RGB + PROCESS + 0 + 159 + 149 + + + C=70 M=15 J=0 N=0 + RGB + PROCESS + 55 + 168 + 219 + + + C=85 M=50 J=0 N=0 + RGB + PROCESS + 10 + 113 + 179 + + + C=100 M=95 J=5 N=0 + RGB + PROCESS + 13 + 48 + 130 + + + C=100 M=100 J=25 N=25 + RGB + PROCESS + 19 + 35 + 91 + + + C=75 M=100 J=0 N=0 + RGB + PROCESS + 98 + 34 + 128 + + + C=50 M=100 J=0 N=0 + RGB + PROCESS + 146 + 17 + 126 + + + C=35 M=100 J=35 N=10 + RGB + PROCESS + 160 + 13 + 89 + + + C=10 M=100 J=50 N=0 + RGB + PROCESS + 212 + 0 + 80 + + + C=0 M=95 J=20 N=0 + RGB + PROCESS + 227 + 14 + 111 + + + C=25 M=25 J=40 N=0 + RGB + PROCESS + 202 + 186 + 156 + + + C=40 M=45 J=50 N=5 + RGB + PROCESS + 163 + 137 + 119 + + + C=50 M=50 J=60 N=25 + RGB + PROCESS + 120 + 104 + 84 + + + C=55 M=60 J=65 N=40 + RGB + PROCESS + 96 + 76 + 63 + + + C=25 M=40 J=65 N=0 + RGB + PROCESS + 200 + 157 + 100 + + + C=30 M=50 J=75 N=10 + RGB + PROCESS + 176 + 127 + 72 + + + C=35 M=60 J=80 N=25 + RGB + PROCESS + 144 + 94 + 54 + + + C=40 M=65 J=90 N=35 + RGB + PROCESS + 124 + 77 + 37 + + + C=40 M=70 J=100 N=50 + RGB + PROCESS + 103 + 58 + 21 + + + C=50 M=70 J=80 N=70 + RGB + PROCESS + 65 + 40 + 27 + + + + + + Gris + 1 + + + + C=0 M=0 J=0 N=100 + RGB + PROCESS + 26 + 23 + 27 + + + C=0 M=0 J=0 N=90 + RGB + PROCESS + 61 + 61 + 63 + + + C=0 M=0 J=0 N=80 + RGB + PROCESS + 88 + 88 + 90 + + + C=0 M=0 J=0 N=70 + RGB + PROCESS + 111 + 112 + 114 + + + C=0 M=0 J=0 N=60 + RGB + PROCESS + 134 + 135 + 137 + + + C=0 M=0 J=0 N=50 + RGB + PROCESS + 155 + 156 + 158 + + + C=0 M=0 J=0 N=40 + RGB + PROCESS + 176 + 178 + 179 + + + C=0 M=0 J=0 N=30 + RGB + PROCESS + 197 + 198 + 200 + + + C=0 M=0 J=0 N=20 + RGB + PROCESS + 216 + 217 + 218 + + + C=0 M=0 J=0 N=10 + RGB + PROCESS + 236 + 236 + 237 + + + C=0 M=0 J=0 N=5 + RGB + PROCESS + 245 + 245 + 246 + + + + + + Couleurs vives + 1 + + + + C=0 M=100 J=100 N=0 + RGB + PROCESS + 225 + 0 + 26 + + + C=0 M=75 J=100 N=0 + RGB + PROCESS + 232 + 93 + 16 + + + C=0 M=10 J=95 N=0 + RGB + PROCESS + 255 + 221 + 0 + + + C=85 M=10 J=100 N=0 + RGB + PROCESS + 0 + 147 + 48 + + + C=100 M=90 J=0 N=0 + RGB + PROCESS + 0 + 55 + 138 + + + C=60 M=90 J=0 N=0 + RGB + PROCESS + 128 + 53 + 136 + + + + + + + + + Adobe PDF library 9.00 + + + + + + + + + + + + + + + + + + + + + + + + + +endstream endobj 3 0 obj <> endobj 255 0 obj <>/Resources<>/ExtGState<>/Properties<>/XObject<>>>/Thumb 309 0 R/TrimBox[0.0 0.0 595.28 841.89]/Type/Page>> endobj 256 0 obj <>stream +HWn$GWt)LjؓXa?@ ;nUUkd`/6+|ӏ9N^)$~4lc&H5˿/O9=Jy>=R n$)ORՖMU{zٟ/׼9SKW$/Wi[-a.5RSIؔc;y` %CYlm,sbbOb6?>儑nRet&gI\]Y+=ezxrD.{wr-K,1:\q.^a81==hbcC)Fݺ̌0uգixQCc?r=93M6Iq_ZZdo! pWh!'EA"K"" 6a{=vpGmI 沕um_7P%lH & jihZN>?5{z$Q KfY߻WY2 gW*l= hDpgDZ% Ys |c5`inآŷ^d(5'vWLݬ 7,qݣJkմ^}@Ġ%${?Ѽ{TLnoPx3.N\3Žl:5 DvPF@<.#O~i?e?Hϙ͝EFbӈgvEd}`UoJc^#gsAVDӛ+H*ˀY} ZY۽f?*'B.,G7M8tYՕ1C!]#ĔyÉ7,Ũ,nNdEW=$4?/u3*.YQI_l`0Pc#Ѳx.^X ȿLI Zb3#Ag)I?oV:=Cc`ȢlÝ,g cQd,~*1+gB:9JJJ!GZ<@-7k2BVwqg!eNx}ǒ-N4u0>zNܿZ'ƕJ2Gi#1:\ب73l +;7N& 458&7]H쬔 4-Vw:褎rc@;"*"G#iaBr4M::*tQ҃uGh,}<5\n^ʾ#7#kON2Dț坪 s($lί/ +؈ +*w +:9Efd[: !NϨ 4.FC^1OǩS 6ve9trI螧 j|i[+yÅkeyG轪I @eUefy>>ۗo~Ͽ;J1@@1ݾ}.)ASEQBHfGkJ:ޖ\3@@죋 h +߆BcDUm,G%W↲]2NgL3W +F% +'ӬGU½\p~|{fR5֠?ui+M/w,=CH^F)J@8!Q d"تn`OQ?$gEtEh{/Vd,|IN5M1kAiJR#Fgϰ*YAs3&r5uEOdHsjKCK:¯p/}ԯջ+, +߽W7^;7ͽ77^9v7wȔ4eMفSv 0 +endstream endobj 257 0 obj <> endobj 309 0 obj <>stream +8;Z\79m5qI#gHtq&Y"%j2U`98@6`Gj;^%$UIq^8nVrkA.(\)g^#fcc@COb`l)nd94 +m2Mql;Q_7?hO]Rk[>Q%b:@DJep'"@Eh1/?B^sig7TE;$OG/;>*KpQh(q"G80#&kVtFN_]D +PQs#!5@O4VI2G)b!MeX1@\(-r?dL@n-@G1tD8` +Mj[#]XC\nTN?K7WV09Q+_D8"oT!4^,)rNWSie;FjZhJWt[XJjY!^#S/(7n5QW`3G2 +:qCf,'k=dFSmr3uQ:`'Rc[q=DmpRAq3Y=r66H*;t!`_`mdB/m)qLnokOE&rAlf1pc +7CCp[K"5g+I<&U6jg.;lm1g:T5FD"'hClNj2O326DJ'52oh_a_BY,TSo6*26V)U6u +EABF%)hmI_7Iq2;'nfD^NS-+%ff"K_^5~> +endstream endobj 311 0 obj [/Indexed/DeviceRGB 255 312 0 R] endobj 312 0 obj <>stream +8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0 +b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup` +E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn +6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O( +l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~> +endstream endobj 263 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 CS 0.102 0.09 0.106 SCN +1 w 4 M 0 j 0 J []0 d +/GS0 gs +q 1 0 0 1 -258.8408 664.791 cm +0 0 m +8.833 -26.092 7.191 -31.022 26.915 -46.226 c +-12.777 43.826 l +-8.303 32.171 -7.345 21.697 0 0 c +h +-12.777 43.826 m +-13.684 46.187 -14.73 48.595 -16.008 51.157 c +-12.777 43.826 l +h +-95.534 51.157 m +-97.589 92.657 l +-102.108 66.155 -95.534 51.157 v +h +24.242 49.308 m +90.191 63.483 36.292 123.152 -1.305 155.203 c +-16.714 167.53 -26.946 163.947 -23.263 165.309 c +-23.264 165.311 l +-20.144 166.46 -16.961 167.604 -13.709 168.634 c +-13.704 168.632 l +-13.707 168.635 l +-12.773 168.931 -11.835 169.221 -10.89 169.495 c +-9.041 173.193 -8.424 192.917 -19.519 202.162 c +-30.613 211.407 -29.997 206.476 -45.868 217.217 c +-61.738 227.957 -99.644 230.514 -100.876 192.917 c +-101.045 180.068 l +-100.945 180.012 -100.876 179.973 y +-90.793 173.822 -89.305 166.675 -66.985 160.713 c +-50.553 131.698 l +-51.293 130.198 -51.72 128.517 -51.72 126.731 c +-51.72 120.832 -47.174 116.001 -41.396 115.53 c +-18.902 75.811 l +-18.828 75.47 -18.752 75.123 -18.675 74.776 c +-20.01 74.11 -21.531 73.03 -23.217 71.291 c +-18.302 73.122 l +-17.124 68.045 -15.519 62.552 -12.328 57.974 c +-104.163 134.158 l +-103.136 124.266 -109.646 101.92 -77.455 73.51 c +-64.318 61.917 -25.032 60.158 -7.046 52.549 c +-0.869 48.063 8.746 46.045 24.242 49.308 c +h +-152.033 178.74 m +-141.555 162.099 -130.461 139.817 -101.493 146.027 c +-101.045 180.068 l +-103.219 181.274 -128.462 194.651 -152.033 178.74 c +h +1282.891 -390.327 m +1314.338 -381.343 1267.503 -355.403 1250.961 -344.886 c +1268.67 -311.445 1250.946 -287.906 1253.709 -286.608 c +1256.525 -285.287 1270.491 -281.218 1266.751 -265.8 c +1263.785 -253.575 1229.962 -276.071 1253.18 -260.854 c +1260.8 -255.86 1277.78 -245.631 1276.009 -234.269 c +1275.114 -228.538 1269.52 -224.302 1264.644 -224.583 c +1259.089 -224.905 1253.412 -226.45 1248.215 -228.655 c +1256.242 -221.201 1262.786 -211.483 1262.634 -201.029 c +1262.562 -196.104 1261.125 -188.647 1258.26 -184.48 c +1253.843 -178.055 1244.03 -171.475 1231.751 -181.926 c +1215.703 -195.584 1206.895 -219.815 1205.627 -239.852 c +1205.707 -240.454 l +1202.164 -238.094 1197.484 -237.857 1189.975 -228.724 c +1179.778 -216.324 1167.267 -205.637 1150.76 -213.073 c +1134.493 -221.022 1134.723 -237.476 1137.749 -253.241 c +1139.857 -264.226 1137.459 -268.176 1136.815 -272.154 c +1120.59 -260.331 1096.321 -251.63 1075.562 -255.25 c +1059.677 -258.021 1058.471 -269.773 1060.592 -277.276 c +1061.968 -282.144 1066.786 -288.012 1070.529 -291.214 c +1078.475 -298.009 1090.131 -299.183 1100.995 -297.769 c +1095.978 -300.358 1091.163 -303.739 1087.367 -307.807 c +1084.035 -311.378 1083.721 -318.389 1087.559 -322.737 c +1095.169 -331.358 1113.886 -324.83 1122.597 -322.158 c +1149.136 -314.017 1110.222 -325.667 1117.731 -335.759 c +1127.204 -348.485 1139.256 -340.34 1142.072 -339.018 c +1137.21 -277.165 l +1137.293 -277.419 1137.383 -277.679 1137.487 -277.947 c +1143.727 -292.932 1162.608 -293.316 v +1170.419 -295.327 1180.047 -298.891 1189.01 -294.55 c +1198.074 -290.428 1201.482 -280.743 1204.926 -273.45 c +1206.788 -271.111 1208.165 -268.783 1209.166 -266.521 c +1218.204 -334.622 l +1195.263 -328.854 1168.101 -320.944 1156.953 -301.872 c +1153.953 -314.359 1146.955 -336.354 1172.442 -358.844 c +1187.563 -372.185 1206.202 -371.807 1222.307 -365.536 c +1222.341 -365.791 l +1222.341 -381.791 1223.841 -406.291 1233.341 -414.291 c +1240.752 -420.532 1244.207 -415.818 1245.368 -413.444 c +1244.484 -415.937 1243.225 -421.734 1249.841 -423.291 c +1258.341 -428.291 1262.341 -419.791 v +1269.341 -433.291 1285.841 -422.791 1280.341 -411.291 c +1290.341 -413.791 1305.406 -403.323 1282.891 -390.327 c +h +-6.735 -560.599 m +-18.901 -558.937 l +-19.804 -572.563 -14.135 -610.113 25.258 -612.296 c +64.65 -614.479 68.896 -583.399 70.882 -571.199 c +69.534 -571.015 l +74.496 -557.76 71.655 -542.815 68.364 -533.44 c +71.466 -551.759 62.125 -563.165 48.864 -568.192 c +3.798 -562.037 l +-2.577 -555.405 -2.613 -546.569 -1.712 -540.814 c +-7.667 -542.243 l +-8.222 -549.106 -7.843 -555.205 -6.735 -560.599 c +h +-1.712 -540.814 m +2.127 -539.893 l +1.231 -538.386 0.42 -536.745 -0.274 -534.938 c +-1.163 -537.305 -1.712 -540.814 v +h +58.222 -450.83 m +58.339 -450.879 59.111 -449.757 59.176 -449.649 c +87.061 -403.399 87.552 -360.158 87.226 -350.862 c +86.9 -341.544 78.785 -319.01 56.95 -346.727 c +23.365 -401.938 34.336 -443.091 y +26.532 -445.924 l +45.29 -373.077 22.373 -342.665 12.125 -343.482 c +9.343 -343.704 6.437 -345.024 4.392 -346.635 c +-33.704 -381.673 -5.437 -453.327 y +-15.814 -470.777 -16.445 -484.762 y +-19.182 -494.363 -5.941 -532.056 27.259 -533.862 c +2.127 -539.893 l +14.71 -561.061 45.414 -553.97 y +63.138 -550.635 70.847 -536.338 61.432 -525.663 c +55.873 -526.997 l +81.861 -511.177 68.096 -474.461 58.692 -454.412 c +25.987 -448.026 l +26.038 -447.835 26.084 -447.648 26.135 -447.458 c +35.507 -445.789 46.282 -445.859 58.222 -450.83 c +h +886.056 -27.046 m +889.856 -41.821 897.458 -45.622 909.617 1.499 c +898.217 31.9 l +882.255 -12.271 886.056 -27.046 v +h +914.937 19.988 m +913.415 12.14 909.617 1.499 v +914.177 -19.021 930.138 -21.301 943.003 -21.301 c +942.949 -28.142 936.216 -43.341 938.496 -64.622 c +940.776 -85.902 952.176 -64.622 y +959.775 -47.902 961.296 -17.5 955.218 5.3 c +914.937 19.988 l +h +971.964 102.183 m +958.107 112.577 941.19 118.134 931.656 117.781 c +926.337 117.781 l +924.059 131.461 900.497 140.581 v +888.269 143.029 891.377 134.5 894.416 124.621 c +902.777 98.021 l +884.833 55.46 913.564 32.66 v +961.299 -4.58 991.698 48.62 v +997.4 64.873 993.261 78.771 984.778 89.755 c +994.33 80.496 l +1007.656 90.421 990.937 123.861 v +975.838 109.516 972.014 102.277 971.964 102.183 c +h +S +Q + +endstream endobj 264 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 290.585 735.9014 cm +0 0 m +10.537 -18.848 29.818 -31.709 47.791 -42.718 c +68.519 -55.414 90.57 -65.771 111.357 -78.356 c +112.463 -79.025 113.46 -77.292 112.357 -76.624 c +92.829 -64.801 72.211 -54.888 52.566 -43.258 c +33.594 -32.026 12.771 -18.744 1.726 1.013 c +1.096 2.138 -0.629 1.126 0 0 c +f +Q + +endstream endobj 265 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 298.3105 270.4365 cm +0 0 m +-11.949 19.857 -30.713 34.986 -52.023 43.813 c +-70.998 51.673 -96.159 54.791 -115.124 45.121 c +-142.708 31.056 -100.804 -2.707 -89.313 -11.783 c +-80.405 -18.82 -71.09 -25.507 -61.36 -31.37 c +-60.253 -32.037 -59.255 -30.304 -60.36 -29.638 c +-71.038 -23.203 -81.214 -15.792 -90.905 -7.967 c +-102.03 1.017 -115.176 11.803 -120.708 25.414 c +-128.031 43.427 -103.874 47.925 -91.261 48.759 c +-65.299 50.477 -39.551 38.677 -20.503 21.681 c +-13.145 15.114 -6.807 7.433 -1.726 -1.012 c +-1.062 -2.115 0.666 -1.107 0 0 c +f +Q + +endstream endobj 266 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 210.459 329.4277 cm +0 0 m +-11.035 -0.017 -25.486 0.459 -34.452 -7.219 c +-44.175 -15.546 -43.059 -30.937 -38.532 -41.764 c +-27.447 -68.279 3.564 -80.306 28.227 -90.46 c +29.42 -90.951 29.931 -89.015 28.755 -88.53 c +6.163 -79.229 -20.227 -68.864 -33.642 -47.099 c +-39.982 -36.81 -43.627 -19.573 -34.433 -9.891 c +-26.271 -1.295 -10.829 -2.017 -0.015 -2 c +1.277 -1.998 1.289 0.002 0 0 c +f +Q + +endstream endobj 267 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 277.1523 290.1406 cm +0 0 m +-11.869 12.875 -25.563 24.362 -40.235 33.903 c +-54.253 43.019 -72.604 52.486 -89.802 47.975 c +-102.174 44.729 -111.434 32.805 -107.238 20.09 c +-102.697 6.331 -88.652 -3.937 -77.316 -11.86 c +-65.385 -20.201 -52.18 -28.108 -38.466 -33.174 c +-37.253 -33.622 -36.739 -31.688 -37.938 -31.244 c +-50.567 -26.58 -62.602 -19.32 -73.75 -11.881 c +-86.001 -3.705 -101.964 7.583 -105.923 22.691 c +-107.813 29.902 -104.375 36.228 -99.012 40.874 c +-91.785 47.136 -82.062 47.98 -73.006 46.317 c +-53.162 42.673 -35.07 29.007 -20.03 16.293 c +-13.491 10.765 -7.212 4.876 -1.407 -1.422 c +-0.533 -2.37 0.873 -0.947 0 0 c +f +Q + +endstream endobj 268 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 305.7363 341.1309 cm +0 0 m +-9.75 -10.966 -16.347 -24.502 -14.3 -39.449 c +-12.596 -51.893 -4.477 -62.894 5.32 -70.349 c +23.312 -84.038 55.051 -91.229 70.605 -69.936 c +80.964 -55.757 80.517 -36.681 72.555 -21.555 c +62.719 -2.865 42.522 10.388 22.564 15.902 c +5.521 20.611 -15.75 17.159 -23.196 -0.96 c +-30.797 -19.457 -21.698 -39.894 -8.742 -53.468 c +4.23 -67.06 24.602 -77.881 43.876 -75.18 c +63.191 -72.472 79.131 -55.595 76.279 -35.531 c +73.376 -15.105 54.426 1.857 38.612 13.166 c +25.732 22.377 7.388 28.642 -8.344 24.084 c +-25.387 19.146 -28.947 0.383 -26.833 -15.06 c +-24.059 -35.318 -12.04 -55.096 4.186 -67.369 c +21.16 -80.209 42.744 -79.961 60.028 -67.741 c +74.166 -57.746 73.326 -41.274 65.192 -27.646 c +53.668 -8.338 31.762 4.181 10.445 9.6 c +-0.076 12.274 -13.328 12.233 -19.792 2.028 c +-26.316 -8.271 -19.434 -23.541 -16.754 -33.961 c +-16.433 -35.211 -14.501 -34.691 -14.823 -33.443 c +-17.549 -22.843 -23.604 -9.556 -18.06 1.028 c +-11.695 13.179 6.457 9.08 16.598 5.704 c +35.283 -0.517 52.898 -11.698 63.467 -28.659 c +68.099 -36.093 70.939 -45.087 68.386 -53.736 c +65.19 -64.559 53.201 -70.5 43.299 -73.636 c +19.673 -81.118 -1.939 -63.342 -13.691 -44.502 c +-23.795 -28.304 -32.977 0.182 -17.812 16.22 c +-5.126 29.635 18.045 22.598 31.813 15.114 c +41.404 9.901 50.003 2.073 57.433 -5.843 c +65.359 -14.287 72.287 -24.468 74.348 -36.049 c +76.637 -48.916 69.247 -60.71 58.847 -67.682 c +49.475 -73.963 37.762 -74.579 27.041 -72.178 c +5.375 -67.323 -14.589 -49.886 -21.671 -28.832 c +-25.077 -18.707 -24.898 -7.309 -19.423 2.035 c +-13.867 11.515 -3.148 15.671 7.393 15.956 c +30.617 16.584 55.358 0.639 68.082 -18.013 c +78.84 -33.783 80.206 -56.951 66.624 -71.582 c +50.4 -89.058 21.409 -80.734 5.27 -67.778 c +-3.547 -60.7 -10.242 -50.932 -12.247 -39.7 c +-14.797 -25.408 -7.826 -11.809 1.422 -1.406 c +2.274 -0.447 0.859 0.968 0 0 c +f +Q + +endstream endobj 269 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 256.6909 269.3965 cm +0 0 m +-2.517 -0.879 -5.49 -2.3 -5.663 -5.322 c +-5.808 -7.86 -3.343 -9.933 -1.504 -11.243 c +2.535 -14.121 10.915 -17.37 15.203 -13.337 c +17.483 -11.192 16.75 -7.288 15.301 -4.95 c +13.326 -1.763 9.75 -0.67 6.197 -1.166 c +0.897 -1.904 -7.109 -8.942 -0.659 -13.624 c +0.805 -14.688 2.757 -15.11 4.494 -15.43 c +5.756 -15.661 6.291 -13.733 5.022 -13.5 c +2.706 -13.075 -1.652 -12.115 -1.012 -8.881 c +-0.512 -6.357 2.348 -4.54 4.547 -3.676 c +7.379 -2.563 10.533 -2.78 12.772 -4.96 c +14.519 -6.66 15.598 -10.221 13.579 -12.12 c +10.526 -14.991 3.719 -11.989 0.816 -10.348 c +-0.875 -9.393 -2.899 -7.99 -3.551 -6.061 c +-4.333 -3.749 -1.099 -2.496 0.518 -1.932 c +1.728 -1.509 1.219 0.426 0 0 c +f +Q + +endstream endobj 270 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 339.4521 296.4297 cm +0 0 m +-2.854 -0.071 -5.358 -0.623 -7.536 -2.555 c +-9.178 -4.011 -10.5 -6.416 -9.434 -8.441 c +-7.003 -13.062 -0.223 -16.405 4.84 -16.204 c +9.124 -16.034 14.055 -10.68 11.8 -6.383 c +9.413 -1.833 3.23 0.938 -1.766 -0.034 c +-3.033 -0.28 -2.507 -2.211 -1.248 -1.966 c +2.291 -1.277 6.087 -3.031 8.641 -5.398 c +12.491 -8.967 8.477 -14.061 4.142 -14.232 c +0.553 -14.375 -3.189 -11.996 -5.767 -9.72 c +-10.557 -5.487 -4.25 -2.106 -0.014 -2 c +1.274 -1.968 1.289 0.032 0 0 c +f +Q + +endstream endobj 271 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 283.2446 203.2236 cm +0 0 m +12.822 -12.775 26.381 -24.678 40.499 -35.999 c +41.493 -36.797 42.913 -35.386 41.905 -34.577 c +27.789 -23.255 14.229 -11.354 1.407 1.422 c +0.494 2.332 -0.915 0.912 0 0 c +f +Q + +endstream endobj 272 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 286.2446 197.2236 cm +0 0 m +17.211 -14.984 42.451 -24.305 65.016 -26.084 c +74.778 -26.854 85.555 -25.753 93.347 -19.282 c +102.285 -11.86 102.411 0.977 100.665 11.469 c +100.453 12.741 98.523 12.212 98.733 10.951 c +100.509 0.277 100.03 -12.021 90.52 -18.971 c +82.631 -24.735 71.664 -24.784 62.375 -23.844 c +41.252 -21.706 17.533 -12.618 1.407 1.422 c +0.44 2.264 -0.976 0.85 0 0 c +f +Q + +endstream endobj 273 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 374.4775 217.1748 cm +0 0 m +2.177 -11.46 9.933 -24.25 0.91 -34.59 c +-5.934 -42.433 -17.563 -45.11 -27.525 -45.239 c +-28.815 -45.256 -28.828 -47.256 -27.539 -47.239 c +-16.636 -47.099 -5.435 -44.007 2.332 -35.997 c +12.44 -25.571 4.187 -11.351 1.932 0.518 c +1.69 1.786 -0.239 1.259 0 0 c +f +Q + +endstream endobj 274 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 293.7441 191.2236 cm +0 0 m +18.899 -16.869 46.675 -21.287 71.193 -19.788 c +72.476 -19.71 72.494 -17.709 71.207 -17.788 c +47.233 -19.253 19.91 -15.094 1.407 1.422 c +0.449 2.276 -0.965 0.861 0 0 c +f +Q + +endstream endobj 275 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 299.9473 732.5415 cm +0 0 m +19.752 -10.043 37.557 -23.963 55.913 -36.29 c +76.151 -49.881 96.103 -63.908 116.994 -76.496 c +118.101 -77.163 119.099 -75.429 117.994 -74.764 c +97.666 -62.515 78.208 -48.922 58.542 -35.655 c +39.692 -22.938 21.309 -8.594 1 1.732 c +-0.146 2.315 -1.152 0.586 0 0 c +f +Q + +endstream endobj 276 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 252.9497 74.3066 cm +0 0 m +-13.779 6.98 -24.767 16.214 -34.294 28.344 c +-35.089 29.355 -36.489 27.924 -35.702 26.922 c +-26.054 14.64 -14.939 5.33 -1 -1.732 c +0.146 -2.313 1.153 -0.584 0 0 c +f +Q + +endstream endobj 277 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 229.1548 90.6504 cm +0 0 m +-7.671 9.6 -17.33 20.904 -18.432 33.667 c +-20.107 53.083 7.629 53.896 20.65 53.085 c +39.244 51.928 57.799 47.555 75.85 43.197 c +96.725 38.159 117.514 32.776 138.526 28.323 c +139.785 28.057 140.316 29.985 139.053 30.253 c +115.623 35.219 92.462 41.325 69.162 46.854 c +49.454 51.531 28.292 56.313 7.921 55.186 c +-4.84 54.479 -21.033 48.964 -20.432 33.682 c +-19.914 20.507 -9.207 8.339 -1.407 -1.422 c +-0.604 -2.428 0.797 -0.997 0 0 c +f +Q + +endstream endobj 278 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 354.1807 118.9736 cm +0 0 m +15.213 -2.73 30.846 -2.019 45.919 1.235 c +59.718 4.215 75.522 9.986 85.146 20.803 c +93.5 30.193 95.324 43.714 95.627 55.784 c +95.985 70.068 93.646 84.232 92.76 98.453 c +92.68 99.734 90.68 99.754 90.76 98.467 c +92.223 75.003 99.704 45.838 85.684 24.755 c +77.579 12.568 60.379 6.8 46.897 3.512 c +31.869 -0.154 15.762 -0.805 0.527 1.93 c +-0.733 2.156 -1.27 0.228 0 0 c +f +Q + +endstream endobj 279 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 436.4063 217.6924 cm +0 0 m +-2.701 11.556 -0.563 22.661 9.404 29.882 c +18.104 36.184 29.775 37.756 40.224 37.877 c +62.896 38.141 84.951 27.965 102.29 13.948 c +136.149 -13.422 155.487 -55.276 173.663 -93.761 c +174.213 -94.925 175.936 -93.906 175.388 -92.747 c +157.362 -54.581 138.312 -14.254 105.65 13.744 c +90.181 27.005 71.285 36.399 50.986 39.12 c +33.778 41.427 10.107 39.327 0.159 22.824 c +-4.074 15.802 -3.729 7.175 -1.932 -0.518 c +-1.638 -1.774 0.293 -1.253 0 0 c +f +Q + +endstream endobj 280 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 203.5894 108.9316 cm +0 0 m +15.896 -37.249 55.787 -64.391 95.094 -70.954 c +96.356 -71.165 96.893 -69.236 95.622 -69.024 c +56.752 -62.534 17.444 -35.822 1.726 1.014 c +1.221 2.195 -0.5 1.172 0 0 c +f +Q + +endstream endobj 281 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 247.4502 69.5742 cm +0 0 m +76.08 -46.768 180.371 -40.563 250.198 15.163 c +251.204 15.966 249.774 17.366 248.776 16.569 c +179.648 -38.598 76.252 -44.526 1 1.732 c +-0.101 2.409 -1.098 0.675 0 0 c +f +Q + +endstream endobj 282 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 523.3027 105.9395 cm +0 0 m +11.964 19.063 30.342 33.051 39.097 54.233 c +39.589 55.426 37.652 55.936 37.167 54.761 c +28.508 33.811 10.111 19.87 -1.732 1 c +-2.419 -0.095 -0.685 -1.091 0 0 c +f +Q + +endstream endobj 283 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 551.5723 201.9287 cm +0 0 m +10.656 -25.217 4.604 -58.372 -11.848 -79.787 c +-12.622 -80.795 -11.212 -82.217 -10.426 -81.193 c +6.642 -58.979 12.773 -25.132 1.725 1.012 c +1.225 2.196 -0.496 1.172 0 0 c +f +Q + +endstream endobj 284 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 547.3018 125.4385 cm +0 0 m +18.954 25.147 25.805 55.385 31.597 85.731 c +30.941 85.821 30.286 85.912 29.632 86.002 c +29.261 76.298 23.542 67.9 19.266 59.497 c +18.683 58.352 20.412 57.346 20.998 58.497 c +25.445 67.237 31.246 75.895 31.632 85.988 c +31.675 87.109 29.882 87.382 29.667 86.259 c +23.907 56.08 17.124 26.017 -1.732 1 c +-2.508 -0.029 -0.767 -1.017 0 0 c +f +Q + +endstream endobj 285 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 247.2466 128.2266 cm +0 0 m +5.694 -6.253 12.424 -11.641 19.125 -16.761 c +27.25 -22.967 35.853 -28.854 45.198 -33.069 c +50.105 -35.283 57.718 -38.354 62.681 -34.668 c +68.962 -30.002 64.978 -18.268 63.167 -12.529 c +62.78 -11.303 60.846 -11.815 61.235 -13.047 c +62.82 -18.065 67.415 -31.12 59.966 -33.888 c +53.807 -36.177 45.26 -31.094 40.111 -28.299 c +26.14 -20.713 12.146 -10.372 1.407 1.422 c +0.539 2.376 -0.867 0.952 0 0 c +f +Q + +endstream endobj 286 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 338.9453 705.543 cm +0 0 m +25.701 -13.804 46.646 -34.471 70.496 -50.998 c +71.558 -51.733 72.549 -49.995 71.496 -49.265 c +47.646 -32.739 26.704 -12.074 1 1.732 c +-0.135 2.342 -1.139 0.611 0 0 c +f +Q + +endstream endobj 287 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 299.9473 132.5713 cm +0 0 m +9.998 -6.146 18.569 -14.556 27.723 -21.845 c +35.827 -28.298 44.866 -35.244 55.246 -37.334 c +62.36 -38.766 70.313 -36.531 71.806 -28.683 c +73.278 -20.935 68.242 -12.384 62.7 -7.422 c +61.973 -6.77 60.772 -7.398 61.031 -8.392 c +63.956 -19.64 75.732 -25.191 85.94 -28.281 c +96.702 -31.539 109.764 -33.204 120.753 -30.098 c +121.993 -29.747 121.478 -27.814 120.235 -28.166 c +110.167 -31.012 98.642 -29.479 88.684 -26.966 c +78.534 -24.403 65.863 -19.026 62.963 -7.874 c +62.406 -8.197 61.85 -8.521 61.294 -8.844 c +66.679 -13.665 71.142 -21.5 69.724 -28.954 c +67.704 -39.576 52.93 -35.249 46.598 -32.154 c +29.427 -23.762 17.135 -8.188 1 1.732 c +-0.101 2.409 -1.098 0.675 0 0 c +f +Q + +endstream endobj 288 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 433.4404 112.4316 cm +0 0 m +-0.293 7.785 -5.571 13.486 -10.796 18.717 c +-11.707 19.629 -13.115 18.209 -12.202 17.295 c +-7.397 12.485 -2.271 7.212 -2 0.014 c +-1.952 -1.273 0.049 -1.289 0 0 c +f +Q + +endstream endobj 289 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 436.6768 126.4727 cm +0 0 m +6.641 -2.605 13.677 -4.706 20.759 -5.651 c +26.358 -6.399 34.37 -7.239 37.976 -1.708 c +45.259 9.466 28.901 22.821 20.263 27.33 c +19.122 27.926 18.117 26.196 19.263 25.598 c +26.813 21.657 43.711 8.424 35.74 -1.309 c +31.778 -6.146 22.255 -4 17.124 -3.002 c +11.471 -1.903 5.886 -0.173 0.527 1.93 c +-0.675 2.401 -1.187 0.466 0 0 c +f +Q + +endstream endobj 290 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 472.6748 151.9717 cm +0 0 m +4.664 -1.064 11.202 -2.023 15.225 1.387 c +18.934 4.531 18.673 9.475 16.752 13.619 c +13.439 20.765 9.735 25.917 3.583 30.977 c +-2.554 36.022 -9.44 40.066 -15.532 45.174 c +-16.512 45.996 -17.93 44.583 -16.938 43.752 c +-8.379 36.575 1.743 31.307 9.216 22.87 c +12.815 18.807 17.128 11.36 15.926 5.782 c +14.581 -0.459 4.771 0.961 0.527 1.93 c +-0.729 2.217 -1.258 0.287 0 0 c +f +Q + +endstream endobj 291 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 446.9404 203.0684 cm +0 0 m +8.116 -3.469 16.4 -6.629 25.138 -8.107 c +32.009 -9.271 42.026 -9.329 46.363 -2.634 c +57.032 13.838 35.205 30.816 23.498 39.23 c +22.45 39.983 21.46 38.244 22.498 37.498 c +33.064 29.904 53.568 14.522 45.296 -0.606 c +40.942 -8.568 29.249 -7.017 21.979 -5.475 c +14.745 -3.939 7.779 -1.166 1 1.732 c +-0.172 2.233 -1.186 0.507 0 0 c +f +Q + +endstream endobj 292 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 340.1563 36.7393 cm +0 0 m +2.856 2.404 5.098 5.234 7.522 8.047 c +10.888 11.95 15.384 13.247 20.411 13.398 c +29.202 13.663 36.146 7.076 44.023 4.238 c +44.666 4.007 45.247 4.597 45.287 5.196 c +45.601 9.852 46.103 15.97 50.677 18.509 c +53.864 20.277 58.394 18.882 61.628 18.003 c +69.274 15.924 76.693 12.713 84.778 13.202 c +85.3 13.233 85.81 13.624 85.785 14.195 c +85.646 17.447 81.786 29.356 85.322 30.302 c +89.606 31.447 94.636 29.685 98.729 28.494 c +104.502 26.814 112.281 23.234 118.076 26.601 c +122.723 29.3 121.14 35.477 119.892 39.542 c +118.449 44.239 127.166 43.247 129.882 43.135 c +136.538 42.86 142.71 38.995 149.018 37.236 c +149.605 37.072 150.325 37.544 150.281 38.194 c +149.805 45.308 140.398 52.8 146.152 59.233 c +150.348 63.925 161.911 57.24 167.54 58.234 c +167.95 58.307 168.309 58.781 168.281 59.193 c +167.917 64.679 162.729 69.731 163.816 75.178 c +165.404 83.136 180.199 79.218 185.017 77.734 c +186.047 77.417 186.593 78.695 185.983 79.41 c +183.446 82.392 183.134 86.228 181.643 89.706 c +181.524 89.249 181.405 88.793 181.287 88.337 c +186.499 90.654 192.074 90.071 197.538 91.232 c +198.36 91.407 198.411 92.36 197.982 92.909 c +195.899 95.576 195.579 99.226 193.482 101.909 c +193.417 101.385 193.352 100.86 193.285 100.336 c +198.767 103.894 203.778 102.06 208.778 98.832 c +209.864 98.132 210.859 99.867 209.778 100.564 c +204.052 104.261 198.334 105.995 192.273 102.061 c +191.682 101.677 191.671 101.006 192.076 100.487 c +194.159 97.82 194.479 94.171 196.576 91.487 c +196.724 92.046 196.872 92.605 197.021 93.164 c +191.282 91.944 185.737 92.491 180.273 90.062 c +179.732 89.821 179.723 89.146 179.918 88.692 c +181.526 84.939 181.851 81.191 184.577 77.988 c +185.544 79.664 l +178.892 81.713 169.821 83.847 164.019 79.06 c +161.583 77.049 161.355 73.91 162.027 71.066 c +162.545 68.874 168.07 59.908 164.533 60.032 c +158.146 60.255 149.373 65.832 144.071 59.903 c +137.856 52.954 147.8 45.396 148.281 38.208 c +148.702 38.527 149.124 38.847 149.545 39.166 c +140.254 41.756 129.118 48.656 119.283 43.917 c +117.842 43.222 117.395 41.524 117.706 40.068 c +118.489 36.399 120.085 33.474 118.417 29.702 c +115.306 22.666 96.247 31.701 91.138 32.326 c +89.335 32.546 83.618 33.279 82.419 31.202 c +79.891 26.823 83.592 18.708 83.785 14.209 c +84.121 14.54 84.457 14.871 84.792 15.202 c +74.643 14.589 65.751 20.108 55.874 21.189 c +46.219 22.246 43.813 13.014 43.287 5.21 c +43.708 5.529 44.13 5.849 44.551 6.168 c +35.614 9.388 28.901 15.653 18.896 15.353 c +8.799 15.049 5.429 7.174 -1.422 1.406 c +-2.408 0.576 -0.98 -0.825 0 0 c +f +Q + +endstream endobj 293 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 401.9355 62.3037 cm +0 0 m +-8.109 -5.903 -8.121 5.387 -6.776 10.178 c +-5.262 15.573 -1.627 20.379 1.931 24.583 c +8.821 32.725 17.376 39.408 25.864 45.786 c +48.763 62.989 79.694 55.313 105.761 62.669 c +107.001 63.019 106.485 64.951 105.243 64.601 c +90.535 60.45 75.119 60.842 60.027 59.337 c +46.264 57.964 33.837 54.316 22.727 45.909 c +13.812 39.164 5.091 31.929 -1.849 23.103 c +-7.646 15.729 -12.072 6.292 -7.855 -2.869 c +-7.716 -3.174 -7.321 -3.364 -7 -3.362 c +-4.188 -3.35 -1.345 -3.441 1.014 -1.725 c +2.045 -0.974 1.045 0.761 0 0 c +f +Q + +endstream endobj 294 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 423.1777 106.9736 cm +0 0 m +7.658 -3.16 14.839 -7.338 22.499 -10.499 c +23.691 -10.991 24.201 -9.055 23.026 -8.569 c +15.368 -5.408 8.188 -1.231 0.527 1.93 c +-0.665 2.422 -1.176 0.485 0 0 c +f +Q + +endstream endobj 295 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 430.4404 110.0723 cm +0 0 m +3.428 -1.667 7.072 -2.833 10.5 -4.499 c +11.654 -5.06 12.662 -3.331 11.5 -2.767 c +8.071 -1.101 4.428 0.065 1 1.732 c +-0.154 2.293 -1.162 0.565 0 0 c +f +Q + +endstream endobj 296 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 431.1992 109.9727 cm +0 0 m +7.965 2.505 15.575 2.95 23.477 0.001 c +24.688 -0.451 25.201 1.483 24.004 1.931 c +15.78 5 7.771 4.539 -0.518 1.932 c +-1.744 1.546 -1.231 -0.388 0 0 c +f +Q + +endstream endobj 297 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 351.1807 691.9448 cm +0 0 m +18.771 -6.484 35.073 -18.896 49.059 -32.745 c +49.974 -33.651 51.384 -32.232 50.465 -31.323 c +36.23 -17.227 19.624 -4.667 0.527 1.93 c +-0.695 2.352 -1.212 0.418 0 0 c +f +Q + +endstream endobj 298 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 452.9395 120.5723 cm +0 0 m +3.428 -1.667 7.072 -2.833 10.5 -4.5 c +11.654 -5.061 12.662 -3.333 11.5 -2.768 c +8.072 -1.101 4.428 0.065 1 1.732 c +-0.154 2.293 -1.162 0.565 0 0 c +f +Q + +endstream endobj 299 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 475.9316 118.9385 cm +0 0 m +0.046 -0.001 0.092 -0.005 0.138 -0.01 c +-0.224 0.027 0.245 -0.051 0.353 -0.09 c +0.122 -0.006 0.354 -0.107 0.396 -0.133 c +0.215 -0.022 0.42 -0.162 0.455 -0.198 c +0.296 -0.035 0.519 -0.344 0.442 -0.16 c +0.502 -0.303 0.503 -0.362 0.507 -0.493 c +0.547 -1.781 2.548 -1.796 2.507 -0.507 c +2.463 0.895 1.441 1.985 0.014 2 c +-1.278 2.014 -1.288 0.014 0 0 c +f +Q + +endstream endobj 300 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 59.8257 738.9072 cm +0 0 m +11.124 20.651 33.12 25.535 54.94 24.547 c +65.582 24.065 76.132 22.261 86.589 20.318 c +99.51 17.918 112.276 15.794 124.768 11.558 c +138.482 6.906 152.147 0.354 164.278 -7.563 c +174.676 -14.349 184.648 -22.328 197.193 -24.536 c +215.652 -27.783 227.789 -12.327 239.339 -0.691 c +246.863 6.887 255.071 13.166 265.579 15.865 c +280.361 19.662 295.95 14.175 309.013 7.504 c +328.167 -2.276 345.094 -21.009 368.054 -19.931 c +371.805 -19.755 374.943 -17.612 377.638 -15.203 c +382.561 -10.803 386.332 -6.157 392.226 -2.839 c +403.671 3.603 418.141 3.017 430.856 2.521 c +457.37 1.489 481.284 -9.092 502.609 -24.365 c +503.659 -25.116 504.648 -23.377 503.609 -22.632 c +482.756 -7.699 459.833 2.4 434.065 4.34 c +422.014 5.247 408.467 5.558 396.931 1.484 c +391.835 -0.315 387.049 -3.221 383.056 -6.839 c +377.9 -11.512 374.28 -17.639 366.623 -17.999 c +353.316 -18.624 341.547 -10.851 330.923 -3.743 c +320.577 3.179 310.325 9.79 298.67 14.364 c +286.313 19.214 272.819 20.683 260.125 16.315 c +251.088 13.206 243.715 6.556 237.105 -0.103 c +230.524 -6.732 224.423 -14.066 216.354 -18.966 c +206.19 -25.137 194.684 -23.174 184.519 -18.082 c +178.711 -15.172 173.388 -11.297 168.023 -7.665 c +162.02 -3.599 155.801 -0.275 149.389 3.075 c +137.143 9.474 124.202 14.406 110.794 17.737 c +99.538 20.534 87.846 22.263 76.394 24.13 c +66.423 25.756 56.345 26.857 46.232 26.676 c +27.014 26.333 8.826 19.478 -1.093 2.186 c +-1.306 1.791 -1.52 1.396 -1.732 1 c +-2.343 -0.134 -0.613 -1.138 0 0 c +f +Q + +endstream endobj 301 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 15.1973 652.9468 cm +0 0 m +6.681 -1.94 13.236 -1.252 20.056 -0.505 c +28.249 0.394 36.6 0.55 44.835 0.868 c +60.465 1.472 76.113 1.464 91.752 1.464 c +124.738 1.465 157.685 3.43 190.55 6.116 c +223.276 8.79 255.932 12.238 288.626 15.264 c +321.182 18.276 353.734 20.824 386.209 24.673 c +404.899 26.888 423.566 29.295 442.242 31.624 c +461.361 34.006 480.562 36.808 499.799 37.972 c +536.868 40.217 574.144 37.967 610.992 43.497 c +612.267 43.688 611.735 45.618 610.475 45.429 c +578.25 40.593 545.6 41.639 513.129 40.577 c +481.018 39.526 449.039 34.478 417.191 30.479 c +352.315 22.333 287.156 16.987 222.062 10.899 c +184.644 7.399 147.156 4.443 109.571 3.647 c +91.817 3.272 74.057 3.571 56.301 3.203 c +47.278 3.016 38.26 2.646 29.251 2.094 c +19.843 1.517 9.745 -0.747 0.528 1.93 c +-0.714 2.29 -1.236 0.359 0 0 c +f +Q + +endstream endobj 302 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 506.7344 774.6948 cm +0 0 m +23.074 -21.958 34.055 -51.885 35.701 -83.278 c +35.769 -84.562 37.769 -84.58 37.701 -83.292 c +36.027 -51.372 24.867 -20.904 1.406 1.422 c +0.474 2.309 -0.938 0.892 0 0 c +f +Q + +endstream endobj 303 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 56.7559 773.1948 cm +0 0 m +6.863 -8.007 10.394 -17.629 12.99 -27.715 c +15.623 -37.939 17.885 -48.288 20.203 -58.586 c +25.429 -81.81 31.24 -104.952 40.236 -127.042 c +40.715 -128.219 42.653 -127.716 42.167 -126.524 c +32.931 -103.843 27.085 -80.069 21.721 -56.23 c +17.357 -36.841 14.779 -14.18 1.407 1.422 c +0.568 2.401 -0.835 0.974 0 0 c +f +Q + +endstream endobj 304 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 365.9443 677.0444 cm +0 0 m +13.461 -7.624 25.193 -17.714 37.498 -26.999 c +38.526 -27.775 39.515 -26.034 38.498 -25.267 c +26.192 -15.981 14.464 -5.893 1 1.732 c +-0.123 2.368 -1.124 0.637 0 0 c +f +Q + +endstream endobj 305 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 344.9453 689.0435 cm +0 0 m +24.616 -11.737 45.658 -28.938 67.496 -44.998 c +68.535 -45.761 69.524 -44.021 68.496 -43.265 c +46.658 -27.207 25.616 -10.005 1 1.732 c +-0.157 2.284 -1.166 0.556 0 0 c +f +Q + +endstream endobj 306 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 371.9434 680.0439 cm +0 0 m +25.821 -12.393 48.129 -34.106 61.135 -59.637 c +61.72 -60.786 63.443 -59.771 62.859 -58.625 c +49.712 -32.813 27.105 -10.798 1 1.732 c +-0.156 2.287 -1.165 0.559 0 0 c +f +Q + +endstream endobj 307 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 219.4585 336.9277 cm +0 0 m +-17.655 1.261 -36.49 -5.657 -38.264 -25.512 c +-39.691 -41.494 -29.022 -56.405 -17.399 -66.217 c +-5.179 -76.533 9.077 -85.105 23.858 -91.183 c +36.191 -96.254 51.823 -101.287 64.778 -95.493 c +77.728 -89.703 79.9 -72.13 76.153 -60.067 c +71.097 -43.788 55.326 -32.681 40.917 -25.232 c +25.711 -17.372 8.483 -12.03 -8.615 -10.688 c +-19.292 -9.85 -42.046 -9.985 -43.918 -24.708 c +-45.088 -33.914 -38.895 -43.011 -33.701 -50.004 c +-26.789 -59.313 -18.438 -67.608 -9.71 -75.207 c +-8.743 -76.049 -7.328 -74.635 -8.303 -73.785 c +-20.12 -63.497 -32.233 -51.644 -39.209 -37.419 c +-41.967 -31.796 -43.827 -24.526 -39.302 -19.305 c +-33.357 -12.445 -20.901 -12.45 -12.736 -12.506 c +4.919 -12.625 23.213 -18.705 38.883 -26.442 c +54.779 -34.291 71.859 -46.778 75.396 -65.276 c +76.931 -73.305 75.266 -82.953 69.854 -89.265 c +65.329 -94.544 58.218 -96.034 51.577 -95.981 c +34.854 -95.849 18.085 -87.23 4.065 -78.887 c +-8.682 -71.3 -21.516 -62.32 -29.464 -49.541 c +-35.104 -40.475 -38.313 -29.183 -34.863 -18.753 c +-31.364 -8.175 -20.542 -3.196 -10.22 -2.026 c +-6.834 -1.644 -3.405 -1.758 -0.015 -2 c +1.275 -2.093 1.278 -0.091 0 0 c +f +Q + +endstream endobj 308 0 obj <>/ExtGState<>>>/Subtype/Form>>stream +/CS0 cs 0.102 0.09 0.106 scn +/GS0 gs +q 1 0 0 1 210.7158 321.8936 cm +0 0 m +-9.995 1.681 -21.98 0.895 -27.766 -8.668 c +-34.027 -19.016 -28.075 -34.601 -22.391 -43.86 c +-8.785 -66.029 18.338 -76.915 41.47 -85.926 c +42.674 -86.395 43.186 -84.459 41.998 -83.996 c +21.218 -75.901 -0.698 -66.706 -15.71 -49.548 c +-23.472 -40.675 -28.741 -28.561 -28.063 -16.628 c +-27.255 -2.385 -11.772 -0.039 -0.528 -1.93 c +0.734 -2.142 1.271 -0.214 0 0 c +f +Q + +endstream endobj 358 0 obj <> endobj 260 0 obj <> endobj 259 0 obj [/ICCBased 359 0 R] endobj 359 0 obj <>stream +HyTSwoɞc [5laQIBHADED2mtFOE.c}08׎8GNg9w߽'0 ֠Jb  + 2y.-;!KZ ^i"L0- @8(r;q7Ly&Qq4j|9 +V)gB0iW8#8wթ8_٥ʨQQj@&A)/g>'Kt;\ ӥ$պFZUn(4T%)뫔0C&Zi8bxEB;Pӓ̹A om?W= +x-[0}y)7ta>jT7@tܛ`q2ʀ&6ZLĄ?_yxg)˔zçLU*uSkSeO4?׸c. R ߁-25 S>ӣVd`rn~Y&+`;A4 A9=-tl`;~p Gp| [`L`< "A YA+Cb(R,*T2B- +ꇆnQt}MA0alSx k&^>0|>_',G!"F$H:R!zFQd?r 9\A&G rQ hE]a4zBgE#H *B=0HIpp0MxJ$D1D, VĭKĻYdE"EI2EBGt4MzNr!YK ?%_&#(0J:EAiQ(()ӔWT6U@P+!~mD eԴ!hӦh/']B/ҏӿ?a0nhF!X8܌kc&5S6lIa2cKMA!E#ƒdV(kel }}Cq9 +N')].uJr + wG xR^[oƜchg`>b$*~ :Eb~,m,-ݖ,Y¬*6X[ݱF=3뭷Y~dó ti zf6~`{v.Ng#{}}jc1X6fm;'_9 r:8q:˜O:ϸ8uJqnv=MmR 4 +n3ܣkGݯz=[==<=GTB(/S,]6*-W:#7*e^YDY}UjAyT`#D="b{ų+ʯ:!kJ4Gmt}uC%K7YVfFY .=b?SƕƩȺy چ k5%4m7lqlioZlG+Zz͹mzy]?uuw|"űNwW&e֥ﺱ*|j5kyݭǯg^ykEklD_p߶7Dmo꿻1ml{Mś nLl<9O[$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! +zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Km +endstream endobj 357 0 obj <> endobj 356 0 obj <> endobj 355 0 obj <> endobj 354 0 obj <> endobj 353 0 obj <> endobj 352 0 obj <> endobj 351 0 obj <> endobj 350 0 obj <> endobj 349 0 obj <> endobj 348 0 obj <> endobj 347 0 obj <> endobj 346 0 obj <> endobj 345 0 obj <> endobj 344 0 obj <> endobj 343 0 obj <> endobj 342 0 obj <> endobj 341 0 obj <> endobj 340 0 obj <> endobj 339 0 obj <> endobj 338 0 obj <> endobj 337 0 obj <> endobj 336 0 obj <> endobj 335 0 obj <> endobj 334 0 obj <> endobj 333 0 obj <> endobj 332 0 obj <> endobj 331 0 obj <> endobj 330 0 obj <> endobj 329 0 obj <> endobj 328 0 obj <> endobj 327 0 obj <> endobj 326 0 obj <> endobj 325 0 obj <> endobj 324 0 obj <> endobj 323 0 obj <> endobj 322 0 obj <> endobj 321 0 obj <> endobj 320 0 obj <> endobj 319 0 obj <> endobj 318 0 obj <> endobj 317 0 obj <> endobj 316 0 obj <> endobj 315 0 obj <> endobj 314 0 obj <> endobj 313 0 obj <> endobj 248 0 obj <> endobj 249 0 obj <> endobj 250 0 obj <> endobj 251 0 obj <> endobj 252 0 obj <> endobj 253 0 obj <> endobj 370 0 obj [/View/Design] endobj 371 0 obj <>>> endobj 368 0 obj [/View/Design] endobj 369 0 obj <>>> endobj 366 0 obj [/View/Design] endobj 367 0 obj <>>> endobj 364 0 obj [/View/Design] endobj 365 0 obj <>>> endobj 362 0 obj [/View/Design] endobj 363 0 obj <>>> endobj 360 0 obj [/View/Design] endobj 361 0 obj <>>> endobj 261 0 obj <> endobj 262 0 obj <> endobj 258 0 obj <> endobj 372 0 obj <> endobj 373 0 obj <>stream +%!PS-Adobe-3.0 +%%Creator: Adobe Illustrator(R) 14.0 +%%AI8_CreatorVersion: 14.0.0 +%%For: (Pauline ROSSI DE LALA) () +%%Title: (animaux.ai) +%%CreationDate: 11/27/2009 4:58 PM +%%Canvassize: 16383 +%%BoundingBox: -412 36 1037 891 +%%HiResBoundingBox: -411.5552 36.4883 1036.2783 890.7217 +%%DocumentProcessColors: Cyan Magenta Yellow Black +%AI5_FileFormat 10.0 +%AI12_BuildNumber: 367 +%AI3_ColorUsage: Color +%AI7_ImageSettings: 0 +%%RGBProcessColor: 0 0 0 ([Repérage]) +%AI3_Cropmarks: 0 0 595.2803 841.8896 +%AI3_TemplateBox: 298.5 420.3896 298.5 420.3896 +%AI3_TileBox: 6.64014 39.9443 588.6406 802.9048 +%AI3_DocumentPreview: None +%AI5_ArtSize: 14400 14400 +%AI5_RulerUnits: 1 +%AI9_ColorModel: 1 +%AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 +%AI5_TargetResolution: 800 +%AI5_NumLayers: 6 +%AI9_OpenToView: -710.001 978.894 0.6667 1591 924 18 1 0 40 116 0 0 0 1 1 0 1 1 0 +%AI5_OpenViewLayers: 777762 +%%PageOrigin:0 0 +%AI7_GridSettings: 72 8 72 8 1 0 0.8 0.8 0.8 0.9 0.9 0.9 +%AI9_Flatten: 1 +%AI12_CMSettings: 00.MS +%%EndComments + +endstream endobj 374 0 obj <>stream +%%BoundingBox: -412 36 1037 891 +%%HiResBoundingBox: -411.5552 36.4883 1036.2783 890.7217 +%AI7_Thumbnail: 128 76 8 +%%BeginData: 5520 Hex Bytes +%0000330000660000990000CC0033000033330033660033990033CC0033FF +%0066000066330066660066990066CC0066FF009900009933009966009999 +%0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 +%00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 +%3333663333993333CC3333FF3366003366333366663366993366CC3366FF +%3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 +%33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 +%6600666600996600CC6600FF6633006633336633666633996633CC6633FF +%6666006666336666666666996666CC6666FF669900669933669966669999 +%6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 +%66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF +%9933009933339933669933999933CC9933FF996600996633996666996699 +%9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 +%99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF +%CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 +%CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 +%CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF +%CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC +%FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 +%FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 +%FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 +%000011111111220000002200000022222222440000004400000044444444 +%550000005500000055555555770000007700000077777777880000008800 +%000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB +%DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF +%00FF0000FFFFFF0000FF00FFFFFF00FFFFFF +%524C45FD07FFA8FFA8FD7AFFA8FFA8FFA8FFA8FD7AFFA8FFFFFFA8FD76FF +%A8A8A8FFA8FFA8FFA8FFA8FFA8FD78FFA8FFFFFFA8FFA8A8FD73FFA8FD05 +%FFA8FFA8FFA8A8A8FD74FFA8FD07FFA8FFFFFFA8FD74FFA8A8A8FFFFFFA8 +%FFA8FFFFFFA8FD4DFFA8FFA8FD2AFFA8FFA8FFFFFFA8FD4DFFA8FD26FFA8 +%FD05FFA8FD05FFA8FD4BFFA8A8A8FFA8FFFFFFA8FD2CFFA8FD17FFA8FD27 +%FFA8FD0BFFA8FD05FFA8FFA8FD1EFFA8FD05FFA8FD07FFA8FD17FFA8FFA8 +%FFA8FFA8FFA8FFA8FD0BFFA8FFA8FD0FFFA8FD09FFA8FD07FFA8A8A8FD1E +%FFA8FD24FFA8FD0AFFA8FD09FFA8FD05FFA8FD1FFFA8FD1EFFA8FFA8FD05 +%FFA8FD05FFA8FD17FFA8FD0BFFA8FFA8FD05FFA8FD07FFA8FD05FFA8FFA8 +%FFA8FFA8A8FD08FFA8FFFFA8A8FFA8FFFFA8FD21FFA8FFA8FFA8FD05FFCB +%FD19FFA8FD0DFFA8FFFFFF7DA8A8FD07FFA8FFFFFFA8FD07FFA8FD0BFFA8 +%FFA8A8A8FFA8FD1EFFA8FD05FFA8FF7DFD04A8FD29FFA8FFA8A8A8FFA8FF +%A8FD05FFA8FFA8FD09FFA8FD07FFA8FFA8FFFFFFA8FFA8FD46FFA8FD12FF +%A8FD04FFA8FD19FFA8FFA8FFA8FD2AFFA8FD1FFFA8FD11FF7DFFFFFFA8A8 +%7DFD07FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8A87DA8A8FFA8FD +%28FFA8FD31FF7DFFFFFFA8A87DA8FFFFA8FFFFFFA8FD13FFA8FD2BFFA8A8 +%FD1DFFA8FD09FFA8FFFFFFA8FFA8FFA87D52FFA8FFA8A852A8A8FD13FFA8 +%A8A8FFFFFFA8FD2AFFA8FD1EFFA8A8FFFFFFA8FFFFFFA8FFFFFFA8FD06FF +%7D52FD05FF7DA8FD12FFA8FFA8FD30FFA8FD17FFA8FFFFFFA8FFA8FFA8FF +%FFFFA8FD0EFF7D527DA8FFFFFFA8FD13FFA8A8A8FFA8FFA8FD5EFFA8A87D +%77A2FFFFFFA8FD11FFA8FD32FFA8A8A8FD31FFA8A8A8527DFFFFFFA8FD11 +%FFA8FFFFFFA8FFA8FD4EFFA8FD11FFA8A8FFA87DFD68FFA8FFFFFFA8FD0F +%FFA8A8FFA87DFD17FFA8FD4EFFA8FD31FFA8FD4EFFA8FFFFFFA8FD17FFA8 +%FFA8FD60FFA8FD1BFFA8FFFFFFA8FD66FFA8FD13FFA8FD05FFA8FD5EFFA8 +%FD09FFA8FD17FFA8FD5EFFA8FD09FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8 +%FFA8FD05FFA8FD6EFFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FD60FFA8 +%FD1BFFA8FFFFA8A8FFA8FD5EFFA8FD10FFA87DFD0FFFA8FD5EFFA8FD0DFF +%A87D7DFD11FFA8FD25FFA8FFA8FD42FFA8A8A8FD39FFA8FD38FFA8FFA8FD +%07FF7DA8FD0EFFA8FFA8FFA8FD26FFA8A8FFFFA8FD39FFA8FFA8FFA8A87D +%FD08FFA8FD06FFA8FFA8FD23FFA8FD05FFCBFFFFFFA8FD3CFFA87DA8FD07 +%FFA87D7DFFA8FFA8FFA8FD25FFA8FFA8FFA8FFA8FFFFFFA8FD3DFFA8FD08 +%FFA8A8A8FD2FFFA8FFA8FFFFFFA8FFA8FD3BFFA8A8FD07FFA87DA8FD2DFF +%A8FF7DA8FFFFA8FFA8FFFFFFA8FD3AFFA8A8FD06FF7DA8FD2AFFA8FFA8FD +%05FFA8FD07FFA8FD3CFF7DA8A8FFA8A87DFD2AFFA8FD04FFA8A8A8FFA8FF +%A8FFA8FFFFFF7DA8FD3DFFFD05A8FD2BFFA8FD09FFA8FFA8FD05FFA8FD6E +%FFA8FFA8A8A8FFA8FFA8FFA8FD05FFA8FD42FFA8FFA8FD2BFFA8A8A8FFFF +%FFA8FFA8FD48FFFD05A8FD2AFFA8FFFFA8FFFFA8A8FFFFFFA8FFFFFFA8FD +%40FFA8FD05FFA8FD2BFFA8A8A8FFA8FD20FFA8FD1FFFA8A87DA8A8FFFFFF +%A8A8A8FD05FFA8FD2BFFA8FFA8FFA8FFFFFFA8FFFFFFA8FD16FFA8FD1FFF +%A8A87DFD04A8FFFFFFA8FD07FFA8FD2BFFA8FD1BFFA8A8FFFFFFA8FFA8FD +%1EFFA87DFD05FFFD05A8FD06FFA8A8FD2DFFA8FFA8FD06FFA8A8FD10FFA8 +%FFA8FD21FFA8A8FD07FFA8A8FD07FFA8FD2FFFA8FFA8FFA8FD05FFA8FD0C +%FFA8FFFFFFA8FFFFFFA8FD1FFF7DA8FD07FFA8A8FFFFFFA8FFFFA8A8FD33 +%FFA8FFFFFFA8FD11FFA8FD24FF7DA8FD07FFA8A8A8FD04FFA8FD33FFA8FD +%05FFA8FD0CFFA8FFA8FFA8FFA8FFA8FD20FFA87DA8A8FFA8A8A8FFA8FFFD +%05A8FD07FFA8FFA8FFA8FD29FFA8FFA8FFA8FD14FFA8FD23FF7DA8A8FFA8 +%FD3FFFA8FFA8FD0EFFA8FFA8FFA8FFA8FD25FFA8A8A8FFA8FD0DFFA8FFFF +%FFA8FFFFFFA8FD3CFFA8A8A8FD3DFFA8FD3CFFA8FFFD05A8FD33FFA8FFFF +%FFA8FD05FFA8FD04FFA8A8FD6EFFA8FD46FFA8FFA8A8A8FFA8FD2BFF7DFD +%07FFA8FD05FFA8A8A8FD07FFA8A8FD33FFA8FFFFFFA8A8FFFFA8FD2BFF7D +%FD05FFA8FD11FFA8FD34FFA8FFFFFFA8FFFFA8FD2CFFFD04A8A2FD07FFA8 +%FFA8FD07FFA8FFA8FD36FFA8A8FFFFA8FD3BFFA8FD05FFA8FD36FFA8FFFF +%FFA8FFA8FD25FFA8FFA8FFA8FD0FFFA8FFA8FD06FFA8A8FFA8FD34FFA8FF +%A8FFFFA8FD24FFA8FFCBFFFFFFA8FFA8FD13FFA8A8A8FFFFFFA8FD32FFA8 +%A8A8FFFFFF7DA8FD22FFA8FFFFFFA8FFFFFFA8A8A8FFA8FFA8FFA8FFA8A8 +%A8FFA8A8A8FFA8FFA8A87DFD05FFA8FD32FFA8FFA8FFA8FFA8FD27FFA8FD +%05FFA8FFFFA8A8FD04FFFD04A8FFFFFFA8FFA8FD38FFA8FFA8FFA8FFA8FD +%23FFA8A8FD04FFA8FD05FFA8FFFFA8A8FFA8A8A8FD05FFA8FFA8FD38FFA8 +%FD06FFA8FD24FFA8FD07FFA8FFFFFFA8FD0BFFA8FD3CFFA8FFA8FFA8FFA8 +%FD25FFA8FD0FFFA8FFFFFFA8FFA8A8A8FD3CFFA8FFA8FD29FFA8FD0DFFA8 +%FD05FFA8FFA8FD3EFFA8FFA8FD29FFA8A8A8FD07FFA8FFA8A8A8FFA8FFA8 +%FD70FFA8FD05FFA8FFA8FFCBFFA8FD74FFA8FFA8FFA8FFA8FFA8FD38FFFF +%%EndData + +endstream endobj 375 0 obj <>stream +%AI12_CompressedDataxq.fx%͖[$*/(HIcұ1hp`(qHϡ[%# $rt***3#o~vYP^?~ox'n*|+y?~N㿞?ի޾yo~qXO~o7߾7zW?ë7?o֖WunKWUy%ջ߿7o|?ͻM營;4w][Z|xap\÷}Շ_aëw_῿~_/No޾.ݗ/jÛkGM?+je|NLJ~-K\(Oׯ?f?Wno{ Cwh];8zsзUalyGX^>n߿y??wN?| fV??û7j:6o^c}^1iz莿yث|}߿G:՟ ؁U}P=}?nc=Ԟo{;Z˷`ؠ~a +>ݛw?o ?_tQ>GǏ݁A72B/ߺf~-v4^øzwM\  +W޼Æ_[Wo??|w}OXW_;A#Ûikyo~x5(~zAW8_oa~`Lp;?7~ÖëwW[~ջW5ܿ2ûW߾g;}7o~x^}7@}{iw^}y}K!*5?|߾[=m١+|;ozko|[xL~m7!۷o`8}ooorԏwiln>y}vo߼>B5~{OћCWc `/M5h7:6i7oo?`łƿy_oMcsY@ֶqmv };؎3S+ȩ@΁4 ]׃ f ݩ;H zhc #dA~Oi@`4 xJ }|O2E&勿+IDls|lL;NO߁2L_{7 nD'?O4VUpNx$8˸^8@#tn+&0h> dd9LH͢WVwSSOSOӴ?=7~{+xHh1V4~jOC;G]{~2;n*E/Gz>s~#+niII+WS77 + (QLfEE|^f|Lq!6{ += ~mNiY&x4 0M~ 3tKi4fI8Fqűc v5@pÙ¸0\'IMBfvm#Djr:΍ut.xz }q +f +6`w45vp" HN4FOZ%-@CZ__He^Dԗ7a2rIk[AVŮ\{.귨V+|__Z*IHg;Yh_Tt\P/ܶaͨH9wh8ZY<|LEּֽ;[K\W'xL:g -DorMV a+84(KkuYy`*p9=tf ˲[꥚O:ɼe.2_GV{[?Yp㘀 rg$"Ozg sV׮n#v\/5\L}r|;5eՂ-pB#78cV%Es N=wdiO0WΠƌ0DԮiUԩGҩt*hU,\'5@ղw{[f GkEXAxp +h٪"K.W? eÑ[IwIW5g虉fBGai-5F͘ H: +:v@+,}f(0E.WSI9M\&M&m&]"= (NC">c hF>zd}ravqeZ6=m}q'0/j ̌ȠaZl۳]]_M߈-ߋ=5=i}dUwfUbUMM]Uݎf`;׃9PEQWQ-faG:i7R<~P 'P36Q'.Y#h\lAcDK5MѪRZSlEЋ5ȫIJB^-T:VcҚ-`?N!cM:dKZ'oH#p79a7r +7-pYwdaubƏqZO'J <iQLz%xHChƳ*눗}Kbϴ}JvYj5`t]D5gThaZ >$;XGv'h xҎUC}]u1m769}g ,A->ӇX ; :u(5[0Nm9׮=u;WMkq{oOvms+KB[6f>{Sir}t6i6/whN뇾vn^M|o\Buf* ~qBwgnZ4:E6 MKqrubͻWo>Fe04"7U2Pq+0nBZXڂer͟o&R ;h;*mHPW)HQyF >Μ q#b/a`Y$Fn9 ŠơXv'v݈#t?ou$FI4(ɤD!S8jĴ n9h[>ηә2]0y cF{qdڢ)!IxlSs8đ9v3I &6%1!1"DH5f>&"Fl6i/k?c/s?&O:C_kokq3]J{>~{Ԉ*1&aYC.4*h3>HTƦ:(!GJeϚhԨvQ#aNObzu ꔇc,Sm6nW[Z8ZY/(hb E C<F`MAXihLx:Q?b +N-0B0t13ڑ6apA l =,3nO'-&1sm]k:jAiy%?:EDcoda)nj'o`ثU7ܷ}zߺ޷럠uo][u"7[wpxܴFH=g|ΐt1t| O.w]7鐛|8|Û>Ύ@-Q K Đv61w!q%S HhKmxz2H(:6;3';6{G8H]He3Չ DBWJ-7w͖9WJ\VBrVI*傱uݐSDeo-D&@[{NIj +-.B;8h ,H+ëaqB K/EYA׸4{Z1<J6,۰`mC@@ɾM5`~d?TS@fLh|!y 5D]i&l@jq;F3)pְ"Ց’8lY( vbLPYdpJ⚠\UQ5 0k>b$2rGLvsp¤t q\ܫ !Э8zq|ˌe n2G)]3c`r8̝'ߦEK c%V_Հ F 0BOm@ 4Gs*'E)~%}w>^cO;Doqú{*9q,i5ݨ0ȕmdPjҀ 4꣐Z%a~Tօ/_$M$~V  kіBZ&dc4&ϫia.o۹lCG+v+l –$s֩zV?Mнg jhFt9js~$8=QB&,s$)ɘ+%'~mu|W?D'M٦8]DyJtXв 0!XrS %QXo'ҋj=9ZIlc&hКCR9G]qtB0z *ґ;D^sp \kpdqT?3.Ovd04x^p{FAy7~[3mq~P!"Tmgod 3~#FZ&CN~* )rr8dW  #{,aE: M8f\d#WJ["Ecp$"Num,.phv\Ok$No) +-ޜ'J9o*仁636aS:!OVn*% +,o*SMIHKa\1h2Zhb"Di,ޥ;11o2p;2CHIH5֬X=17R]Gq)((PGA';'JVMt-E4>h0d9!V`T ֨V-Z $#0c$Q'V;l0 bf2N7~[^h1~=_!Ӯ2EsyX ebahObzE$"*둸E/(miO0'b +6,sZWa PZp6[x2 7UXe[ TgnQ̯Y,X!|3VX +-Іl(t3 [(Juf5DI) )%XazP`f!l`` FP+&H V-s$`-ƞ,b9v^ۼ;$*ТdHk2}!PGGx)l}}2 5_iue5x)3o 3S> +xS4S挴R,1+ .ɘ1)?tp=+݋V w<x :ƃi]aޠ{Al d .@[R1XV\ гc2>3Z=SDWFVBT+Yt% @Q%/Utח.$kKg)u{TwK߶V+`ޑ{^*CV}R[=*Um ZK{ngۻGȗbG-IEN@&cHDpZ&$@3?]:B@}ɞo-=H\uavI+.Lg(yHjʇyR>3Btԣjg䏞Ny'M]gZ#颜[_IoޒQj2l2Q}Io/a%|wdð)Ao(iB^J#QD(>ud .&'CHhڈK$ +R<挸Ls-{U U |-fZ 04QZȆ)u29n_+'c"ĉPҐZ33`OZ3YFU,DS{R]֟k%29XU*! ЈL7&-iӸne-g^\|&M!AtVU^< +a1꣑G0B(v:pYOQH@%8Al,Ʊ+Z +1H#gz/dDG"jGZ]#ʞ#ȇ.K jv=7/=b11Q8%4LHkьBõ*T*,{aLKXg_t՟-qUBX@Dbnabi΢egN,5r󄘗3gw`fI>~-10XX,۴5RO=ۓr ^ߞAP#,=!m!&jɈ@F2<ĵ.yX1~.gx.!Nq6bnUI UH0ꈰh5*GkA342?De<9LO1aY!?ba hk,*\3Cb^0L($ʂy!{R34(d-WnW +`D"WyD̷+y5{M>7/DLc:"HE4k-P[X`B7ZQ9e>%̂P}3:53莄? 5)Y-Yl2+u,s:;tB,:6($R!0dq+ãޒ<(.ϚK,NoS?-#Ĩ^ F WqXVvM +;ZNu x҄oZr[2uDH:f!9̂ +ff_w8%.hYlhx-l:-s\BJ*'r h9/|F蚖PA5BAyuA%s:ϐfc>?0 +& +]cMwQ{Mʹ#˹9JslwDݿ=Mdџ /ֻީv@dy0S&{JFQll5@ 䉅?e~uז`W[hRC-nw}J'!M~ !a)_N~f-o^ 7? ++QG=Ha; gyp@u y H # U-?e-O lIIǁ" Q 4Ud"SlHreYQm^ڣŸBAOZS0rh3)N)":G?Tv]4)ĉE-(3jiW$.-EkP_Gm +Oe%Oh&@nv+Z|gyIlj{@)5c 1Z#ʨI +R47Myr=jSВ-r>Dx_xb|t|/$٢|Y9IJ2˸ DGUQ\* e8VPby*"Ghxc¦}"ͅZ ب00AOaij8'k(fn|')L"9;C6XGgW(\/?%L5bn*'=+8kR!H-~v-J޽\huPXKbM*g3YO*rlʆYHJ#9F"X1J#yR#)oCYc'l- ~[<+=eke$bz@JNۚldu.%g!EĜuX#E9s> D, 1/b`D4,#,SǢ|^ې{; +_dwKuP ݐRx 40.C]SgrL1a`1TC1a}ǏEw;3$-"Ē`WR"Yh-ve?GbpCBP@o>b1<1=g,='k3 +#E)(p?Q9 C> +!Z-qA e z팣 `Y7e%$NBHYoHfb!Qy_2R'^2 B5Tg}Wcё=V9{+⟘?p}>3Snm }|d^ȕɰ\Ot:NXmY 13cw^hI4ggupuT#*aCyxq91)/]KۙcwP䌷 8YH}bP=)6l}\wTV+ W~v6yRWw* rs ^p<}p!H +#,Q-[uNBVv΅/u{}l?hib u6 tcSd&h,K; +@LZ I^W2du҂;佁 (W qEOHS, ɳp % SU`>EH-@6->>I +Й2h 3b\21P&# +!5 0<8f"#c,bhb*[ a:4FMR"Fhb +iE4 QQg}#%n/UGe5'P4濴\42Cp Bb\BH[)DeH"@X{;Yۑbl=툁[gMJ[hv1gzhbDeC)Ś5RAl )Պ(V5'ИS"jiBD4JH[pS/;02ID^(.ED1%^!|c!%{]${(ѬV +s@>Р`j @B 0uig[\cWbbVϐ{;<+Eϒl=ྜQIPfwy6 b 8@9ק\x_?`vwR坬n~o=P%T R8\ y;}g0!fm>po?IvJw~-^~>q..B\TybWJ#d F)4 Q B/KPX&WF7DZm5Y!g/^{CY4/S>* he3*UߘHIݴaIF-2fE>Yɓ:Q^]v8%bLi=Մ03RD6v׹[# fHڈ2G*$cH[&hJE?E\ QNGM  bfb`RApԉc;Ik"G""T1"Dě:Z6-ؐGBPMFl.R,#EBMԇ[=h䓪\$J#9![5bSMDcL{8̅" )V)9O| -^ǹq=9sF6:pj{-+JwbY/]}k, +DtL^{z ;Bý咄 FZݔa`(ώwO##iw]3Kp~@~hct3$gw߽&z}}}-{d/E+rH7XųO؞MES!-Eyl&Q|M::;֢vItCX#~jCu8 SK\)S菽xj_R)BD5B;v)B40!'lQ45AxDg'qY dȐe'?w>AvC鑝 %{K*L%-7FGKiIS2 W A2K3S^`pm$sr6@T}JׅyER/SʼZhZMB/Ngs9BG2#AHF)찈b4PD[Ǥu %j&:J Ču5ab{* yLg*q\{$Ot!T*dB#R77HUT$HZ$-M j#azYhi5Eh!#_H'FJ+3!tq&iu]R*ɗ/hF:2sZZuVrhhZg!dɊb̢V%( ٿ +B}KAL<0"^'DDԑbRNJja4vh*O IU9!vr\ytuGef`~%Lr0۵8N@*GoBQCJ0S%05S!8= ƒEHF5-p&3X($L^cFXtJV QRrm:MVꨢ*)M'!*|/nx+X$*F*vM3&:̠lgVJ(ŝ:fJ fGsp0uE739jG{'nyb>9_@'bNdNǛ%3Ex۫*ob>>'OVVQ/V5FuJR(kr^wdku˧wS$~luѵqc孅Gu Z{,*R18(G/D1Vb_$5sBci%R, ]m)}\HэӑCOiH#͔pG xJ (`Pӈ䡁XG +Μ2$ Cp +*!؞KrY#,l,6^1;7*iD6x'{)BG4UGH+g7$e;eP4VO , +P g!HB^K m6uiZM]O8s!}-IԶ%9R)ӪcEn;J= ¦hvQzLԶ񵇴LvVeWjSeiIb2*!xҎrFn#oN2iV}++{_. V؈oO@>޳"PnĂh]/w2z<2dxZ/J6}0"1OH^IC#MZL58i4-:a+d ;m#Y#3%VdTdԮ}a!y1$44G_^5)zgՌk˸zqR+9FUT+0g:N ZU(\ةs npKVD6l`:P xp#8<(!5F"53C{wZs I"r'b +3`]Ś@Qr/0c}Z{jC\2K4/9 fNE[{##SMHV'H8I@Ļ %GBg~xV'T҉0zj y=ybOatd8PZ'"5<WÈ֔6Y5_U'UO e!Cs/:"<po4|稆o-e ++Ӹ3v7gw["93]>,)!2ILEaCc2PK)Q, :hu' +!5aw%s*nqeP-F86lQ9c$p$PsyS`ZlDX8%u֯x<ա;yW=NB8WSXK ?{|`߻?F +ݷ/bX;y((LAdq%Bi.vI0p+p%ϋ2(~G{rBgS: Zٝu +%Ff;7PW'``%voUuZk+X!n`Cub2+5e'WyF4fQϬ}F+kIZR]8/3G^۩ Y3%n-i^5#1MhX?IO|;u/)[!RJ§jDj],R;maX EZ΂KF\Xb". C8+ ĥ!--Yy1"ŻJeo: l_Pރ_^hH@[W{]u뼮Ƈh$^z.;~53ґQQyRWZq%ˁ(aҏtFU$",isP8ݭKxåX}"O%=R^m;n[<-.M5:)Ƴ!)V(J,EyA9%؆OD?~~-m_ )Z(X+H3ŧb96YBAh1e6>,kQTCs*dkOn)k-Z#Au- +j*~:,+(]*|qIJzm;^J(‡PMbhhO("B\Er0K2& s sl ݢ}b^R"Exѽn[(~D?gd8/I eHW[ 債L5Kbܶ85 fIE \xCl t,.i'v;&H +ˬA#Ul7#rFݬIIzlswXël1 /? —j$=%{=SV\ 6Q/odN*1n"}+jېr)}GH6En[En=->w-rw"wgu`;XֽuZuu`;Xֽu`?XN^p'/ w;yH^p'dvw2;ݧ';K"K"|-K"K"K"K"K"K"K"K"XJ"ty}n{0>'s 5lTC1SZ1UL*q+aTZq馿׼O VPFQi̗Ҷ… G8C+ *aOˁ"NtڋDg,*jD]i@(rrWMAezBuRv @ȝ4;]eH< Z' M n*A =OiEnGv/clGl]Sv^1Sg^H&>[4'g6e `[|[57ƀBEbU?`j|j7iO0>2 Z;VٳzTdjE +Z&>d&ĉ m=޲M-;oV}Y:\ĩ dFBl=R\ ʇsp-E(Mdi6I EG+N +~g)$!C y؍cq]I:дaY(Ñ]:!,rN)q#;nbQqpq2:V{^+4󋥜VFfcf%\h1X[wPUb.(cTLAT|26qBݶV[&"|n UdGTB Ā@IQvHrlX]"Z$*͎6g-: WIN7N ]r>Zb) ۖNQd%8z-[f'ln\ "SdN9JMMI)2WyI r$و[Y2,IWD-AUQ%hC v.Q^2.:A"tW/=crYIsX jaMv#̌3E\e}rZ庄2t08'{f /,dkqO_U޽moh/0t'}diѶͭb[^^9v {hQw~v#{֣x.9{%g8ؠA gN+#>遘H]%+~YAew~33$94;*҆\f[M]GG_{ȶĩDו2gg+ZRYFdDɄLjqWG2k斳G9x,ZMGKyv;7yd7kE z. +g3fȡ<&cMcDX[v5mθeʶq0Ȉ}]G>eK}ha} |RCcs_'nnIyVIEo\Ϲr 6;ŽcCq;BP=Z5KA"Zox:no Գ5yJ,;֠)b-%KiR8[F`Ւ(D:+XXgа#3?mpϧ@2#_;IGO)&8#hyQR(pn̎xJTGAOKt970txJ’3̑pCeL]ߘthWS`pmE{tG@VbܲX7:_M7:$,7:$^d@z-&8?F5D +22Mj[6uwCy-U*`flSS:ˎNR`X)񆤾cB'\GGG%miTw#aPMJ vч2gEnl"cXS%'vZ#6[@Wjg #ĉY_D-R ̄;i)b,iz[8ѽ(mn]u w>+…z;fv3Vɏ=ܓ`p;[Ke^j\1 tp>* ph띭d$LB1^Z \uxkR5E5* 1盆TDM=GFRQSDSDF1я1V߬FDGjKr#z8S 4hG1^7=4j9F!^_T7Cw=y'DI8ޠrfj9Io8e`y՘ WԂmWUҽB[y*S{ʎr4JDd5FHӓ=i4k?tS ^?lf35TǶJj!PrDA1lϗ/$K>9je#T?\54urc rsbPԁ!u?aD?G´Ts%V^ısk>gȄi P\&0z*X40j4-9fPK:0D%.e=p+cq'8F;vjje `(t^ DC>+Uz +A*os3bwPВULQB^(3{d>Gn&?0:;4 epM1(<'6 _ +B-n _躀tDۘ'DyL=0e]޸ӛɚvkb]Fʵ3U|J~^[pO;%)Adr5紕ɔE # So)YtC1xCJft$>d,"/fW9t5s +O<pQfm[hsUR [B ÐRƖ}\lj7S%+kh:;Cx5X:}b ?m>+VIJjhEa$E?:$E)iLn1s঄O1c/EJVe*TѓU l-$6=dfo +v hݪMn *q܈ /bxDM;b EdtL !>*.Q%Q*1h5 1r +(Fp2S6B+bc7$UojYtymhHpAsIٌ.qǖJm;2J\L r ?Ksݿ]nڬ&nGO I|UE:Nblsݍs}UN v8Q@SM]nۮӒ6z_ˢڀtЃ5( Xq{vS]~ES2brH#Dl0<]ԼfpcLfϐ-$t8FgX:7;ټʦħMm;*;&ᶉg|?NJZhf@cRB%z|k2{ 'Ea}GկiRn+Ψ Y\}~[ӣ)-3b)Xo]zRKBi]A#ݲY]P?54վwYzZTr=/lT W*%$l3mAHeIJ> H=Ĕh- TuBi +$ZHb +xB_R+ȉ]Si“6Z6QQ5/ +DT ԛ"6#HCR8*f¬L|R'MɶT.!YJ%J|&{TE-r\e[T%Ɏ,\%c$NX/&Bk58t%kѕ=@9"nV/:ځSa&$/$*$'r.{w֎-#)nAgUDH f%Yc1M[TWJ`ⴆҺrPktz$h4mC.j[{#c5Hk֭ĈPuM4= DeV?W2}f,DHzyK\}\& q_bK޽W欜)Dr[LVMJhE534szOeqR2*)}Y0 G`O&-bU5)τѠ.Tŝ$uB% Tc+C3R'*#5(2rD`5(+4~ߓ+$3hϓOC.=O˃9jil=壋&ޑ7ŤMZ9~9-~%Cbl#א +EVS?$'G'(}VIc}N@(q׈@tQty0bB.p9Mx&$ݬ\ftZS-aկE/XgD?cPx(c>q; ap'UhZK2T`6.rA"->K78*"И 2P{HYa( +Q"JAŇ/CN}U}Na ٷ"; "J/W*8|: a + + ) +(UqDbI1ؤbg-T0]ǀBD Q<~ób{Ug%*xNwZ|Ļ-nE 1i.O]8,n2V%9)_hGWb!˔06oԹ"2%n` YJ8-/J8cZ<`"ݧ)2GӁq^$^jB>YЌB?$$%juI!0$DQŐs`DDM9&)9DʆzJ],(ɥy@}Zm1[3!\ 'r^N͒2Wc: L^Y)>^||> |aP#2j̚..=*z5ߑh?Հ-]dr`]vB)V؇oD|= ~*֡aTvMvcEO8g.V9]\,eL)1^YeUןA7h1=?W(gGt%i@l;䑡Bݦ{EdN]FԈi"dN)W)$E19JnQDŠDJ(1Lq+w]/+6)BHGi\-`VrGlAKjLHTH2IsV6O!Ȗ2#JmO}wAZ|'S%/]]t?GTs{^!&npALIدٷL֚zs:HvLPr90IKos<ApՐ1^y8=/cbh(%Yk"B/`Fҙl +E;&'JryZϔTa/TP7?Gr;FZq2g|:]븺PĜ #gCcOV4皇y'xfLwͷ_Z?Y qfJIKM1(BJjid'Ox=LO9*B.xZvŠ.+eaE/ EqdNe6V*`1TO^:5y^tgDEyJ2Yc0BGT!;b ~#7(M2DUNul jҏcQ;'g9;4|EZn9ϩ>8k]ZMM{`D>k3IiOέC۪ہtJRe wZ ԯ߷iJ\gB>.R;R{bbR`Y)LkI%ᙧ AV#j-F0h1"XXKJ^,-jW[&|g}y+rn}zix p+Qٗ$#h E̋OHρP'un$s, D!'Kdȗ#F{.>@\,ht^:ߍ>E: +{<߽vPTlٮ}8ڑU5EUvyt)J#c@{6R !cr-{B=1wJ)$xxd{{(Y` +8≒xVVn[Bf[N/Z0{8z̜+(Lcb34QZJAꄌ%l9Jݲgx(țml6cTARyĪc N$ G@G/4GgLt0~WJҷ#̠V`SG2^UsU[HIRènC)wgT^`IuCDZQk}N:nצT^803ʓ\;gb +0Ԍ)MJc}qmW[\ +_gm1?d'!Ôw"S;˵FiO/Mv@aEArOywąʸ6RH$P'iﹾ-Ze/3jf~w+HkȚN5ڦ3h +Q8Gx*KB䡞^_u\KPKw_lq*4#ؤA%hWM%ı:WCzt:Y#k;t ªZVMb?hfx;2UǙ!\~jGžc4f=IYhQ_Q1 + >O3D0 M\Y5kABϊMKHmx0Zn9F~<0RXiGc/S,uX7 l10q!(*v78! y):(|CsHEKK3BA3f3u3c!MΥZRk?^E+)K +k&ҥX;%ALE +E4Kʀ9WAw)orE`THB6eC;+b}5q|Z.1)g2ڌeg\=2({P" Ƨ0#Ἕa!J޴{^EDEr]'tV3ElvN%˾q-"F&|g$ O䢏 ÿ.Nqj??}bſdFf̠~#U+CCPox1(r0ăRg*j߱Xњ-&/Y/ٙ00xh%n%+\IdDKnDd 2$R>)ͪIcFQ0ugլHי>n1R*̍)c51DߩOY"J1'q::-i(?cЂq^P\dX8~ +l΋PukzɀX)2ߊ +qpz6t'?Is#4cP3۶+Ror6޽7^^}߼F i/MM> sZQ{LlXݤgZ k_-[NcRhƶ= .2߾qA^?ax{ZUsaC'7+霛6{a_}xw)勿~ +͇w0~{߿I8 CaYu=4U3a/;O7sטiU4փOݤ*%9j@Zx>F7 +؝Ki`X6-M}kia*@纇7pԣ o%]:O 0a,i[ 8G8K0{[Um .t\tA#Cs?B[ !܈w~yZ6. 6/Sztp04Uqo9=){5]}u C&P}C=4pJm@s 51 N )arxGPuִo-6V olO4t8 *6iɣmMK:69y<@F/cN.r#~)60憆>9=kʉ2[ g-Cx`/V/6e>)v6uH&j$dŤTI\!ZK:FXAJhu4cS K׵C\BlPj(*\Hb$i.F$=FTÛ f865tu)X ZAUTCC$4:ޏq8ڭf%6؀i5F`Ze0[W-hvN";(VQɟ(zߤ6$[u|*@Ms/ٛ`EH*N0ք9=)d'oSFvX55XA=g7`ŚY4>ahZ"i`q C5fG{2^ǦTG[QF+1~7)[>zjQ'tI,6R$rwr|AXEl[‚~0`-swQɩ'p,֣ +TسmettUc;ʃYD^z:X֏iA9{mtÝ¡HM90ġG\h`8Y9)>k:59wX,es_^?0xXq3Ԃ_6GX0aѿpS3k-NaPweDBP}_ T8Mi߁җp{<4icY-AຖcM݌Cc bঀ^Oko=}- S5FE:`lk +wMZ6r:&9[m#O9P#`؇u'NuwĮ J P٫:FmCY7ccOpā5]衛ע`Chzv!;C5C{"ySWh{:u Yv^#/`~>U ňL1_cFGch̢ 2;x%K2=;]> Mn,f`/A:#GsAaY lkA:8A[sUW,i:Y,l@/'0ړ  ^;.Yc=ZyX[ vPnD.^xвLV8;w Ǡ.j/H}r5|u +[p ^,tN$+R7,uvPyV1[R/ur +F +de6d ӯ9;}6]ϧ`W=!ҥΎZ`МJ;}lCz[FZMW;лvȓj˝N;(˝"~U[luKJQN`ҧ +hHc-@:usXZAׂ{܅kvqn M֙v@ٵi`,1#X`W$]=u$ ѓᯁ] Ri`WZ`>vuu7iTM׷![4Q]ܿѨn%֦Q]8@8N>FA]|jH_ %`\e;aCQQ#O`ez j :,̪ҠnxkCCz@/ 68&u1Dw^Oa6=qk(?FXC7YXZ=@f6cwţhZ C:4L]t@O1^jͥ5BE,~,hnx1u8 yrd7%.3_,<'EHJŤlQ .^ Q_ӏU0o?6WㄻB"*q׍}Y?!GkFuGʺQer0g<icQ6;̇4ߙēQ|xrԉ ov O@v'a(99 0TCqBq (<>cLwr nv +NA(޻(;>yGf'w)ȧ٧ X/#MjO~L.͔>G?~y I8'W::t7U!j3QbI=#&XӴpZi=z0z<+hzxǴfQl *RI5XL&KA3` b)}8`Hp-Z)뒱~֌ f 39U;ͯ|U;.vu(Znܸ8 K3f! ^ڵm)G~,eq; |Tz];슔!@'zg*T‰ȠPOSb_~8跡BtV[{:S`=lC"@8M뤀eV`В j .Pt*ɪdГ50'2Xg oA_pq I VX[(#U`A3Xt*D`ln1kUuI`r}N4  v_NXT_'@#FsO9+T<0k@oSo4ؗNrE9 ]LLRWZpZ-=, vM rhdqYZU1S=U q:CKN: N`Z8.]3Xk' +3'8D-/d +em5iZlup|T8yτ6!/Ppe jPZ|ڢ0^Kf":6\Z==SeeUՓQ650'VûI].8/XJ6!_ːM>du >6aI NtLUA\' lq!4qA(~ +n3x)3V4!ut\DI0 <̝:zLRj6 o\GS ah5$ÐwئPڴ8XFY/(kx%Pp 1rjcPvN*rdga +K53L.p+ pC/lvJˏ?Tc lurIyUep 54W1}׫x u*ఖ`0u|?uwz:ܵg ܛv` `)h[gw"~t!muΧDK +AGɯ/p +^uW|]0TTK`0dQf1%O}Mrj[b[zgmehqz[Y#$mCn2yԆ%\q|U+UzZK8PdA'{ZRkݚb`:1> Ñskќ#TEtOWpOAYo ގ!Γd8GbU5Wf^B>@?BώkJqLx43>SC ZSdIǃ' H8XM- Z uf +/k>FCTZOټ7|lH %Ňu-FM.nX븅,@k'UZ:0.|gz˸ToY:Sf4kuF&Rڬ<i~zg7j.U|C|c]׬Khd?v3 +LšU%+ yӥײŒ ѧg +4^Qt X'vգƤǞ7$M={˓H Gn4]7ѺrtWfcԺ9>!A,xV&6f3dwW8+s~ !x|r4b\i\CkpWu.4/N6 H6jX^K˵]p׻A4³L,p񬭴s]N4AJ'᡼5E#j)y?VpҠOӟm]?N]E Qmp Գw|i)@NX*V^vKV]kIvBAaֶS6碂Mҝ8^xd6fC#*zL–b&A0C#GlF +C#ͰP# +Qɺ>-oNlCjLT6 /ovۋq8݅*cщ=8EـhT2vNEXE)W5 &m3!=TRw 9V6}k$)[:9!W?݅v$ؘP ύi*8tRRqxZkIv +:Ȭ"f&@JEI :)A^i .ˇ/b4(1K,ar7II ErQZP៚R #i);>,&ߞawTpjÌ:5o-Zf@Y4~"NP%_3w:+MpnECg,pqg`ѐ̔ 8"j"_'M6MF Fw&:8ZCd%ۡ +ɠr6;,G:y+hINH7 +] AZCQlm 3jOׂC-Y)\_O(+8=h^o( /"gl̯QJQ&GB| /-`⺮hM+"TŚ^zK A#!vЌhRw$o9KcwN;lNDzjÉv.rbuk6MD=Ǔ_,fl52x +Q#f [9(U[ l&V4̊L~PJ"{ܢǪVwj"YƆ8OT6B\BT r)X.yxI! Hm$ᠰYbXWm&c ƙkwki9<"A󞨠MIϥ0ԃVT111Ɏ^d=NS9z E`&"d;j͢36YתQ@پ,*BO~X14}Η"h@FVˑjfbd(CjJ5VB[L ~Dw-k֒~ +b !ijegk l*%E +FH#*K+^Ch_t*(0}`AQV:aծ&7QɊ۸cA(@8:wbqFPYcy782B<{n=إ%'(e8vxTmH02p ;y_ G]oYJzQzd8 2t=YONlCқ2Zxrv\'!\Sy"5_eI +#ꑱ} E~:I~&ٌ4NrE jfjSm ,ϡJkaKp BZfET,\mыS]lAd;\Rk| +ߨ21 nQ+ .~lPY>N nh:M;,Xd (SiT++@vˁ8͂єZTT Zn nut0#!(1sm.JNs꫽e\8KE/:&2ZA׀7#*Pp @A5/rckHۘ^3[W-LD4t>fK:HR#ݑSvGVУifwf2]mbʒ?Pv 9TP Q61!U>) q jη]X"mn Ӣ8r$5W*sBr ,˾x'Sv& $3܌@Ns'E`2B؃ZC)y;DٙrL鉬 K3=f'Ycي5[!NI[ +nVdiCa΢D|Zso+"!_\rl#sXn*SQֺBz1X+HO {9`*($I{ԥ o[Q.F4NR~Hs0z2>p 3B ,]sNͶHš'd9h\} -6Zl%sDŽ$&.`e|0|h,R *h=%4T^Aҡƫ 'IG5FFHASj-@l7P'9|9wVygӥ*C%*e k$)'6!3ؒ jywBJ- uY@hErptI5X}2}Xt:Ic:*Tb n3|*edRy-`eDAFS{&$A[dMv))10E. $񶂆H&ETun4:ӭa^d12SPj>{ .&Irk{ +D${KS2FO5( + v@Z`ۘ4֪FP615]'׬GU`~9=`\K&Q#`9䧄x,lg%ldah1Y/.i Nj&bEi2L% =M\m%L?//alusz eH$R&q|xq'𪤞މU/`L~FQpYgd$؇ +rYjq8e:Gd&Ll Ezؾ7Sfz +ZbĐ0Nr"k [JU+~ˁBIJ&˚ϟڃN;sd`BӅ*OIx:J/v6tM+3q֩s֓*#iXPmhPL#wb!h$P("@3Y !|'J[ 1Hx:.Y^NBq$1 vL)>nD{+=(rN*p2h,אeI4 dcsR #1KpZ4AeM;Q HC$<@5'D%̅vI+eE](%IIv物$1Y]29tVո߿$Ʉ# re&AL<`B#GprZ +AaB[3Z۽QLWEa"/[ +<9') HhMVMl*ǘU.L}}' [\X'r&z┡%:QvV`'}L`[%lnCdq-0gYerSv )y%b%ʲ^VCMR8inAx9Y߂(bvJޯ$z~ LО$ʼsI'9OEH/6g;L} iAA8*^ZZ&t$R+S#b5vun p3vFIb +#yIG%jt=G#悠T8T-o6+:EAȃ4?A ^E;wpYx~X T]A NHRY2su'ckz0fUzI0&c>Մ?< +rJ[kH~$55vY x?^Pp2 e\ngωup|ħċ9;(fOʷ4+2|lAsgvF"J>hH\|oI +o'\ր&ReyBɣ03: F@}? @d,Dg(%BܦpI& 'Fc0QtM1h(鼀v5"C9&3+8&d1lia3}091Oݦ%|Yx ƶ"KwWp8Je|_,!īD"Y$%qIbmBߵ)$K]&1D-k%F»凜\"^S4lGлs9#Yā ZE/ApM'v +c&G*!@H$$/n ;!&SPm^͙v s~%t!qbJJ"=t-a%X[>;f7A 7,p\YX?0~0#]0BQ)FBHhi$`ړ)Hh w, _}@&l.&GVM +St0sbwKCAaPup5ەlR$ ز 8fvY_+N\~ 5(z27E +4wn*ruD<|ZaJ LKj)s[-l/Kwmx0,@`P}+ONtg8+LsXst`Pկb7+/,(DO\hF\ +"녅Hm1=~n +-1ie&Q"ł`8cuqlGc&נD|؞%̺Hp^fqvcePPk.+XY!'t5txKlf'tb +tkCLtX`s[֊NEO/)΃0 +aХn7vVwj7Sȭ y'IХ:BP5b +.:^aj !V8(L[8]%j4jbblllR썸{9(&t(3#5+#ڐIaa eg GHTUAm-)bERFl=5HX+ *%OS7a\7. w=1F`.pgUҁ!@e%rLBg07L#l*I#Q̟%x-<*@t̬5@z9*ϬVɷ2O5 rsRq +!#Gveu{;f%<p$Bm +Xja S9}bf x"6s(G@_) t>ЩCpbყRQ(]AG.sk9:h*L~Kk`.X(^ny)Yb/0d2 -hBT,D}_I圧6 W(c#kdvKrg7Y-R#):~pTZIUg5a*AVW)< &D j'$+ 54AL+yRxcis齾W-It`;Ya^X@'P6(85ʗIOUz+ ΢j|>wҮI@˶N <v.t`\Yam/.iZC^(qq]⼴Lxp0T9Il G&bsNBP;} .ZpT kJ ^ +,5`)UdN\Ha,׀iܺ% yة}Id>%+V)W7W*6 DC%vn4ű4*MЈ #u[wD.0!4FW!~yAݯΪTP$"PP@! +C.!K-䱪Nn?)9g\;  (P33‹E`z~>h}2YoAē0 F^X|Q3M,_ +Kˬ 5[v/ؿ׽W 0cA0\LN|v 2rYn.dSwmZӁ]$~!Ybl.k``7`N̏T ɅVLf٭lK\e W, 4ћ5j$бK8VjaG+Kvk\q=k5H +# f~|k 9z?f;l ~i(ڒU 9DPgzN0 8LL2YB'v3cZg +K^A c7答mg{*]f^:G?0E-ғ:0=_zl+^4~? `Dl+;iΑC3Is}v SJtDSy &vqKF]#p +wk8@!r׬(YCg]ӛ+lj ZC1xv_|}PZ01shbˏ4EpWtEZkݲDVΨLFw@36Jl-c#q4j;RܬfPkܼ0gZ"Z:; g߫W?6`5!PF6|͍=sbu&2Zu&2ޯU*{Ʉ$bH8hS) lVJf9iA֘rh( 1 űknZ9 =3(uقR_Ja& b*wQWW6}x@gf0:ܠut|Yh`$)B:phxf w+J𢌆-lT^@TTX~ڑn9ւ{ kI_``U#MeheoDvS./!uҕ-t}&:@F$yU r @D5t-ڂyïM2txAozz"Qk0Ya]z09b Z 8֭9LB.̩ܘ.Ar"ň]׸`(Lhpڙ4K9 Fk,+ o [ӡ/ rԁ2|µp]xlk 1à.pÙ]iC{hpRRBmN?Qkh`d2Е CЍ^O.߁(\X:/#7_r.5-˷+DLK  ; +2"W{efe <@5rj=bB͢30xGyO,Ne1B! +'2+YО>R9uG;ŗ3;ЋFleɓA>05+ 1YVrbs lA;YuKH3(3_w,9gc Qؠ) w761ZCNj#/' ua7G6ERf;gPk,gcǤZ/x+5dܟ>?L .[ ;~Ӳ>ciNF,wh֍L537#<= 8VCRO .%D ldG\0 יcnCDZiO&`eŸʰȃڑzTp%H.4ARvB*0ij)j5LZ)a+co~oTd̵4aB8{nj7L[iF 1p)p!p1Q Kٔ \?iK9ar^0 v=[v!v;Lݔ;K& KI35z89˺K&o/ez‡$,SyП׀ѫu'Yѱ/q)?$/q c!=f.! >Sw?6Z=0za-'&ޣXxb71Kۼ? ψYzdۗM6=2|pgїbF Yzfwn63kͯO Zx1h!wZ4|ݏ!uAgdz+ >4|i.禖ަuXZ1~X-<{k_ç†-@6|lٜPkX6ΩfV i8}C*ͼ=INp=8$*SƸ.A8&o=I!#Ap@bwl+Gj_'DR'-, IMLcPHM􌸩4V?+JyH$gjn9nA2׀kH(6["* )ІdijC mKn*4nannH\no#ސooߌpH!78 'nAyC"%!E2%MOr{R+UZ]/Q`254Cv!4tatHG:&.] 9S7vB#';v3쌔vH_u;"R.y4KC%*5U'&Q8k)<0g4a0;i4{ZȐZJ^ 6tu_LMieaRj0n1^7fXVk'] ϓ aflqb0r)r9ܜ3I7v)\֥,a>;oǙL]mÌ]0y)z/=J^k7$2ƇQ~R20s~c?_!6 ^g&X0X"mȵʶm6d[r 6Suw%:!K&Jo*@8*Llر %.+GW GK>!s'=s#f9}AKCv!!uȥxu:s]"2njK$!f"kwLa_"m28l{ugȿL? +0~B`ec݅ K9 ~K1|-c{ {$×K92|=eS+wY\6?2|fgiF،3KH^+= ,=3|)hЦkEw@Zx-i#Lo;iXTWp\z{j҃X׳l5zk"wƖ%`6|la+j洄 t%n7zE${ t|+}͝N +vu)Gdcf!p3;bw..34ď#5c&q*] +uӵ^yPVT(:!3֐skkלkD'6-Q φLiKjl+lmE\=[݄2mqѭ-Q܍yiAͩb02n G,K~!fGqq9d܂~rU9d\`eWsgq{.XCKdC!fBԎ=uȳL:`prnC ۱f76"!=Kw;b$|HM80\;܂0Ϸ7q1=U}N|ۛ铣qm7o/^Ղݝ9 G}G_on.+Uyήp$8bAhwzp2]wwE׾&sŧWwl˗~A7훛n7kPֲGm=6{Gָ{suqs~um;?0g7g/opp|P][_~a#{/p{4ϷMt{+~_DR"D"z.E8<zwQtEU?n{]p#}z7RsG?nv'Ymߣ}3#0W/wK//{ nn}w̧, | x$嗢 Dm;ѷcn!_^z}qBaIٗ-o[h+p-o?iZ>Z> s2A7󧨋lNپk|gwg77W_]\]dXzN|}v닛?}t$sf[0*/G湽;*v ߃V}{^XRR>ts.n_D7wm_D/? jKiv髻j(}Ww; +K?Tή~<{ASˡ}P wBX:rrp=o1lk:]uޝ|yyexs{C\d2bCuly~vu +mCuq{ťOiß/[O\^=lR޼~}yvAvHytv!1NʾlCj+GՐ.`CD/2a+Q7IOQyI;tm_CDҸGumC5D$"|xI;tm_CDc,}O7"i틈xIȪ]!gOw^x)Ka^LڊlbQ/>7Wx8LuKb.IޕAM{z|)k'1'yN`]1}Br y\sHMo#n?;.۝RCIqn6DrGq؄^!ҷMi8^rK&%ի7;ص"0Ox|W;2_;z^n\ tÇ]~Zx wfI|gmy=Ƹ7\^ <NlLWl'}vQA|,/\&}.ZدL3^\u1i:챪d접|Ż}U2zz;/A?v}C*}?9PV=Az!d#o(:yٛSiG_]r5q᳻/wa;p?a~}9w>g,xm?)XyӼᅣ0ݟY[,qS}'󠣇'pxI}r_l=;Lߣd_?ԯx{ó\imo}}˻_tov}r٣$8 +<聥onοKtI:~]K% g܃/WѿL.rC*Cvwq}¹vyy2@Wή~<{a:n3}P4{qv7-p#󉝎L{:'} yyfƻ/?ev{rǛۛ:x~٦u:y~vuۗ;t}P]R'Sg9Ajg7g{jkxᇧ[4"`Nv<]Ǔd?6L;/ǣ:0=dJߗ3}y`gx#<-He^n'i<{'̀f~zL/;L)A;Lڗ+if/F`t:iq`t:F`t:ښ|rQS_ږǨv0>K8:faJCݾG{ stޜ<_.7>0a|i;ƏG{rq_䝏};lxv_jmCQ}uvp}l޲gZӥMءk}uwv~vlj{Kߜ +;Ĵ?{0˛7.qk7śۯ/7DQH\.v==XpL.;vg}O3Td{JNۇ!DxQ!¿H:7B~7o刺i/r-#!Oʮ|llr6g% +;uiCZj].i#\|/oR:_ۛgo]8{sŋ;8qt`SƆ1fNxУx?IW^_?$Cʈ`OR:̳v侾?lVfAל_;WLui'3ҩ0۝:w?\ a8KC.!QhrQfbC.{xNF}lKd8t A'?S![x?Cp͞(k~k5|}xUYI_Jh: _`2Go^U#^~l㏲?1#OJ'q(oS\lOq'1|b9>#m6(Z4#{TRI|̹B(`)WyS MCؐQ;TR7b96Ӊ+S[ +ȓKOhw;Xc1@`?u֒:XS'R G$\ vEŜ )8,MsX! GܕZN-…}ɜǺQrWc;);\Qu%EKHr#Q?` O"ode4eVx$|SWG]CՌ`9Q\]0R]Zo^]ҭ(:RP@Z̈́5֡vnpj O.Xi{T&ctiT#Bus@žhn:)X&t{>_S]p6ڣ|}=*PpðW`mS`N Še0D ;njuV”ӏ fM]a! ucY'O@(A~A*@H,sY+Ō_[_Epƭʀ ,j:yk<:uMЩ l}<_-<쫳l@Xn *RS''Jm HS{J %cvج2*r + +]Kgg1 Bر` eͅTj+LV.ɺCGDGlPxe˹W?_`aHeqT݂?<܀:hf:n)P7ew9 (u ]Vj&i//*U˩s%CMT aU.\qxL:W jBu&{1kΛ1UPPoU7Tm,]uoa Z>Pg&aE_ .#CuGV\UUkP@՟1 ֪.6%ZZNIRk:L)*]m +ajUSGiV]n~1rp8S U&;\UD1kS +A]=+X9dJ?;jT]*O u)zpK_F&F4## +Zڪ@Dk@u]qVIAx8GusəPKx_U΍cX|7*Yq\O=iAa7W!g]d|m>&u kDp+GNXa eAd /EMW _4sn0* vk&U, +yZK h&}Ug SY!XLȇ o +@; +H7; ꚡFS*8֥,pv&04p0è9 È/Uqh]N-9Ru 7vZy8ICkkCttp:3hHj  +$AuPu^[I~^MtG?"6~-c\TUBO..uW+o +h#m}B?w9ޤ= `+7 +N~T{T66N~g]EC9KV& ! = +L,yX>Bx]a_\ґ4s 1]%u'[8d\T ~fR}P$u+0M'bůShW5LnCWn]P =PFsVg;P?[e՜~GoiBcU@iBSʸcW&8Xx+R733mha؂(:^^ZGj` YKJОT:u@;u-+z?z|K:413e\t}]`ql iU|&9]JmQUPLf!7O=^ЊÛh:|U*~Ծсiw9^-#k=z>%u.߼wswo^+?׳//|}o^xNo=b`k8?Y嫫Kz?].rgWߜ}su7/ٻ6l,>vw5N/>j'H_\?ϿȮ?fF0DĂކ@$ր6mB K`ʖ0cx8'o7{yˋ: ;-YUmRTఠ~:ތR}Yv]SYU3$87n϶ͪ&&=TM>M~x]^ N臬*UiunT%0Ѡe7u*}դat4 m/@aS߅}=> 4T*TP,M:՟p  |-ᔬ!pB֡i~u%}yz EriwRg9IFA[Rƶ8Jh瀊cpW42EO=h8_[@aR΁cPgv|34pVi|aXai=M3SU_& &EC7 ǦO!B*ҬzC! ~*  BUW$9ZZ2MrF*p_ 鿩a]o%7dWJΖuѳu-ϝdA? nvئbp3TI@zwx7F$2,D Bt JMkW@í` {"m{d.6!Ei +UqptXDpx{Ɛ-7$k]UɃbU'X\n_~*zХ{hv? ks`vK&-m +'tq꾀nԫ"^C3`W;b!-D|ѿX; +Vx<"}#֋vlUr J NL U + +M%PHo5xǃnă1J >tK.}!WD+O_~sq[<9zvwG7Zb0N`_|g?sϪfQ}1gUyU;G;MLֿ_|Q o পFxݿu K &aQTts To`ehnj˾(V[&{&:>uLQȹ!ȭuB i[]t i5v}!Fh7UNZB@D1!xsLLnCXW c)?cЁ2b.mՓB4[a=6Sy_%3O[-Db\=(aA%“Xdy8pSL zӟSu#i 4:#-2w7^QUDF^n~s< H6n!睮&ȅV~e=ْ3[2%TmE_ - LQDm> 0(H cIsI ])r?賬)A4W2C2:xY' + +\Э] Xo/p"OKݑ?: a(JybF:Ke8FpĬ 7=JhEp]$S!NVbe)y{Hϟg_8\eqӪYpMe\ʰ 0={/oYo[]?O߯~3tK˳/o^Yӛo.Lro.^n>9mYj +{/G07,+)/oڀ:?idvv⏟e>cy?.@#_W9o_w-Kgs/yRRg5`:`)00RR:nV/ z[@mEDHzz00EՃLp XGMmK'>/bV ԷQ?Pjq +(\c1<taCXpXP.} +9eb`Wz .CwG2 tC +ceAxgՑ Bw!I A5II[ ]x'3 [4b]Xgc*2%[%IMHcTU쿣 0OpY9X)P/PDk/-69 "E=&;\ehuH(vAXϻvƘO"gv4{qӀd 봗} f6SF7P +TTl&fhm,y}cȌL쮒dgO8cٶU6 JRե19R Z8?UV3:b_Qua ż[w=Oꊺ~ߡ : AC4i0 +&oVLt{iR]5tJlִ{wE#7W+p PHI-q-3 E(!2~;mL.$Ԗp1e.'e]v<0J᧶]d_-'o'(ޑ#ĖGg7;l@QI"4q2ۢS$QdNنOY{fzS"`̱O@lNr*ɗ"8kk=~V}a5k76-O~Rc"|Rc']~f@]8 n_cy\?{s/9՞\WAfC]x"'"g~?<?71?ұڨW>ڹW}{hg^).μSgOF]<%>O>SҢx_i^I3w9Xv g}w.Kk>tAtxorepq选5t͟^:aj=D;c?se>˩9ҟszt.}:Z?ū_֏贯= IdJGJpu}M~*.v7 }Dnv GRSҕ|yt(gK{Tjw+X^>Q `_om[~7/qQMNfݷW?o)O` %L2omVw8 Gܡ/- ve+,zA^d`=K}Mlr; X~VkQjJ*lÝ#`n)F*VmR߉x?܅ӡ+iM;ObQ7Zg;իL%vxmRAKE=OVAT^8"m6u|f9u[o=5[y~*ɮ͗ +l$[u5dJFaozJ_Rv +%s,P@WTj->EH)zg!:5nJ|Hj6(`: Iq*}FovlY\9ֽaQQu-iຓLOowjv +Cl2eyUy ᤂ)( +Rrx<`ByI +B+z] ~զ6܂uv L|r4O6]-2xE9$[͙ H|u--|Qf9b=l5Y˒go.xI\lxZ[h\h됎"NT>.-ͩ.~"3'G3bfl8 IPm;:~P;sI$Z79Xtta#źV; BCa"cN{u_?bzֆ#5ӏ?¸W-_ iLJP0B M߾HSo^c"*h#qږ6=SccΣ4+<3J<+RH{4`-vHO)| )[scР=punKk>Jk@vf g5y'Ked̠J-(6m u;0֤t-u-67`MZ,)"DeY,j<ѧ~"cLB'_ |tPn!'yAH0`jKt~ԘZwzGA.Ld5os ֑/f֏l:mCmUqeY< + Ə~\1ХDC[|(Rk$蠪׾3+xN?f&jdtW"Ќ m)(bUҲM[)1$b7jS ~ro %I@#roh\,cU8 Bv =V "!DiH>xO JmNe 9Gqz)4$ŀP#zcuyr0%pHPxtV)݊MtW8yPlgpFA=)Z*d(f zF{QJsPT?2v+ي+7-c`lye 3XRŇtnR>BBZX?IY`<V֗!haT$)]O| b>~'cKk3(+Uڊ BV7'_XyZ!euDBQKRBh,%ӝUUg8sJĖ"F/IKRow:Cdoҥ_Ea{m9a%S;M#qXҮiq1;$Vֱ:6(ošλ+cD_kR1"ĭ^0`; cW& a/N_IZ]g;.>Zr +VE 6!&2jp j\h2ݰY -dq.Rȳ H2j.ܞKFl_3,PMAlgk?XϾLRĀRL|4[mm%1e\L9.SYZB;" + dmVngy)2tym9̣2\sC& 3m fs!Lͱ)\LP7' +=s[= GSg!,9YWtM;=/"0HhNk2']v\ЭyI〝ZRcz".S}VTϮZҩ&Ll͜{܅9 jm(x/D +sGR_,"FdkFhTg(7Ye**Ȫ=O X^'!p~7|qJ<ӗj1^(͒J1[=9Kc8`*[N{JaHb8sFhy@e 5srUhbNh$S'a'@#@u9,nSn (!oʏ.:탋fy|E3mpO8t?Ee Z4Mm?B cfvعlOEg/I;+ǬxC9<L-(px 0h~k@d#[EըlC4Q<S%.uxOV8B za D8o.lf-+2`*g*L~4lEG:Xen |+Vͭ`ftq@9)).pe6vոJ>V6pi*&:4LԜŃ@^;.]zmoZ]%kUZ5ZLU{ +x/Ԏ?džyo F"& 8n]7fWA/Ja#sRk!r,Mͥ)Uul*P^#k&5o+gZ|E_ᬑN)'cP5 E;8 uRa;sѧ! l0`J +!SCDMΉ5 +!sBx?SgL(+9DTZ¶ 7(Pb] +ag^QYg5 P bi^ +^խKI* oV."/ơ +Jd`U WeUF}!*qh +* +YEΤDb{c5J\!hOЏh?&%Q6 2ʆLbZ~[eE@>ꈼ0:ZNA+O9:ڧFm愮:"4AI7$ +N`0/`KfU]vȠooq/6^4d :x&hO^M>/!*KX#aG_v:DNa3S ņNGZ'@k^\;E2$?"VgYxEEaVspkC> 58%-GEfrᶲ|@%!Ms/*DQ_SFW<+ +endstream endobj 376 0 obj <>stream +n5EˡGڮfzѝz5LL/,ICf+McKU ͉Ӷr꘦aSpuNfJQ@GKSln}qݳ!L0 uQ }vLm>Nc;NZLܶ9x2 t$|Lh*{Q)[j\-$ >ӷ2Xe/>yE,t`;;jk Hab}֏Jr*pp\Vvο5/ #{B*z-ȅΛ5?|E.7'\t5csc}b]O3=DXǿOz wP?tPK&۩>ra;iӾsűye<Իu&y'ЗgylIYe,] ӄ\D`83SAsT8snWn7(: #щd'j0p!VܝZ;i M3ܜs1Wuq:L]Ly=ʥpe$kCMNjk:gq*u}:P|@M~AӦdk )h!9{BJ;z(t~@޺g-.`xѱwϲ5l\|b]_./9R|u_ V'[ÿFha}SJII+`?tm3?$RGz)$2g>Z)BJh3h= {@g遦u.)˩b*^)rVO\O>@Y^HL(V rF]az,שQ^(ݳ $hQ1CzY/_?xQxCNSwMM֯ϝŎ(IoqZ":DE;et0qgrvJc=܊Js'M'޾]ϓ!3uE5^7^zvz>L9KB{gshЄwyЁ@st"ȢkMϜ7CZ\f 9gOCb5]@BTbp7u|aLZj{Wq/H^snmŗ=,W1q@/?b00lc`= } cO.,a-8϶.1@xWCu$\AZsLelǫ?C<WׇѧMdb]8Q|\\Ii=ۭ-DL6G)L)وx0/T[Z#eJ:SxwBK;O\~y\ub+IAZX>oQ!AgtE$Ks$*?x%ښ,@m8F>?~B ʾ)]j*po`doqdZN${SCVU}@r>2I|: +=Hs &}V#}k'v&g"@w9e/ ۲iXG'd4H9ա.+@޿L;hDKvS,V~Hq:ϦYwQW̒!CtH;tlDLp7X#q#Ϯ4 )w (wҗvx5thdKyW͹6~0Ix>+/{jW^-\R_L { +'/.ӎzڰ+wGtpͼ菂XvnY?/ }1sW߸{- q*B@I%DhՇVIxRH Em (O :Mh%Xի_{U2 P-Axdg ؋~poV@620 ם!sd)Mui"Ooʙߴ} != Jc,45Uac6J ޏtB= g¤T٪/Nݑj-_lJ[ V8 W c0*ػ< M3-Q*$TOѠ l>@=1'>&YFnllṫ,N㉫IC_˓;}ڳVúBW{SЉk[.V)dWs SoOC̞մ uqVk'*$dfu0OyPFk_˂glR*1D-y:4c/V3A۫5\ontoMsIoگ6vaFb'W?arFk I>TSOK) 3>fA>c^F:|EA}G\ WFweQf?VQ@Za݇}:m +|F^ 5]5T})>!ي%CGIsC2}&>b,ǀ(?ggE#qn$fpS^~xՏ55@.U_\?xUs{($qU7.T` V0Ds ܡJQM@V*e9Q^cuIkU] R[>l(L4s@qNuɇ!X4hA,pnI/_5r{5e/^%Ͻrd\߃NLSڊhTs{<k{xh{ʡ8aEi*sbO[35޿pSq6\1S]]4'ƶ`/&;r'掽Е%EQ:L8oT)J{O{fPqsJе˰RHrճȅ*1/ଵ *+=) +N1%`+MFFh:9I +}s+gۦ[vAcIwLCgOr~kNxx,C=ȒJ# UQGVE4 FZyfQIkJn.ظ>!l;/{تTU>xظ*ٽӱ9ȸ_“jGmd-nלwi߿_'WCD|0H^Nq!J Nٱjx$XG +;:'jbI[ԼU3DLoF|,SE]ˬaX(ӿ놉-;%'˗.& +r*<n~hQ +t8gU t*=8iaAd"B_$- [dkD)SLiH`t\UzsB@HQϫRUi1Z*3<,'ڝbgڽAN8o!6q\$Yt5ZwlPQբ.Z)HoQ9m]0_Isl*s]UD]!bo8ˮ:,{pa|rU3MFkrū5/E{1xya@yF;^ʮ(P~&Sȝ&-2aXXaCËﴬ{? +OS.,Sy/"#]bQyBW>9%wή 5QPݶkHtXC)M&h,B pLl.QD{V&2# c^M Cb-g!G(&x6;A2Hqu176 BOqtԦiZSҜP*I."g5 FopwUp0D'~jsDd((rq>UXp^*J:+e'cW^_ơ_$}dH|t{cwkuͻ\z_9|9vC}˹en///_ { +~=p^MQ%Op;^BǞ]g sJfS"7r>.@,yŬ֖(¬d|#=Pȭ%X:TN[ F-'֪XУ CT`@dc OpgVX+,~y'0X!k;BUFǼ՞mjL){|%RAɡP+6HTq**-jxvUٻ|:P}UǿX#$ĕWkf܂sRw<@\,st#d7*mH*d1iY&{IRrd7;o]dA=eO#vqFd>О7)JÕSz@4w=ABOۄ;z`Ϟ]d=o?w[,y@p!GII3MaN&JzC8N@,vULw=YO#vG#Pb}<b:;fn72|y{oAI­<ʭۂ~7p{+> eq" JSi.0 +>aIyG '_0όo0W9}G?;?㢖cFA'Ғ.џYԫOt $EkIV<$ +vzD(mzXm$"&9 ̻G>k̩zVVp,+Ti̮;CsIσQM׉i֞ghY͊?8wYTIZuDұVExd I* uWt)ٳ$)8Ҥ: SW)O<[JM=&+TX1VskR)YBu"aY.3x80]y|N/x5/<'\?Fa߃TNCqii{ qQFiOrnӟœ8ܛ9NnǘN_=p % ׈\OBC\Ufbm$bd|[O{b[/_,Cː#[ڶ(}`H ƫX:Lh߻MlHSvjO׹&g)Ri:QE. 6dR79H65ΉI8%:Z!ZթG"ä5ʯ˪HnKɂ]Dlj--F(-REm6{uiW|:h[hϺ/Q:p^qre)gBl5P~(eEnC"{Vݩd8˹R8e> (*8@y*7KXS*Iجq*~-P*}֫g1ՉɊe^8صq%vjBXqcÀK>w~,#nnwCvkS~]pYJv>CBrv%HVFzP)<Wg+#g"U%7ybL\ZOƑq4i=FK1"i:kIy.֠&mO +&GW}"r!dN߶{ذEc#ѳy`ۺO9H{|bA1>3=WS>/A42W:AE=/#J[vS^J_ k(u%M]\r(Ϟa'vƅ9`r=6=L04@;cw0oF!pwWXiJ4TnOF.˙Tl?\Ord+V,ܳݻ`@T7=GpI쁁 ={3i6 ↉.|` &6X؏ [edSK[}\dniyI [x>`]jiSDĖu + +LS\=[Yۿ~/ǿ|z߾ w/|7;WowW{ۻ?^w?៮W^_ta]xl#W#QS Ӑz;I~֊)>lTbf {‣`;R,rRH%dm}hd6 ТcT[ŲEjlw.d7 nr[(4r;bCleyO7Þ' %!7fͪU T3?-D8nt=Vx6oM-Dć "_R@ FܞmsS \J^`6麭}n. +݊3)hJ*V>NJQ&_>_́T/qqgn7*uJ91Nl('8"mLx$ Iy5,3J›?D[A7i %_Li8 xyPw)wCpsBh%rz\b5Tp+5w+KWzլQvM΂TVOUmvzM59d%bR6R"QaV6UX2M*T7ac̳񊙎lZ7  HX/uN^xu0 +.30 URtjN}Y gx87ĄVF9?>_Z3x\gI +Ag?E0vDKi9$+R-D ݯcy¤Xx^ѠGZd¿alFJsV&QwMue7%7lvkxRBr%EMpKSt)pZ,DyR%2}Z5rT1]rE R:]ؖb b9H搪rb1A##P`M:\Z#{kI7m)bHf +)fv VڼYPM/},u i82(?EIǍԍ$1 * +P%~d7s"sNd㲸dr-a.涖qc'I=*>{,^yVg,!vUl Ppagl^,w.iZHʘנT7{^WVsJN{OF̱.9->&49CWDu!o%^\}μ-j؟"Wo`vvC%fVmԍ D_@顫i|I#9jNJH4qfULz;1x)+ؠݺ#miKڔQA[Q)V@Ǣ6|3[f jv'ABO 0o"YαCnd]w?UJT䲸DlP'g*3 cv]\1@"40~nͪ#tS ),0"^>T4I&n|nU4{ݰbs<2N ` 2Y[La*isE!SaE3pF 1KaglVV%^WeX)R6elh&9f-Յyvj7}BcRڲs݂*Mkpm~u(Fn&ʱW"CtޫUoZ?pVkuͪ쒦 +Cuq4n~+N +| ṯU-LX$ W̃cUSjGi(2 6}K+!)lTCל c1DùAͅz!طվgdtP+:U{_̟ȧ\o^5VWԫd.[$cY% hJog5{fP&<6w㼮NZlU B~|fSЀoVNTY\eۨ3LV%QTը<2T$ϞM", ۉX]:LaxQm3N%}f0gEO^ܼ1D9-m_߈W_{3<*$CMV93[w*;yE-8BcmƃeXHEpFZh~ضoVH>lRć۪&G +msϷT zbIu[f~D2a9eA-s8P-`lgO;l1A.[@5 D3AnSLam6䒃Yjѱd K Jݽ nڦI_g:OnuvwsHZ]<_)_ch:|%4'+c B $ҥm/jYO^YO1)au. Vim!{ڌr7LGKi)A]1+Qv{5c 3 TEGH' a沂] +boQӦ!GkVԬ"$`;m޼ΣXd`w?llH@) q^(k1]Y>%/)v K! +S{'qh +'&ç`*ˁlD5)O@3c(7Wvsޢh(諭ՂoG[+N&eHl'9bwڈҔHAn>wV^S ]Ca3쑛xͼ4d4`TzEixg!B=0;p9wA%Y-2|dk>$wݴMb%8v}ԢAC9XTW1Cl!NJbkob#%(`R8Lf^xw^X%%]/#֕ll[@O㩃[CV 6d%7%e폰)X[X*rruKV2Bv98p*EYxʡ;r0CUH)\zh!o .ldGs0{m D&G8=J(Obuaz]Hݕ!N 1N"SrD]kjD2_Ĕ6^N 6q=[Ddk;ed +eհ11ST?"I# k]#` A܊ebhڋO榏k'Hڄɫ1tSN*x4)] +*zhձ%j :UBiU3X#@Ʌ5kQAjp36A ^md6',Kʋd?#j:<% z ClU-iY{&JBm@tޤH "kכ4UV孼G[D^B*ws]JXB$~4lu[ߚ`(]s/%Ifv@xgӔn=d\'igE}e" b)iA,t{P\D⫪ΉDppJm0= :pr +Ţ7{2jibrxfOG3= +]w%דȳ I6rru5\^ sEBuF ]G˕#5 J!1k!ȀKQ+$?q3esezcFP &Xwî@o- d @y_ R2kߙkgL%#KXcSR"b*j` +Xp&Vb7Aލ( I))!s-9؂K/X}@0[)9tvnV9[ʗ+@  }X=@{NUh7adƯ|ܕј/p ƩpSf?w&oC-kB_OD S|p|7ƄSvJoѐe;ڵgFCj=wqH8hP=Fzl4@ ˰[E$eIi=8z9(Ka&~G&jGIywk jݷꨤ§)mGUvުҋ\s +Gk^|F +g@ge,W,,":#Z6a^5MwBBGEZ7V[D:߉H<[muЪE!ղs>Xd 6<9v%덬P+l]*C$" oaojLo9,}R"5LHa#͝K<ƶTa5eFC)dq4 .謷$xTͣ}9!\cymPXGӂiq+*PB!lE!P䉴͛ikkR6aQkh856Jނ'ϞޡmmN`Xm]WmK$9&2#QwZE!LqkmpAmMnŔi=@ ?BF}! 6!u<3=|5p"82&+J06 6ç輪lZg(Φ]!4T7B l"į19P]mJӐJ)a(<&O[i7YZcASy: $F9dnD Xr}p̲Nj&QZ)$z,5H:s4&J<)LG1>aqbCE |6JP5"Tm5Si[#QZk#1L 6;8#{[Edqŭ}#6[2-ͳ,*H$f Ab}@h^F[#-V<)QNb%N@L;mqEv5¶xQR NPuZ%Ƴ +^`GҌMJc\g\6?CuW:PF4D"2kfU mS>ڽՓ$aK> ۲C%Q1v[v]hJ[-#FGb iX~(tw'%HV$I3M}^cQ, 4!߸ĝxf82ݚތHznDZn2t+~D;Y_$Nrf7dVlqpٜHl;\]3KB0*J{-sYy2nG2 +cͰh]#ԞxHW\ژ5AMcg#sk + s^P&S^dvHLGCW5`Z"[ +X`ޚWW\*bھgԂ-/+ݥ.db \>/;AV֛"e9p53n5OңMIbE$ZVÝ`'ށYb7 CD,S荇:&%ޕIMŸ^B'Zm̖݋9cr%41*. `AV| =a=za;qr\vn=@>A*|SL±.+كxRQ,O9g)mLmy{4Mif+q:¢Mȱq+X9EtR*i٘;JD/ii.CA$ + -)9D)RwȀUU (4%pa>p=w.aCye9=63tps% 9Z@OPξ) +vwO)Gm+%-Hn9^C-~͚v{2 X0BF 8˞nGEgmI.g~ZLE9 +Φ%J͚sݝj$*y C)9lS`/Xkq˅ PON3(R!SuXfk]"4)K $XHTL Լ$8)C #`-A"0қU wN[D3J-7 +>cR601{wLV$˯06ټ[bwb1Nc.fmIYVjC)a+h9.Oќugr2a'RJaicO, +R@l9dYY;V +#X`\i~7%HcR H Zva9eab(|V$o9iE4 l O2G=t& +sH3Xұa@#$`Uޝo!ŘlQ k-uKBd)d[N]KCI)Qp˷ʇQFn.Hٚ~Lq +>秬n֮DUiKN@}0DG[cJO'pzNkyu.z7UR< vy5.YZڵ(uEHsQTCue]4Rs3aQaUٹV[+{* TNqZRQ癳1g"y`5hB~IEP Mh-׸;hzOe~;$1_-4*6'lҭS%PG6T32x"AaJ9q ;'Ո3VѹL@ %XKe+ +'\Yw&KӄUUXCU(}U&b5 +P {ݨHCKnpd8/)8Or%_5B JF۝(k%z){@1r G-W:j4XEdTVyAۭ'J}@4+Ýzk +y0sRbv?Q9= 3VIodp]|VJT@nSudEIy@[?5۔7( 2$ D@!*eRPTxS~[p ~Ha ݩ0QEi\$zu`,_7<%0(y)-*b[Z" +K5 !N*KblN)v,iPV>'J`?݁ܡ GLMh|xX7O /%xAE 㠄*UL@V)X7]XV9wUt%htp鞻hQ4Ĉ,*$B(Vi΁Vlmd + y,5" E!X0oq=aD:0w bL'BJܩ{>Ӵ^;NKl߼T4')LbP^u)9z8q/Pn8*\aXҙ(\C»98-p#@XEVpDH$#6U"=*Kį ZIw6ٜ;nRx T"ĕ/9s[snFEZeaH٭={$]Ne `PC*)e*]B/>IQX]2gxf-4T ٲHIC%@ "|z{Q ԫr2ufAU’/pl$҅c1~"HjaM_wY-*;"pyti!#{DqY|b,PF+␀u+@!QPglK(G^0H:WTMӡm0 +XzҤH4I#a:8ݱoJґ?W)q[;vwP,BZZB#-\Y&u<<ě GIX = XzjJL͌(!ЫYJA=8H4(Z=`"*%)T +_S%޺6 L$*;\*"LR9wXEsY3& B NUN +#3[(w*AFy;L&Sqw3TO@9 ntWF[1XG 48Ҡ0Z-L$荻ΗBŇCS2INZ^6l=gJs.X"= Wf¢-1 ZK6m@2wFo}ƛGЊ;($"|e&Bڷn^Ә+%I60_ӍpwKd#I?9N8q/\cZC\zpuX{Z1ʈ$8Wb2lVWC *9Z߸_8yf1R!f -q;*qOlݶ"_ʺf<^dZms$fgf2jZ\{戒n#u7j!IFם(%TBݪ!Y71RP=J" LفMy2R9tVk25[&b\-^pRZфT 2jRVDS'{}i&P Fc Iz +6*8qX4kWJ!DA'0H͹@pڅCx-iVQLֺȊƗS*ŕ(;p+ O e ZR2)_gbZJ93(QBR$sDJVT+X+7ުEޘT&IDr+(V8komD{1F̋U1BƂ~=4a=]>ܶE&q΄fKm)K\LNrKr(ڪf:" +z(CQ#ȝܐG9uZ%Vђ()sXbC"ͳfVcw(Z H'9;~P:%s+z ϚZ$Qe.pܦxJ#FtJh߈TB(GN/ t{!R[UI^+HEJ'MTzAIb1ob871-SB Y3S`W\U^XF!`)ƼiSoPV\K݂ݒGqdi,<(6}>hWhͳhٽj9[{/T0#}sgr; +PAW\)ǯwzBՙܯ9^}{|&۫{;w??Y71A>T)?8 ?Z&?\? ~x>?=1;>?zj,xWOO?O^(m0$ ֹ~w7WJ]7CO^| {Ta\k@Sg/g/yswNf/ӻg6PY3#Ӹs8;o_7sK7ڰ߹9_Y v?330px8Kmpah1zQ3ORHlT u#B(;@iI ˅+ ۰3nu?l}rtC7p/8GL7NzL ~n4$Doqh5d4ם jSvXhmmয়ڨ?J۟=㻀 ^ tF 0; Fk"MjK| B裧yk n) 䕌z=u ~'xM>]9g33Z-&y X_5 wh$L+E<,\V&nPGE0/X}:l~ۇ4nr)z* YkƭV| Iܬogr$ܪ = eF*ntOVWSe)bSi%$5$c9Yz*3N4@G{X0Å+,M4}V 5nm0Xqkhlq2٠d|k#Ɓb "x !Qܦ~Vdh]{G"W]pXhH *g/QP.4Rzef~F$ "BtAL I9?MS)奾H,D9  8(l w.M5wz!%{TOڀ 8ڀ 8ڀ_1qhl>ɯ+]&1kcIxvbЕQPV\w;uH Bf: g3O8]N]NM9r q߼K ]w98sc~ c~$SXtF;XN=+֡pX?,itCOb,6I:&:ڏcb pr ,D ,tL }n&(7DsC?ʛgBB#RH a8x1;0ځx)}_0'Ą0xo:018|A8f~iqݡ<~<6A_K?sA6&:+`e3aQP2XApcIթ]¾j?{+&a 3o"! Ş13t4't3C?VdGЯA43p13 vSC(5Oe}y)(*M`E|K8I(pF1hr ",:= +A tc[Y =DTt3EGr 1SEzx'rLЎ' "긅C]>~YOeс$!b(h측/I柑(K1՝OiF3߶&3ѿ][.FSԿX(B:Mϗ3n̆8.i \ +qݬ|=w(/t1~+pӑ͍fknuڛo&fpÎ,J9swNRG$=jNJv„wRCbܧ~,1*)_ik SK~ ,&@YP"i~{yAHArfYo/ '8@ R$LĖfXdgv -G1r>[]>V>߿?qHSW0!aqTGhy4\1B>Zvf|9u)AvDՒ"zg 1n8&V΄kԹeX|Ys/ `:"%7p7lEuQ8 ùNPN|l3M#nݧ'kg֟OEuM +0Y}>zkzQ{[rܮ毧k;*Q\1Mzx_xYa?>ˢhj) b\lNmw2닪Wq˵!ii)Lh=E4so N|*N,0ɾM K{fN6K[tFj[-{Q?}Mwc^įn#?5 +m=DcnS? C58ehci:̟A!Leזo"o> ˆNAJaB)“!x(0%0|"@^y XoNY<[B t?f b"J5t245BҩZ5l{ȾdgrB?O/Y8k)΄[9^_ջѦ0 kÄX +gJ3l8H}t=4şYɍHfů'SJK0YOz<|;SXa:c*TtkFR$@8jk-DqT t~U@)u63 + +=p8pRy-@P&K"WDH"ğ%9$!e 1#2eC$F4™-1DñFH./ RrQL˰`"p>KOtPPtU[ N3DɌQ$ 2G (ad B&\ Č\3,ŀ} >*B@q!bHd #cXAa' +bocB1%09<@abIh,XI?(`3ؐa1 0I*ˆ 6|w1%0 +iqQo€ 0&LhWV:ȕ'dQ':#H'+V-C, : 4J(CYx!⯬tH5)2&_%$0a  s #a ͪ(;,3 "_#th~.gY aaAaLЎBrBRҰǐS!KO!! "24'^~Ij,I% BJp Ä`^"@BN峑4DլFF1ޒIqj$8HibÀt-谠m36aW4ITYR!E?@vHa)DRBfIxE ؏u;Ël8*Վx +g k`,͒h7iЅSp['f'2;B"8BD );2Jk:&>ɠy{(xJ L=b!d20 "5Jb <3&%$fI~J@IajbWK2 d(8`dfyK Btt'!C`*$&ym^DrrhQn$AI]/^S/ O#ב%$DBk8DW%D~?ohQE7D%hNaOx$uD:JZV)9b*,z <T\z / σ6Ҳ +04BP W* +%.4 >%"t!!?0N'ٟ֪}{uR CS4hj2F9L!mbӟoz#3t0xQE4m-Oj>FG +Dsn8vo8&rrی~[x4/]z48]GL+a{P|:]lO]U߾%h}(<5yA +X$,!H4(i?6+$C o 2i!ɲ@3 +Y7@oHց`EPHoC7hkZHT8 E᝝hRXa=Aŧ4H FJσ=a5T dDԱ`T5R0v&*EK40؎?ZĴ*NPV58#'h!U#tBPPxjɦ}"A }Uz$jyQ$H <}&>͡,igu7N!_d_v;An<S +&~$"S)9e-%\2}? !卞IhJK{OI1(2(!soo%N}؟{O @h?f>:^7[qS8<߸I؟tr;ru\<}o_} u=?7G9p9i0RUw _̼2[g:k\_LjV?=@g^P'YW|Ep4/Jܐ#>ֿ/81[YӠGDd#5V NJO|ƙds}Sd~vG@p3<֗h4&lLu້bP$LpRf^-?q6|E#m(CߐС??(WQQ~ᰴxMI +ns;M dPx(D$_>oblBb|L8 H2wxCn64e?MÑukiT_a +-Ͳǥ.ëǔ[w3y˝)S_h|of3S[[,nfnYs3nyZ_r>IRaB(;,dGNw46cU:M;ʙɗp\jvGC\z9_$\/њ.yGiz  R!4̆4yb&ʔbQj)/:XKbֶLF+%C-FG|00?KYf=MGEg$y5n& /6H3~묚*\kSM8^PȥrDd6x<=&7)t|eL[ޠ5,fTm[09QKςʊM*nT͞2f?Z\* Γ`ZS3vw%Ƽn O&_ɉqrmZچk&o :`õRe]y^%2B[o~7wBm`]j\īF5&]qVƓ AMx(j= 9qF\͉ڍ'ͼO|}̴ +.8ebM#wG&4Yzwӥ"̦=UIU +k+nmEyw5zutbmK4]!\ DLΪ;ZO;=mFK5M|ϡ̻No/h9J:R +yY͐PAәOrYf8bW`Mz+Ěy L vn'-v7/̌fvmCGv2~v]DF-&Z˺Dc0\?(rDk&+{nz@=͊Nr1w`'`>۩@o+1h*ȥ5單B~^7Sj 4"ZУܐBv\+" cj4 Y;թrr]W>4p*U 6S%h6<&G9tw?Ajgd9Bئ(l2Y3H:7 4͒"]m镠m6nԛv4Z&rЈv4Bl9ZJJW߼Sώnզ+WlE@u̸"5hn8hPng;a-.ൗBwzМ]ml^']*h1_vy&jPF-$h n^of=s5fRT淚@+8וw $A>)NP!OD ʝ.&ʠC/`:ə 19I"Ytp*}+D SP6Z0&_;d'?H.+n5-@67vK+vqG1~m_QܨG0ґrj*ͻE(J0բ3{'o\6ńKcIN&M&y$cB⡐k$BzTjʕ솭R/xk6ؗTsPUŲ,Pkv>6Ü#_Y5cxVUL!ob!˫">-V~z+tK>|C h?}};%5H&zƭ^~I\.+~d_[MHlemK|]׳Ѫ_QWÙ@ÿ8zKs~g_WN}GÂQ <0mayf  +ʞ˦<)=uwH6T6h oL|:淞|zM#}_nRp  BV$M{)b23۵i ŎX- 6O9x)ܶ@F1 6_YYHBn)Kq^P>۳_&Q%-l.TD-ϖhsЖxT\co ZROƒ~X%WHX +ţX[,e&it/ RwPEU794Ր7VܗܿĆ5my,Z o|X>`;mZ)^[ __Xxu +Xtwvέ݇5=l4rZ|*NBZWCjf^[; zLN֗ڝԃhmd]ԙ"Dv^S6ksyfs.6{c7 '[Azm̴FDޖ,l&Un1[*dk;i{bV[YڸN۬Vۙv18sog –q//,g <+IY{>kV@$l:٠Z_W:[@cw^Vލю_vfvj/q{ +4^-&H[daᅬ(kݾKr8+G1]8B5Gbt4<.;㙣WB7.en}tnN?uOg*L!g; ȵp>oU瀺:s=N\@m,uQ,qޙ+޺@GZE5Iak*[s޺TUȬ-oܯsK= DŽ {'xẏ{ +r;9Nۣg"W*7z+Nzd7oYk^?'ˏy2GxSvjrt'pmM]9? tׅ4%gE%D{Yr/Dža6^z&oE_/&C/]|g˒{C\[jOa]U9v.7 h]q1˻J셀Õ3 U7niWTںTr߆I*py]^N3@5e1`jٱN ;zqSY?,O[j\ +ֱN7L1.5㺙6{ѲX-uqުG\zнڶC9r}L\]/[q;a87T3$7zijTuEoޝ<]w #}O>XyCtzǴ}t]ğ,O@tykB?:f?{bshtfgS_uzݥ16@k޼\){- 0WݧI:ޠvs $[xy!y5i{8/У;:q4VB;ݾzxLҁ1VeS|{JDV7or| z^_j醆mdCWQՑᕏ9`x@ӫ|r|`ǒ3gɦ6;qoe{+Wzz'Û(O] _O#ovPq,ءOMNIV8w"Z蘊Y9ƪ b#U%Li}Cb(Iƫ1ǥ&t}ʏbO綛 @)S|lq:=k{Bp3;RHV# +NCpۊy\Q.n!U}It +?M6_K(lUove[܋e%,%sP[068D HP&^M;+cdc(]u +@:Ctc $w9e.6xIhG-]h*qdI$4rej,UGHa)aHL]CtIuRoW7f ]hGTB+i̊N_|-yR&~^8`Ds>МKMC# (?7%[oh.u&!\hҜv:Kc8mH%1DM^ qxS.aѠ7Qϥ1a_w3!&kϿi!z ! F&|g2~Anr^*e#LCF(s<|7%AA`˨CE)ireJ@Lf-z^WQ b@% G"QwgLCp1 "bHK"@i Lh%@Ǚ4 E}'_MΒ[Ƅ'[,qޞ\vQ +qse {Y<(Vxۂ^nYFv(nJy΂-w?FtK +C0: BGH&%u.NJХI.bp2HҩNU`^:*@:ڻn$R#SZz^`$Åb|su/t>ίjR+Lo1 Ewѝ'\! ~x$VSѪ̥`p0Bn\%=zF;=W!hN@m~hw·~/?#%n7e +2 +|3=ޥx,"̗Thq ?* + &z*< q^ ,> +ҫe;٪$<K?I(]8łi*}|νDAmtEc +fwXq8_$]B'fi}s9pIK'(2VXJel 14xcC)i +k@Ё?$-F0b8/nR!#\R;u!rj?VSJ]d\Iv>*1G %_H2i=+È2羫;+_3)SuHm&bQnaX!~̀-M2]V^dfEil Ӡe:)σG\5;W.lXYqC +R\2pؙطTѤzj@ȔִF}]._AkY<>Ĺ%Dy`pIt~W v͹$;|2* rE-ACP +$^h-6I7m$!qu&2U5i!` FT[swY3: B!~N^T]41hkL1m%o2oaH$%&/IuZK,>"R]mHBDF3QGY.{?(f&jAsS{Z2$Z^nZLnrsh@eܴ~+k*HM:-u +N6W*+kH=,ub*tA~]֐Ωz?Dڇ4U×)YvN?Id!r +19fɋLr[qL)f[M`,߻g>3bI_){#*Vv8Tkkmsm >O?VAky%HӮ"J$*޾q5s7,)yma%,0~Zp_5*GD'M_@pxIΓ\N t%u[xH6' +zH|7,w,UY5wط%o#˃3>2ؙ}ؒ ^Znf\8XwOk XRxt0^r%tbA?rL2֞I|*Y(4c0S*)KTWfj7[uއ,Vq2٠#aKt7{H_v~W(kh4<7 śb}><@('-κ[>D|:{|e%ljjޮ7{,A+'I]'q%skwk脭<͗v<'pfU3r.HbGzꃎ4dRavqbϜt V>D?IP!@Á[\`~>} {9[ ԄcUNS\s:̟|+U!nt1{`JWޠCU epk{!>:TͰm{SL48[nn{kρth +t: +ܼ7~WL;23&{/pvnIv>4W#fr_~ḯc9T1wnfQoݩf|z O~Zqxdž;m7`?N}dM|ٜN8 ip +|~F| ֫`j:0F!FC8x֊toc,2OD*<No>uF+8EI:.ˍ؁9X{bϡv +:]Tg͹Zi +Xä\{N{SKe?絙=VCPUh j1FYsf:̝|<:zބdoh0^L'U9ҝHr5AH ߴJtXB"%><1Ox^A,z5 ?)=>QNel~\5bKO oA -nNc⍏-{AD_6ccU \ؒv_VĪ +z.jq2Ő uOMQ3*? +/X.nҷ,%*o}]>c]+Kz'I4ٕ74 +e4`~H՟Vr]_ @侭ܺM< wpf-4t~n2@pCG|Ow5SJk~G]gpGXJy>skK}u1d%"+@##^9}Uqe܏sM3]/&bT9aֻ͖g,7lXOP]8o9wॗ.|hR^Vݬz^aq@uݑ [ URֺөO_2VC_OYkCѹ3\kkp-O+ېkcJgӊVotXܨsqc a9jͽg)Q +sOO9R6xVMa`yk[kK:9}4Z"m ߥ!e(&P88ΩޟcڏC Vfe-ps1вqc~kzXqfŏH}yڌ:ICeIbzX!=۪7ܕ\..]mksxEX>vyqBگs>Eɪjh Pn.}xISm\DX]3`O_bZcRA_!bˎ%]̥jIf"1h: ^u}Q0|:n+6Ѧ\uЧhiowޛuGc4v9ç%Š ZB#O,󈣎BxF5 ZyORnZ| u·^t!0R%IӈBQ{ r_nP'wv)Q .3V Aj?-n# 23'vo7@Z\Bh b]7PTrt6P+eqЃ>ݽ!V`Sq. + B3Wb 5a$@\ +}&cEPXV*# +Al,cQ + *dX {n ZqbEibhTS˱"~I w?5y3AS񜽭m +'cRT[BB5ɜxn􌣙~pd?bz@|>_H ]F-~C(N&e5%g_u.`@ޱA:Ab &Yt`*82vOCM 98~-](:}ݍG$ Y1CMC)Ezv;FFuk5Re\ Lw$ա|ڤ~:-#s# 1FqshF9MoЯA/s8G\*&I A!N"Kf.+q8uI\{L}"uϖv̀^j-BY" i;g$X\*i(uZECE@d>@>|O;P'JzTf hȝ/;ݞdq̚b\\=X`Kf!*A UWG%w-Ι:!|6ν1} 2@p@!+<əgz96wbc7tezS +U̪f0BI%n3HJꓢchyݾݕ0nygӢ +iygfИyhCKe›amh`q$!Y +;UZWkmzU-ѧEzBzOƎ8}41nocoDƙ}qS_֜OSЎIA!Tz{_';nvlټ˭v_}sV߀F#_WfۡeNSx⹾~xn? ݭ,LN;ll"{ZWޛo /7*jz"2vgS=z#I{ l[M%ypy?{p-<]Mb (ݾ/7e*ШN({OknKmVeU`:/`PӍ Bw C,^e^ ">:PUAZKh4ja ZM&Sv%JD;dʴ B\08@,ppFw:T&B0JۙFm?A!`|iAHӌz\'RDvP r;@9V(QUJbr 8(jh6tYfB>OC=DyqM;5l2eI\wn?ek(ۛOe!4gf3i˞dX}vI +Q?Tn +CS2t(pX|{ԷM=Y^5 eiRBaB +@%4smR:<; +;諢C"Nt!:T U*w9|.3F;bey"J7фЈ+41EMcP5v[[m5.nɔ4MZnR{_y=kP絛,9=vnY׵ Ў$_%*J8[︄| ;Nmeo_]ʕaLzli 񵵞T Ô.ֶqኇ +TQĭ?㶀l2T6s 9*[z dZZo)A$7oPG+޿U;A2ahodn!{z`g Y+H f ! qqǗ8hy4oU./zf[; iQRgf +InU9WtLGA4S8Q&4`Jw,æIJw3;4}2d ~^9Zteҁӄ"R}[o9jhk?rj>~<+ߒLD; 6:| nN_-b1 S2[B&i^eWg0nofϮ^,sOϰ/Ww4r>Yۮ;-}ow-+;1,]xxt"`v'vIx=Sj'_5tX%4-&rf_Nom'Ek.M% &fhGF+3bwV(UgvU/Jj(NT栩7x+>]co!9iwI㭚*9@/qOε5&޽ݠE;fXth{15;)r\5rE8lfԭ^ͨyoz[B?zٚBf&gGhChVM c :-U-\5?LY}h|17Q4Ot +̹[8LAswUcu`8S &A9(LǃFvB~ƾs\\H,`&,۟8lXgmqj=π!>EsfdUBʬMP]brA:@R@eYk]?hVe7ދ傌+6'}T=) An\&썪Ov8$SOj6av*dO{/էSF1_ٛ|uLdoĕ=Bg+e^ ڵezdoNJpƹ5_+:({FP2Q d:V 3KJ0#]{8ŵf#NvWH.߳7M= 0kb3p578u@f}%~o JL Y(fոn;)<_K!Jsv7OᦧChcʼY>ʼxPs3UtEDe*[ė]w=K/<ł~c<-ۊ֌օ`M7Cm[/(KC˽Z`5&ў s.eTB4 +ʶFy_:?m>[-Ώ=Fm+(?)_Gm18:Pfqi~XLnt1t釸зef&^@f{ǹٚUWo;vH7r6m'Q<4bll5[Sv=ET$ }K,[z,-E5qA$(GY@-_GMQN<`^(ӟo/BSM!!4./)K?Ey4.QNC諭n5sOKMfAPasIHg|C=#]8Qߙ\u0Pz6PfյR:BdxO0 +xm;/[ͱg\+^&k'4}[gdjҩ@/NڅBOB~J]W(PԕL.jUeZ5<-b~+kvkjy?xWʴ.t;H6jJѬ]ewx@D}OatjTGlѨaWtO߼2pþVn=WXyC +VnM-]azib +d] ƴB鸨Rm&Z+lbڹ΄ѨV MaOʵڷv!\99D.jz2wb)O<`ྛv4U{n3ME+fGhvӄwNJ4/\w9 ֚oO3}Zا%io,3{ [r>b 6OEAxo+u0O}HXا#[o+ӫSO쮡 +b}z3'OQR>Pطݧm>={ Po >C{  W +4VؿsD;ow׷T}wa [;£OOQhB(۷M}&wCWaނ)ӫ2R/Lj +;sN;<5a꫏=bhnr|>2w0Gyd:7\"ѽކI>PFcuK'JAZ-֧u=ջ7X Վ{ '2k/4!y.;'}]&\glT}vpұo'FejLسZk[fggR6_OlvN*=[~Z.=xmӢ,zyL.6˥Dھ;q),!佩>3;!};CL;B4;67I 0Yahs6 k8S'[kكnzVMwR#E=__Iw-8\dL݁Eqlٟ0ﯴGLU|mζ}o<B{yN5ؔ̐-=jύ2ܵk۳: l؍vmsH';^llLyfJHjy-p}ކKx./sɮ{qgvXj1 qMWh)Wj߭BWMر[Z2 Jt+ˁ}Ki3d\)(Z*x!zTU+|[q i.3ne9gᓪĪhV]]R_MI?T湍К@cTHɨ+5 m?{oҴ_94B7UY*4 "$"y|糏c>wDE4q.*gfrxWil'i@YύZ#wc^d˾޷OFaJ|5!;y ϾYcԨ~?5n2ԳQ7F}µPQ?68z+c43|0Bo_ ֒U=/_eUzq45$D|-zg^>6'%4[)wE5:n9'&軧Um%'*Lz +Ӵwou%Mʹ,>#<=53=t1.Ayǜ|Mˈ lZw6'λ܌pXZKDAŘ^L nMt*zٴT-ݲhk1)vd y ++][ +IlO#]!ͮ[ɝqkj5y ݡOuE)0L>Cf3ٱdi׆.wh1z_ԯfn6u|ON}p_=\HNZ:T<Ƀ"R1T?L|^3\@bΝ%?k!I>GW*@߳9CB-SD]'ev}zB~Vw<iFZ cvoI!mNo[Csؘ ͐V?Pxf.-$X~HHb3l[!#zi +m}XHb񈌴C36 Oj#j݂ͫ)!*x\tǃIyV6 +@Н(x$St=e7v7.x|qZ ̤a1/Mӷw}ڣ 6[YYWkk<(yVnm<,̎`VWy/*xؼtas{L~xdOd-xX _km<;^müpFtaĊXW)Y䋟WW.fu~\;6T޳:nr]\y[>ߏv_ގE];uy~1cxy:&x~r2[ +ױ+oOߗއߞ}?.=~ufm嶹|~5rmϳw*zL|U8txeyiҿ5^ zFɱ1o;o*z?\woE4sh٧IޏsT"svto@&m~U_̽+|&xur9w`{ucy _vw#]u;p!n#$F'{E/p^+_#ZB5iis#WX}q]t /F#ߖ/^O|7mebhC%~zD]^&#H^lb5zJV36zz>w=:v4~/TT< pYM/<oC'gŽ9_KJ_ 0.Wkcwx~;;=3zN_Zop^VΠ? hγPj9kmM03!xp4aanjejrOU凞"N@xIkǪ/?/N}Tޮtѯ #Y۬!p`?csbB‡At4@#\+ZXQ"xfOoq@mt4B\+5+mAQyY .b^MC[Y5돆ytI;w.^HPWrTBK~FK~v|nv? :]LY +-e;AӾ>.,үkg/i+]y: `{ru9\"ݶsmb#EWpWlqտ;lbH`+5ir &,^lr&o>d},6qK99{mқà~Ta0~LaDK3xS&Ȑ!Vӻ&qT=U_p)ݒ$=NyΒ}>/{37۟dRyc?I2U-7H"pipCVe2!ID2Y&x`78$C_uc+AM'w$l1 yMm򼦃liUWO>Ow9vߛ2]X8}$,=nZJ;z$=ϋ)ؽZ7-{M()2oXM}#$w`v;lKk]׿O⸐rvy_=>sZ8#M¿:YTwI@Z&{p|Ny?./Z:* zDxH5]G~.;=nUjyvx`I +j;ӵiSjZ1ozM]ET]. ]7 lćKώ&6 ^ݘߘ\]M\54oL_<FFDܿ=VxU צj9Uc:/;gN9Ӽ҅8<fӘS?psxA〯>wz.QeT}Ӯo+nŁLfo[5kٴjlZ8ԲYmn*if"V'lPuf\E'ZE6DlYȦn5kٴˬf"ìF*b,o +-ZјƬ7SZ'YophcV[8tY-Y}cVsKV]hcVsK{?ޘܒL]7f5d2f5d%N<6f5d01f5WƬ斬'Y +CHcVsKV^Y(cVsLc!kjށۛcVsҮsojncVslmQ{1%+!hc)%cVsK)mֺ1%Ȣ֖1]Y-YuYmX1f5d%H{196-jm#Y-YZ5fbQ1Ѭ5cVsK-jΘܒ%2t1AƬ,|1%Fߜ վsjcxCcx. Ӧ^{,?HNEzj2StmW|Ӑxljg-Z^+PgE +-̔KP\. ׏š]>8ֳJS/畋*:,Ҿ'8,ܱrPQZ6+ .\zFt>0nǎ]S=F^}ݙ|3w,싦Deղk/vWѺw9~fz;V_6+O>U?0G26~Zt\w/Vߚj7b ߶TP4r.N蟭~5[ިW6y7l~oV'mu8_yyywyZ=ү'v_M};|fuuȳ[ޭz>;7>;&\X]^MqOڨoKQEY29~[uګ;<6 Vgt<>gbwoK..Wz&Oy;Yj:7&P@7[[.j3&Y{w7;w]%L^t$UﴹM{2:z18YwSsClukH}[T_ͣyۛk/>l|;㧫Ej9G^7l@=1t3(|?1/9_8~>1jfvɯޏ[~7N$1L_ \.ԡZѫ/GǷF mΌW*=^8jSHg7L~v蠣H{no_Mv@:ϟL#t(N?ioAy,<i-ܛj~ +:{EA^M{tI5fo~a:ho5#ڽzH{i_p<:x,Z7O _*?:g9Tۦ!)%{@N*vHҽyH*؛:ϤVUOۛZ:E'AզVMJ<?ZYMqHզz:%.m.Z]E_6_ş7( :E#HE_şA(A\t_F]޴B^˩/YurY`@ςl|[H%Nf_Z_(f޾ڧ'̡KY.'ʼn/TBq cy/y/q0-~|x!S6W<Ʊ_ZMzO??%]/}h,MES(D!h>l%o#M_y+] |œ&{D/S16ZIԡ}XAϴLU(S +m$aJ> +uEe-Nf) 5 Вkh[D#7)m66$?%iU<B0֖#CxHdQA)Lk n_`!UEhgXya/c"0J6 G:RzRvC&d!VnK.?Y +m*O"Y!j!AxrD1 !0Gbuq&y*6=:#V'$3lŨ0kXŋS9y85ZyP 1l1OaOAg|B>B]F*x1h9*ڊCARa g'B /URB4m*('#py,G`+42 m!4oL~(Qv~7sPE 6ஔnP;+s)C,ZHȟa ZrUy|U~h<9O@|؆Y̸H {mAf|O)'U`D5%(U ;2dIGuKsTIȭ.݂g">g=_''CEq+?V شuR)rsN[,+}x͹#w(k=83Cjc J3$)hg=%]!)3 )ts=E F5y>kao'L"yzЋ"f$HI7_c8g*SI`AXuF5#>kaohDgl0 +ȯAHSQ )kuD$DUW@7SHcFti#L/ZC1%_yFOEԎ;!-uNwQzZ,)5.PHIM,d>h Q,)k )3DP +H ME !q1N` 6mP4t|ACɀa$8Np+/t'[B` 9X +}](:@$X( c 0EB@X $By:L(*P:dG܈?w!B4!LA`ȎCȷFv΋t?vjLwZ/5O8@(8"*)D1='O(ǜ$GY zh7S?b`-!j֍LeX@ AsR=DϹ|YzҐFp=v,|Wa' K(;Ċ2Jdw:~ XD3Y2) +$z  Bv(!@`X,w혤<@|>Dergt/DדŶ5,^nB,I xG+ 6k D/"LSVn$I4X:a'v 5Y#w 01Gn8e|b# `l4wknQo76`'9%Y,+5д| !@W)e= n@nA짔H4:X@6-MԸŽ9zSž  dlq[\.J]ӿC¯L@;%fz?RU)N(4x7Wl0Vz |y;x (b#7L Ks¥|EF Ȳr')H|d I/5 Db\ + 9 q8$%jW$f2>tB܈0R8x$7L&~N>a}Co[Px?A^'iHB~z jY?U(^pw7Zt\?uˏ dRNi!$RZjb-k3?(+4t17ўµŌByKgJ\5NDB QlsO2޳X?XcPdIъ^H0OUhh-sT(Me^_6 "=m+J96?qJ>fg7D>$~N( +^Y.6>Z)i}Ỏ=νfA6w ^m&rjw +DދAh :0b%BJY'6=Wnyi*Edq6L>ѧC;r}+>0I _uYƚPؘR•z8HCpBBH$#+z^r*!~B3;bX~cAM8-I% Y%O)/z1@&tLȽt1'LJՐ>G(~eG%? +/DgLB;ݲ, 1*>Pz*4 tZ pqo;  >!XKPzA2"E`НKa]a5,'VIVm ,CB}f{U]&ҁVوhHz:/B lM?tVxb8NXq!èް9UWrV[({GVc`/AOu@bN˽DNw̭~iGx(9Z$yCp`=?7ZA!ۃ/:gXn)Ƽ7Qĺ_쵐@l2 Ar Pэ:'12`uNLjx :?1b9*@C؝e1gS<rxvjv `84,}>a5S+ZLE)8:ͮNmp_@88Z(cHLI3x#c*bh((2}3?<vËi6t"+k&fv3l5e!y>aBچ7 +PD tm`k3Y؇v#"%zqA> -c(&%X>ݤ +(~1'ro@Ʃ'!5+юK_Kjxq:5)֑TeV>;!5M`fCBH iδa gQ'tCurPT jOR@ؚ֎0CRi g3:j&39*Ggufqr4:#4mj &ѹ 6UH `/feXJh@,`Rg;p"Ac@( !sH0wxp!L%-1P|'~ɤ&3X4A%NqF6 `@nL2*ZP~7a!ل]Vrt3}e5ҩ8lcG/&4Oo2ÐC8Kaybwx0?>7^%8Q7@hhP/@ 31A1{n3#"íoĹ!hxbÀN> i %A$n:hS$8Epc PLa=i $9ڧ~U^Hc`a 0jS4q h6=m}I,&Bjg)R y5YُOtzUG5.AaJZ@ Y s[1b +Z].E=yAd@L׏!k&PG3R^*uIl.ee0˰Kqlgʥ󋣃n]Fp +C)wS=zJl[= :{tw6id_OIE C#lw ((X`]3Av:c%52pQ"i7]P wgPaH\ iC$ +In0t3trl.qV0$kh# A$h#}EGUYeicd,» -_xēh"7i!/u>XI@ +.6ĴlQ +I"bsiC֎#>#a ƃ!ڊ8R?R aHd'K J\q!oD &V|ag?K$mvS3g$YGWhY6c\qBy Ty*6"Q*T߷i΢T󠓎 ڟhOSXA9%WDĊ/ڐBsbBD"* 8ttI^ 탺S841ݖF6*n,#=V7d] POzQءo0$uዎAF=@#Q +< #拚)m8v&|hZdi&H8F`X =&q9`eRB:ꚙj^6VzJn?&$PԦ{h+%/<XV3,Q;芉D V:NB>@ǘj,=+?`G/Rqԡ˓^::X‘eCBqr,P(q'TY+:+haB!bI[~X } *g$&ˋـutb^ +b(@Ml'z Vs 36]B)ߘ5 y~#qChb7"&@;<Wcd Rя/ +|qB0]gIH4bbgm3SS nhheICrpLا@?Nņԇ?:EԸCLH!+sP2n!~|X^v Z`7bqCV^0,$cJJ 3i,*QSڦ}-,qʬ;}fzH ᡸuy\Jbd}~28 rokK@ɸ6*-E6lqiw `>pcvhюB[+W}|j,׳Ilmߖm\tS辢!!qT4* G y|ro0@ ܃41 >@ +x0A_ 7tն'lj]gnSiK,BpD/2(xZLA+ngZr63g7S/a-|}_N4^(!B 1 "ˍI(O6órRﱠ=­eq"-"cחMPbM`C@Xe=CY:TWcnnZA 8@q 1x#ZC 4,fԾ#{ % S'QmC8(Td` fgNun e< qt?18`;y+eE@ko +ҞqgD߾n`"w'dn# +nv4@e]5ٌ7' @9grvރPK GYtia8rcD#%a%'7vB_Uz_= 'L:LX 颒;ٍ#:k7waK(͎-?~}XG%˷nB%f&llÉ _6B_bltnKfwW`JNrDbnz\ +B?"bbbO"ŌPoPn^,nrVZ@!w#w/0nࢧ U` مס > x)umHApuBBNF=CJU=_܎N0| +l\7C R q") _j8e BiɔC*!jB,"̽H40K;"d`§΃/ᮜJʦ1<Xu@le̲'O 4fֱ h1ܞLrE8Af6:&$n=h]wes>e#162[Ҍd^pc3k)`/2pÉ_̾r׈D +qcGAipt:3{׮ٞxoW׳?YVfΎnvNgZI+h|zpuaw,a?:{iL^snJG''4U@-{ag"3~:{. e?^_1bY~Y9_ V?:|^;[޿6&fqci'~<g-޽)VvvNnh6q5zv~9Cx?'-NA-iO]=9>اY|>AѭoDc&涳͡n28z72œ7=[W'$8[;gG6OlnYmm0*T|g󱾥݉gggcOc;෧kgk_G/0^[:6qt~6s~9{m'w`d=̾x=§짮`zupfgݮ'vT]|^8ػ|Vg3&FFo˃[_Lμ/VO_,lETnZ[\d<o>>uuέsH5kߏ4>썶gǟf?١);fyݷ/]7,<]>:Z\\V&oJ瓯WQ4if=GG9ٚmGe?ǖNN,W?D;ۮލ iz~}zŷ7$3 ޷zYx.}}6֛p~`֏Nz'j4W;, 6gK{ּ_O?0C]qo{pi7[JT_K&<~[~p=8=4U_1K"2ޣ-$$DUÖ"'F(dQ"{R31OgVB29! +| +#.,V}1Б)Q9r],Z攦Rȉ6XD(ZڈSm Q܍j +N:c_8sb;.L J\c)Hه2K\(b4 + O맬 Gїu\_ hHĵ<[Óser!tW*DZu)EQ3 #i"P*\75Abd !;JRosSD1RrFT'(^-D1f`mNJ@,fG%FfʏeGȌ@p5^-=fw+6~gQ.Xl} KZ@BgH)d!lČK,Ljc'$TH6Jp,|ӦK=fnhx.ȃ!VIM.9eg) +$gO9a!_#WS7B8pW0*rK0ຳ?j 8C*1N0 M\ !*: ͅXA"$.RR5"QEVC 8H$1 R2bƜ4P[1*@vA'14i+?u. IaP #Vs'hqi$;, +mq"?:ΦCXVnpVRO/<"Q9!5A4)-uNi!YmR9~5 4_W`Rc*\iX}%oTbsJiWآ>ҫC`#ITp9+$rX%\LޢN~$7Q)W@CC.e4!0\]H'"Oq#Ev׾Мkq;q0Hh,8c0_'XeԤ";$D֥=BuGN~kS> +nIp0R蚰pJrk tBeg 2FXH+wO\3*eW)ʷSvɉ\[q& K:uic(J#ߕÃmxIǮTl}!l֚e:RR% fQTQg<(f +{r-1KZB4\(By|%]xEP \b"Db}>xp.p%ZΑlu$<Ԉԣ c@Wh" \)1<'Qq? )VyZ2 +e6b݁Àu;|{g$RjF ~6u5j5Js x"lPy \f r! l +}g%Jvo]+)1Af1\x* +PU A:b'dj-&b9,]R['JU98wX>stream +=<M9NozÖɍKsNMw8?pW* xưvMU%&=/$HǬ]th|e?<1&E=YGm0'놬2j &/3 IXcPGpx:ƞTcmf"8Z\Z|) q EUa +܎K Lp@G8,pBqȦtl{+`WlHPvbD0 !)Չ +͠-6bQC9P̄~[ܐ *(/Qu#2;0Xaa3bK5)xoK*@4VT,Gr=K9alPآ鞵^ӌFby0\ɜ4 +AuM`ǭЏsت8%bWb]C4HN4 jЂCn6Z=b]6=.`xV'Xg !32> .ْd, Ùґ"b^(n !5#1{D#{VF7)V=6t@8?B슡yxƕ`̺P\$vAJY׀0BNߖ`K'vM|CR`!yƌD krAB1Nrg)S;h@~>^ P"ZyoDkW(Sd 3bq +ĚcEJ!Ua-D^xlrJҲz9dh߹*?I·XCAUw BkdhQț/x% f#iQ6l%q*M\jX k!9RQ*|9Am\Mjnڠ9es5b($^f -l+rOlqN`!^zrӡ7k=K :XcZ'!nEbHbءg*9JGF"WsL M˜3q Z+JrNGR -{Ȇ"؉`p5V"ȓ>$hQڣ50(,%DX2 Po9k '~>ՊWQM3ɢ",Npűm5Y-p΍ي+}Cdi +(L zMD[ 69C{ :&΅QpFFֆ_eQx^ilt}tzp[ +ۿQ|*y&ۍSbڱ_q" :׆dZbwv@TnjM'N7oJWP=J+gGMU Avoo#7;E6_N,`amBhb l-v<&E+6KrEDh j!в&hjPXP6 %<5? $ @vJd h~h G8?<™PY1 P?5hӣm`d>Ar,*jû)5 +!C{ K8|UrK€ )xam. oF+=|o;NWqH5(c>V &iZ"L  PKXATGDtc\nґ%M J!΋g\58#CeH±8`W@Y #,AONk~ABcUDR4Α,/: nTiJoRl FM T"K?LvְߗACPe-N֋@9D/Tϊ~>{6#(ObY#)Y|b^}7EX2m`_ˆWf[*[69x,QH|qRJ]/] O bDܢTj% 80jfp(;qR-I~|j(})"O&ȝoJ!!l\oopf']X'6 @02svȊ/$PJdiAwf ;'þ Cb-`q$RnP$LrCxoXg. ܇2rkB<rQ◭Ce5gxHdS/bi\4eB. pe]fgAA ,1ÀÅgℕ>x5).id?K^Nr"Y㴒=۬pQ38M!l(D 8ZϨFXє8Axs)80tޱG iK +V;oab"R:8<Mo.  C%˼V䫀)TKYg1`nv/`luI w/a5쾘] +EN)C-ޞsk`t_ /\` 4A]l[ܸ[M1inyI+}7)Ha4V.ԝ"x  ‘8 @ +Wb a(e~8gd5NËW#k\&M$̯nFIζ $mq\F4∈ԤW5BD"O U8VrAR&PZ. "$*I.i_.#$9UR@!e,ߍ$p1P Dd<2&sֲ㑕=|;y+i %&u{܆(gE'^V%QIծ”9.O#!U)s'!nHF9BY$ypDf6(9Ӻ$Fd&E9ќFx$ZNKA*r^˿/2q$t @b&\8tl9WhO?Sf`F#Ant ڞ,HH9~g5ĭ +cl,J;7 8k:9t$M5'+v ‘WS>r!}Z ]3]\)FGr)I*/8$ +jpߚ2Db("/r +RF4E7|O'a&NxR(SO#$Dn@/qE8 T+%A5."yPrϢNQc)mmqn3u f'FcIhHИu!O#HzdS < +t y9N>ۉ:Dr콅+GW mZa=Alx``IDr. ="BC!Bf9^ ~uoJ`CK k;9w{!:%"3gnuM[#xۍᥖpcv3 aV܄Oy 9W`4cSOMۊk#p~8g \~$̀Q2{C Tr)F/a8=\j)e)fOJ@RdFu9yIj.r--j9i'̓:HFmZJkN6[1>lp~9'ۓLfu" \η)mɄz"{ u.'vg+8\7Ha 7w# @,)8aGÙ=I_ 1 Z+[O+aĈw c=v<a#t$ƙ:‡%Vm@uPK!ƕu +'  8NxdiDݸwpsvrw(T;9*GqN +sHp;9 tH*Hgu#\?+wB;{wx÷{UWÃoǂx@ە< jy0]m~C=VEq,lگFkG6 +&s)u&Pc]yTSwއsm7.6~=.x۵?Xf~ۮ2mUma[CU@xګ£Y W CaCE۬ ҮXjVeq 堽 +!0ķY#b$T h;B~ǡ=ۭvSP9D\ڪ"ڪ~f/sD_9*"Q:"&J"Z"DQH:h.Zv늤uQmȝmWqք6닰a +#No.28IFfIFNnfb2[uYsry{PJsw:ϭ̌^k6}OE9Jֹ-h$>至ψ +؝fVuAW#6* %ť|~=h9f.@Tpd,pJI~0i%^B/ et:EʇRDQ=RI,Y$V8$7h^%ƬAbC(~Gև|C[Z X_1Hr&iq:"CCN2+lt@@ 4xڇQ KPx=bplx|*I#w>%ɕwƀgrDhv]UFu3''2/'V Q/yöȈdeϜO%4w\(Lு,i$%SȫQA UJM\14Brܺ]@c".gqXNj;q{wa6K;eE[m5O4>$H{ETJAR+Rkɍe2)$F(.3eUJdr'6qrw +}e-q3Ʀ!k)VS+)Ғ 瞣@&}l0 :XeAZDsADMT dr`VC9Bii nELJb V%կN:xm\ĞJiXڤ%^(˅3ۢHczc7*7[38HR{fT![\&B҃\!&#+Rρ0z\\m[6tfx9V2K0 pv~ r$!nhXRU@yX 띵Ս$ƺlUled+wһ!.VD0ЌV1pr 80S ]dž.oM@J8Zw8ѐ du,O$q-,"$" # mF-b(-|tP C`Y?_s9&ݍ_~{?owOn̖B;\ucո /!ǽ{m'kgPe_t8"^7_'@}R={wqڇ?~OmB3D i$ ߎ]w(Ƃ{9Oc[h8xv4?sԋ,zw/1ק#ǯ^_ۓӗ͏ˏǯ_?_o>.! x7P3 @8#dʋZ6._&&,U4Z +!e@-ZșՅ +#PdX&Ud|hmL %rbc*?g<xy)Ya:ùA'-kJq%^hD{44(^}9Q1jpb¼7xme:Za9dQ(3'j^V&+=]{=ҕtzRӤcrX΋7 +. jBM.02R4!:CUd6,P]zfrҶ3TpV|H/؂'N\q{'AePؤPtez݀F:Ob;%瀳US:gMjD|"LYMJdN_'wbطx}=.F3w;(B89ՏU[OsWKS%ﶖ/dJtEkpJ/A P8zdj:H)`G D0#:3dMhd[裐\-mH;28c}LAxs}kweqwú-Y:Vk؂Yۀfç-'/8u%lk߹~JX &䔬m5 ֖l)u-[caP v` +p-`ߟgclON%v"/dSE 9oN'<{Xʖ0C?4xW +7dmpƗ'X>[[T0z+]HGJ3%*l@-ad}:V +kh͹#l3@ X]^}$*޸Jz+@_E_ڋ FYT :/eJ߱f`Ѕ6-E}'{V޹ʳ*~j|qѴZ$6#A+c|uБ!gE7sz<3ױq.p9釥$x|Le\}/ސr'^s?/[8v{<"/ Nz]4gOU]Mѭw`%IQLZ2pjPrt)n:U:G0ב]JД0j]J\} rDpJ\9xտ@ȏ*˭SBUQw'N* s>lMk5 6زPD5Z"MuG]ya8}P]m;W΂ w*XOb(jb]@?nXd4"w2.Bhi`B1A1X1E-Lɡ򚧌:PCYnmh +j;h,5U ݂7D1$ڼ}ǾO&ˇ!<~K _W7dK  Oj3xmm%簠, xɖ;*ԓxRbyvjfIk`qNMu1 Q)q0(&*)gcx¬s76 )K0#Uj#j V'B^M4+NưXJpćJp0Cܧ{s| #xcoǧ 9k`. (ef)n +v핍HXf=KfV&haA[<}̑]ϹGN8\GH<)H0^ ڜj'+7ڠ2&=X<و԰(u :8C!c2W7d3ը xK1~㷘'lo1v#)yЇ₇6KJjVϢcG+UM:IȉN ryh&ƝذpkS7Ji-P4.@9T 'Ç :x~w#Ts޸!s[O .{9~9^}@EX0-0 z=G=}>K?tu +:;I X) "(L0p<2]5naNIpV[T߇.?z:X+7ꍨyai<@3+ Jw +5n4R8 c>xoưVta%1F +B|h$4 Z6HbU# %لwiln{U<R Qr o#C(6lbxzw>RW# %y1-X:݉w(=;8"ڤKGA 0K@U!ѵ4؟! eKCV#PF =ñ4Ej4$ o)ă: HGh Js:WpN:h`5DLRKg?\89ˢ@EBe6)-ȅH +/ӡՇT!= S~0qׄnnC1-%KIKFg\zQ$e{ĵGX#>E؛h[mbM-cv=0Wy ,T +\ķUb@MMbżVeʝSquF؈r!!HGKB { FG +7>H7-kbO%R&% +nA7+:i18FSbLο܂ޏd| 6T\*.`ZA&H<1#O5| ŷhrbd@/K.L"#bQ:}|s/J5W}0 M91}]|=}rIr$b֖qP6Jb6QiAY +k5EQYu6ʼe773Iem3 {g RdH%&LýIuI)Ydm 2v1J}3>ta2>s`vXCq= +:QPH+C>-Z1,އ+V?4DŝWg"0f>b T܃܊rYayYD<"2ܢ6d&&="g9.DRo@U_#*5ʺ3u5 BQZNKyD?_ls+:1?<]z;cޕ!\K:YOg;">S3ڔ܌a@Jh9"=lA-7%Ljj9`J@<<$*53?pwݢpo<:IDTņ=+uDʛ6#w1T18!P%eMמ,P*xmYmnYIꘆ +hJYMd: luU$V\veI.DTB&x#yЭsk|lY2W]U=uaَAƲ CoM.Qy +ԑ-^XȃΊx*0[/2Lg ͯJ5ziI E8r;H;I;}gs +R$gj8sH{!p4j,]t ݉؞/&Yܪ,ƽ !n"{) ^K3=zcPRŸmb(8'7 ^ B,XuQO@oYGSd}Z*#N>OsZ/qMWŘr"&zV+U +6 l +amBhuk+1e[ ]x#8(}iİ @dc3 lsTuéY?#o>҆rJDI!8vÜT*Rnx'#@m$qԟlj00XˤݦUT$pӥc ypOf4RUl*l0l뷯K&o2aͩ|qb$ qeуzA%MgZF|@Y[PE8p1EJ#(JIZ-d[7b_)s* 4Is%Û@nFAZXLr'HRfėCjt٫[u!G +rФ,Q'To +ļn##/pYFr{nAF,.֊xlB|ZO+iW/ӥn5[8O1<4JtKͶH'P2LaVXtKUupj;2HGmjEijt!39[^ ꐅ4;o$vgJҚܙ*TG'&c΍"RSQG4 JRvtOɳ(Aȝƚ$<"߲vƒ`O(${Qluܨ^zy Bl)UŒҁgRi76f``|JsNmr*?rM&~ 0 (O8>i3| @{_35 f,tvh72A$z3o<9 r$!nkC?G hc B=E'/J4= +mc<[j[.t\" ilCgM h$Ш|&"(XXAM H}` q/W/#Rd{wAgڥm32fZyo<]J~~sg:m~_mP;0#4i*BUO&&9Њ|@o]mhK,GHLIr B^r4J{wB9ζ 4(9NJV4H|lGAV9u" fOqsxYfQNOE[(F=eR0:;3UjHu#, I&M0ʈ%j'.';@,{'YI>3 +r&ϴd&##X)ګIYdVe,,Xjs:~|}}߳s?(B[\`3xKqocTI>B~)u {[ ؿF޵3I+zf="@ AB+>er2MR, LY%dsdv5e"Ǽ;+L)%kEd +ŠQ#>s_I]܅]o%Cr7d=yKzibgB\Uے0@˩fLo| ܴ4hf^Pl%tc)9Q2QUsǎcI2H nA{3*TbdJ*E;o)yдf0H.V2q3!-4q D31zIj_xCн8q : 4JZ4I&ezx!~$] )N2zGV]PY .g>Xe EاT}҇mWi>[GX`o8z%Dy}&FJ'97Yr ܤKhZOWOZRÎQɝm];+^F|kP#$7AܭT.~ Ni3=:\gr̍*e )idnOGђ|J Tрit?ie5\W-Y rj[NGec/ mg8E+^$_:x]j?<ԏE6 GCNrN\?][:@QN +=- #vȁ7dp?j`uzYv xTFNb.̢ވ Am]g!wA_LUO`)A!˪=*E`=c8x߻]:7w %c tpl2YtHXk`ċDwK'o.6F|78eտ[X)\y-j E !,d39!.,ڙTjwO}U`Gcy0gL}YC·Ov<ױC N>?v%/:X>]j RpTzλihbL3(웈F,ڣ!V{?[ q>|?>n?S}W\cr"8њij5ԝ&kIy[S[]NS?\w&BnKו=@Ǚ!SjEWΆ?H%2NZhWؔMcW9/JY' HZL5x*PT}æbRbᐕ!kp/eRf##4>p:?^ V i3`s#Mb*)f?Hy [}W >P-7Vdн /TWO3-XS_,:Qu150-9(Ǻ{z'a.وdrdE,E\3 ig'I>AB0G > qi_㹅tJN=}ac{Pn]r^ wsWgΛ" +fLLyl 1l];Fy|ûÔ/$K +[358arz&OΣ:[Lf]s|_yCܐԩ/khxz]4e&aS[=jL#P0r e6qD!YLdR'Nm6It ooGKfڌy 7-OD˂Sy20S['C `Bt@oh akۇ:ypݏ@}`IWdwSXj񩾮uZsY-s̪ep<(Tke\FI<1W!Ys4]X%,9 +& 6(eAKR +#MLOSu[Ie!20 ruQodr/6|KLAJxݮ<H1鷋Z]{9NW?W\kU]J F@êF!ҲZçKQKH eNUƼe +3}ty)mÊ*  rm9b$CHn))ŔSt ?v3H0'{@&dsf6B`W u, `7bFD +GX91Lqзh#j\X&K" ҨKAːGNC\l5ߩGòΨըH}s/:{!o=˶ 5.V-J.m\ 4|]hnH#'FnfL L86 TLi(%j4"<"NE2^IH͔҉qÍFa̓.&)j}\i$! >S$ 'Mܰh`Fz%'5.[Ew ]m#èD=)榶8Ùw#gѨ@AZg\ₑ1VqLzRQ/zRo*qα8l'gB6JM\32L)3*F&FQ|Q3BF$Gpٙ59QwD MϊByN B1suE\3)Bl&&]Ln䴤g__%Gn + <ĺpn-ե l!0G#w/7κN>^h=8\ǒָ!kϽչҶucUxKN0/9Og:m'j]lm/jEecD~H՛\ ZȓI 5K"u$dHJݫfRq !G֝ӹ'-7Bx EC]፩K~EF3Gz?ɩyMH9m\X?&xqf:}\v]ckfRy}m!/[ν]I&dmdU!hyVړ+%ݒU-Er'S5b/[yiY_)y9nITIcejFL}viç>H_jY XC5j.FQE HGhZ aۄŘ$p{oDbIO3 5ԭ>ns WKpX3*}`27K剙*I%?rFӘ)V$N2qTg +Eh +OD0ѡ]!>`[%[F! +H"hnӝ27}FDx)G6Qϐj2aD2]u.&zjWC>ca=J*Ĉ"s‹hf#DO^+X)=6?#2—8†a5; 8{tH37?Y2b/=h~4P'S/)_XGY @L`Q\#%Lj>kQ`s1i8 |{B`[x|mHX͔BgqR)LYaH&eNm J/ubA$q>aEn +aqu x~0y2@/V^L[}jM"(h+#` ʸpv80cue -?8b @XT m&'Ld}Bg4Ҁ=WbgK('jInfIAXhe98y Xj:MaQio;SSXnt՘|@!e&By)n7Wg67%3>Ӎ ZOR5#A]? DgwM(4Odk ֩p|:T06tPWe:xwQ1bDnFkN:8\n1)ES$F@FNupp \DBL%+Gj@p'c;noۋH< āxol*V1xQ r3Q#`_3+շ8=m!}g*3J Rn(9\by.Ҳ~7q)2MUb9j&]" 7uVڒMeTȬ YN_mw.Ln&Աtm" "Mkqc@duE*S(a҂~oFZQ9J68_S'~J-Wa,k\ҥv`͏/%2@ܷ» +x4z2i<{ⴓDf6vSLQv dĮGrvKUȳ! PNŊv~X0k# '1m-ki2K[Ddͥ^G[[8[ݗ-/SL8`zUQEW4A}MYEYcfU=iރ'=ҼY~~lYojP.:ׇ3텻cEW7dkϗI&'(RFv;7~fYD[ wkD|tf~`|q=ۅ3Dߗ 9ٝ5T2SKMjfΉܕ7Bg^_ 6lθjfD{oxT;C{ir@ػ 5шFShVvWj}3r\V6  /xPǔwh yݧBBo<֒lD!:#Ʌu`hL$nsɱš73 fƼN]F +X\ Ux8FJu!54QQQnCz[F|1=+Au0ϑo܂wmqAîHPvܴ4z\*B6> +Q#l#!sl +9V `#w2,fO4ou4XմC٬EX 4SP8T:yt|7.C\Jkky QzT6Fڹ6y46F0 "[[Ѹ0ЭDOly/v:]!SPiPUc̫ *-E AXY;%@JRFnt8*8-C6zɪO @ӧlkM#dQXhpUͫeX THԊa3{5TگfZ0H}-3=Q|.%/ +L`Esf<,SFv:l{ r,^XvPhL˰R/SglgAT3d]gw]0&8,m(=2G,>lլ^u6!WI +YXgV 8S0pG#FmtD&*R.* Ai:kvbeQoK?SQp{TNJ 2['sW/І#sNs|4^C FE#D76,:f8/сPX\Α(k6ؼ{)Vb8#{n0kFK-3l80 +}b-yJ$qofK]s!'.x̘.`0!'[ua:] %/.bEy7 +Խ-d0SE,-l3%LRx %(.Ex1]&!XGX +C^)So `]V*`kw-SXի *xܢ~6 +Jb\γ`j\ 6O=`ò8Pf@tsWiЭl-ϸ뜶 .ױ{>RU3 `Ne_@WSS-jtWx?`me%본'iad2$7;l-tۀ:xp^,*nI+]z7/`df",|>wƦT\kdx,}`? Y]^OȘ + XWOQ#(*9?3wԁ^{IFA`\(f% @̬\fvfktJ#~| +ŎT1U=iBh &NV4B^SYjqZ +;C^)(Õs] pDKGv5ZOHCTRa YNyx+[7vQKz2C ;^['#cM[U}e꫰Sc,H>&7u/-cC Z`'< ,N31L(yHH!ΪW Ż)J؁+ԯf<,=ɥt> ߤ@,/}ï4~=*ۜfX;I4O[ڼ0mpq('Z5=$r~!Oi7l֒8j[m, ہ./ +e*7xe`{ғfR$xN)Gdx(Rb@県kJ҆.,=Yt~ SŅF aNUx}^EIwHŤne"^t%^r}Sx?2Q_e )d8T(L,s8˅?5u}H/fxM\ s~㠸Cuy\J`_sA>> ??|_?_?}ǟ~_O?킿?iA@ +endstream endobj 310 0 obj [/ICCBased 359 0 R] endobj 5 0 obj <> endobj 6 0 obj <> endobj 7 0 obj <> endobj 8 0 obj <> endobj 40 0 obj <> endobj 41 0 obj <> endobj 42 0 obj <> endobj 43 0 obj <> endobj 44 0 obj <> endobj 78 0 obj <> endobj 79 0 obj <> endobj 80 0 obj <> endobj 81 0 obj <> endobj 82 0 obj <> endobj 116 0 obj <> endobj 117 0 obj <> endobj 118 0 obj <> endobj 119 0 obj <> endobj 120 0 obj <> endobj 121 0 obj <> endobj 239 0 obj [/View/Design] endobj 240 0 obj <>>> endobj 237 0 obj [/View/Design] endobj 238 0 obj <>>> endobj 235 0 obj [/View/Design] endobj 236 0 obj <>>> endobj 233 0 obj [/View/Design] endobj 234 0 obj <>>> endobj 231 0 obj [/View/Design] endobj 232 0 obj <>>> endobj 229 0 obj [/View/Design] endobj 230 0 obj <>>> endobj 107 0 obj [/View/Design] endobj 108 0 obj <>>> endobj 105 0 obj [/View/Design] endobj 106 0 obj <>>> endobj 103 0 obj [/View/Design] endobj 104 0 obj <>>> endobj 101 0 obj [/View/Design] endobj 102 0 obj <>>> endobj 99 0 obj [/View/Design] endobj 100 0 obj <>>> endobj 69 0 obj [/View/Design] endobj 70 0 obj <>>> endobj 67 0 obj [/View/Design] endobj 68 0 obj <>>> endobj 65 0 obj [/View/Design] endobj 66 0 obj <>>> endobj 63 0 obj [/View/Design] endobj 64 0 obj <>>> endobj 61 0 obj [/View/Design] endobj 62 0 obj <>>> endobj 31 0 obj [/View/Design] endobj 32 0 obj <>>> endobj 29 0 obj [/View/Design] endobj 30 0 obj <>>> endobj 27 0 obj [/View/Design] endobj 28 0 obj <>>> endobj 25 0 obj [/View/Design] endobj 26 0 obj <>>> endobj 254 0 obj [253 0 R 252 0 R 251 0 R 250 0 R 249 0 R 248 0 R] endobj 378 0 obj <> endobj xref +0 379 +0000000004 65535 f +0000000016 00000 n +0000000519 00000 n +0000062698 00000 n +0000000009 00000 f +0000279686 00000 n +0000279757 00000 n +0000279828 00000 n +0000279907 00000 n +0000000010 00000 f +0000000011 00000 f +0000000012 00000 f +0000000013 00000 f +0000000014 00000 f +0000000015 00000 f +0000000016 00000 f +0000000017 00000 f +0000000018 00000 f +0000000019 00000 f +0000000020 00000 f +0000000021 00000 f +0000000022 00000 f +0000000023 00000 f +0000000024 00000 f +0000000033 00000 f +0000283406 00000 n +0000283437 00000 n +0000283290 00000 n +0000283321 00000 n +0000283174 00000 n +0000283205 00000 n +0000283058 00000 n +0000283089 00000 n +0000000034 00000 f +0000000035 00000 f +0000000036 00000 f +0000000037 00000 f +0000000038 00000 f +0000000039 00000 f +0000000045 00000 f +0000279978 00000 n +0000280050 00000 n +0000280122 00000 n +0000280194 00000 n +0000280274 00000 n +0000000046 00000 f +0000000047 00000 f +0000000048 00000 f +0000000049 00000 f +0000000050 00000 f +0000000051 00000 f +0000000052 00000 f +0000000053 00000 f +0000000054 00000 f +0000000055 00000 f +0000000056 00000 f +0000000057 00000 f +0000000058 00000 f +0000000059 00000 f +0000000060 00000 f +0000000071 00000 f +0000282942 00000 n +0000282973 00000 n +0000282826 00000 n +0000282857 00000 n +0000282710 00000 n +0000282741 00000 n +0000282594 00000 n +0000282625 00000 n +0000282478 00000 n +0000282509 00000 n +0000000072 00000 f +0000000073 00000 f +0000000074 00000 f +0000000075 00000 f +0000000076 00000 f +0000000077 00000 f +0000000083 00000 f +0000280346 00000 n +0000280419 00000 n +0000280493 00000 n +0000280567 00000 n +0000280649 00000 n +0000000084 00000 f +0000000085 00000 f +0000000086 00000 f +0000000087 00000 f +0000000088 00000 f +0000000089 00000 f +0000000090 00000 f +0000000091 00000 f +0000000092 00000 f +0000000093 00000 f +0000000094 00000 f +0000000095 00000 f +0000000096 00000 f +0000000097 00000 f +0000000098 00000 f +0000000109 00000 f +0000282361 00000 n +0000282392 00000 n +0000282243 00000 n +0000282275 00000 n +0000282125 00000 n +0000282157 00000 n +0000282007 00000 n +0000282039 00000 n +0000281889 00000 n +0000281921 00000 n +0000000110 00000 f +0000000111 00000 f +0000000112 00000 f +0000000113 00000 f +0000000114 00000 f +0000000115 00000 f +0000000123 00000 f +0000280723 00000 n +0000280798 00000 n +0000280873 00000 n +0000280948 00000 n +0000281031 00000 n +0000281106 00000 n +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000281771 00000 n +0000281803 00000 n +0000281653 00000 n +0000281685 00000 n +0000281535 00000 n +0000281567 00000 n +0000281417 00000 n +0000281449 00000 n +0000281299 00000 n +0000281331 00000 n +0000281181 00000 n +0000281213 00000 n +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000114144 00000 n +0000114219 00000 n +0000114294 00000 n +0000114369 00000 n +0000114452 00000 n +0000114527 00000 n +0000283522 00000 n +0000062751 00000 n +0000063820 00000 n +0000067964 00000 n +0000115558 00000 n +0000108577 00000 n +0000108463 00000 n +0000115310 00000 n +0000115434 00000 n +0000069845 00000 n +0000075670 00000 n +0000076229 00000 n +0000077032 00000 n +0000077681 00000 n +0000078575 00000 n +0000080677 00000 n +0000081646 00000 n +0000082398 00000 n +0000082862 00000 n +0000083507 00000 n +0000084059 00000 n +0000084520 00000 n +0000085077 00000 n +0000085533 00000 n +0000086258 00000 n +0000086919 00000 n +0000087651 00000 n +0000088110 00000 n +0000088571 00000 n +0000089026 00000 n +0000089490 00000 n +0000090119 00000 n +0000090810 00000 n +0000091270 00000 n +0000092233 00000 n +0000092690 00000 n +0000093311 00000 n +0000093976 00000 n +0000094598 00000 n +0000097385 00000 n +0000098187 00000 n +0000098642 00000 n +0000099085 00000 n +0000099529 00000 n +0000099989 00000 n +0000100431 00000 n +0000101075 00000 n +0000102825 00000 n +0000103895 00000 n +0000104358 00000 n +0000104957 00000 n +0000105416 00000 n +0000105877 00000 n +0000106337 00000 n +0000107745 00000 n +0000068028 00000 n +0000279649 00000 n +0000069280 00000 n +0000069330 00000 n +0000114080 00000 n +0000114016 00000 n +0000113952 00000 n +0000113888 00000 n +0000113824 00000 n +0000113760 00000 n +0000113696 00000 n +0000113632 00000 n +0000113568 00000 n +0000113504 00000 n +0000113440 00000 n +0000113376 00000 n +0000113312 00000 n +0000113248 00000 n +0000113184 00000 n +0000113120 00000 n +0000113056 00000 n +0000112992 00000 n +0000112928 00000 n +0000112864 00000 n +0000112800 00000 n +0000112736 00000 n +0000112672 00000 n +0000112608 00000 n +0000112544 00000 n +0000112480 00000 n +0000112416 00000 n +0000112352 00000 n +0000112288 00000 n +0000112224 00000 n +0000112160 00000 n +0000112096 00000 n +0000112032 00000 n +0000111968 00000 n +0000111904 00000 n +0000111840 00000 n +0000111776 00000 n +0000111712 00000 n +0000111648 00000 n +0000111584 00000 n +0000111520 00000 n +0000111456 00000 n +0000111392 00000 n +0000111328 00000 n +0000111264 00000 n +0000108399 00000 n +0000108614 00000 n +0000115192 00000 n +0000115224 00000 n +0000115074 00000 n +0000115106 00000 n +0000114956 00000 n +0000114988 00000 n +0000114838 00000 n +0000114870 00000 n +0000114720 00000 n +0000114752 00000 n +0000114602 00000 n +0000114634 00000 n +0000115634 00000 n +0000115858 00000 n +0000116945 00000 n +0000122675 00000 n +0000188265 00000 n +0000253855 00000 n +0000283589 00000 n +trailer +<]>> +startxref +283764 +%%EOF +%BeginExifToolUpdate +2 0 obj +<< +/Type /Metadata +/Subtype /XML +/Length 48004 +>> +stream + + + + + + application/pdf + + + Impression + + + + + + f7e5a792-6b1d-4abe-b315-5697afa4843c + + + + Print + Document + + + + Adobe PDF library 9.00 + + + + 2009-09-03T12:21:23+02:00 + Adobe Illustrator CS4 + 2009-11-27T16:58:06+01:00 + 2009-11-27T16:58:06+01:00 + + + + JPEG + 152 + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAmAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9PtqlgGI9UMo2eRQzRqf BpFBRfpOKpfJEJ/9KnFvLE7PxmkjE6KgYhKEOvEFQCffqcVVhay2lGjZIENPjhWkNT05wkmn+srD 3xVH2tx68Ak48Wqyuta0ZGKsK9/iBxVVxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2K uxV2KuxV2KuxV2KuxV2KuxV2KoJGaxX0jG72q/3LxqXKL2RlWrGnRSB06+JVcts3Ez2bmB5CXZHQ 8GJNavGeLBvkQfGuKpeLVZrhrZXitWYH95BGsUpI60VnfkK/zoVNDtiqNsY306JLWZjLDyPC6PUs 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 + 256 + + + + + + + + xmp.did:264F4C2C7BBDD6119286AC08E167C812 + uuid:68dafb6c-2481-0c47-a84f-3e384d157169 + uuid:5D20892493BFDB11914A8590D31508C8 + proof:pdf + + xmp.did:EEB21D887398DE11A18DE4B176236835 + + + + converted + from application/pdf to <unknown> + + + saved + + + / + + + xmp.iid:D27F11740720681191099C3B601C4548 + Adobe Illustrator CS4 + 2008-04-17T14:19:15+05:30 + + + converted + from application/pdf to <unknown> + + + converted + from application/pdf to <unknown> + + + saved + + + / + + + xmp.iid:F97F1174072068118D4ED246B3ADB1C6 + Adobe Illustrator CS4 + 2008-05-15T16:23:06-07:00 + + + saved + + + / + + + xmp.iid:FA7F1174072068118D4ED246B3ADB1C6 + Adobe Illustrator CS4 + 2008-05-15T17:10:45-07:00 + + + saved + + + / + + + xmp.iid:EF7F117407206811A46CA4519D24356B + Adobe Illustrator CS4 + 2008-05-15T22:53:33-07:00 + + + saved + + + / + + + xmp.iid:F07F117407206811A46CA4519D24356B + Adobe Illustrator CS4 + 2008-05-15T23:07:07-07:00 + + + saved + + + / + + + xmp.iid:F77F117407206811BDDDFD38D0CF24DD + Adobe Illustrator CS4 + 2008-05-16T10:35:43-07:00 + + + converted + from application/pdf to <unknown> + + + saved + + + / + + + xmp.iid:F97F117407206811BDDDFD38D0CF24DD + Adobe Illustrator CS4 + 2008-05-16T10:40:59-07:00 + + + converted + from application/vnd.adobe.illustrator to <unknown> + + + saved + + + / + + + xmp.iid:FA7F117407206811BDDDFD38D0CF24DD + Adobe Illustrator CS4 + 2008-05-16T11:26:55-07:00 + + + saved + + + / + + + xmp.iid:FB7F117407206811BDDDFD38D0CF24DD + Adobe Illustrator CS4 + 2008-05-16T11:29:01-07:00 + + + saved + + + / + + + xmp.iid:FC7F117407206811BDDDFD38D0CF24DD + Adobe Illustrator CS4 + 2008-05-16T11:29:20-07:00 + + + saved + + + / + + + xmp.iid:FD7F117407206811BDDDFD38D0CF24DD + Adobe Illustrator CS4 + 2008-05-16T11:30:54-07:00 + + + saved + + + / + + + xmp.iid:FE7F117407206811BDDDFD38D0CF24DD + Adobe Illustrator CS4 + 2008-05-16T11:31:22-07:00 + + + saved + + + / + + + xmp.iid:B233668C16206811BDDDFD38D0CF24DD + Adobe Illustrator CS4 + 2008-05-16T12:23:46-07:00 + + + saved + + + / + + + xmp.iid:B333668C16206811BDDDFD38D0CF24DD + Adobe Illustrator CS4 + 2008-05-16T13:27:54-07:00 + + + saved + + + / + + + xmp.iid:B433668C16206811BDDDFD38D0CF24DD + Adobe Illustrator CS4 + 2008-05-16T13:46:13-07:00 + + + saved + + + / + + + xmp.iid:F77F11740720681197C1BF14D1759E83 + Adobe Illustrator CS4 + 2008-05-16T15:47:57-07:00 + + + saved + + + / + + + xmp.iid:F87F11740720681197C1BF14D1759E83 + Adobe Illustrator CS4 + 2008-05-16T15:51:06-07:00 + + + saved + + + / + + + xmp.iid:F97F11740720681197C1BF14D1759E83 + Adobe Illustrator CS4 + 2008-05-16T15:52:22-07:00 + + + converted + from application/vnd.adobe.illustrator to application/vnd.adobe.illustrator + + + saved + + + / + + + xmp.iid:FA7F117407206811B628E3BF27C8C41B + Adobe Illustrator CS4 + 2008-05-22T13:28:01-07:00 + + + converted + from application/vnd.adobe.illustrator to application/vnd.adobe.illustrator + + + saved + + + / + + + xmp.iid:FF7F117407206811B628E3BF27C8C41B + Adobe Illustrator CS4 + 2008-05-22T16:23:53-07:00 + + + converted + from application/vnd.adobe.illustrator to application/vnd.adobe.illustrator + + + saved + + + / + + + xmp.iid:07C3BD25102DDD1181B594070CEB88D9 + Adobe Illustrator CS4 + 2008-05-28T16:45:26-07:00 + + + converted + from application/vnd.adobe.illustrator to application/vnd.adobe.illustrator + + + saved + + + / + + + xmp.iid:F87F1174072068119098B097FDA39BEF + Adobe Illustrator CS4 + 2008-06-02T13:25:25-07:00 + + + saved + + + / + + + xmp.iid:F77F117407206811BB1DBF8F242B6F84 + Adobe Illustrator CS4 + 2008-06-09T14:58:36-07:00 + + + saved + + + / + + + xmp.iid:F97F117407206811ACAFB8DA80854E76 + Adobe Illustrator CS4 + 2008-06-11T14:31:27-07:00 + + + saved + + + / + + + xmp.iid:0180117407206811834383CD3A8D2303 + Adobe Illustrator CS4 + 2008-06-11T22:37:35-07:00 + + + saved + + + / + + + xmp.iid:01E540664A3DDD11BD33D3EB8D3A1068 + Adobe Illustrator CS4 + 2008-06-18T22:24:01+07:00 + + + saved + + + / + + + xmp.iid:6B6AE2A5723EDD11A6F1BABF7C5A7A51 + Adobe Illustrator CS4 + 2008-06-19T20:30:34-07:00 + + + saved + + + / + + + xmp.iid:BEE4CD276D42DD11A0BEF9B17C50C624 + Adobe Illustrator CS4 + 2008-06-25T07:44:32+02:00 + + + saved + + + / + + + xmp.iid:BFE4CD276D42DD11A0BEF9B17C50C624 + Adobe Illustrator CS4 + 2008-06-25T07:47:41+02:00 + + + saved + + + / + + + xmp.iid:0730D8D3F647DD11AA8EC5F1D0957DC6 + Adobe Illustrator CS4 + 2008-07-02T12:22:13+07:00 + + + saved + + + / + + + xmp.iid:FA7F117407206811ACAF9F0F41229DDF + Adobe Illustrator CS4 + 2008-07-21T13:59:24+05:30 + + + saved + / + xmp.iid:254F4C2C7BBDD6119286AC08E167C812 + Adobe Illustrator CS4 + 2002-09-01T14:20:33+07:00 + + + saved + / + xmp.iid:264F4C2C7BBDD6119286AC08E167C812 + Adobe Illustrator CS4 + 2002-09-01T14:21:03+07:00 + + + saved + / + xmp.iid:EEB21D887398DE11A18DE4B176236835 + Adobe Illustrator CS4 + 2009-09-03T12:21:24+02:00 + + + + uuid:92a05eaf-880e-4039-9d18-af54f0b22fb2 + + + + + + + EmbedByReference + + \\darkvador\VP_office\Ventes-VP\VENTES 2009\3-Jul-Sep\CHICCO17\Créations\1 Sources\Docs\2191.jpg + + + + + uuid:5D20892493BFDB11914A8590D31508C8 + proof:pdf + + + + False + True + + 296.999959 + Millimeters + 210.001652 + + 1 + + + Cyan + Magenta + Yellow + Black + + + + + + + + + 255 + 255 + RGB + 255 + Blanc + PROCESS + + + 27 + 23 + RGB + 26 + Noir + PROCESS + + + 26 + 0 + RGB + 225 + CMJN Rouge + PROCESS + + + 0 + 236 + RGB + 255 + CMJN Jaune + PROCESS + + + 54 + 143 + RGB + 0 + CMJN Vert + PROCESS + + + 223 + 157 + RGB + 0 + CMJN Cyan + PROCESS + + + 130 + 41 + RGB + 23 + CMJN Bleu + PROCESS + + + 122 + 0 + RGB + 225 + CMJN Magenta + PROCESS + + + 38 + 9 + RGB + 189 + C=15 M=100 J=90 N=10 + PROCESS + + + 45 + 52 + RGB + 228 + C=0 M=90 J=85 N=0 + PROCESS + + + 30 + 81 + RGB + 230 + C=0 M=80 J=95 N=0 + PROCESS + + + 0 + 147 + RGB + 241 + C=0 M=50 J=100 N=0 + PROCESS + + + 52 + 178 + RGB + 247 + C=0 M=35 J=85 N=0 + PROCESS + + + 10 + 233 + RGB + 252 + C=5 M=0 J=90 N=0 + PROCESS + + + 0 + 218 + RGB + 222 + C=20 M=0 J=100 N=0 + PROCESS + + + 13 + 189 + RGB + 150 + C=50 M=0 J=100 N=0 + PROCESS + + + 43 + 165 + RGB + 64 + C=75 M=0 J=100 N=0 + PROCESS + + + 46 + 137 + RGB + 0 + C=85 M=10 J=100 N=10 + PROCESS + + + 46 + 99 + RGB + 0 + C=90 M=30 J=95 N=30 + PROCESS + + + 98 + 168 + RGB + 54 + C=75 M=0 J=75 N=0 + PROCESS + + + 149 + 159 + RGB + 0 + C=80 M=10 J=45 N=0 + PROCESS + + + 219 + 168 + RGB + 55 + C=70 M=15 J=0 N=0 + PROCESS + + + 179 + 113 + RGB + 10 + C=85 M=50 J=0 N=0 + PROCESS + + + 130 + 48 + RGB + 13 + C=100 M=95 J=5 N=0 + PROCESS + + + 91 + 35 + RGB + 19 + C=100 M=100 J=25 N=25 + PROCESS + + + 128 + 34 + RGB + 98 + C=75 M=100 J=0 N=0 + PROCESS + + + 126 + 17 + RGB + 146 + C=50 M=100 J=0 N=0 + PROCESS + + + 89 + 13 + RGB + 160 + C=35 M=100 J=35 N=10 + PROCESS + + + 80 + 0 + RGB + 212 + C=10 M=100 J=50 N=0 + PROCESS + + + 111 + 14 + RGB + 227 + C=0 M=95 J=20 N=0 + PROCESS + + + 156 + 186 + RGB + 202 + C=25 M=25 J=40 N=0 + PROCESS + + + 119 + 137 + RGB + 163 + C=40 M=45 J=50 N=5 + PROCESS + + + 84 + 104 + RGB + 120 + C=50 M=50 J=60 N=25 + PROCESS + + + 63 + 76 + RGB + 96 + C=55 M=60 J=65 N=40 + PROCESS + + + 100 + 157 + RGB + 200 + C=25 M=40 J=65 N=0 + PROCESS + + + 72 + 127 + RGB + 176 + C=30 M=50 J=75 N=10 + PROCESS + + + 54 + 94 + RGB + 144 + C=35 M=60 J=80 N=25 + PROCESS + + + 37 + 77 + RGB + 124 + C=40 M=65 J=90 N=35 + PROCESS + + + 21 + 58 + RGB + 103 + C=40 M=70 J=100 N=50 + PROCESS + + + 27 + 40 + RGB + 65 + C=50 M=70 J=80 N=70 + PROCESS + + + + Groupe de nuances par défaut + 0 + + + + + + 27 + 23 + RGB + 26 + C=0 M=0 J=0 N=100 + PROCESS + + + 63 + 61 + RGB + 61 + C=0 M=0 J=0 N=90 + PROCESS + + + 90 + 88 + RGB + 88 + C=0 M=0 J=0 N=80 + PROCESS + + + 114 + 112 + RGB + 111 + C=0 M=0 J=0 N=70 + PROCESS + + + 137 + 135 + RGB + 134 + C=0 M=0 J=0 N=60 + PROCESS + + + 158 + 156 + RGB + 155 + C=0 M=0 J=0 N=50 + PROCESS + + + 179 + 178 + RGB + 176 + C=0 M=0 J=0 N=40 + PROCESS + + + 200 + 198 + RGB + 197 + C=0 M=0 J=0 N=30 + PROCESS + + + 218 + 217 + RGB + 216 + C=0 M=0 J=0 N=20 + PROCESS + + + 237 + 236 + RGB + 236 + C=0 M=0 J=0 N=10 + PROCESS + + + 246 + 245 + RGB + 245 + C=0 M=0 J=0 N=5 + PROCESS + + + + Gris + 1 + + + + + + 26 + 0 + RGB + 225 + C=0 M=100 J=100 N=0 + PROCESS + + + 16 + 93 + RGB + 232 + C=0 M=75 J=100 N=0 + PROCESS + + + 0 + 221 + RGB + 255 + C=0 M=10 J=95 N=0 + PROCESS + + + 48 + 147 + RGB + 0 + C=85 M=10 J=100 N=0 + PROCESS + + + 138 + 55 + RGB + 0 + C=100 M=90 J=0 N=0 + PROCESS + + + 136 + 53 + RGB + 128 + C=60 M=90 J=0 N=0 + PROCESS + + + + Couleurs vives + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +endstream +endobj +xref +0 1 +0000000000 65535 f +2 1 +0000291526 00000 n +trailer +<< +/Size 379 +/Root 1 0 R +/Info 378 0 R +/ID [ <559643BA38127C49B804BCBDCCCABCB4> ] +/Prev 283764 +>> +%EndExifToolUpdate 291504 +startxref +339623 +%%EOF diff --git a/tests/testfiles/test009.TIFF b/tests/testfiles/test009.TIFF new file mode 100644 index 0000000000..6e9b9831d0 Binary files /dev/null and b/tests/testfiles/test009.TIFF differ diff --git a/tests/testfiles/test010.fla b/tests/testfiles/test010.fla new file mode 100644 index 0000000000..f6847913e5 Binary files /dev/null and b/tests/testfiles/test010.fla differ diff --git a/tests/testfiles/test011.swf b/tests/testfiles/test011.swf new file mode 100644 index 0000000000..3ffea0a35b Binary files /dev/null and b/tests/testfiles/test011.swf differ diff --git a/tests/testfiles/test012.wav b/tests/testfiles/test012.wav new file mode 100644 index 0000000000..8c8dde9aa9 Binary files /dev/null and b/tests/testfiles/test012.wav differ diff --git a/tests/testfiles/test013.ai b/tests/testfiles/test013.ai new file mode 100644 index 0000000000..a0f803600f --- /dev/null +++ b/tests/testfiles/test013.ai @@ -0,0 +1,8623 @@ +%PDF-1.5 % +1 0 obj <>/OCGs[15 0 R 37 0 R 59 0 R 81 0 R 103 0 R 125 0 R 147 0 R 169 0 R 191 0 R 213 0 R 235 0 R 258 0 R 280 0 R 302 0 R 324 0 R 346 0 R 368 0 R 390 0 R 411 0 R 433 0 R 455 0 R 477 0 R 499 0 R 521 0 R 543 0 R 566 0 R 588 0 R 611 0 R 633 0 R 655 0 R 677 0 R 699 0 R 721 0 R]>>/Type/Catalog>> endobj 732 0 obj <>stream + + + + + application/vnd.adobe.illustrator + + + Vector_plane + + + + + Adobe Illustrator CS3 + 2009-10-22T12:35:57-03:00 + 2009-10-23T13:07:52-03:00 + 2009-10-23T13:07:52-03:00 + + + + 256 + 184 + JPEG + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAuAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8AimFDsVdirsVdirsVdirs VdirsVdirsVRy6HrTRLMun3LQtE1wsghkKmFPtSA0pwXu3QYqv8A8O+YPrUVp+jLv61OnqQ2/oSe o6fzKnGpHuMVQ66bqLPwW1mL+o0PERtX1EFXSlPtKOo6jIHJEGiR/byZCJPRv9F6nSE/VJqXH+85 9N6SbV+Db4tvDIfmMe/qj6ee429/cnw5bbHfk1Jpuox09S1mSr+kOUbD95QHhuPtfENskMsDyI5X z6d/uQYEdG30rVEd43s51eNDLIhjcFYxWrkU2UUO+COfHIAiQIPLfn7knHIGiDsrS+X9ehNJtNuo z6RuKPBIv7kEAy7r9irD4umWsFh0XWRDbzmwuBBdsEtJTE/CVj0EbUo5PtirV1o+r2nqfWrG4t/S b05fVidOLlS/FuQFDxBanhiqDxV2KuxV2KuxV2KuxV2KuxV2KuxVR9XFXerirvV98Vd6vvirfq++ Ku9X3xV3q4q71cVd6uKu9XFXerirvVxV6Fc+c7MC9CzUUJpUICylxItueUqovRVC/Cyrtt4nCqaX HnW0kuJYTeKVnW6aNxeMHPrTQy0Nxx/cKyxUCDpSnhiqTL5ktr3Xbi6muoUkjv3ug1eMbxtF6R4s 1KkUHz65rdThPGZAXxCI/wBLIn9Lm4JR4aJquI/MV+hXl802xi4pdxq1yrC3laYs0chtWiRqUHoK jNQ9z1zFx6WQFkfTXToJX/nd/wBzfPJEnYje+vl9iHt/MZi+qpPqiTmzuoZLt+SqJF9NEXjQ/vfS YUJ9uWWnTbyIhXFH9JNfHu5MBkGw4uUv0BCXOuPDocltJqKzXvCKN545PVYg3DyceVauAu57ffk8 OA+Nx1UfV90B+gsMsx4fDdy2/wB9+sMjn822kkt1dfpGOPWL+1urWK4iuZPRKyem6yMJGP1ZmZPs g0zZuCv0zzxYW1w9zPfo9pdLYx2Fvz5G2eC1aF2Mf+6qMeoxVCab5qhtrZLPVNTj1G5ekC3gkUG1 WS2miZg3xCbgstOTfzU7YVeczJLA4SVeLFVcDb7LqGXp4g5GMhLcMpRI5qfqYWLvUGKu9QYq71Bi rvUGKu9QYq36gxV3qDFXeoMVd6gxVLfrUf8AOPvxS761H/OMVd9aj/nGKt/Wo/5h9+Ku+tR/zD78 Vd9ai/mGKu+tRfzjFXfWov5xirvrUX84xV31qP8AnGKu+tRfzjFXfWov5hirf1uL+YYq761F/MMV d9ai/nGKu+txfzjFU1t/N97BCkStCUjT01DRodqUBO25GY8tNAm9/mW6OokBW3yDn8337qVMkIG/ 2Y4wdyDWoFdiMRpoA3v8yp1Ej3fIOPm+7ZUD+gzRuzq5QVqyFN6bGnKvzx/LRvr818eXl8lr+bL1 3Ry8QKMWoqKoJYMDyoN9nOEaeI/tQc8j/YqP5z1F61kiHKtSqIDQ0qKge2AaWA7/AJlkdTM/2BLJ 9SE8zTSOC7mppsPAADwGXxAAoNJJJtZ9bh/nH34UO+txfzj78Vd9bi/nH34q763F/OPvxV31uL+c ffirvrcX84+/FW/rcX84+/FXfW4f5x9+Ku+tw/zj78Vd9bh/nH34q9k/QGg/9W21/wCRMf8AzTnm /wCez/z5/wCmKXfoDQf+rba/8iY/+acfz2f+fP8A0xV36A0H/q22v/ImP/mnH89n/nz/ANMVd+gN B/6ttr/yJj/5px/PZ/58/wDTFXfoDQf+rba/8iY/+acfz2f+fP8A0xV36A0H/q22v/ImP/mnH89n /nz/ANMVd+gNB/6ttr/yJj/5px/PZ/58/wDTFXfoDQf+rba/8iY/+acfz2f+fP8A0xV36A0H/q22 v/ImP/mnH89n/nz/ANMVd+gNB/6ttr/yJj/5px/PZ/58/wDTFXfoDQf+rba/8iY/+acfz2f+fP8A 0xV36A0H/q22v/ImP/mnH89n/nz/ANMVd+gNB/6ttr/yJj/5px/PZ/58/wDTFXfoDQf+rba/8iY/ +acfz2f+fP8A0xV36A0H/q22v/ImP/mnH89n/nz/ANMVd+gNB/6ttr/yJj/5px/PZ/58/wDTFXfo DQf+rba/8iY/+acfz2f+fP8A0xV36A0H/q22v/ImP/mnH89n/nz/ANMVd+gNB/6ttr/yJj/5px/P Z/58/wDTFXfoDQv+rba/8iY/+acfz2f+fP8A0xV36A0L/q22v/ImP/mnH89n/nz/ANMVd+gNC/6t tr/yJj/5px/PZ/58/wDTFXfoDQv+rba/8iY/+acfz2f+fP8A0xV36A0L/q22v/ImP/mnH89n/nz/ ANMVd+gNC/6ttr/yJj/5px/PZ/58/wDTFXfoDQv+rba/8iY/+acfz2f+fP8A0xV36A0L/q22v/Im P/mnH89n/nz/ANMVd+gNC/6ttr/yJj/5px/PZ/58/wDTFXfoDQv+rba/8iY/+acfz2f+fP8A0xV3 6A0L/q22v/ImP/mnH89n/nz/ANMVd+gNC/6ttr/yJj/5px/PZ/58/wDTFXfoDQv+rba/8iY/+acf z2f+fP8A0xVGmSMSCMsBIQWCVHIgdTTMfhNX0VBS67pcUxieccgaNQMQD8wMtGnmRdMDkCNjkjkQ PGwZGFVYbg5SQQaLIFdgS7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq 7FXYq7FXYq7FXYq8D/5yPmmh1/RJIpGjkS3kZHQlSD6g3BHTOv8AZ0Xil/W/QhgOjfmV5q0105XA voV6w3QLgj/XBWT/AIbNtl0GKfSj5fimBxgvX/y2/OSw1fUU0SbT5LW7ueTW/BxJDVIy7ircXUEJ UdfnnO9pdkGEfEErA+f4+SQOEPVbe6WYkU4sN6dds0E4UmMrVsrZOxV2KuxV2KuxVTkuI1BowLeG SESi1izs3QV+QwmKLX+t4jBwptsSqTTGltfkUuxV2KuxV2KuxV2KuxV2KuxVxxVwIOKuxV2KuxV2 KuxV2KvDfz8jibzj5X+sIHtnHGRX2Vl9deSk7bUO+db2Af3E653+hDDfzg8paJ5Y8429pp0TQ6dc W8dy0XJn48pZEdVZiT0TxzP7I1c8+Eyl9QNfYFTS10HTfLP51aZY6TJJJYuyNA0jgvSeFlZagL0Y kUIrlZzSzaKUpipUfsLE7jZ70bmGxIkuKgkHhGBVjnJcJnsGAPDzVP0zC0dSrQlv7tnAofpBNPpy P5c33p8RGQTViVpWUFuhqNxlMo77Mwdt0p81eZl0CKyle2muRe3UVmiQJzbnKaCoqNtsydHpPGMh YHCL3U2nmYbJ2KuxVYIYgahFr8hkuIopfkUuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuBBxVY3wnkO n7QyQQvyKXYq7FXYq7FXYq8K/wCck0Uah5deUH0ClwGK9SA0RYCvehzrPZw+iY62EMQ/ODyVD5U1 Wwtbe+uL21ngZoVumDvEFcgoCAo4712AzY9la06jGZEAEHoq/WNM1Pyx+Zuhrdak2pSiSznhu5VI k9J5KBXBZ/iAB/aOOLJHNppVHh2kKQdhs921RrmWSKa4CglQh41psSf45y2IAAgOPIk80ZFE11Hx S4tzUf3ZU1+jnvlMpcPMFmBbzrzJ+cek+XNUn0iC1OpS2zlLiWKThErg0ZFLKxYr3265utP2RLNE TJ4b8kiCGvvzn8sa1qnld5GmsLOwuZbvUxMpbg0UDLBx9Llz5s5HT5jJY+yMmKGSqlKQqPxO/NtD 0Xy1+ZnkzzJeGx0u/D3gHJYJEeJmA3PDmF5U7gZpdT2Znwx4pD0+SWUZr1dirsVdirsVdirsVdir iAeuFVpRfcY2ilpibs5+nDa0tEcoYVYEd8NhFK1MgydirsVdiq19vjHbr8sIQW9mHiDilZCTxKnq pphkgKmRS7FXYq7FXYq8R/5yZiJh8uy1+FGu0I71YQn/AI1zqPZs/wB5/m/pQ89/MTTfPNpHpMvm m7F7HcQs2mz8xI3p0RmBNFf9tftfRm60WTBIyGIVR32rf7uiqXmTUPNTeZ9Jv/M0Po3aR2zwOQqK 9uj1RwVJWnXcZPTwxCEo4+Vm/f1V7v8AnTq8OjeRrqWL93fXbpa2kikqytIeTkEEH+7Rs5LsXGcm cX9MRZ/HvQYh8+Wv5hedbWBoItYuDG4KsJGEhoRTZpAxB8COmdfLRYZGzELwhjzMzMWYlmY1JO5J OZSWsVROm6hc6dqFtf2rFLm1lSaJh2ZCGH6sjOAlExPIq+q9H/Mry3q9rDNa6naRTTIrm1mkVZUJ G6shZTUHbOEzdl5MciDGRHeGBJZEt1eKokaNLiFtxJAd6ePE9foOYRhHlyPmmyqwXiTPxUHiRUN/ ZkJYyAolao9JI2WNxy6VB6fdkRsd0nd0COkYVzyI74yIJSBSpkUuxVb6sdac1r4VGGii12BLsVdi rsVdiriQMUKbSZIBbdFJUlfpGJCgqh32yKVkJ/d0/lJX7jkpc0Bam08o9lOE8go5quQS7FXYq7FX Yq8Y/wCck1VtI0WQ/aW4lUfJkBP/ABHOn9m/qn8P0oef+fdW8xX3lry+mraRJZxRxK1nqHPnFPE0 KcAoAonwgNTlXfNxoseOOSZhK7O47jZtACC/MLzNFrq6CUtJ7Q2Omw2ridQok4biSMg7owNQcs0W mOLj3vimZfNLMvz/ANbkuo9AtC1fUgN5JXavqKqoaDp+3mu7DwCHGf6VfJA5sP8AIX5Zav5zt76a xuIbZbJo0rPz4u0gY0BQN9niK7d8z9d2jDTEcQJ4u5KV+cfJ+p+U9WXS9SkhluGiWcNbszpxZmUb sqGvweGX6TVxzw443XmqC1DRbuxsNOvZ2T09Uiea3RSeYSOVoquCAByZDShO2WwyiRkB/Ca+y/0q hhY3pgFwLeQ25rSYI3DatfipTamT4hdKoYVfQ/8Azj9HcReTroRwuDPdtMLk8fTYcVj9Nfi5clMZ J+EbEZyXb9eNGz/Dy/SiXk9Plt5ColjPGelHA6P4g/1zQiY5HkxIW2C9ZK+Kle4PgcOU9FgEbyym maFlvWL+lAOb9z2GWxx9SxMu5Te3qA1zManoB0yQl/NCCO9UFvHGPiQOn8wrUZHiJTS4Q8figcr7 dVwcXetL0uDXhIOL/gflgMeoTaryyFJa5Yq5pAMaW1JpMmAi1JpMkAi3W7ky/IHGY2UIvllVMlKB vhY9izEffkpBAaias0rdqhfuH9uEjYKFblkKS7ljSu5Y0ruWNK7ljSvHP+chopZPK+n3b7KL5URf ANDIRt4tx3zpfZ+QGSUf6P6WIYD5y84Q6v8Al/5c0prG6t7nTRGi3MqBYJY44TFWNwd91HbNxpNG cefJMEET6dQebJAefPMml6zpHlaKzkLTaZpqWd5GVYcJIwq9SACDxrtlmi08scsl/wAUyR8VQ35g ax+lNT05gai10rT4OtQCbZZWA+TSkfPJ6LFwRl5zkf8AZH9Cq/laD8z7XTDe+WU1AafM5JNoGdGd PhJ4LWp7dMjqTpjKsvBxf0qVI/MWp69qGqyza68r6lHSGb114SLw24stFoR7jMnBCEYgQrh8lUNT uNUkeC31EyCSyiW3hilXg0cQJdUoQDSrk7+OShGIsx67q9I8kfnTaeWtEt9MOkvKLaNl5rMAHZmL 1IKfDVm980+t7H8eZlxVfl+1jW9vOdb1H9J6zf6jQr9cuJbgKeoErlqbeFc2+LHwQEe4AMn0j+Tz RWvkXS7IyD1ZInnNCOS+vK7LUb9ulc47tiJOeUunL5AMb3ZjYmWOOKAyvcLEgQzy8fUfiKcnKhRy PegzWZADZqr7kA2VjyN6rsvwknem1aeOSA2YE7rmuJZCsI6n7R9sHABumyUTDGsSUAFT9o+OVSNl mBTciJJTl26YgkKRa/lkaSsB4Hb7J7eByXNDbhXFD9BwDZK2ORgeD9ex8cJHVAVOWRpKyQmlRkgg odpMsARamXJNB1yVIRNunBan7R65VM2yAXyyFU2+0dlHucACSXclii9lH341ZRyahBWMV+0fib5n GW5ULjKocL3PTBwrbhISRTYdfnjS23zG1N698aS3ywUruWNK8v8A+cgYxJ5GhY1rFfQuKeJjkXf/ AILN92B/fn+qfvCh5zfecdCvPydsvLrTn9NWU4cwFGAK+tIQVcDgaI46mubrHpJx1ksv8Eo/q/Ul LvOF9pF35I8oC0ltzf2kdzDfxRlfWB5J6fqAb0oppXLtNCYz5LvhPDXdy3pWGySPK4LGrUVB8lUK PwGbBXoflb86Ne8taRb6KmmWr29oGVOQljkqzFyXPIivJv5c1Gq7Hx5pmZMrPu/UrB9W1R9T1q71 S4SrXlw9xJFyP+7HLFA3tWmbPFjEICI6ClX+YdYfWdbvNUeMQm7kMghBqEHRVBoK0Ap0wYcfBAR7 goZ/cSflcPLdzHa2tvf39rFC1rPLM9tJM719VSoWD+6ptWpbxJzXAak5ASeGJuxQNd3fz+xG7y/N sl9S+QRpk3lex+rXFvcy20UdpNNbLxZTAgASQksSy1O/Q1qBnFdo8YzGwQDvv59zXNlkEgB4kDfu O+ayQWJWXHHmFQVdu3b54Yct0EL0VYAWY8mbavtgJ4k8lYOCAR3yBDK1heT1AAPgwgCkWbX8sjSV j+oXHE/D3yYqkG14O2RS01GHv2xCuV6jfriQrfLBSrSkZ6jJWUNqqL9kAYCSUuaRVG/0DucQFtat S3N9iOg8BhPcELlikmYMRSNd1rtU+PywEgJq0QID3OQ4k0424P7Rx4k0ta1BIPI7GowiaDFZJFMo 2FSPDphBCCCsB67UPfCVbDbY0rzz88U5/l9dGtPTngb5/GF/42zc9hmtR8CoebeX9M8u335QaxcS W9u+t2MshhmIVbhUHpNUEUYrRm6++bzNkyx1cQL8MjfuvdKR6noGkxflno+uRRcdTubya3uJQ7EM i8yoKk8QRxHQZkY80jqJQP0iIKsb0i7gs9Vs7u4iM8FvPHLLCDQuqOGK1NacqUzLyRJiQDRIS9U8 4/nRoev+Wb7To9NmhvbmMRwySCN1WrqW+KvIfDXoOuaTR9kTw5RLiuI/Uxp5v5VvdLstaju9TT1b WKOciIoJA0phcRBge3qFc3GeMpRqPPb5Xv8AYyd5U0RNb12DTXlSBZllPrStwRWSJnXk29AWUDHU ZfDgZUTXcrKvN/5XReW/JtnrVxe8tQllWCa1QrJFzfk3wSDj9lUNdjv3zB0vaXjZjjA2Au/l0RbB LO3a5u4LdTRppFjB93YD+ObMmhaX17pul6dYWxgsLWO0RnaR0iUKC7mrMadznn+XNKZuRtq5ohi0 VGpUV39sr5rybRHP7wmjtv8AR2xJHJaVWda8TvXIAJJbLhR7YKTayVyVFOhyUQxJWxsQ3seuGQQF blkKZth1J4VHIioHfAR1S1yw0hwO+BXcsaV3LDStFvFqfdjStxIXaiKSe7H+pxka5qEYkKL7nxOU mTOlTIpdirsVdiqnLc20LKssqRs/2A7Ba08K5OOOUuQtBIDmWKRagg16MMG4VDSIyHfp2OWA2xfL nmL8zvNeqaZfaFqMsV1aSyAeo8SrKoilDrxZOI/YpuDtneYezsOOYnEUfemlbyH5H0vzJ5f167nm mivdLQSwCIqQ1Y3bi6kGtTHtSmR1mslhyQiAKma+79akpXF5aM35fTeYlu3At9Q+qvZEVjNY1YSA 12b95Tplx1H7/wAOv4bv4rbvy+Tyw3mWJvMzoulJHIXWQOVdyvFVPAV6ty+jHW+L4Z8L6/h+lSyD 8zbH8tbfTrWXyo0LXc059cQzSScYwrVqjs3GrEfdmL2dLUkkZuXw/QgWxDTdLsp9C1jULiRlmshb raRqyjlJNJRuQIJKhFY7U3pmfOZE4xHW7/HvpkiPJ/k7U/NepS6fpzxRSxQmdpJyyx8QyrQlVc1P PbbK9Xq44I8Ur51sglW83eRNe8qLbjVDDxuy3o+jJz5GMDkaEL054NLrcee+DooKG8k231nzjokP UNfW5b/VWRWb8Bk9XLhxTP8ARP3JL6zIDHwPYjOAa0Fqt9c2mmz3MFo9/LEvJbSIgPJvuF5bVpvl 2HGJTAJ4R3oBYlpv5z+TLuzaW7mfTJom4TW9wpLqd60VOTN07DNhk7FzRPpqQZEFleh69pWuacmo 6ZN9ZtHLKkvB0qUNDtIFbr7Zr8+CeKXDIUUUj+VRvlSHVFKdsCXCg6YShKdV8wWGnWUl9dXa2dtb OxuDNG5Zkjbg4jWqsasygMoYfOuZOLTSmeEDiJHQ9/f+rZICLspobtRqKW6rKysttOShMkJPJGDp yISSgan4ZXkiY+gnbqO49fiEopJJfTQyqqysoMioSyhqfEFJCkgHpsMqIF7ckEruWClXtuoYfTkQ lZyyVIXxI0j8R9J8MjI0kJgiKi8VG2UE2zWvKidTv4d8IiStrDdxDrUfRh4CjibFyhAIVyD0IVv6 Y8BTaoCCAelfHIqkev8AmNLJDBasr3ZNG7hBStT777ZsNHojkNy+n72rJkrkw66vry7atzM8tDUB iSBXwHQZvMeKMPpFOOZE8058q6ulvL9SkUBJ2+GUdeZ2Ab28Mwe0NMZDjHRsxTrZlzEFSD0zRhyH xXf/AO91z/xlf/iRz02PJLNPyt0TW9WGsRaTrEmlzRwoXiReaXCtzXi61A2rsaGlc13aGaGPhM48 W/yQSlVpFrP/ACr2+ljv1XSRfxpPppRSzTMgKyh6chstKe2Xy4PHG3r4Tv5XyVG/ll5W8u+YL69j 1y7+rQQxL6IEqQu0jt+zzryoqntlXaGoyYog4xxG+4n7lJd+Znk/QvLN7ZQaTdy3SXMbSu0rRvQA hVoY1Trvj2fqsmaJMxRBUFjsujNF5ettYeUD61cy20VvTcrCiM0nKvSsnGlMyxluZh3AH53+pKa+ RofPT3N1L5S9T140X60YmiB4M3wg+oRXcdBlOslhAAy1XmgrvPWoeerma0h82iQTQq5tfVjjjJV+ PKnphQ32Rvg0ePBEE4qo86NqG/yuEX+PdIaVgqJJJIWP+RE7dvlj2hfgSA50sjQfRcnmWxWoRXfw IAA/E5ycdBM86aDkCHk807nhbfSzfwAy4dn98mPiPNtV8mJqOuHV3jhDPqa3kts4Lo8A4+pG383q FOVPembvHm4cfBZ+mrZ+MzyDXLm3hSC2hhggjHGOKNOKKo6BVBoBmrlo4SNkkn3tfiFv/EWp/wAy /wDA4/kca+IWx5j1IdSh+a/0OA6HH5r4hQ9x5w1KC6t0CJKsnISRKvxBar+9LF1oqdDsTv7ZIdnY yDz/AB8GQmUq/wAX3moaSy3+nuBMLiIiaMNLwaVVDhkSSMLwb7JQ8qd6VN40EYTuMuVe7l77+1nx bp7pnmu1muZ3kiUTQcbd1t52kVaKGIaM8FVgzGm1aU+WYeXQSAoHY77j9LE5E3i8wac/V2Q+DKf4 VzElosgTxhGRXttLT05VYnoARX7solikOYTYRULAkoejdPnlMh1ZBYahuPfpklTK3h9KMD9o7t88 xpystgCrkEuoK1pv44UNEjFWi2KrS3c4aV56sJ1O+uJDPFCXZnBmbiDyOwGdOZeFACifc4dcRQXD 4C3IUBC07mtdx92X3uxV57S9sZIzNGYnPxRk0PT78rhkhkBo2kghmWh6k1/pkc7msoLJIdh8Smnb xG+aDVYRjyEDk5MJWHx9f/73XH/GV/8AiRz0OPJsZH+XeoecLXV5o/K4SS8liLS28gjpJHGQaVkK 7ivZhmHrseGUP3v035/oQUrln1mz0e+0WVBHaxXqG8RuPqLcosiKvWtPhatPDLwISkJjnW3u2Sj/ ACX5F1DzY95HY3EMEtoqMRPyAYPyHVQxFCvhlOr1kcABkCb7kE0lfmLQbvQNYuNJvJI5Lm24+o0J Zk+NQ4oWVT0YdsuwZhlgJjkUgqN7DqdvBawXfqLA8f1m0idiU4TftotaDnw3+WTiYkkj3KnHk/z5 rPlNrk6ZHbv9b4et66M1fT5cQOLIR9s5j6vRQzgcV7dyCLU/OfnPUfNl/b3t9FFBJBCIAkPIKaMz FviLHfl44dJpI4ImMb3N7qBSZ/lLZrdecoUaQRqkMrlj0+zT+OQ7QyGGIkC+THINnu8dloMP99cC QjqOVR/wmc2c2eXIV+PNoqKqL3y7FT04kanjGW/4nXIeFqJcz9v6k3FUXXtIT7MAXtVYkH6qZH8p lPX7SvGF/wDiWwBPBWUeARRg/I5Op+1PiBaPMGmmtU69axqa/rx/J5e/7V4wles31tcun1dEVV6l Y1Qk+9ADmZpcU43xE/O2E5Ascms9Rn1CRpJIfqIiH1RTHykjnIZWckmhHFqf577ASiB5oBFLTofC S7ltryeCS7MVOLBkiEVPhjRgVAYChw+LysA0vEmNrp1latLOkSwm4b1JeAAaRqUqcx55ZS2G9fYi +9XNw42jAjHgvX7+uR8Idd0W2t3cL+3yHcNuPxwHDE9FtONG1QtI0IWjAc+IO1AaEqD069MwNXgo W2QkyE3EKSQzuDxcAgCnU9DuRms4SQQG60et5CahuUbKKlXBBp4+/wBGUHGWfEqSOVQlRyYDZelc iAku5HFWicKFpbGlQupz+lp9zJ3WNqfOlB+OX6eHFkiPNjI7MJtUjSymmnsnnjbaO4DMqodx29z3 zoMhJmAJUe7vcYcuSEAjogYFamrP/k7DYe1Dlxvdirai1uJF9C4e4iC15SAgg1NRvleHir1ARKZM l8ppw0GA95C7n6XI/UM0+vN5T8HIx8nyTf8A+91x/wAZX/4kc70cm1OfI3mhfLPmGLVHgNxGqPHJ ErBSQ4pUEg9Mx9Zp/GxmF0gi0Hr+rQ6hqup3VujRwX9212qPTkOTO1DT/jIcngx8EIxP8IpIU9I1 7WdGmebS7uS0kkHGRozTkB2PjksmGExUgCqhqOo3upXst7fSme6nPKWVqVYgAdqDoMlCAiKAoBVX V9XudUnhmnRIzBbw2saRAhRHAgRepbeg3yOPGICh3k/PdWefl1+Ynlby9oT6ZqdjPPJNO00kqRxS J8SqoFHdTsFzWa/Q5csxKEq2rqxkLYj521fTtX8zXl/psQgsZSnoRBFjoFjVTVV2qWBOZ+kxShjE ZG5JDIfyYsBeebJkLcVjs5HP/IyNf+Nsxe1c3h4rHexyCw9yTQrMfaJb/P55zZ1ky08AQ+r6NL+i 7n9F8F1ERk2plHJOY3AYe/TJYdSeMcZ9PVIiGG/l75+07XjJp+rWyWuqQgtVBSORF+0aNUqwPVfu zY6/RZMfqgSYszjAeg/oyx/32PuH9M0/jz7ywoLTpFif2Kfdh/MT714QgNcsbOzitliB9aYNI9T0 StF2998zNHlnMyvkGMwAlGZzWvQKPjbcDoPE5GRPIKlqeZNFuNR+ox30Ul6a0iVq/Z6gEbVHhlow SjG62ZGJq0fkGLsVVLeeO2vLeV9wW4NvQBW2JOU5oGUSAmLOGhae2jkK8kiYKY16lVp0zQCXCa73 Iqwj3Ec6By1YiCfp8a9qZji4+9nzUlcQyM0702Cq5+yR4n3yZFjZHLmvmnbkI4hydqmvYAd8EY9S klTaCpX1GZ2J69h36YRLuRS4RqpoPhPYrtXG00l+vmY6TcIqlmIXceAYE7fLMnRV4oLCfJispjXT kWO9ZyxHqWdGCr3ruaeHbNzGzkNx/wA5oPLm7T0umvYhblHkRapzpxFRUjf3bDmMRA8XJY3eyH1N 5TPcNMqrKKh1TZQVFNqfLJ4QOEVyQebMdEX09Gs1/wCKVb/ghy/jmh1JvLL3uRHk+Qr7/e24/wCM r/8AEjnoI5NqhhV2KuxV2Kss8s+RrbU7W1vdX1mDQ7O+mNvYNMjSyTspCuyIvEBFY8S7MBXMLUas wJEImcgLNdPx3C1SHXNJm0jWb7SpnWSWxnkt3kT7LGNitR86Zk4coyQExykL+aoHLFZr+U1wsPmW X976ck1s8USgkM7F0egp7ITmFro3DcdWvLdPXJLm/ESyRGSUMVpRiAVYj4gTtsprmpEIXWzjbukv J40Z3mcKgJbdjsN+gwjFE9Atl455j8t6iC+pSW0iXer6lIttCSOYSYlkVgD9py3fwzc4ssfpvaMX JjIcmfeT/ImreXJhPda1IirypYQuwiIYEVdATU717b5rNRq4ZhQhfmwnO2XNqE6/YnlY+LGg+74s xBgieYDTxOaO+vKT3DURQF9aXZQB0A8foxEoQ9MfkE7lSkeFAUgq1dmlbYn5DsMsiCdyhiVxPN5i lmUTm18t2pZJ7hG4Ncsn2wH/AGYl/aPfM2MRj6XM/Y2fT70o0TSfKsvnFL3Sry3ENvF+5sombm0o UqzfF1AU1+EmuW5JzGOpBlIy4d2e5gNDsVULwVir4EYQr0Ly1dC50W3lJqxBV/8AWU0Oc1rMfDlI crGdlaZksw1Ryt5N+HcV65XEcfvU7NX6/WI0jjO7Co+XjhxHhNlZbqtvEkCLEN3pUnx7dcjM2bSB S9nowHalS3hkQE207HiaCp7DEKlPmS7ltrWB4jR/VB9iApqDmdoMYlIg8qa8hoJXr9vP6VsywokR oKIByDEfZLDYjwzM0cxctySwmEstTbG4dp4GkioTwjNONT/DMzIJcPpNHzaxSDvCnCQoCEr8IPUC u2WxvqhndiOGn26fyxINvZRnOZd5n3uSOT5Zv/K+sWUiapq2m3UOi3UjcbwIQOLkhXBO3fkoNOWd 3DUQkTGMgZDpbahtQ8tXmnevLdkLaJQWt0oqlyZEEkRhrTkrIwdj+yOvxUBnDMJcuf3e9UrFvOVj YIxWVikZp9phSoH/AAQyy1ej6b5E/LY6pB5e1LzFcfp6QiKV7aNDZx3Lbej6jA8iG+Go2J8M1OXW agROSMBwDvPqI76/BVhnm3y3deWvMV7otywkktXAWUCgdGUOjgb9VYfLNhpdQM2MTHIqzP8AMvy9 5j0W9sdQs7WRNG0uys7S0vuCsiP6YdmHKvFjKzHlT7XvmB2fqMeQGJI45SkSOvOvur4K82d3d2d2 LOxJZiakk7kknNsq3FWXflzZTX97qNpboDd/VPXtJCePpzwzRmNw1DSjGh9sw9ZMRAJO17+exYTN PW/Jur67qC3BvY4Laeyke2NjDzVYlHHiHj/m+H4WGxU5o9ZhxQqrPFvff8WuXkyFbmziZ5WdOc7H mRU1aIcDQVanHjQ0zC4JS2AO36WNrJrjSJjGZeDmFxLFyRjxkAIDDbqORyccWUXXXbmgSCW3/mny rb3x06SaNb5lV1rC/pguaIHcDiOR23OXY9LnI4t+H3shGxanZ+Y9OmSSSzsxGIOS3Mk+/CVPtoPH h3bp4e10tLP+KXupiduSB1i98zXEsYsLdZmZDI890XSNAfsqFUVq2/yy7DDFEbmvciu9j41XW9e0 j6ja2ksGoTXUlhqEkSu6W4ioZn5gUB4kBanqcyiIY5WSKqx5sxAA2v8APmi39v5NNnp1rKsMBiV4 kVq+iv0VO9Ccjpc8ZZLJFox/VZeTaQZ49WtXib0pIpVfmdggU1Zm8AAN8209wXIL3uG9s5nRIpkd 5IxOiAgsYm2D068T45ozEhw6KpJJHGheRgiLuzMQAPmTgAtC25FYG+/8cQrIfIV8eFzZMehE0Y+f wt/DNT2pi5S+DfiPRk93FHNHSVuKjv0zVwkQdmyQtbFHwjLSHcUJI8F2HTCTZ2QG5bmKEF53WOPk FDE0+0QB+JpjGBlsNyyQMeqi5jMElvLbzySSLCjqaske3M0qFDdRyy+WDhNgggAX8UFWt7VEgQTo HkNRVvi8SBvXtlcpknZiB3pd5hitvqscZqpBZ0pvQCgag/2QNMzNEZcRIYZAKSfTpPq1y9pc7QXA CsR2P7Dj5HM7MOOPFH6o/ghhE1sVkS6lY3ssEDcJU2kbbjxG/IltqZKRx5IAncIFgoK6gt/SZTcq GNPi4txrXxpX8MuEz3IpnEQ4W6L/ACoB9wzn5byJch4x5U1/zV5p8xyXGqXaQ+WrGOPTb2yKM0Fy shMccCwciGnlO/IfZ69Ns6bVYMeGFQH7yRMgeo6k3/NH28uraybzV5C8ty6Jp1pFpt1qCaXJJZWs dveRxziL02maRvUTh8RQtT/KFNqAYel1uTjkTIR4gJG4mudVz+Hw96sL8yeTPLMt3ZaRpeujS9R0 63jWKx1RPTV5Zv8ASj/pcf7r1R64U/D2AzY4NXlAM5Q4oknePl6fpO9bX1VLtG/K3X4daiu9fuLX TdLt5lmudQe6hcOobnSIRuzM79tsty9owMaxiUpnkOE/bY2VZ50ax83+cIdZXVbS1sNYujax+qxE ltDbBY/VnSg4hx8S1bevbs6OJ0+Hg4SZQjfvJs0Feoa5fappOoatqSzJJpw020ezmnn9Sykug8H1 dEt1DMvIo5ElQDX/ACajT4YQyRhGvVxyvapAeq9/iLCXl8f5e+cPNFxqHmBoLSzSSa4YQrQI8kLM GjgjiDhhyQrWu9CanvuDrsOHhx2Sdv7SUJBJ5diksxrUbGHRxVboE1kiuFIBtlB3ZmqGSv7O5+y2 ZYzergP1fo7/AMdfeFb8pX0un3N5fRJ8HBIEDcSpkklWSNGr12iY/RhzwEqB/G37UEW9MudR8m6d rbS3Vxc3erPMkbTwyvbCOFqiNBRqSkKAWB2+IdOmagY884UBGMa5EXZ/QgDZP7zTdA0LncpdyTNq DGaJ5XMhCO68vjPRPUkr23Pc5jafNly+kxA4ef49zVkF8krm8z6VHNPCJKyQI7v4cUj9QmvyzNGC VW1jGUn81azYzWXpWvpxtexul1ccV5PHFB6wjVm2+PnxWvSpOW6bFIG5dP10zhDdIUk8vWujsv11 kup0hM7RXUoZyZmiu3Kh+LcohyoQe+XkTM+Wwvp5CvtbN7T6485alpWmpNa6otwl0gkhhuYPWukU gkNyjaFCtBUF1GYv5KGSXqjy7tgxEbPJCeWvNk9jBqOm3Fvzved1cRzueEjTLGJpGlIJX941OPEf ZyzPpRIiQO2wryTIWzGfzJ5OiWF574wCdPUHNozQUBoRyrU8tswRhz77A172rgYvqPm78vIXufq/ CVnWjenAv78FCOExMZHGtOjHp9GZmPBnNX/Z7t2YgUh1T8x7GWwnt9Pgks7gDhaXMYQFEVyUjr8P wcKbU2PjSuZMNKQbJtkMe6g/5o3khDS2tWAjIWKQxpVCC4ZWEvJXPyIG3vhGkA5L4Qbn/NS9mVle wiYFg6BnailQOlAtd998RowOqjEEXpf5i+drO+a80/SebAcfTeGaRACCP2Ch369cpz6DFkjwyP3M hjAThPzT/N11nJ0ITRXDB0U2V1xQcVXjGVcfD8PLcncnMM9laUV6qI/pD7WRFuP5p/mpE8VxJ5cQ GBGiVmtL5V4uVJqvqhCf3YoSKjt1NX+StMbAmd/OP6lpTl/Ofz96ckM+jpQ+ozuYZVlVGqQFP2QE BoCVO3XffJx7GwAggn5rSXP+ccs1qbSbSyluZEk4W108JBjcutG4ue4U16gDwy8dmAS4hLfzF/q/ BK0yW1/5yJtiv+l6M6tVj+6mDUHL4R8Sr+z3zXy9n+6f2ftRwql7+dvlbUXhJhvLV4g27RxulWoO qyE9P8nJ4OycmO94m/f+phLGSnGk+Z7HzLDdXNuZpIoKu13JC8ca+KtJT0wT4VyE9P4BA236X93V rOORRNz5r8uX1miwavayTwsIZ4/VjDuFFFcAkMwBqMrxabJCZuJo7jy8llE0viNuqO7SIUUkepyH Cg7humDMZEgbtdIqDz15XsLeSO/1i1Up9hBKruBTpxQs2YktBlmfTE/c3QBR/lPyDeeXbSSztLyI W000lxK7wcp2aWNRTkHVBwcGmzbZVrO0YZpCRibG3Pbn7uvwb0XqPk7S7mS+JMq3F9bxwXU0B9Nm 9MBQ5ptyC/DX+XbplWLXzjXKoyJF+fT8dd1QfmH8v7HWI5UScQ3E83ryXMsQk4qZeZVE5KPiQCPe o4jpucu0/aksdWLAFVfl1+/3qtXyRe6W1/daBa6Xa6jMySwXsqNzaT1OUvqFUokbKAAkYHiWPcnt CGShkMzHkR05bddz5n5JSq68geY9Z0yeHW59PuNSnujcQ3Lu91Daq/wulvDJCnwlGJ4FvtAGtdxf DtLDjkDASERGqoC+4k3+hWRXXkp59J0bR1vJ4LfSIoolu4HiSSQxwG3qySQ3Cn4GagqNzmDj1wjO c63mTsbNb3z4o/d0VLl/KrSINDXRvrly9nDMJdM4lUmtZK8maKWh3L8mPIHqRlx7XkZ8YiBIj1dx 94QjpvJHlu6tJrS7sEuIbi4+sXRI+3OVCesePGjsO4plI1+WMgQaoUPd3Klsn5P+SJbf6rFZvBAs 4uTEk0qFpOIHUsx2HQDpXLo9s5wbJB27lUJvyS/L1o6HT7lGXoRcSED6WYj8MmO3NR3x+Sr/ADB+ T+g69Z6dbG/uLRdNg+rW0ULI68BTduQLM3wip5YNP2zkxSkeEHiNqxW5/wCcauptfMTCopwltq1+ bLKO3+TmdH2k74fb+xUvl/5xv8wIawa3bMQfhLJIm3Ttzpl49osXWMvsVD3P/OPHnp6KNVsZoxU/ vJbgUJNTt6T5Ie0OD+bL5D9a0hJP+cevP6D01ubGSP7Xwzyha9OjRLvk49v6c/zh8P2q5/yB/MV2 Z3uLRnbdma4kJJpTclPDJfy9pvP5LTJtO/I/yxpmj/WNWuE1PWUUepZvdi0tBITQoZFUygDx7+Az Cn2xlyTqAMYd/CZH5clQEnljUbeUR2tn5MsYv2PrEv1p6f5TXHqV/wCBzI/MRI3OeXwr/c0rMvKt j5bL8NW1by7qN0tQbKwgskVT0oT8Uhp7Bc1uqy5a/dwyxHeTP+xKp5kshacZbfy75ciiQ1jubxJn 6/yrHZ7fQ+R0pM9jkzE+RA++apRD+ZL6aDHNN5ZhJBVltpr6BqNv0W0dhtmTPs3j6Zj7+A/75Uv1 Hzl5N1ActQ8xT2SMaNHp+q6nKv8AwBshUbdxluPS5ocsYJ84QH3ZFSK6s/ybuStfOusSu2xR1nlJ ZutCbZMyIZdaP8lD5gf74oQV15Q/JyFUeTzLqNurdPWspvi77H0VGWx1Ws/1OJ/zgqxdB/IcEBvM upNtuwgYCv025OJz67/U4fP9qpzpXkv8g7s1j8y3LtSnp3MsdsCfb1YIf15jZNX2hHnjj8N/ukqZ XP5c+W7aL1tG8l3HmCKvwzNqsSJ23/cysT92V/n8hNTyjGf6h/3ysfv7Xz3a8xpH5f22mADlHMll 9euFoO0kvqivyTMuE8B+vNxf5wiPspWL6to35q6wwbU9M1m6C/YSS2uSi/6icOK/QMy8eo0sPplj HxCpd/gTzv38vakB4mznA+klMt/PYP58P9MFUZvKuuQf71QJa70pczQwGv8Az1dMtGaJ5b+7dUvu rX6uVUzRyuftrE3MKfAsBwP+xJywG1fRR/NbWT0tYB8uX8Tmi/0O4O+X2fqVTP5n6ty5fVoa771Y dfpw/wCh/D3y+z9Srf8AlZmq97aHandu304f9D+Hvl9n6lXP+Z+rP9u2hP8AwX9cA9n8I6y+z9Sr m/NLV2ABtIKDt8fb6cf9D2H+dL7P1Ku/5WtrP/LLB/w39cH+h3B3y+z9StH81NYJBNrBUdPtd/px /wBDuHvl9n6lWD8z9WAIFrCAaA/a7dO+H/Q/h75fZ+pXD8z9WDcvqsJNa/tdfvx/0P4e+X2fqV0n 5oatIatawnwFXoPxxHs9hHWX2fqVe35p6uxWtpBRTUD4+304P9D2H+dL7P1KoXf5m6/P/dFbX/jE EP8AydWTLIdgacc7l7z+qlSi583eargnlrd8gNaLF9UjoD2BW2BzIh2Rp48oD7T95VLbi/12f7fm HWhvX93eiP8A4hGuXDs/AP4If6UKgJNIuZ6M+qaxKB0LXZbr/sMvjgiOQHyVCzeRrWWJHmbUZIjU oXkVhtsaViywRVRH5faWVLBL0qOp5JQf8ksNFW/+Vd6dUKIr7kwLKOS7gdT/AHXTGiqz/AGlcC9L zgACW5JSh6GvpY0qM0zSptLIOnatqVmBuFgufTH3KgGVZMEJ/UBL3hU6/Tnmnh6ba9eyJWtJfq8n T/XhbMf+TsF2Ige7ZULcXOsXDh5dVuGcCgYJbKQOv7MIy2Okxx2A+9UHPZ6jOT6ut6mQ3VRckLt/ khQMmNPAcoj5Klb+SdKdizzXLM25YuhJP/AZbSrf8D6R/v24/wCCT/mjGld/gfSP9+3H/BJ/zRjS qtv5RsbZ+dvd3cL/AM0ciqfvCDAY3zVHrYX6KFTXNVVR0Au2AH4ZWdPA/wAI+StPp986lX1zVWU9 Va7Yj8RiMEByiPkqDuPKtrc/703t7PvX95KH3G37SnLBEDkqh/gfSP8Aftx/wSf80YaV3+B9I/37 cf8ABJ/zRjSvd/8AoWz8xP8Aflh/yPf/AKp4Vd/0LZ+Yn+/LD/ke/wD1TxV3/Qtn5if78sP+R7/9 U8Vd/wBC2fmJ/vyw/wCR7/8AVPFXf9C2fmJ/vyw/5Hv/ANU8Vd/0LZ+Yn+/LD/ke/wD1TxV3/Qtn 5if78sP+R7/9U8Vd/wBC2fmJ/vyw/wCR7/8AVPFXf9C2fmJ/vyw/5Hv/ANU8Vd/0LZ+Yn+/LD/ke /wD1TxV3/Qtn5if78sP+R7/9U8Vd/wBC2fmJ/vyw/wCR7/8AVPFXf9C2fmJ/vyw/5Hv/ANU8Vd/0 LZ+Yn+/LD/ke/wD1TxVH2n5F/m9Z24t7XULSGABgI0uHAo5q3+6+9MVVP+VJfnHzZ/0jac3BDn6w 9TU1Nf3WNq2fyT/OQkMdRtCw5UYzsSOf2qExV3xtWh+SP5wigGoWdF2A9dqfd6WNqpyfkT+bckLQ PfWZhZeBj9duPGlKUEWKoD/oWz8xP9+WH/I9/wDqnirv+hbPzE/35Yf8j3/6p4q7/oWz8xP9+WH/ ACPf/qnirv8AoWz8xP8Aflh/yPf/AKp4q7/oWz8xP9+WH/I9/wDqnirv+hbPzE/35Yf8j3/6p4q7 /oWz8xP9+WH/ACPf/qnirv8AoWz8xP8Aflh/yPf/AKp4q7/oWz8xP9+WH/I9/wDqnirv+hbPzE/3 5Yf8j3/6p4q7/oWz8xP9+WH/ACPf/qnirv8AoWz8xP8Aflh/yPf/AKp4q7/oWz8xP9+WH/I9/wDq nir/AP/Z + + + + + + uuid:C0693FE917BFDE11829FB7E4595AC072 + uuid:16907a6e-6b01-4ac6-a546-78446b0ba868 + + uuid:df9847c4-e834-4621-a9bd-d2027292e152 + uuid:B9693FE917BFDE11829FB7E4595AC072 + + + + Print + Document + + + 1 + True + False + + 841.889771 + 595.275574 + Points + + + + Cyan + Magenta + Yellow + Black + + + + + + Default Swatch Group + 0 + + + + White + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 0.000000 + + + Black + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 100.000000 + + + CMYK Red + CMYK + PROCESS + 0.000000 + 100.000000 + 100.000000 + 0.000000 + + + CMYK Yellow + CMYK + PROCESS + 0.000000 + 0.000000 + 100.000000 + 0.000000 + + + CMYK Green + CMYK + PROCESS + 100.000000 + 0.000000 + 100.000000 + 0.000000 + + + CMYK Cyan + CMYK + PROCESS + 100.000000 + 0.000000 + 0.000000 + 0.000000 + + + CMYK Blue + CMYK + PROCESS + 100.000000 + 100.000000 + 0.000000 + 0.000000 + + + CMYK Magenta + CMYK + PROCESS + 0.000000 + 100.000000 + 0.000000 + 0.000000 + + + C=15 M=100 Y=90 K=10 + CMYK + PROCESS + 14.999998 + 100.000000 + 90.000004 + 10.000002 + + + C=0 M=90 Y=85 K=0 + CMYK + PROCESS + 0.000000 + 90.000004 + 84.999996 + 0.000000 + + + C=0 M=80 Y=95 K=0 + CMYK + PROCESS + 0.000000 + 80.000001 + 94.999999 + 0.000000 + + + C=0 M=50 Y=100 K=0 + CMYK + PROCESS + 0.000000 + 50.000000 + 100.000000 + 0.000000 + + + C=0 M=35 Y=85 K=0 + CMYK + PROCESS + 0.000000 + 35.000002 + 84.999996 + 0.000000 + + + C=5 M=0 Y=90 K=0 + CMYK + PROCESS + 5.000001 + 0.000000 + 90.000004 + 0.000000 + + + C=20 M=0 Y=100 K=0 + CMYK + PROCESS + 19.999999 + 0.000000 + 100.000000 + 0.000000 + + + C=50 M=0 Y=100 K=0 + CMYK + PROCESS + 50.000000 + 0.000000 + 100.000000 + 0.000000 + + + C=75 M=0 Y=100 K=0 + CMYK + PROCESS + 75.000000 + 0.000000 + 100.000000 + 0.000000 + + + C=85 M=10 Y=100 K=10 + CMYK + PROCESS + 84.999996 + 10.000002 + 100.000000 + 10.000002 + + + C=90 M=30 Y=95 K=30 + CMYK + PROCESS + 90.000004 + 30.000001 + 94.999999 + 30.000001 + + + C=75 M=0 Y=75 K=0 + CMYK + PROCESS + 75.000000 + 0.000000 + 75.000000 + 0.000000 + + + C=80 M=10 Y=45 K=0 + CMYK + PROCESS + 80.000001 + 10.000002 + 44.999999 + 0.000000 + + + C=70 M=15 Y=0 K=0 + CMYK + PROCESS + 69.999999 + 14.999998 + 0.000000 + 0.000000 + + + C=85 M=50 Y=0 K=0 + CMYK + PROCESS + 84.999996 + 50.000000 + 0.000000 + 0.000000 + + + C=100 M=95 Y=5 K=0 + CMYK + PROCESS + 100.000000 + 94.999999 + 5.000001 + 0.000000 + + + C=100 M=100 Y=25 K=25 + CMYK + PROCESS + 100.000000 + 100.000000 + 25.000000 + 25.000000 + + + C=75 M=100 Y=0 K=0 + CMYK + PROCESS + 75.000000 + 100.000000 + 0.000000 + 0.000000 + + + C=50 M=100 Y=0 K=0 + CMYK + PROCESS + 50.000000 + 100.000000 + 0.000000 + 0.000000 + + + C=35 M=100 Y=35 K=10 + CMYK + PROCESS + 35.000002 + 100.000000 + 35.000002 + 10.000002 + + + C=10 M=100 Y=50 K=0 + CMYK + PROCESS + 10.000002 + 100.000000 + 50.000000 + 0.000000 + + + C=0 M=95 Y=20 K=0 + CMYK + PROCESS + 0.000000 + 94.999999 + 19.999999 + 0.000000 + + + C=25 M=25 Y=40 K=0 + CMYK + PROCESS + 25.000000 + 25.000000 + 39.999998 + 0.000000 + + + C=40 M=45 Y=50 K=5 + CMYK + PROCESS + 39.999998 + 44.999999 + 50.000000 + 5.000001 + + + C=50 M=50 Y=60 K=25 + CMYK + PROCESS + 50.000000 + 50.000000 + 60.000002 + 25.000000 + + + C=55 M=60 Y=65 K=40 + CMYK + PROCESS + 55.000001 + 60.000002 + 64.999998 + 39.999998 + + + C=25 M=40 Y=65 K=0 + CMYK + PROCESS + 25.000000 + 39.999998 + 64.999998 + 0.000000 + + + C=30 M=50 Y=75 K=10 + CMYK + PROCESS + 30.000001 + 50.000000 + 75.000000 + 10.000002 + + + C=35 M=60 Y=80 K=25 + CMYK + PROCESS + 35.000002 + 60.000002 + 80.000001 + 25.000000 + + + C=40 M=65 Y=90 K=35 + CMYK + PROCESS + 39.999998 + 64.999998 + 90.000004 + 35.000002 + + + C=40 M=70 Y=100 K=50 + CMYK + PROCESS + 39.999998 + 69.999999 + 100.000000 + 50.000000 + + + C=50 M=70 Y=80 K=70 + CMYK + PROCESS + 50.000000 + 69.999999 + 80.000001 + 69.999999 + + + + + + Print Color Group + 1 + + + + C=0 M=30 Y=70 K=0 + CMYK + PROCESS + 0.000000 + 30.000001 + 69.999999 + 0.000000 + + + C=5 M=70 Y=90 K=0 + CMYK + PROCESS + 5.000001 + 69.999999 + 90.000004 + 0.000000 + + + C=5 M=90 Y=75 K=0 + CMYK + PROCESS + 5.000001 + 90.000004 + 75.000000 + 0.000000 + + + C=30 M=0 Y=95 K=0 + CMYK + PROCESS + 30.000001 + 0.000000 + 94.999999 + 0.000000 + + + C=60 M=5 Y=95 K=0 + CMYK + PROCESS + 60.000002 + 5.000001 + 94.999999 + 0.000000 + + + C=30 M=0 Y=10 K=0 + CMYK + PROCESS + 30.000001 + 0.000000 + 10.000002 + 0.000000 + + + C=60 M=10 Y=5 K=0 + CMYK + PROCESS + 60.000002 + 10.000002 + 5.000001 + 0.000000 + + + C=80 M=5 Y=10 K=0 + CMYK + PROCESS + 80.000001 + 5.000001 + 10.000002 + 0.000000 + + + + + + Grayscale + 1 + + + + K=100 + GRAY + PROCESS + 255 + + + K=90 + GRAY + PROCESS + 229 + + + K=80 + GRAY + PROCESS + 203 + + + K=70 + GRAY + PROCESS + 178 + + + K=60 + GRAY + PROCESS + 152 + + + K=50 + GRAY + PROCESS + 127 + + + K=40 + GRAY + PROCESS + 101 + + + K=30 + GRAY + PROCESS + 76 + + + K=20 + GRAY + PROCESS + 50 + + + K=10 + GRAY + PROCESS + 25 + + + K=5 + GRAY + PROCESS + 12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +endstream endobj 2 0 obj <> endobj 15 0 obj <> endobj 37 0 obj <> endobj 59 0 obj <> endobj 81 0 obj <> endobj 103 0 obj <> endobj 125 0 obj <> endobj 147 0 obj <> endobj 169 0 obj <> endobj 191 0 obj <> endobj 213 0 obj <> endobj 235 0 obj <> endobj 258 0 obj <> endobj 280 0 obj <> endobj 302 0 obj <> endobj 324 0 obj <> endobj 346 0 obj <> endobj 368 0 obj <> endobj 390 0 obj <> endobj 411 0 obj <> endobj 433 0 obj <> endobj 455 0 obj <> endobj 477 0 obj <> endobj 499 0 obj <> endobj 521 0 obj <> endobj 543 0 obj <> endobj 566 0 obj <> endobj 588 0 obj <> endobj 611 0 obj <> endobj 633 0 obj <> endobj 655 0 obj <> endobj 677 0 obj <> endobj 699 0 obj <> endobj 721 0 obj <> endobj 722 0 obj [/View/Design] endobj 723 0 obj <>>> endobj 700 0 obj [/View/Design] endobj 701 0 obj <>>> endobj 678 0 obj [/View/Design] endobj 679 0 obj <>>> endobj 656 0 obj [/View/Design] endobj 657 0 obj <>>> endobj 634 0 obj [/View/Design] endobj 635 0 obj <>>> endobj 612 0 obj [/View/Design] endobj 613 0 obj <>>> endobj 589 0 obj [/View/Design] endobj 590 0 obj <>>> endobj 567 0 obj [/View/Design] endobj 568 0 obj <>>> endobj 544 0 obj [/View/Design] endobj 545 0 obj <>>> endobj 522 0 obj [/View/Design] endobj 523 0 obj <>>> endobj 500 0 obj [/View/Design] endobj 501 0 obj <>>> endobj 478 0 obj [/View/Design] endobj 479 0 obj <>>> endobj 456 0 obj [/View/Design] endobj 457 0 obj <>>> endobj 434 0 obj [/View/Design] endobj 435 0 obj <>>> endobj 412 0 obj [/View/Design] endobj 413 0 obj <>>> endobj 391 0 obj [/View/Design] endobj 392 0 obj <>>> endobj 369 0 obj [/View/Design] endobj 370 0 obj <>>> endobj 347 0 obj [/View/Design] endobj 348 0 obj <>>> endobj 325 0 obj [/View/Design] endobj 326 0 obj <>>> endobj 303 0 obj [/View/Design] endobj 304 0 obj <>>> endobj 281 0 obj [/View/Design] endobj 282 0 obj <>>> endobj 259 0 obj [/View/Design] endobj 260 0 obj <>>> endobj 236 0 obj [/View/Design] endobj 237 0 obj <>>> endobj 214 0 obj [/View/Design] endobj 215 0 obj <>>> endobj 192 0 obj [/View/Design] endobj 193 0 obj <>>> endobj 170 0 obj [/View/Design] endobj 171 0 obj <>>> endobj 148 0 obj [/View/Design] endobj 149 0 obj <>>> endobj 126 0 obj [/View/Design] endobj 127 0 obj <>>> endobj 104 0 obj [/View/Design] endobj 105 0 obj <>>> endobj 82 0 obj [/View/Design] endobj 83 0 obj <>>> endobj 60 0 obj [/View/Design] endobj 61 0 obj <>>> endobj 38 0 obj [/View/Design] endobj 39 0 obj <>>> endobj 16 0 obj [/View/Design] endobj 17 0 obj <>>> endobj 720 0 obj [721 0 R] endobj 5 0 obj <>/ArtBox[0.0 0.0 841.89 595.275]/Group 727 0 R/MediaBox[0.0 0.0 841.89 595.276]/Thumb 731 0 R/TrimBox[0.0 0.0 841.89 595.276]/Resources<>/Properties<>>>/Type/Page/LastModified(D:20091023130752-03'00')>> endobj 726 0 obj <>stream +Hѱ@֘N>qF:tira ma Jqe +};aV'fErU . 5 ו`ze ھ92&Ym}WbQxT5TܭL]yz>ϡMK 0Y) +endstream endobj 727 0 obj <> endobj 731 0 obj <>stream +8;Z]";&4g-#XqtAr42j8YaoLNb"80gOue$\3r[q[+:0GFI?M)!jpE'FMNDF01msg6LujJ3''ug.KHWfd\$-.ofT( +*,2J=-jXX2-Scl.)-l.)+g"PGS0]Z]e?XIj=ifMHSI +.GrnPbFE$UccjBSKsO80Sg-3`=($M%In)oNUtt1SOB>:57iGk*a)1R,O,,XrZ[t2Q +iN>:S\P%ZDThm-Og;S150d["4o,jI30MKN2@@0!6$cC2"50S:!>F2iUrVsurJJ3.1 +$"CiiN-O+%;9:LF(C&]R +;L2C19IR:33rf@_K.I~> +endstream endobj 725 0 obj <>>>/BBox[-102.917 69.2871 102.917 -69.2871]>>stream +0 0 0 1 k +1 i +/GS0 gs +q 1 0 0 1 -100.4565 -67.835 cm +0 0 m +-2.461 0 l +-2.461 -0.888 l +3.528 -0.888 l +3.528 0 l +1.056 0 l +1.056 7.2 l +0 7.2 l +h +f +Q +q 1 0 0 1 -96.1484 -69.1543 cm +0 0 m +1.056 0 l +1.056 3.623 l +1.08 3.623 l +1.248 3.323 1.512 3.047 1.836 2.879 c +2.148 2.688 2.52 2.58 2.916 2.58 c +3.696 2.58 4.944 3.061 4.944 5.063 c +4.944 8.52 l +3.888 8.52 l +3.888 5.185 l +3.888 4.235 3.54 3.455 2.544 3.455 c +1.86 3.455 1.332 3.936 1.128 4.512 c +1.068 4.655 1.056 4.813 1.056 5.017 c +1.056 8.52 l +0 8.52 l +h +f +Q +-89.489 -66.443 1.056 5.809 re +-89.489 -60.635 m +-88.985 -67.51 m +-89.381 -67.51 -89.645 -67.822 -89.645 -68.195 c +-89.645 -68.566 -89.369 -68.867 -88.961 -68.867 c +-88.553 -68.867 -88.289 -68.566 -88.289 -68.195 c +-88.289 -67.822 -88.553 -67.51 -88.973 -67.51 c +h +f +q 1 0 0 1 -86.8247 -61.7031 cm +0 0 m +0.324 0.193 0.876 0.408 1.404 0.408 c +2.16 0.408 2.52 0.036 2.52 -0.455 c +2.52 -0.959 2.22 -1.236 1.452 -1.523 c +0.396 -1.908 -0.096 -2.471 -0.096 -3.168 c +-0.096 -4.104 0.672 -4.871 1.908 -4.871 c +2.496 -4.871 3.012 -4.716 3.324 -4.511 c +3.072 -3.756 l +2.844 -3.887 2.424 -4.092 1.884 -4.092 c +1.26 -4.092 0.924 -3.731 0.924 -3.3 c +0.924 -2.807 1.26 -2.592 2.016 -2.303 c +3.012 -1.932 3.54 -1.428 3.54 -0.552 c +3.54 0.48 2.736 1.2 1.38 1.2 c +0.744 1.2 0.156 1.032 -0.252 0.793 c +h +f +Q +-79.385 -66.443 1.056 5.809 re +-79.385 -60.635 m +-78.881 -67.51 m +-79.277 -67.51 -79.541 -67.822 -79.541 -68.195 c +-79.541 -68.566 -79.265 -68.867 -78.857 -68.867 c +-78.449 -68.867 -78.185 -68.566 -78.185 -68.195 c +-78.185 -67.822 -78.449 -67.51 -78.869 -67.51 c +h +f +q 1 0 0 1 -76.7207 -61.7031 cm +0 0 m +0.324 0.193 0.876 0.408 1.404 0.408 c +2.16 0.408 2.52 0.036 2.52 -0.455 c +2.52 -0.959 2.22 -1.236 1.452 -1.523 c +0.396 -1.908 -0.096 -2.471 -0.096 -3.168 c +-0.096 -4.104 0.672 -4.871 1.908 -4.871 c +2.496 -4.871 3.012 -4.716 3.324 -4.511 c +3.072 -3.756 l +2.844 -3.887 2.424 -4.092 1.884 -4.092 c +1.26 -4.092 0.924 -3.731 0.924 -3.3 c +0.924 -2.807 1.26 -2.592 2.016 -2.303 c +3.012 -1.932 3.54 -1.428 3.54 -0.552 c +3.54 0.48 2.736 1.2 1.38 1.2 c +0.744 1.2 0.156 1.032 -0.252 0.793 c +h +f +Q +q 1 0 0 1 -66.2207 -63.5986 cm +0 0 m +-1.152 -0.024 -2.46 0.18 -2.46 1.308 c +-2.46 2.005 -2.004 2.315 -1.476 2.315 c +-0.708 2.315 -0.216 1.837 -0.048 1.344 c +-0.012 1.235 0 1.116 0 1.009 c +h +1.032 1.571 m +1.032 2.075 1.056 2.567 1.116 2.964 c +0.168 2.964 l +0.072 2.231 l +0.036 2.231 l +-0.276 2.688 -0.912 3.096 -1.74 3.096 c +-2.916 3.096 -3.516 2.269 -3.516 1.428 c +-3.516 0.024 -2.268 -0.743 -0.024 -0.731 c +-0.024 -0.853 l +-0.024 -1.332 -0.157 -2.208 -1.344 -2.196 c +-1.896 -2.196 -2.46 -2.039 -2.868 -1.765 c +-3.108 -2.472 l +-2.628 -2.772 -1.92 -2.976 -1.188 -2.976 c +0.6 -2.976 1.032 -1.765 1.032 -0.601 c +h +f +Q +q 1 0 0 1 -63.4966 -64.8711 cm +0 0 m +0 -0.611 -0.012 -1.092 -0.048 -1.572 c +0.888 -1.572 l +0.948 -0.623 l +0.972 -0.623 l +1.26 -1.164 1.932 -1.703 2.892 -1.703 c +3.696 -1.703 4.944 -1.223 4.944 0.769 c +4.944 4.236 l +3.888 4.236 l +3.888 0.889 l +3.888 -0.048 3.54 -0.84 2.544 -0.84 c +1.86 -0.84 1.32 -0.348 1.128 0.24 c +1.08 0.372 1.056 0.564 1.056 0.733 c +1.056 4.236 l +0 4.236 l +h +f +Q +q 1 0 0 1 -50.2964 -63.9941 cm +0 0 m +-0.804 -2.328 l +-0.972 -2.856 -1.092 -3.337 -1.212 -3.804 c +-1.248 -3.804 l +-1.356 -3.337 -1.488 -2.832 -1.644 -2.341 c +-2.436 0 l +h +-2.652 0.816 m +-3.492 3.359 l +-4.572 3.359 l +-1.824 -4.729 l +-0.564 -4.729 l +2.196 3.359 l +1.08 3.359 l +0.216 0.816 l +h +f +Q +q 1 0 0 1 -43.1338 -64.0781 cm +0 0 m +0 -0.145 -0.012 -0.313 -0.048 -0.457 c +-0.204 -1.117 -0.78 -1.668 -1.572 -1.668 c +-2.664 -1.668 -3.312 -0.709 -3.312 0.563 c +-3.312 1.752 -2.724 2.724 -1.596 2.724 c +-0.888 2.724 -0.24 2.243 -0.048 1.463 c +-0.012 1.32 0 1.176 0 1.008 c +h +1.056 -5.076 m +1.056 1.944 l +1.056 2.459 1.068 3.047 1.104 3.443 c +0.156 3.443 l +0.108 2.436 l +0.084 2.436 l +-0.24 3.084 -0.936 3.575 -1.884 3.575 c +-3.288 3.575 -4.38 2.387 -4.38 0.623 c +-4.392 -1.32 -3.181 -2.496 -1.776 -2.496 c +-0.876 -2.496 -0.276 -2.076 -0.024 -1.621 c +0 -1.621 l +0 -5.076 l +h +f +Q +q 1 0 0 1 -37.9258 -61.2949 cm +0 0 m +1.008 0 1.764 -0.947 1.764 -2.268 c +1.764 -3.252 1.272 -4.488 0.024 -4.488 c +-1.212 -4.488 -1.752 -3.336 -1.752 -2.232 c +-1.752 -0.96 -1.032 0 -0.012 0 c +h +-0.036 0.792 m +-1.596 0.792 -2.82 -0.359 -2.82 -2.195 c +-2.82 -4.14 -1.536 -5.279 0.06 -5.279 c +1.728 -5.279 2.856 -4.068 2.856 -2.304 c +2.856 -0.145 1.356 0.792 -0.024 0.792 c +h +f +Q +q 1 0 0 1 -32.6816 -62.9629 cm +0 0 m +0 0.133 0.012 0.264 0.035 0.385 c +0.24 1.117 0.864 1.621 1.62 1.621 c +2.736 1.621 3.384 0.721 3.384 -0.611 c +3.384 -1.788 2.784 -2.783 1.656 -2.783 c +0.936 -2.783 0.263 -2.279 0.06 -1.488 c +0.024 -1.355 0 -1.211 0 -1.043 c +h +-1.056 -6.191 m +0 -6.191 l +0 -2.556 l +0.024 -2.556 l +0.396 -3.204 1.068 -3.611 2.004 -3.611 c +3.444 -3.611 4.452 -2.412 4.452 -0.66 c +4.452 1.428 3.132 2.46 1.836 2.46 c +0.996 2.46 0.336 2.137 -0.108 1.369 c +-0.133 1.369 l +-0.192 2.328 l +-1.104 2.328 l +-1.068 1.932 -1.056 1.344 -1.056 0.829 c +h +f +Q +q 1 0 0 1 -23.2134 -64.1152 cm +0 0 m +0.012 -0.66 -0.276 -1.716 -1.452 -1.716 c +-2.532 -1.716 -2.988 -0.73 -3.072 0 c +h +-3.084 0.756 m +-3.06 2.185 -2.16 2.773 -1.104 2.773 c +-0.348 2.773 0.12 2.641 0.504 2.484 c +0.696 3.229 l +0.324 3.397 -0.324 3.601 -1.248 3.601 c +-3.024 3.601 -4.104 2.412 -4.104 0.672 c +-4.104 -1.092 -3.06 -2.472 -1.368 -2.472 c +0.528 -2.472 1.02 -0.804 1.02 0.265 c +1.02 0.48 1.008 0.648 0.984 0.77 c +h +f +Q +q 1 0 0 1 -19.1938 -66.2266 cm +0 0 m +0.348 0 l +0.744 0 1.08 -0.145 1.08 -0.516 c +1.08 -0.779 0.888 -1.044 0.348 -1.044 c +0.192 -1.044 0.084 -1.032 0 -1.021 c +h +0 1.668 m +-0.48 1.668 l +-0.48 -1.344 l +-0.228 -1.381 0.012 -1.416 0.372 -1.416 c +0.828 -1.416 1.128 -1.32 1.308 -1.188 c +1.488 -1.057 1.584 -0.852 1.584 -0.564 c +1.584 -0.168 1.32 0.072 0.996 0.168 c +0.996 0.191 l +1.26 0.24 1.44 0.48 1.5 0.925 c +1.572 1.392 1.644 1.572 1.692 1.668 c +1.188 1.668 l +1.116 1.572 1.044 1.296 0.984 0.9 c +0.912 0.516 0.72 0.372 0.336 0.372 c +0 0.372 l +h +0.504 -2.184 m +-0.684 -2.184 -1.656 -1.164 -1.656 0.096 c +-1.656 1.381 -0.684 2.388 0.516 2.388 c +1.716 2.4 2.676 1.381 2.676 0.107 c +2.676 -1.164 1.716 -2.184 0.516 -2.184 c +h +0.516 -2.604 m +1.992 -2.604 3.168 -1.404 3.168 0.096 c +3.168 1.619 1.992 2.808 0.504 2.808 c +-0.972 2.808 -2.172 1.619 -2.172 0.096 c +-2.172 -1.404 -0.972 -2.604 0.504 -2.604 c +h +f +Q +-11.142 -60.635 -1.056 -8.088 re +f +-9.223 -69.154 1.056 8.52 re +f +-6.414 -69.154 1.056 8.52 re +f +q 1 0 0 1 1.2544 -62.2188 cm +0 0 m +0 0.6 0.012 1.128 0.048 1.584 c +-0.888 1.584 l +-0.948 0.636 l +-0.972 0.636 l +-1.236 1.104 -1.86 1.716 -2.892 1.716 c +-3.804 1.716 -4.896 1.2 -4.896 -0.828 c +-4.896 -4.225 l +-3.84 -4.225 l +-3.84 -1.02 l +-3.84 0.085 -3.492 0.84 -2.544 0.84 c +-1.836 0.84 -1.344 0.348 -1.152 -0.132 c +-1.092 -0.275 -1.056 -0.468 -1.056 -0.672 c +-1.056 -4.225 l +0 -4.225 l +h +f +Q +q 1 0 0 1 2.8613 -61.7031 cm +0 0 m +0.324 0.193 0.876 0.408 1.404 0.408 c +2.159 0.408 2.52 0.036 2.52 -0.455 c +2.52 -0.959 2.22 -1.236 1.451 -1.523 c +0.396 -1.908 -0.096 -2.471 -0.096 -3.168 c +-0.096 -4.104 0.672 -4.871 1.907 -4.871 c +2.496 -4.871 3.012 -4.716 3.324 -4.511 c +3.072 -3.756 l +2.844 -3.887 2.424 -4.092 1.884 -4.092 c +1.26 -4.092 0.924 -3.731 0.924 -3.3 c +0.924 -2.807 1.26 -2.592 2.016 -2.303 c +3.012 -1.932 3.541 -1.428 3.541 -0.552 c +3.541 0.48 2.736 1.2 1.38 1.2 c +0.743 1.2 0.156 1.032 -0.252 0.793 c +h +f +Q +q 1 0 0 1 9.0308 -67.835 cm +0 0 m +0 1.392 l +1.512 1.392 l +1.512 2.197 l +0 2.197 l +0 5.328 l +0 6.048 0.203 6.456 0.792 6.456 c +1.079 6.456 1.248 6.433 1.403 6.384 c +1.451 7.188 l +1.248 7.261 0.924 7.332 0.516 7.332 c +0.023 7.332 -0.372 7.164 -0.624 6.888 c +-0.912 6.564 -1.032 6.048 -1.032 5.365 c +-1.032 2.197 l +-1.933 2.197 l +-1.933 1.392 l +-1.032 1.392 l +-1.032 0.325 l +h +f +Q +q 1 0 0 1 11.7417 -64.6309 cm +0 0 m +0 -0.684 -0.012 -1.271 -0.048 -1.813 c +0.876 -1.813 l +0.924 -0.671 l +0.96 -0.671 l +1.225 -1.452 1.872 -1.943 2.58 -1.943 c +2.688 -1.943 2.772 -1.932 2.868 -1.908 c +2.868 -0.923 l +2.748 -0.936 2.641 -0.947 2.496 -0.947 c +1.752 -0.947 1.225 -0.384 1.08 0.396 c +1.057 0.553 1.044 0.721 1.044 0.9 c +1.044 3.996 l +-0.012 3.996 l +h +f +Q +q 1 0 0 1 18.6411 -63.5986 cm +0 0 m +-1.151 -0.024 -2.46 0.18 -2.46 1.308 c +-2.46 2.005 -2.004 2.315 -1.476 2.315 c +-0.708 2.315 -0.216 1.837 -0.048 1.344 c +-0.012 1.235 0 1.116 0 1.009 c +h +1.032 1.571 m +1.032 2.075 1.057 2.567 1.116 2.964 c +0.169 2.964 l +0.072 2.231 l +0.036 2.231 l +-0.275 2.688 -0.911 3.096 -1.739 3.096 c +-2.916 3.096 -3.516 2.269 -3.516 1.428 c +-3.516 0.024 -2.268 -0.743 -0.023 -0.731 c +-0.023 -0.853 l +-0.023 -1.332 -0.155 -2.208 -1.344 -2.196 c +-1.896 -2.196 -2.46 -2.039 -2.867 -1.765 c +-3.107 -2.472 l +-2.628 -2.772 -1.92 -2.976 -1.188 -2.976 c +0.601 -2.976 1.032 -1.765 1.032 -0.601 c +h +f +Q +q 1 0 0 1 22.5894 -67.835 cm +0 0 m +0 1.392 l +1.512 1.392 l +1.512 2.197 l +0 2.197 l +0 5.328 l +0 6.048 0.203 6.456 0.792 6.456 c +1.079 6.456 1.248 6.433 1.403 6.384 c +1.451 7.188 l +1.248 7.261 0.924 7.332 0.515 7.332 c +0.023 7.332 -0.372 7.164 -0.624 6.888 c +-0.912 6.564 -1.032 6.048 -1.032 5.365 c +-1.032 2.197 l +-1.933 2.197 l +-1.933 1.392 l +-1.032 1.392 l +-1.032 0.325 l +h +f +Q +q 1 0 0 1 27.6177 -61.2949 cm +0 0 m +1.008 0 1.764 -0.947 1.764 -2.268 c +1.764 -3.252 1.271 -4.488 0.023 -4.488 c +-1.212 -4.488 -1.752 -3.336 -1.752 -2.232 c +-1.752 -0.96 -1.032 0 -0.013 0 c +h +-0.036 0.792 m +-1.597 0.792 -2.82 -0.359 -2.82 -2.195 c +-2.82 -4.14 -1.536 -5.279 0.06 -5.279 c +1.728 -5.279 2.855 -4.068 2.855 -2.304 c +2.855 -0.145 1.355 0.792 -0.024 0.792 c +h +f +Q +q 1 0 0 1 31.8169 -64.6309 cm +0 0 m +0 -0.684 -0.012 -1.271 -0.048 -1.813 c +0.876 -1.813 l +0.925 -0.671 l +0.96 -0.671 l +1.225 -1.452 1.872 -1.943 2.58 -1.943 c +2.688 -1.943 2.772 -1.932 2.868 -1.908 c +2.868 -0.923 l +2.748 -0.936 2.641 -0.947 2.496 -0.947 c +1.752 -0.947 1.225 -0.384 1.08 0.396 c +1.057 0.553 1.044 0.721 1.044 0.9 c +1.044 3.996 l +-0.012 3.996 l +h +f +Q +q 1 0 0 1 37.4214 -66.2266 cm +0 0 m +0.347 0 l +0.744 0 1.08 -0.145 1.08 -0.516 c +1.08 -0.779 0.888 -1.044 0.347 -1.044 c +0.191 -1.044 0.084 -1.032 0 -1.021 c +h +0 1.668 m +-0.48 1.668 l +-0.48 -1.344 l +-0.229 -1.381 0.012 -1.416 0.372 -1.416 c +0.828 -1.416 1.128 -1.32 1.307 -1.188 c +1.487 -1.057 1.584 -0.852 1.584 -0.564 c +1.584 -0.168 1.319 0.072 0.996 0.168 c +0.996 0.191 l +1.26 0.24 1.439 0.48 1.5 0.925 c +1.571 1.392 1.644 1.572 1.691 1.668 c +1.188 1.668 l +1.115 1.572 1.044 1.296 0.983 0.9 c +0.912 0.516 0.72 0.372 0.336 0.372 c +0 0.372 l +h +0.504 -2.184 m +-0.685 -2.184 -1.656 -1.164 -1.656 0.096 c +-1.656 1.381 -0.685 2.388 0.515 2.388 c +1.716 2.4 2.676 1.381 2.676 0.107 c +2.676 -1.164 1.716 -2.184 0.515 -2.184 c +h +0.515 -2.604 m +1.991 -2.604 3.168 -1.404 3.168 0.096 c +3.168 1.619 1.991 2.808 0.504 2.808 c +-0.973 2.808 -2.172 1.619 -2.172 0.096 c +-2.172 -1.404 -0.973 -2.604 0.504 -2.604 c +h +f +Q +q 1 0 0 1 44.4888 -60.6348 cm +0 0 m +0 -5.003 l +-0.804 -5.003 l +-0.804 -5.809 l +0 -5.809 l +0 -6.083 l +0 -6.9 0.192 -7.645 0.685 -8.112 c +1.08 -8.496 1.608 -8.652 2.088 -8.652 c +2.473 -8.652 2.784 -8.568 2.988 -8.484 c +2.856 -7.667 l +2.688 -7.74 2.484 -7.8 2.172 -7.8 c +1.284 -7.8 1.044 -7.008 1.044 -6.12 c +1.044 -5.809 l +2.448 -5.809 l +2.448 -5.003 l +1.057 -5.003 l +1.057 0 l +h +f +Q +47.898 -66.443 1.056 5.809 re +47.898 -60.635 m +48.402 -67.51 m +48.006 -67.51 47.742 -67.822 47.742 -68.195 c +47.742 -68.566 48.018 -68.867 48.426 -68.867 c +48.833 -68.867 49.098 -68.566 49.098 -68.195 c +49.098 -67.822 48.833 -67.51 48.414 -67.51 c +h +f +50.706 -69.154 1.056 8.52 re +f +q 1 0 0 1 57.1978 -64.1152 cm +0 0 m +0.013 -0.66 -0.275 -1.716 -1.451 -1.716 c +-2.531 -1.716 -2.987 -0.73 -3.071 0 c +h +-3.084 0.756 m +-3.06 2.185 -2.159 2.773 -1.104 2.773 c +-0.348 2.773 0.12 2.641 0.505 2.484 c +0.696 3.229 l +0.324 3.397 -0.323 3.601 -1.247 3.601 c +-3.023 3.601 -4.104 2.412 -4.104 0.672 c +-4.104 -1.092 -3.06 -2.472 -1.367 -2.472 c +0.528 -2.472 1.021 -0.804 1.021 0.265 c +1.021 0.48 1.009 0.648 0.984 0.77 c +h +f +Q +q 1 0 0 1 63.3423 -67.835 cm +0 0 m +0 1.392 l +1.511 1.392 l +1.511 2.197 l +0 2.197 l +0 5.328 l +0 6.048 0.203 6.456 0.792 6.456 c +1.079 6.456 1.248 6.433 1.403 6.384 c +1.451 7.188 l +1.248 7.261 0.924 7.332 0.516 7.332 c +0.023 7.332 -0.372 7.164 -0.624 6.888 c +-0.912 6.564 -1.032 6.048 -1.032 5.365 c +-1.032 2.197 l +-1.933 2.197 l +-1.933 1.392 l +-1.032 1.392 l +-1.032 0.325 l +h +f +Q +q 1 0 0 1 66.0415 -69.1543 cm +0 0 m +1.056 0 l +1.056 3.623 l +1.08 3.623 l +1.248 3.323 1.512 3.047 1.836 2.879 c +2.148 2.688 2.52 2.58 2.916 2.58 c +3.696 2.58 4.944 3.061 4.944 5.063 c +4.944 8.52 l +3.888 8.52 l +3.888 5.185 l +3.888 4.235 3.54 3.455 2.544 3.455 c +1.86 3.455 1.332 3.936 1.128 4.512 c +1.068 4.655 1.056 4.813 1.056 5.017 c +1.056 8.52 l +0 8.52 l +h +f +Q +q 1 0 0 1 75.7612 -63.5986 cm +0 0 m +-1.151 -0.024 -2.46 0.18 -2.46 1.308 c +-2.46 2.005 -2.004 2.315 -1.476 2.315 c +-0.708 2.315 -0.216 1.837 -0.048 1.344 c +-0.012 1.235 0 1.116 0 1.009 c +h +1.032 1.571 m +1.032 2.075 1.057 2.567 1.116 2.964 c +0.169 2.964 l +0.072 2.231 l +0.036 2.231 l +-0.275 2.688 -0.911 3.096 -1.74 3.096 c +-2.916 3.096 -3.516 2.269 -3.516 1.428 c +-3.516 0.024 -2.268 -0.743 -0.023 -0.731 c +-0.023 -0.853 l +-0.023 -1.332 -0.156 -2.208 -1.344 -2.196 c +-1.896 -2.196 -2.46 -2.039 -2.868 -1.765 c +-3.107 -2.472 l +-2.628 -2.772 -1.92 -2.976 -1.188 -2.976 c +0.601 -2.976 1.032 -1.765 1.032 -0.601 c +h +f +Q +q 1 0 0 1 79.7095 -67.835 cm +0 0 m +0 1.392 l +1.512 1.392 l +1.512 2.197 l +0 2.197 l +0 5.328 l +0 6.048 0.203 6.456 0.792 6.456 c +1.079 6.456 1.248 6.433 1.403 6.384 c +1.451 7.188 l +1.248 7.261 0.924 7.332 0.516 7.332 c +0.023 7.332 -0.372 7.164 -0.624 6.888 c +-0.912 6.564 -1.032 6.048 -1.032 5.365 c +-1.032 2.197 l +-1.933 2.197 l +-1.933 1.392 l +-1.032 1.392 l +-1.032 0.325 l +h +f +Q +q 1 0 0 1 85.3726 -66.4434 cm +0 0 m +0.78 2.94 l +0.937 3.589 1.092 4.201 1.2 4.801 c +1.236 4.801 l +1.368 4.213 1.561 3.589 1.752 2.953 c +2.7 0 l +3.588 0 l +4.488 2.904 l +4.704 3.589 4.872 4.213 5.004 4.801 c +5.04 4.801 l +5.136 4.213 5.292 3.601 5.484 2.916 c +6.313 0 l +7.356 0 l +5.484 5.809 l +4.524 5.809 l +3.636 3.037 l +3.432 2.377 3.264 1.801 3.12 1.117 c +3.096 1.117 l +2.952 1.813 2.772 2.425 2.568 3.049 c +1.632 5.809 l +0.672 5.809 l +-1.08 0 l +h +f +Q +q 1 0 0 1 96.7964 -63.5986 cm +0 0 m +-1.152 -0.024 -2.46 0.18 -2.46 1.308 c +-2.46 2.005 -2.004 2.315 -1.476 2.315 c +-0.708 2.315 -0.216 1.837 -0.048 1.344 c +-0.012 1.235 0 1.116 0 1.009 c +h +1.032 1.571 m +1.032 2.075 1.056 2.567 1.116 2.964 c +0.169 2.964 l +0.072 2.231 l +0.036 2.231 l +-0.276 2.688 -0.911 3.096 -1.739 3.096 c +-2.916 3.096 -3.516 2.269 -3.516 1.428 c +-3.516 0.024 -2.268 -0.743 -0.023 -0.731 c +-0.023 -0.853 l +-0.023 -1.332 -0.155 -2.208 -1.344 -2.196 c +-1.896 -2.196 -2.46 -2.039 -2.867 -1.765 c +-3.108 -2.472 l +-2.628 -2.772 -1.92 -2.976 -1.188 -2.976 c +0.6 -2.976 1.032 -1.765 1.032 -0.601 c +h +f +Q +q 1 0 0 1 99.3765 -61.7031 cm +0 0 m +0.323 0.193 0.876 0.408 1.403 0.408 c +2.159 0.408 2.519 0.036 2.519 -0.455 c +2.519 -0.959 2.22 -1.236 1.451 -1.523 c +0.395 -1.908 -0.097 -2.471 -0.097 -3.168 c +-0.097 -4.104 0.672 -4.871 1.907 -4.871 c +2.495 -4.871 3.012 -4.716 3.323 -4.511 c +3.071 -3.756 l +2.844 -3.887 2.424 -4.092 1.884 -4.092 c +1.26 -4.092 0.924 -3.731 0.924 -3.3 c +0.924 -2.807 1.26 -2.592 2.016 -2.303 c +3.012 -1.932 3.54 -1.428 3.54 -0.552 c +3.54 0.48 2.735 1.2 1.38 1.2 c +0.743 1.2 0.155 1.032 -0.252 0.793 c +h +f +Q +q 1 0 0 1 -102.1729 -47.2031 cm +0 0 m +0.324 0.193 0.876 0.408 1.404 0.408 c +2.16 0.408 2.52 0.036 2.52 -0.455 c +2.52 -0.959 2.22 -1.236 1.452 -1.523 c +0.396 -1.908 -0.096 -2.472 -0.096 -3.168 c +-0.096 -4.104 0.671 -4.871 1.908 -4.871 c +2.496 -4.871 3.012 -4.716 3.324 -4.512 c +3.072 -3.756 l +2.844 -3.888 2.424 -4.092 1.884 -4.092 c +1.26 -4.092 0.924 -3.731 0.924 -3.3 c +0.924 -2.807 1.26 -2.592 2.016 -2.304 c +3.012 -1.931 3.54 -1.428 3.54 -0.552 c +3.54 0.48 2.735 1.2 1.379 1.2 c +0.744 1.2 0.156 1.032 -0.252 0.793 c +h +f +Q +q 1 0 0 1 -94.2168 -49.0986 cm +0 0 m +-1.152 -0.024 -2.46 0.18 -2.46 1.308 c +-2.46 2.005 -2.004 2.315 -1.476 2.315 c +-0.708 2.315 -0.216 1.837 -0.048 1.344 c +-0.013 1.235 0 1.116 0 1.008 c +h +1.032 1.571 m +1.032 2.075 1.056 2.567 1.116 2.965 c +0.168 2.965 l +0.071 2.231 l +0.036 2.231 l +-0.276 2.688 -0.912 3.096 -1.741 3.096 c +-2.916 3.096 -3.516 2.269 -3.516 1.428 c +-3.516 0.024 -2.268 -0.744 -0.024 -0.731 c +-0.024 -0.853 l +-0.024 -1.332 -0.157 -2.208 -1.345 -2.196 c +-1.896 -2.196 -2.46 -2.039 -2.869 -1.765 c +-3.108 -2.472 l +-2.628 -2.772 -1.92 -2.976 -1.188 -2.976 c +0.6 -2.976 1.032 -1.765 1.032 -0.601 c +h +f +Q +q 1 0 0 1 -91.2393 -51.9434 cm +0 0 m +1.14 3.252 l +1.332 3.793 1.488 4.272 1.608 4.752 c +1.644 4.752 l +1.776 4.272 1.944 3.793 2.136 3.252 c +3.264 0 l +4.368 0 l +2.088 5.81 l +1.08 5.81 l +-1.128 0 l +h +f +Q +q 1 0 0 1 -82.3247 -49.6152 cm +0 0 m +0.011 -0.66 -0.276 -1.716 -1.452 -1.716 c +-2.532 -1.716 -2.989 -0.73 -3.073 0 c +h +-3.084 0.756 m +-3.06 2.185 -2.16 2.772 -1.104 2.772 c +-0.348 2.772 0.12 2.641 0.504 2.484 c +0.696 3.229 l +0.324 3.396 -0.324 3.601 -1.248 3.601 c +-3.024 3.601 -4.104 2.412 -4.104 0.672 c +-4.104 -1.092 -3.06 -2.472 -1.368 -2.472 c +0.528 -2.472 1.02 -0.804 1.02 0.265 c +1.02 0.481 1.008 0.649 0.984 0.77 c +h +f +Q +q 1 0 0 1 -76.0356 -49.5781 cm +0 0 m +0 -0.145 -0.012 -0.313 -0.048 -0.457 c +-0.204 -1.117 -0.78 -1.668 -1.572 -1.668 c +-2.664 -1.668 -3.312 -0.709 -3.312 0.563 c +-3.312 1.752 -2.724 2.724 -1.596 2.724 c +-0.888 2.724 -0.24 2.243 -0.048 1.463 c +-0.012 1.319 0 1.176 0 1.008 c +h +1.056 -5.076 m +1.056 1.943 l +1.056 2.459 1.068 3.047 1.104 3.444 c +0.156 3.444 l +0.108 2.436 l +0.084 2.436 l +-0.24 3.084 -0.936 3.575 -1.884 3.575 c +-3.288 3.575 -4.38 2.387 -4.38 0.623 c +-4.392 -1.32 -3.18 -2.496 -1.776 -2.496 c +-0.876 -2.496 -0.276 -2.076 -0.024 -1.621 c +0 -1.621 l +0 -5.076 l +h +f +Q +q 1 0 0 1 -70.2637 -51.9434 cm +0 0 m +0.78 2.94 l +0.936 3.589 1.092 4.201 1.2 4.801 c +1.236 4.801 l +1.368 4.213 1.56 3.589 1.752 2.953 c +2.7 0 l +3.588 0 l +4.488 2.904 l +4.704 3.589 4.872 4.213 5.004 4.801 c +5.04 4.801 l +5.136 4.213 5.292 3.602 5.484 2.916 c +6.312 0 l +7.356 0 l +5.484 5.81 l +4.524 5.81 l +3.636 3.037 l +3.432 2.377 3.264 1.801 3.12 1.117 c +3.096 1.117 l +2.952 1.813 2.772 2.425 2.568 3.049 c +1.632 5.81 l +0.672 5.81 l +-1.08 0 l +h +f +Q +-61.852 -51.943 1.056 5.81 re +-61.852 -46.134 m +-61.348 -53.01 m +-61.744 -53.01 -62.007 -53.322 -62.007 -53.695 c +-62.007 -54.066 -61.731 -54.367 -61.324 -54.367 c +-60.916 -54.367 -60.651 -54.066 -60.651 -53.695 c +-60.651 -53.322 -60.916 -53.01 -61.335 -53.01 c +h +f +q 1 0 0 1 -57.7725 -53.335 cm +0 0 m +0 1.392 l +1.512 1.392 l +1.512 2.197 l +0 2.197 l +0 5.329 l +0 6.048 0.204 6.456 0.792 6.456 c +1.08 6.456 1.248 6.433 1.404 6.384 c +1.452 7.188 l +1.248 7.261 0.924 7.332 0.516 7.332 c +0.024 7.332 -0.372 7.164 -0.624 6.888 c +-0.912 6.564 -1.032 6.048 -1.032 5.365 c +-1.032 2.197 l +-1.932 2.197 l +-1.932 1.392 l +-1.032 1.392 l +-1.032 0.325 l +h +f +Q +q 1 0 0 1 -55.0728 -54.6543 cm +0 0 m +1.056 0 l +1.056 3.623 l +1.08 3.623 l +1.248 3.323 1.512 3.047 1.836 2.879 c +2.148 2.688 2.52 2.58 2.916 2.58 c +3.696 2.58 4.944 3.061 4.944 5.064 c +4.944 8.521 l +3.888 8.521 l +3.888 5.185 l +3.888 4.235 3.54 3.455 2.544 3.455 c +1.86 3.455 1.332 3.937 1.128 4.512 c +1.068 4.655 1.056 4.812 1.056 5.017 c +1.056 8.521 l +0 8.521 l +h +f +Q +q 1 0 0 1 -46.0127 -46.7949 cm +0 0 m +1.008 0 1.764 -0.947 1.764 -2.268 c +1.764 -3.252 1.272 -4.488 0.024 -4.488 c +-1.212 -4.488 -1.752 -3.336 -1.752 -2.232 c +-1.752 -0.96 -1.032 0 -0.012 0 c +h +-0.036 0.792 m +-1.596 0.792 -2.82 -0.359 -2.82 -2.195 c +-2.82 -4.14 -1.536 -5.279 0.06 -5.279 c +1.728 -5.279 2.856 -4.068 2.856 -2.304 c +2.856 -0.145 1.356 0.792 -0.024 0.792 c +h +f +Q +q 1 0 0 1 -36.9648 -47.7178 cm +0 0 m +0 0.599 0.012 1.128 0.048 1.584 c +-0.888 1.584 l +-0.948 0.635 l +-0.972 0.635 l +-1.236 1.103 -1.86 1.715 -2.892 1.715 c +-3.804 1.715 -4.896 1.199 -4.896 -0.829 c +-4.896 -4.226 l +-3.84 -4.226 l +-3.84 -1.021 l +-3.84 0.083 -3.492 0.839 -2.544 0.839 c +-1.836 0.839 -1.344 0.347 -1.152 -0.133 c +-1.092 -0.276 -1.056 -0.469 -1.056 -0.673 c +-1.056 -4.226 l +0 -4.226 l +h +f +Q +q 1 0 0 1 -33.9404 -53.335 cm +0 0 m +0 1.392 l +1.512 1.392 l +1.512 2.197 l +0 2.197 l +0 5.329 l +0 6.048 0.204 6.456 0.792 6.456 c +1.08 6.456 1.248 6.433 1.404 6.384 c +1.452 7.188 l +1.248 7.261 0.924 7.332 0.516 7.332 c +0.024 7.332 -0.372 7.164 -0.624 6.888 c +-0.912 6.564 -1.032 6.048 -1.032 5.365 c +-1.032 2.197 l +-1.932 2.197 l +-1.932 1.392 l +-1.032 1.392 l +-1.032 0.325 l +h +f +Q +q 1 0 0 1 -27.6167 -50.2266 cm +0 0 m +0.228 0.06 0.528 0.084 0.864 0.084 c +2.135 0.084 2.892 -0.54 2.892 -1.633 c +2.892 -2.713 2.135 -3.229 0.984 -3.229 c +0.528 -3.229 0.18 -3.18 0 -3.145 c +h +-1.044 -3.889 m +-0.54 -3.984 0.12 -4.057 0.96 -4.057 c +1.992 -4.057 2.748 -3.816 3.228 -3.385 c +3.672 -3 3.948 -2.411 3.948 -1.692 c +3.948 -0.96 3.732 -0.385 3.312 0.035 c +2.76 0.624 1.86 0.925 0.84 0.925 c +0.528 0.925 0.239 0.912 0 0.852 c +0 4.093 l +-1.044 4.093 l +h +f +Q +q 1 0 0 1 -21.2324 -46.9512 cm +0 0 m +0.264 0.049 0.648 0.049 1.056 0.049 c +3.288 0.061 4.5 -1.199 4.5 -3.384 c +4.512 -5.315 3.432 -6.504 1.224 -6.504 c +0.684 -6.504 0.276 -6.455 0 -6.395 c +h +-1.056 -7.164 m +-0.408 -7.26 0.348 -7.332 1.176 -7.332 c +2.676 -7.332 3.744 -6.996 4.452 -6.336 c +5.184 -5.676 5.604 -4.727 5.604 -3.42 c +5.604 -2.1 5.184 -1.019 4.44 -0.275 c +3.672 0.481 2.424 0.889 0.852 0.889 c +0.096 0.889 -0.516 0.864 -1.056 0.793 c +h +f +Q +q 1 0 0 1 -14.2969 -54.2227 cm +0 0 m +4.356 0 l +4.356 0.877 l +1.056 0.877 l +1.056 3.564 l +4.104 3.564 l +4.104 4.428 l +1.056 4.428 l +1.056 8.089 l +0 8.089 l +h +f +Q +q 1 0 0 1 -1.7925 -46.3389 cm +0 0 m +-0.276 0.132 -0.888 0.336 -1.668 0.336 c +-3.42 0.336 -4.56 -0.853 -4.56 -2.64 c +-4.56 -4.428 -3.336 -5.735 -1.44 -5.735 c +-0.816 -5.735 -0.264 -5.58 0.024 -5.423 c +-0.216 -4.62 l +-0.468 -4.752 -0.864 -4.896 -1.44 -4.896 c +-2.772 -4.896 -3.492 -3.899 -3.492 -2.688 c +-3.492 -1.344 -2.628 -0.516 -1.476 -0.516 c +-0.876 -0.516 -0.48 -0.66 -0.18 -0.792 c +h +f +Q +q 1 0 0 1 1.7705 -46.7949 cm +0 0 m +1.008 0 1.764 -0.947 1.764 -2.268 c +1.764 -3.252 1.272 -4.488 0.024 -4.488 c +-1.212 -4.488 -1.752 -3.336 -1.752 -2.232 c +-1.752 -0.96 -1.032 0 -0.012 0 c +h +-0.036 0.792 m +-1.596 0.792 -2.82 -0.359 -2.82 -2.195 c +-2.82 -4.14 -1.536 -5.279 0.06 -5.279 c +1.728 -5.279 2.856 -4.068 2.856 -2.304 c +2.856 -0.145 1.356 0.792 -0.024 0.792 c +h +f +Q +q 1 0 0 1 5.9585 -50.3711 cm +0 0 m +0 -0.611 -0.012 -1.092 -0.048 -1.572 c +0.888 -1.572 l +0.948 -0.623 l +0.972 -0.623 l +1.26 -1.164 1.932 -1.703 2.892 -1.703 c +3.696 -1.703 4.944 -1.223 4.944 0.769 c +4.944 4.237 l +3.888 4.237 l +3.888 0.889 l +3.888 -0.048 3.54 -0.84 2.544 -0.84 c +1.86 -0.84 1.32 -0.347 1.128 0.24 c +1.08 0.372 1.056 0.564 1.056 0.733 c +1.056 4.237 l +0 4.237 l +h +f +Q +q 1 0 0 1 13.8433 -53.335 cm +0 0 m +0 1.392 l +1.511 1.392 l +1.511 2.197 l +0 2.197 l +0 5.329 l +0 6.048 0.203 6.456 0.792 6.456 c +1.079 6.456 1.248 6.433 1.403 6.384 c +1.451 7.188 l +1.248 7.261 0.924 7.332 0.516 7.332 c +0.023 7.332 -0.372 7.164 -0.624 6.888 c +-0.912 6.564 -1.033 6.048 -1.033 5.365 c +-1.033 2.197 l +-1.933 2.197 l +-1.933 1.392 l +-1.033 1.392 l +-1.033 0.325 l +h +f +Q +q 1 0 0 1 20.1548 -49.6152 cm +0 0 m +0.013 -0.66 -0.275 -1.716 -1.451 -1.716 c +-2.531 -1.716 -2.987 -0.73 -3.071 0 c +h +-3.084 0.756 m +-3.06 2.185 -2.159 2.772 -1.104 2.772 c +-0.348 2.772 0.12 2.641 0.505 2.484 c +0.696 3.229 l +0.324 3.396 -0.323 3.601 -1.247 3.601 c +-3.023 3.601 -4.104 2.412 -4.104 0.672 c +-4.104 -1.092 -3.06 -2.472 -1.367 -2.472 c +0.528 -2.472 1.021 -0.804 1.021 0.265 c +1.021 0.481 1.009 0.649 0.984 0.77 c +h +f +Q +q 1 0 0 1 22.4829 -50.3711 cm +0 0 m +0 -0.611 -0.012 -1.092 -0.048 -1.572 c +0.888 -1.572 l +0.948 -0.623 l +0.972 -0.623 l +1.26 -1.164 1.932 -1.703 2.892 -1.703 c +3.696 -1.703 4.944 -1.223 4.944 0.769 c +4.944 4.237 l +3.888 4.237 l +3.888 0.889 l +3.888 -0.048 3.54 -0.84 2.544 -0.84 c +1.86 -0.84 1.32 -0.347 1.128 0.24 c +1.08 0.372 1.056 0.564 1.056 0.733 c +1.056 4.237 l +0 4.237 l +h +f +Q +q 1 0 0 1 30.3667 -53.335 cm +0 0 m +0 1.392 l +1.512 1.392 l +1.512 2.197 l +0 2.197 l +0 5.329 l +0 6.048 0.203 6.456 0.792 6.456 c +1.079 6.456 1.248 6.433 1.403 6.384 c +1.451 7.188 l +1.248 7.261 0.924 7.332 0.516 7.332 c +0.023 7.332 -0.372 7.164 -0.624 6.888 c +-0.912 6.564 -1.032 6.048 -1.032 5.365 c +-1.032 2.197 l +-1.933 2.197 l +-1.933 1.392 l +-1.032 1.392 l +-1.032 0.325 l +h +f +Q +q 1 0 0 1 33.5112 -46.0029 cm +0 0 m +-0.409 0 -0.696 -0.323 -0.696 -0.755 c +-0.696 -1.188 -0.396 -1.512 0.023 -1.512 c +0.443 -1.512 0.731 -1.2 0.731 -0.755 c +0.731 -0.323 0.443 0 0.012 0 c +h +f +Q +q 1 0 0 1 -100.4565 -39.5449 cm +0 0 m +-2.461 0 l +-2.461 -0.888 l +3.528 -0.888 l +3.528 0 l +1.056 0 l +1.056 7.2 l +0 7.2 l +h +f +Q +q 1 0 0 1 -94.4326 -33.0049 cm +0 0 m +1.008 0 1.764 -0.947 1.764 -2.269 c +1.764 -3.252 1.272 -4.487 0.024 -4.487 c +-1.212 -4.487 -1.752 -3.336 -1.752 -2.23 c +-1.752 -0.96 -1.032 0 -0.012 0 c +h +-0.036 0.792 m +-1.596 0.792 -2.82 -0.359 -2.82 -2.196 c +-2.82 -4.14 -1.536 -5.279 0.06 -5.279 c +1.728 -5.279 2.856 -4.066 2.856 -2.304 c +2.856 -0.144 1.356 0.792 -0.024 0.792 c +h +f +Q +q 1 0 0 1 -86.6445 -34.709 cm +0 0 m +0 0.145 0.012 0.301 0.048 0.432 c +0.228 1.164 0.876 1.668 1.62 1.668 c +2.736 1.668 3.384 0.757 3.384 -0.575 c +3.384 -1.74 2.784 -2.736 1.668 -2.736 c +0.948 -2.736 0.264 -2.232 0.06 -1.439 c +0.036 -1.296 0 -1.14 0 -1.008 c +h +-1.056 -1.548 m +-1.056 -2.292 -1.068 -2.892 -1.104 -3.442 c +-0.156 -3.442 l +-0.096 -2.447 l +-0.072 -2.447 l +0.348 -3.168 1.044 -3.575 1.98 -3.575 c +3.396 -3.575 4.452 -2.388 4.452 -0.623 c +4.452 1.465 3.168 2.496 1.812 2.496 c +1.032 2.496 0.372 2.16 0.024 1.584 c +0 1.584 l +0 4.74 l +-1.056 4.74 l +h +f +Q +-80.873 -40.865 1.056 8.521 re +f +q 1 0 0 1 -75.0044 -35.3086 cm +0 0 m +-1.152 -0.023 -2.46 0.18 -2.46 1.308 c +-2.46 2.004 -2.004 2.316 -1.476 2.316 c +-0.708 2.316 -0.216 1.836 -0.048 1.344 c +-0.012 1.237 0 1.115 0 1.008 c +h +1.032 1.573 m +1.032 2.077 1.056 2.568 1.116 2.964 c +0.168 2.964 l +0.072 2.232 l +0.036 2.232 l +-0.276 2.688 -0.912 3.096 -1.74 3.096 c +-2.916 3.096 -3.516 2.268 -3.516 1.428 c +-3.516 0.024 -2.268 -0.744 -0.024 -0.732 c +-0.024 -0.852 l +-0.024 -1.332 -0.156 -2.208 -1.344 -2.196 c +-1.896 -2.196 -2.46 -2.04 -2.868 -1.763 c +-3.108 -2.473 l +-2.628 -2.771 -1.92 -2.976 -1.188 -2.976 c +0.6 -2.976 1.032 -1.763 1.032 -0.6 c +h +f +Q +q 1 0 0 1 -68.1406 -32.5488 cm +0 0 m +-0.276 0.133 -0.888 0.336 -1.668 0.336 c +-3.42 0.336 -4.56 -0.851 -4.56 -2.641 c +-4.56 -4.428 -3.336 -5.735 -1.44 -5.735 c +-0.816 -5.735 -0.264 -5.58 0.024 -5.424 c +-0.216 -4.62 l +-0.468 -4.752 -0.864 -4.896 -1.44 -4.896 c +-2.772 -4.896 -3.492 -3.9 -3.492 -2.687 c +-3.492 -1.344 -2.628 -0.516 -1.476 -0.516 c +-0.876 -0.516 -0.48 -0.66 -0.18 -0.792 c +h +f +Q +q 1 0 0 1 -63.2925 -35.8242 cm +0 0 m +0.012 -0.66 -0.276 -1.717 -1.452 -1.717 c +-2.532 -1.717 -2.988 -0.732 -3.072 0 c +h +-3.084 0.756 m +-3.06 2.184 -2.16 2.771 -1.104 2.771 c +-0.348 2.771 0.12 2.64 0.504 2.483 c +0.696 3.228 l +0.324 3.396 -0.324 3.6 -1.248 3.6 c +-3.024 3.6 -4.104 2.412 -4.104 0.673 c +-4.104 -1.093 -3.06 -2.473 -1.368 -2.473 c +0.528 -2.473 1.02 -0.805 1.02 0.264 c +1.02 0.479 1.008 0.647 0.984 0.768 c +h +f +Q +q 1 0 0 1 -56.0205 -33.0049 cm +0 0 m +1.008 0 1.764 -0.947 1.764 -2.269 c +1.764 -3.252 1.272 -4.487 0.024 -4.487 c +-1.212 -4.487 -1.752 -3.336 -1.752 -2.23 c +-1.752 -0.96 -1.032 0 -0.012 0 c +h +-0.036 0.792 m +-1.596 0.792 -2.82 -0.359 -2.82 -2.196 c +-2.82 -4.14 -1.536 -5.279 0.06 -5.279 c +1.728 -5.279 2.856 -4.066 2.856 -2.304 c +2.856 -0.144 1.356 0.792 -0.024 0.792 c +h +f +Q +q 1 0 0 1 -51.8203 -36.3408 cm +0 0 m +0 -0.685 -0.012 -1.272 -0.048 -1.811 c +0.876 -1.811 l +0.924 -0.672 l +0.96 -0.672 l +1.224 -1.452 1.872 -1.943 2.58 -1.943 c +2.688 -1.943 2.772 -1.933 2.868 -1.908 c +2.868 -0.924 l +2.748 -0.937 2.64 -0.948 2.496 -0.948 c +1.752 -0.948 1.224 -0.384 1.08 0.396 c +1.056 0.552 1.044 0.721 1.044 0.899 c +1.044 3.996 l +-0.012 3.996 l +h +f +Q +q 1 0 0 1 -42.9648 -33.0049 cm +0 0 m +1.008 0 1.764 -0.947 1.764 -2.269 c +1.764 -3.252 1.272 -4.487 0.024 -4.487 c +-1.212 -4.487 -1.752 -3.336 -1.752 -2.23 c +-1.752 -0.96 -1.032 0 -0.012 0 c +h +-0.036 0.792 m +-1.596 0.792 -2.82 -0.359 -2.82 -2.196 c +-2.82 -4.14 -1.536 -5.279 0.06 -5.279 c +1.728 -5.279 2.856 -4.066 2.856 -2.304 c +2.856 -0.144 1.356 0.792 -0.024 0.792 c +h +f +Q +q 1 0 0 1 -37.7207 -34.709 cm +0 0 m +0 0.145 0.011 0.301 0.048 0.432 c +0.228 1.164 0.876 1.668 1.62 1.668 c +2.736 1.668 3.384 0.757 3.384 -0.575 c +3.384 -1.74 2.783 -2.736 1.668 -2.736 c +0.948 -2.736 0.263 -2.232 0.06 -1.439 c +0.036 -1.296 0 -1.14 0 -1.008 c +h +-1.056 -1.548 m +-1.056 -2.292 -1.068 -2.892 -1.104 -3.442 c +-0.157 -3.442 l +-0.096 -2.447 l +-0.073 -2.447 l +0.347 -3.168 1.044 -3.575 1.98 -3.575 c +3.396 -3.575 4.451 -2.388 4.451 -0.623 c +4.451 1.465 3.168 2.496 1.812 2.496 c +1.031 2.496 0.372 2.16 0.024 1.584 c +0 1.584 l +0 4.74 l +-1.056 4.74 l +h +f +Q +q 1 0 0 1 -28.2534 -35.8242 cm +0 0 m +0.012 -0.66 -0.276 -1.717 -1.452 -1.717 c +-2.532 -1.717 -2.988 -0.732 -3.072 0 c +h +-3.084 0.756 m +-3.06 2.184 -2.16 2.771 -1.104 2.771 c +-0.348 2.771 0.12 2.64 0.504 2.483 c +0.696 3.228 l +0.324 3.396 -0.324 3.6 -1.248 3.6 c +-3.024 3.6 -4.104 2.412 -4.104 0.673 c +-4.104 -1.093 -3.06 -2.473 -1.368 -2.473 c +0.528 -2.473 1.02 -0.805 1.02 0.264 c +1.02 0.479 1.008 0.647 0.984 0.768 c +h +f +Q +q 1 0 0 1 -25.9258 -36.5801 cm +0 0 m +0 -0.613 -0.013 -1.093 -0.048 -1.571 c +0.888 -1.571 l +0.948 -0.624 l +0.972 -0.624 l +1.26 -1.164 1.932 -1.704 2.892 -1.704 c +3.696 -1.704 4.944 -1.225 4.944 0.768 c +4.944 4.235 l +3.888 4.235 l +3.888 0.887 l +3.888 -0.049 3.54 -0.841 2.544 -0.841 c +1.86 -0.841 1.319 -0.349 1.128 0.239 c +1.08 0.371 1.056 0.563 1.056 0.731 c +1.056 4.235 l +0 4.235 l +h +f +Q +q 1 0 0 1 -15.4497 -39.5449 cm +0 0 m +0 1.394 l +1.512 1.394 l +1.512 2.196 l +0 2.196 l +0 5.328 l +0 6.049 0.204 6.456 0.792 6.456 c +1.08 6.456 1.248 6.432 1.404 6.385 c +1.452 7.188 l +1.248 7.261 0.924 7.332 0.516 7.332 c +0.024 7.332 -0.372 7.164 -0.624 6.889 c +-0.912 6.564 -1.032 6.049 -1.032 5.364 c +-1.032 2.196 l +-1.932 2.196 l +-1.932 1.394 l +-1.032 1.394 l +-1.032 0.324 l +h +f +Q +q 1 0 0 1 -12.7495 -40.8652 cm +0 0 m +1.056 0 l +1.056 3.625 l +1.08 3.625 l +1.248 3.324 1.512 3.05 1.836 2.882 c +2.148 2.688 2.52 2.581 2.916 2.581 c +3.696 2.581 4.944 3.061 4.944 5.064 c +4.944 8.521 l +3.888 8.521 l +3.888 5.185 l +3.888 4.236 3.54 3.457 2.544 3.457 c +1.86 3.457 1.332 3.937 1.128 4.513 c +1.068 4.656 1.056 4.813 1.056 5.017 c +1.056 8.521 l +0 8.521 l +h +f +Q +-6.09 -38.151 1.056 5.807 re +-6.09 -32.345 m +-5.586 -39.221 m +-5.982 -39.221 -6.246 -39.532 -6.246 -39.903 c +-6.246 -40.277 -5.97 -40.576 -5.563 -40.576 c +-5.154 -40.576 -4.89 -40.277 -4.89 -39.903 c +-4.89 -39.532 -5.154 -39.221 -5.574 -39.221 c +h +f +q 1 0 0 1 -3.4258 -33.4121 cm +0 0 m +0.324 0.191 0.876 0.407 1.404 0.407 c +2.16 0.407 2.52 0.035 2.52 -0.456 c +2.52 -0.961 2.22 -1.235 1.452 -1.524 c +0.396 -1.907 -0.096 -2.473 -0.096 -3.168 c +-0.096 -4.104 0.672 -4.872 1.908 -4.872 c +2.496 -4.872 3.012 -4.717 3.324 -4.513 c +3.072 -3.757 l +2.844 -3.889 2.424 -4.093 1.884 -4.093 c +1.26 -4.093 0.924 -3.732 0.924 -3.301 c +0.924 -2.809 1.26 -2.593 2.016 -2.305 c +3.012 -1.933 3.54 -1.429 3.54 -0.553 c +3.54 0.479 2.736 1.199 1.38 1.199 c +0.744 1.199 0.156 1.031 -0.252 0.792 c +h +f +Q +q 1 0 0 1 4.1099 -32.3447 cm +0 0 m +0 -5.004 l +-0.804 -5.004 l +-0.804 -5.807 l +0 -5.807 l +0 -6.084 l +0 -6.899 0.192 -7.643 0.685 -8.112 c +1.08 -8.496 1.608 -8.651 2.088 -8.651 c +2.473 -8.651 2.784 -8.567 2.988 -8.483 c +2.856 -7.668 l +2.688 -7.74 2.484 -7.8 2.172 -7.8 c +1.284 -7.8 1.044 -7.008 1.044 -6.12 c +1.044 -5.807 l +2.448 -5.807 l +2.448 -5.004 l +1.057 -5.004 l +1.057 0 l +h +f +Q +7.518 -38.151 1.056 5.807 re +7.518 -32.345 m +8.021 -39.221 m +7.626 -39.221 7.362 -39.532 7.362 -39.903 c +7.362 -40.277 7.638 -40.576 8.046 -40.576 c +8.455 -40.576 8.718 -40.277 8.718 -39.903 c +8.718 -39.532 8.455 -39.221 8.034 -39.221 c +h +f +10.327 -40.865 1.056 8.521 re +f +q 1 0 0 1 16.8179 -35.8242 cm +0 0 m +0.013 -0.66 -0.275 -1.717 -1.451 -1.717 c +-2.531 -1.717 -2.987 -0.732 -3.071 0 c +h +-3.084 0.756 m +-3.06 2.184 -2.159 2.771 -1.104 2.771 c +-0.348 2.771 0.12 2.64 0.505 2.483 c +0.696 3.228 l +0.324 3.396 -0.323 3.6 -1.248 3.6 c +-3.023 3.6 -4.104 2.412 -4.104 0.673 c +-4.104 -1.093 -3.06 -2.473 -1.367 -2.473 c +0.528 -2.473 1.021 -0.805 1.021 0.264 c +1.021 0.479 1.009 0.647 0.984 0.768 c +h +f +Q +21.69 -38.151 1.056 5.807 re +21.69 -32.345 m +22.194 -39.221 m +21.798 -39.221 21.534 -39.532 21.534 -39.903 c +21.534 -40.277 21.81 -40.576 22.218 -40.576 c +22.626 -40.576 22.89 -40.277 22.89 -39.903 c +22.89 -39.532 22.626 -39.221 22.206 -39.221 c +h +f +q 1 0 0 1 24.4985 -36.5801 cm +0 0 m +0 -0.613 -0.012 -1.093 -0.048 -1.571 c +0.888 -1.571 l +0.948 -0.624 l +0.972 -0.624 l +1.26 -1.164 1.932 -1.704 2.892 -1.704 c +3.696 -1.704 4.944 -1.225 4.944 0.768 c +4.944 4.235 l +3.888 4.235 l +3.888 0.887 l +3.888 -0.049 3.54 -0.841 2.544 -0.841 c +1.86 -0.841 1.32 -0.349 1.128 0.239 c +1.08 0.371 1.056 0.563 1.056 0.731 c +1.056 4.235 l +0 4.235 l +h +f +Q +q 1 0 0 1 36.1021 -33.0049 cm +0 0 m +1.008 0 1.764 -0.947 1.764 -2.269 c +1.764 -3.252 1.271 -4.487 0.023 -4.487 c +-1.212 -4.487 -1.752 -3.336 -1.752 -2.23 c +-1.752 -0.96 -1.032 0 -0.013 0 c +h +-0.036 0.792 m +-1.597 0.792 -2.82 -0.359 -2.82 -2.196 c +-2.82 -4.14 -1.536 -5.279 0.06 -5.279 c +1.728 -5.279 2.855 -4.066 2.855 -2.304 c +2.855 -0.144 1.355 0.792 -0.024 0.792 c +h +f +Q +q 1 0 0 1 41.5615 -39.5449 cm +0 0 m +0 1.394 l +1.512 1.394 l +1.512 2.196 l +0 2.196 l +0 5.328 l +0 6.049 0.204 6.456 0.792 6.456 c +1.08 6.456 1.249 6.432 1.404 6.385 c +1.452 7.188 l +1.249 7.261 0.924 7.332 0.516 7.332 c +0.024 7.332 -0.372 7.164 -0.624 6.889 c +-0.912 6.564 -1.032 6.049 -1.032 5.364 c +-1.032 2.196 l +-1.932 2.196 l +-1.932 1.394 l +-1.032 1.394 l +-1.032 0.324 l +h +f +Q +q 1 0 0 1 44.2622 -40.8652 cm +0 0 m +1.056 0 l +1.056 3.625 l +1.08 3.625 l +1.248 3.324 1.512 3.05 1.836 2.882 c +2.148 2.688 2.521 2.581 2.916 2.581 c +3.696 2.581 4.944 3.061 4.944 5.064 c +4.944 8.521 l +3.888 8.521 l +3.888 5.185 l +3.888 4.236 3.54 3.457 2.544 3.457 c +1.86 3.457 1.332 3.937 1.128 4.513 c +1.068 4.656 1.056 4.813 1.056 5.017 c +1.056 8.521 l +0 8.521 l +h +f +Q +q 1 0 0 1 54.6055 -35.8242 cm +0 0 m +0.013 -0.66 -0.275 -1.717 -1.451 -1.717 c +-2.531 -1.717 -2.987 -0.732 -3.071 0 c +h +-3.084 0.756 m +-3.059 2.184 -2.159 2.771 -1.103 2.771 c +-0.347 2.771 0.121 2.64 0.505 2.483 c +0.697 3.228 l +0.325 3.396 -0.323 3.6 -1.247 3.6 c +-3.023 3.6 -4.103 2.412 -4.103 0.673 c +-4.103 -1.093 -3.059 -2.473 -1.367 -2.473 c +0.529 -2.473 1.021 -0.805 1.021 0.264 c +1.021 0.479 1.009 0.647 0.985 0.768 c +h +f +Q +q 1 0 0 1 56.9458 -36.3408 cm +0 0 m +0 -0.685 -0.012 -1.272 -0.048 -1.811 c +0.876 -1.811 l +0.925 -0.672 l +0.96 -0.672 l +1.225 -1.452 1.872 -1.943 2.58 -1.943 c +2.688 -1.943 2.772 -1.933 2.868 -1.908 c +2.868 -0.924 l +2.748 -0.937 2.641 -0.948 2.496 -0.948 c +1.752 -0.948 1.225 -0.384 1.08 0.396 c +1.057 0.552 1.044 0.721 1.044 0.899 c +1.044 3.996 l +-0.012 3.996 l +h +f +Q +q 1 0 0 1 -98.9688 -20.8086 cm +0 0 m +-1.152 -0.023 -2.46 0.18 -2.46 1.308 c +-2.46 2.004 -2.004 2.316 -1.476 2.316 c +-0.708 2.316 -0.216 1.836 -0.048 1.344 c +-0.013 1.237 0 1.115 0 1.008 c +h +1.032 1.573 m +1.032 2.076 1.056 2.568 1.116 2.964 c +0.168 2.964 l +0.071 2.232 l +0.036 2.232 l +-0.276 2.688 -0.912 3.096 -1.741 3.096 c +-2.916 3.096 -3.516 2.268 -3.516 1.428 c +-3.516 0.023 -2.268 -0.744 -0.024 -0.732 c +-0.024 -0.852 l +-0.024 -1.332 -0.157 -2.208 -1.345 -2.196 c +-1.896 -2.196 -2.46 -2.04 -2.868 -1.763 c +-3.108 -2.473 l +-2.628 -2.771 -1.92 -2.977 -1.188 -2.977 c +0.6 -2.977 1.032 -1.763 1.032 -0.6 c +h +f +Q +q 1 0 0 1 -95.1885 -20.209 cm +0 0 m +0 0.145 0.012 0.301 0.048 0.432 c +0.228 1.164 0.876 1.668 1.62 1.668 c +2.736 1.668 3.384 0.757 3.384 -0.576 c +3.384 -1.739 2.784 -2.736 1.668 -2.736 c +0.948 -2.736 0.264 -2.232 0.06 -1.438 c +0.036 -1.296 0 -1.14 0 -1.008 c +h +-1.056 -1.548 m +-1.056 -2.292 -1.068 -2.892 -1.104 -3.442 c +-0.156 -3.442 l +-0.096 -2.447 l +-0.072 -2.447 l +0.348 -3.168 1.044 -3.576 1.98 -3.576 c +3.396 -3.576 4.452 -2.388 4.452 -0.623 c +4.452 1.465 3.168 2.496 1.812 2.496 c +1.032 2.496 0.372 2.16 0.024 1.584 c +0 1.584 l +0 4.74 l +-1.056 4.74 l +h +f +Q +q 1 0 0 1 -88.3604 -20.209 cm +0 0 m +0 0.145 0.012 0.301 0.048 0.432 c +0.228 1.164 0.876 1.668 1.62 1.668 c +2.736 1.668 3.384 0.757 3.384 -0.576 c +3.384 -1.739 2.784 -2.736 1.668 -2.736 c +0.948 -2.736 0.264 -2.232 0.06 -1.438 c +0.036 -1.296 0 -1.14 0 -1.008 c +h +-1.056 -1.548 m +-1.056 -2.292 -1.068 -2.892 -1.104 -3.442 c +-0.156 -3.442 l +-0.096 -2.447 l +-0.072 -2.447 l +0.348 -3.168 1.044 -3.576 1.98 -3.576 c +3.396 -3.576 4.452 -2.388 4.452 -0.623 c +4.452 1.465 3.168 2.496 1.812 2.496 c +1.032 2.496 0.372 2.16 0.024 1.584 c +0 1.584 l +0 4.74 l +-1.056 4.74 l +h +f +Q +-82.588 -26.364 1.056 8.52 re +f +-79.781 -23.651 1.056 5.807 re +-79.781 -17.845 m +-79.277 -24.721 m +-79.673 -24.721 -79.937 -25.033 -79.937 -25.403 c +-79.937 -25.777 -79.661 -26.076 -79.253 -26.076 c +-78.845 -26.076 -78.581 -25.777 -78.581 -25.403 c +-78.581 -25.033 -78.845 -24.721 -79.265 -24.721 c +h +f +q 1 0 0 1 -72.8325 -18.0488 cm +0 0 m +-0.276 0.133 -0.888 0.336 -1.668 0.336 c +-3.42 0.336 -4.56 -0.852 -4.56 -2.641 c +-4.56 -4.428 -3.336 -5.736 -1.44 -5.736 c +-0.816 -5.736 -0.264 -5.58 0.024 -5.424 c +-0.216 -4.62 l +-0.468 -4.752 -0.864 -4.896 -1.44 -4.896 c +-2.772 -4.896 -3.492 -3.899 -3.492 -2.687 c +-3.492 -1.344 -2.628 -0.516 -1.476 -0.516 c +-0.876 -0.516 -0.48 -0.66 -0.18 -0.792 c +h +f +Q +q 1 0 0 1 -68.5244 -20.8086 cm +0 0 m +-1.152 -0.023 -2.46 0.18 -2.46 1.308 c +-2.46 2.004 -2.004 2.316 -1.476 2.316 c +-0.708 2.316 -0.216 1.836 -0.048 1.344 c +-0.012 1.237 0 1.115 0 1.008 c +h +1.032 1.573 m +1.032 2.076 1.056 2.568 1.116 2.964 c +0.168 2.964 l +0.072 2.232 l +0.036 2.232 l +-0.276 2.688 -0.912 3.096 -1.74 3.096 c +-2.916 3.096 -3.516 2.268 -3.516 1.428 c +-3.516 0.023 -2.268 -0.744 -0.024 -0.732 c +-0.024 -0.852 l +-0.024 -1.332 -0.156 -2.208 -1.344 -2.196 c +-1.896 -2.196 -2.46 -2.04 -2.868 -1.763 c +-3.108 -2.473 l +-2.628 -2.771 -1.92 -2.977 -1.188 -2.977 c +0.6 -2.977 1.032 -1.763 1.032 -0.6 c +h +f +Q +q 1 0 0 1 -64.5767 -25.0449 cm +0 0 m +0 1.394 l +1.512 1.394 l +1.512 2.196 l +0 2.196 l +0 5.328 l +0 6.049 0.204 6.456 0.792 6.456 c +1.08 6.456 1.248 6.432 1.404 6.385 c +1.452 7.188 l +1.248 7.26 0.924 7.332 0.516 7.332 c +0.024 7.332 -0.372 7.164 -0.624 6.889 c +-0.912 6.564 -1.032 6.049 -1.032 5.364 c +-1.032 2.196 l +-1.932 2.196 l +-1.932 1.394 l +-1.032 1.394 l +-1.032 0.324 l +h +f +Q +-61.877 -23.651 1.056 5.807 re +-61.877 -17.845 m +-61.374 -24.721 m +-61.77 -24.721 -62.033 -25.033 -62.033 -25.403 c +-62.033 -25.777 -61.757 -26.076 -61.35 -26.076 c +-60.941 -26.076 -60.677 -25.777 -60.677 -25.403 c +-60.677 -25.033 -60.941 -24.721 -61.361 -24.721 c +h +f +q 1 0 0 1 -56.6699 -18.5049 cm +0 0 m +1.008 0 1.764 -0.947 1.764 -2.269 c +1.764 -3.252 1.272 -4.487 0.023 -4.487 c +-1.212 -4.487 -1.752 -3.336 -1.752 -2.23 c +-1.752 -0.96 -1.032 0 -0.012 0 c +h +-0.036 0.792 m +-1.596 0.792 -2.82 -0.36 -2.82 -2.195 c +-2.82 -4.14 -1.536 -5.28 0.06 -5.28 c +1.728 -5.28 2.855 -4.066 2.855 -2.304 c +2.855 -0.143 1.355 0.792 -0.024 0.792 c +h +f +Q +q 1 0 0 1 -52.4814 -22.0801 cm +0 0 m +0 -0.613 -0.012 -1.093 -0.048 -1.571 c +0.888 -1.571 l +0.948 -0.624 l +0.972 -0.624 l +1.26 -1.164 1.932 -1.705 2.892 -1.705 c +3.696 -1.705 4.944 -1.225 4.944 0.768 c +4.944 4.235 l +3.888 4.235 l +3.888 0.887 l +3.888 -0.049 3.54 -0.841 2.544 -0.841 c +1.86 -0.841 1.32 -0.349 1.128 0.239 c +1.08 0.371 1.056 0.563 1.056 0.731 c +1.056 4.235 l +0 4.235 l +h +f +Q +q 1 0 0 1 -45.9658 -18.9121 cm +0 0 m +0.324 0.191 0.876 0.407 1.404 0.407 c +2.16 0.407 2.52 0.035 2.52 -0.456 c +2.52 -0.961 2.22 -1.236 1.452 -1.524 c +0.396 -1.907 -0.096 -2.473 -0.096 -3.168 c +-0.096 -4.104 0.672 -4.873 1.908 -4.873 c +2.496 -4.873 3.012 -4.717 3.324 -4.513 c +3.072 -3.757 l +2.844 -3.889 2.424 -4.093 1.884 -4.093 c +1.26 -4.093 0.924 -3.732 0.924 -3.301 c +0.924 -2.809 1.26 -2.593 2.016 -2.305 c +3.012 -1.933 3.54 -1.429 3.54 -0.553 c +3.54 0.479 2.736 1.199 1.38 1.199 c +0.744 1.199 0.156 1.031 -0.252 0.792 c +h +f +Q +q 1 0 0 1 -41.9336 -16.3574 cm +0 0 m +0.264 -0.719 0.588 -2.004 0.72 -2.878 c +1.896 -3 l +1.608 -1.979 1.08 -0.647 0.744 -0.084 c +h +f +Q +-37.05 -23.651 1.056 5.807 re +-37.05 -17.845 m +-36.546 -24.721 m +-36.942 -24.721 -37.206 -25.033 -37.206 -25.403 c +-37.206 -25.777 -36.93 -26.076 -36.522 -26.076 c +-36.114 -26.076 -35.85 -25.777 -35.85 -25.403 c +-35.85 -25.033 -36.114 -24.721 -36.534 -24.721 c +h +f +q 1 0 0 1 -32.9697 -25.0449 cm +0 0 m +0 1.394 l +1.512 1.394 l +1.512 2.196 l +0 2.196 l +0 5.328 l +0 6.049 0.204 6.456 0.792 6.456 c +1.08 6.456 1.248 6.432 1.404 6.385 c +1.452 7.188 l +1.248 7.26 0.924 7.332 0.516 7.332 c +0.024 7.332 -0.372 7.164 -0.624 6.889 c +-0.912 6.564 -1.032 6.049 -1.032 5.364 c +-1.032 2.196 l +-1.932 2.196 l +-1.932 1.394 l +-1.032 1.394 l +-1.032 0.324 l +h +f +Q +q 1 0 0 1 -27.8696 -18.9121 cm +0 0 m +0.324 0.191 0.876 0.407 1.404 0.407 c +2.16 0.407 2.52 0.035 2.52 -0.456 c +2.52 -0.961 2.22 -1.236 1.452 -1.524 c +0.396 -1.907 -0.096 -2.473 -0.096 -3.168 c +-0.096 -4.104 0.672 -4.873 1.908 -4.873 c +2.496 -4.873 3.012 -4.717 3.324 -4.513 c +3.072 -3.757 l +2.844 -3.889 2.424 -4.093 1.884 -4.093 c +1.26 -4.093 0.924 -3.732 0.924 -3.301 c +0.924 -2.809 1.26 -2.593 2.016 -2.305 c +3.012 -1.933 3.54 -1.429 3.54 -0.553 c +3.54 0.479 2.736 1.199 1.38 1.199 c +0.744 1.199 0.156 1.031 -0.252 0.792 c +h +f +Q +q 1 0 0 1 -22.9736 -26.3643 cm +0 0 m +1.056 0 l +1.056 3.624 l +1.08 3.624 l +1.248 3.323 1.512 3.048 1.836 2.88 c +2.148 2.688 2.52 2.579 2.916 2.579 c +3.696 2.579 4.944 3.06 4.944 5.063 c +4.944 8.52 l +3.888 8.52 l +3.888 5.184 l +3.888 4.235 3.54 3.456 2.544 3.456 c +1.86 3.456 1.332 3.936 1.128 4.512 c +1.068 4.655 1.056 4.812 1.056 5.016 c +1.056 8.52 l +0 8.52 l +h +f +Q +q 1 0 0 1 -13.9136 -18.5049 cm +0 0 m +1.008 0 1.764 -0.947 1.764 -2.269 c +1.764 -3.252 1.272 -4.487 0.024 -4.487 c +-1.212 -4.487 -1.752 -3.336 -1.752 -2.23 c +-1.752 -0.96 -1.032 0 -0.012 0 c +h +-0.036 0.792 m +-1.596 0.792 -2.82 -0.36 -2.82 -2.195 c +-2.82 -4.14 -1.536 -5.28 0.06 -5.28 c +1.728 -5.28 2.856 -4.066 2.856 -2.304 c +2.856 -0.143 1.356 0.792 -0.024 0.792 c +h +f +Q +q 1 0 0 1 -4.8657 -19.4287 cm +0 0 m +0 0.601 0.012 1.128 0.048 1.584 c +-0.888 1.584 l +-0.948 0.636 l +-0.972 0.636 l +-1.236 1.104 -1.86 1.716 -2.892 1.716 c +-3.804 1.716 -4.896 1.2 -4.896 -0.828 c +-4.896 -4.223 l +-3.84 -4.223 l +-3.84 -1.021 l +-3.84 0.084 -3.492 0.84 -2.544 0.84 c +-1.836 0.84 -1.344 0.349 -1.152 -0.132 c +-1.092 -0.275 -1.056 -0.468 -1.056 -0.672 c +-1.056 -4.223 l +0 -4.223 l +h +f +Q +-3.114 -26.364 1.056 8.52 re +f +q 1 0 0 1 3.6538 -21.2891 cm +0 0 m +0 -0.144 -0.013 -0.312 -0.049 -0.455 c +-0.204 -1.114 -0.78 -1.668 -1.572 -1.668 c +-2.665 -1.668 -3.313 -0.707 -3.313 0.564 c +-3.313 1.753 -2.725 2.725 -1.596 2.725 c +-0.888 2.725 -0.24 2.244 -0.049 1.465 c +-0.013 1.32 0 1.176 0 1.008 c +h +1.056 -5.075 m +1.056 1.944 l +1.056 2.461 1.067 3.049 1.104 3.444 c +0.155 3.444 l +0.107 2.437 l +0.084 2.437 l +-0.24 3.085 -0.937 3.576 -1.884 3.576 c +-3.289 3.576 -4.38 2.389 -4.38 0.625 c +-4.393 -1.32 -3.181 -2.496 -1.776 -2.496 c +-0.876 -2.496 -0.276 -2.075 -0.024 -1.619 c +0 -1.619 l +0 -5.075 l +h +f +Q +q 1 0 0 1 10.0615 -20.1729 cm +0 0 m +0 0.132 0.013 0.265 0.037 0.384 c +0.241 1.116 0.865 1.62 1.621 1.62 c +2.737 1.62 3.385 0.721 3.385 -0.612 c +3.385 -1.788 2.785 -2.784 1.657 -2.784 c +0.937 -2.784 0.265 -2.279 0.061 -1.487 c +0.025 -1.356 0 -1.212 0 -1.044 c +h +-1.055 -6.191 m +0 -6.191 l +0 -2.556 l +0.025 -2.556 l +0.397 -3.204 1.069 -3.612 2.004 -3.612 c +3.445 -3.612 4.453 -2.412 4.453 -0.659 c +4.453 1.429 3.133 2.46 1.836 2.46 c +0.997 2.46 0.336 2.137 -0.107 1.368 c +-0.131 1.368 l +-0.191 2.328 l +-1.103 2.328 l +-1.067 1.933 -1.055 1.345 -1.055 0.828 c +h +f +Q +q 1 0 0 1 19.5288 -21.3242 cm +0 0 m +0.013 -0.659 -0.275 -1.717 -1.451 -1.717 c +-2.531 -1.717 -2.987 -0.732 -3.071 0 c +h +-3.084 0.757 m +-3.06 2.184 -2.159 2.771 -1.104 2.771 c +-0.348 2.771 0.12 2.64 0.505 2.483 c +0.696 3.228 l +0.324 3.396 -0.323 3.6 -1.248 3.6 c +-3.023 3.6 -4.104 2.412 -4.104 0.673 c +-4.104 -1.093 -3.06 -2.473 -1.367 -2.473 c +0.528 -2.473 1.021 -0.805 1.021 0.264 c +1.021 0.479 1.009 0.647 0.984 0.768 c +h +f +Q +q 1 0 0 1 24.4136 -21.8408 cm +0 0 m +0 -0.685 -0.012 -1.272 -0.048 -1.811 c +0.876 -1.811 l +0.924 -0.672 l +0.96 -0.672 l +1.225 -1.452 1.872 -1.944 2.58 -1.944 c +2.688 -1.944 2.772 -1.933 2.868 -1.908 c +2.868 -0.924 l +2.748 -0.937 2.641 -0.948 2.496 -0.948 c +1.752 -0.948 1.225 -0.384 1.08 0.396 c +1.057 0.552 1.044 0.721 1.044 0.899 c +1.044 3.996 l +-0.012 3.996 l +h +f +Q +q 1 0 0 1 31.9365 -21.3242 cm +0 0 m +0.013 -0.659 -0.275 -1.717 -1.451 -1.717 c +-2.531 -1.717 -2.987 -0.732 -3.071 0 c +h +-3.084 0.757 m +-3.059 2.184 -2.159 2.771 -1.103 2.771 c +-0.347 2.771 0.121 2.64 0.505 2.483 c +0.697 3.228 l +0.325 3.396 -0.323 3.6 -1.248 3.6 c +-3.023 3.6 -4.103 2.412 -4.103 0.673 c +-4.103 -1.093 -3.059 -2.473 -1.367 -2.473 c +0.529 -2.473 1.021 -0.805 1.021 0.264 c +1.021 0.479 1.009 0.647 0.985 0.768 c +h +f +Q +36.713 -20.7 -2.964 -0.769 re +f +q 1 0 0 1 37.8062 -18.9121 cm +0 0 m +0.323 0.191 0.876 0.407 1.403 0.407 c +2.159 0.407 2.52 0.035 2.52 -0.456 c +2.52 -0.961 2.22 -1.236 1.451 -1.524 c +0.396 -1.907 -0.097 -2.473 -0.097 -3.168 c +-0.097 -4.104 0.671 -4.873 1.907 -4.873 c +2.495 -4.873 3.012 -4.717 3.323 -4.513 c +3.071 -3.757 l +2.844 -3.889 2.424 -4.093 1.884 -4.093 c +1.26 -4.093 0.924 -3.732 0.924 -3.301 c +0.924 -2.809 1.26 -2.593 2.016 -2.305 c +3.012 -1.933 3.54 -1.429 3.54 -0.553 c +3.54 0.479 2.735 1.199 1.379 1.199 c +0.743 1.199 0.155 1.031 -0.252 0.792 c +h +f +Q +q 1 0 0 1 45.7612 -20.8086 cm +0 0 m +-1.151 -0.023 -2.46 0.18 -2.46 1.308 c +-2.46 2.004 -2.004 2.316 -1.476 2.316 c +-0.708 2.316 -0.216 1.836 -0.048 1.344 c +-0.012 1.237 0 1.115 0 1.008 c +h +1.032 1.573 m +1.032 2.076 1.057 2.568 1.116 2.964 c +0.169 2.964 l +0.072 2.232 l +0.036 2.232 l +-0.275 2.688 -0.911 3.096 -1.74 3.096 c +-2.916 3.096 -3.516 2.268 -3.516 1.428 c +-3.516 0.023 -2.268 -0.744 -0.023 -0.732 c +-0.023 -0.852 l +-0.023 -1.332 -0.156 -2.208 -1.344 -2.196 c +-1.896 -2.196 -2.46 -2.04 -2.868 -1.763 c +-3.107 -2.473 l +-2.628 -2.771 -1.92 -2.977 -1.188 -2.977 c +0.601 -2.977 1.032 -1.763 1.032 -0.6 c +h +f +Q +q 1 0 0 1 48.7368 -23.6514 cm +0 0 m +1.14 3.252 l +1.332 3.79 1.488 4.271 1.607 4.751 c +1.644 4.751 l +1.775 4.271 1.943 3.79 2.136 3.252 c +3.264 0 l +4.368 0 l +2.088 5.807 l +1.08 5.807 l +-1.128 0 l +h +f +Q +q 1 0 0 1 57.6528 -21.3242 cm +0 0 m +0.013 -0.659 -0.275 -1.717 -1.451 -1.717 c +-2.531 -1.717 -2.987 -0.732 -3.071 0 c +h +-3.084 0.757 m +-3.06 2.184 -2.159 2.771 -1.104 2.771 c +-0.348 2.771 0.12 2.64 0.505 2.483 c +0.696 3.228 l +0.324 3.396 -0.323 3.6 -1.247 3.6 c +-3.023 3.6 -4.104 2.412 -4.104 0.673 c +-4.104 -1.093 -3.06 -2.473 -1.367 -2.473 c +0.528 -2.473 1.021 -0.805 1.021 0.264 c +1.021 0.479 1.009 0.647 0.984 0.768 c +h +f +Q +q 1 0 0 1 63.9409 -21.2891 cm +0 0 m +0 -0.144 -0.012 -0.312 -0.048 -0.455 c +-0.204 -1.114 -0.78 -1.668 -1.572 -1.668 c +-2.664 -1.668 -3.312 -0.707 -3.312 0.564 c +-3.312 1.753 -2.724 2.725 -1.596 2.725 c +-0.888 2.725 -0.24 2.244 -0.048 1.465 c +-0.012 1.32 0 1.176 0 1.008 c +h +1.056 -5.075 m +1.056 1.944 l +1.056 2.461 1.068 3.049 1.104 3.444 c +0.156 3.444 l +0.108 2.437 l +0.084 2.437 l +-0.24 3.085 -0.936 3.576 -1.884 3.576 c +-3.288 3.576 -4.38 2.389 -4.38 0.625 c +-4.392 -1.32 -3.18 -2.496 -1.776 -2.496 c +-0.876 -2.496 -0.276 -2.075 -0.023 -1.619 c +0 -1.619 l +0 -5.075 l +h +f +Q +q 1 0 0 1 69.3892 -17.8447 cm +0 0 m +0 -5.004 l +-0.804 -5.004 l +-0.804 -5.807 l +0 -5.807 l +0 -6.084 l +0 -6.899 0.192 -7.643 0.685 -8.112 c +1.08 -8.496 1.608 -8.651 2.088 -8.651 c +2.473 -8.651 2.784 -8.567 2.988 -8.483 c +2.856 -7.668 l +2.688 -7.74 2.484 -7.8 2.171 -7.8 c +1.284 -7.8 1.044 -7.008 1.044 -6.12 c +1.044 -5.807 l +2.448 -5.807 l +2.448 -5.004 l +1.057 -5.004 l +1.057 0 l +h +f +Q +q 1 0 0 1 72.8091 -21.8408 cm +0 0 m +0 -0.685 -0.012 -1.272 -0.048 -1.811 c +0.876 -1.811 l +0.925 -0.672 l +0.959 -0.672 l +1.225 -1.452 1.872 -1.944 2.58 -1.944 c +2.688 -1.944 2.772 -1.933 2.868 -1.908 c +2.868 -0.924 l +2.748 -0.937 2.641 -0.948 2.496 -0.948 c +1.751 -0.948 1.225 -0.384 1.08 0.396 c +1.057 0.552 1.043 0.721 1.043 0.899 c +1.043 3.996 l +-0.012 3.996 l +h +f +Q +q 1 0 0 1 79.0493 -18.5049 cm +0 0 m +1.008 0 1.763 -0.947 1.763 -2.269 c +1.763 -3.252 1.271 -4.487 0.023 -4.487 c +-1.212 -4.487 -1.752 -3.336 -1.752 -2.23 c +-1.752 -0.96 -1.032 0 -0.013 0 c +h +-0.036 0.792 m +-1.597 0.792 -2.821 -0.36 -2.821 -2.195 c +-2.821 -4.14 -1.536 -5.28 0.06 -5.28 c +1.728 -5.28 2.855 -4.066 2.855 -2.304 c +2.855 -0.143 1.355 0.792 -0.024 0.792 c +h +f +Q +q 1 0 0 1 83.2368 -22.0801 cm +0 0 m +0 -0.613 -0.024 -1.093 -0.048 -1.571 c +0.876 -1.571 l +0.924 -0.637 l +0.96 -0.637 l +1.284 -1.188 1.824 -1.705 2.796 -1.705 c +3.576 -1.705 4.176 -1.225 4.428 -0.54 c +4.452 -0.54 l +4.632 -0.877 4.872 -1.117 5.112 -1.297 c +5.46 -1.561 5.832 -1.705 6.384 -1.705 c +7.164 -1.705 8.304 -1.201 8.304 0.815 c +8.304 4.235 l +7.272 4.235 l +7.272 0.947 l +7.272 -0.181 6.853 -0.841 6.012 -0.841 c +5.4 -0.841 4.944 -0.396 4.752 0.107 c +4.704 0.265 4.668 0.455 4.668 0.635 c +4.668 4.235 l +3.636 4.235 l +3.636 0.756 l +3.636 -0.181 3.229 -0.841 2.424 -0.841 c +1.776 -0.841 1.284 -0.313 1.116 0.215 c +1.056 0.371 1.032 0.551 1.032 0.731 c +1.032 4.235 l +0 4.235 l +h +f +Q +q 1 0 0 1 -98.0327 -6.7031 cm +0 0 m +-0.804 -2.328 l +-0.972 -2.856 -1.093 -3.337 -1.212 -3.805 c +-1.248 -3.805 l +-1.356 -3.337 -1.489 -2.832 -1.644 -2.341 c +-2.436 0 l +h +-2.652 0.815 m +-3.492 3.359 l +-4.573 3.359 l +-1.824 -4.729 l +-0.564 -4.729 l +2.196 3.359 l +1.08 3.359 l +0.216 0.815 l +h +f +Q +q 1 0 0 1 -90.8687 -6.7871 cm +0 0 m +0 -0.144 -0.012 -0.312 -0.048 -0.457 c +-0.204 -1.116 -0.78 -1.668 -1.572 -1.668 c +-2.664 -1.668 -3.312 -0.709 -3.312 0.563 c +-3.312 1.752 -2.724 2.723 -1.596 2.723 c +-0.888 2.723 -0.24 2.243 -0.048 1.463 c +-0.012 1.319 0 1.176 0 1.008 c +h +1.056 -5.076 m +1.056 1.943 l +1.056 2.459 1.068 3.047 1.104 3.443 c +0.156 3.443 l +0.108 2.436 l +0.084 2.436 l +-0.24 3.084 -0.936 3.575 -1.884 3.575 c +-3.288 3.575 -4.38 2.388 -4.38 0.623 c +-4.392 -1.32 -3.18 -2.496 -1.776 -2.496 c +-0.876 -2.496 -0.276 -2.076 -0.024 -1.621 c +0 -1.621 l +0 -5.076 l +h +f +Q +q 1 0 0 1 -85.6606 -4.0039 cm +0 0 m +1.008 0 1.764 -0.947 1.764 -2.267 c +1.764 -3.252 1.272 -4.488 0.024 -4.488 c +-1.212 -4.488 -1.752 -3.336 -1.752 -2.231 c +-1.752 -0.96 -1.032 0 -0.012 0 c +h +-0.036 0.792 m +-1.596 0.792 -2.82 -0.359 -2.82 -2.195 c +-2.82 -4.14 -1.536 -5.279 0.06 -5.279 c +1.728 -5.279 2.856 -4.068 2.856 -2.303 c +2.856 -0.145 1.356 0.792 -0.024 0.792 c +h +f +Q +q 1 0 0 1 -80.4165 -5.6719 cm +0 0 m +0 0.133 0.012 0.264 0.036 0.385 c +0.24 1.116 0.864 1.62 1.62 1.62 c +2.736 1.62 3.384 0.721 3.384 -0.611 c +3.384 -1.788 2.784 -2.783 1.656 -2.783 c +0.936 -2.783 0.264 -2.279 0.06 -1.488 c +0.024 -1.355 0 -1.212 0 -1.044 c +h +-1.056 -6.191 m +0 -6.191 l +0 -2.557 l +0.024 -2.557 l +0.396 -3.204 1.068 -3.611 2.004 -3.611 c +3.444 -3.611 4.452 -2.412 4.452 -0.66 c +4.452 1.428 3.132 2.46 1.836 2.46 c +0.996 2.46 0.336 2.137 -0.108 1.368 c +-0.132 1.368 l +-0.192 2.328 l +-1.104 2.328 l +-1.068 1.932 -1.056 1.344 -1.056 0.828 c +h +f +Q +q 1 0 0 1 -70.9487 -6.8242 cm +0 0 m +0.011 -0.66 -0.276 -1.716 -1.452 -1.716 c +-2.533 -1.716 -2.989 -0.731 -3.073 0 c +h +-3.084 0.756 m +-3.06 2.185 -2.16 2.772 -1.104 2.772 c +-0.348 2.772 0.12 2.641 0.504 2.484 c +0.696 3.229 l +0.324 3.396 -0.324 3.601 -1.248 3.601 c +-3.024 3.601 -4.104 2.412 -4.104 0.673 c +-4.104 -1.092 -3.06 -2.472 -1.368 -2.472 c +0.528 -2.472 1.02 -0.804 1.02 0.265 c +1.02 0.48 1.008 0.648 0.984 0.769 c +h +f +Q +-64.997 -3.344 -1.056 -8.088 re +f +-63.077 -11.863 1.056 8.52 re +f +-60.269 -11.863 1.056 8.52 re +f +q 1 0 0 1 -52.6016 -4.9277 cm +0 0 m +0 0.6 0.012 1.128 0.048 1.584 c +-0.888 1.584 l +-0.948 0.636 l +-0.972 0.636 l +-1.236 1.104 -1.86 1.716 -2.892 1.716 c +-3.804 1.716 -4.896 1.201 -4.896 -0.828 c +-4.896 -4.224 l +-3.84 -4.224 l +-3.84 -1.02 l +-3.84 0.084 -3.492 0.84 -2.544 0.84 c +-1.836 0.84 -1.344 0.348 -1.152 -0.131 c +-1.092 -0.275 -1.056 -0.469 -1.056 -0.672 c +-1.056 -4.224 l +0 -4.224 l +h +f +Q +q 1 0 0 1 -50.9927 -4.4121 cm +0 0 m +0.324 0.192 0.876 0.408 1.404 0.408 c +2.16 0.408 2.52 0.036 2.52 -0.455 c +2.52 -0.959 2.22 -1.235 1.452 -1.522 c +0.396 -1.907 -0.096 -2.472 -0.096 -3.168 c +-0.096 -4.103 0.672 -4.871 1.908 -4.871 c +2.496 -4.871 3.012 -4.716 3.324 -4.512 c +3.072 -3.756 l +2.844 -3.888 2.424 -4.092 1.884 -4.092 c +1.26 -4.092 0.924 -3.731 0.924 -3.3 c +0.924 -2.808 1.26 -2.592 2.016 -2.304 c +3.012 -1.932 3.54 -1.428 3.54 -0.552 c +3.54 0.481 2.736 1.2 1.38 1.2 c +0.744 1.2 0.156 1.032 -0.252 0.793 c +h +f +Q +q 1 0 0 1 -44.8257 -10.5439 cm +0 0 m +0 1.393 l +1.512 1.393 l +1.512 2.196 l +0 2.196 l +0 5.328 l +0 6.048 0.204 6.456 0.792 6.456 c +1.08 6.456 1.248 6.433 1.404 6.384 c +1.452 7.189 l +1.248 7.261 0.924 7.332 0.516 7.332 c +0.024 7.332 -0.372 7.164 -0.624 6.888 c +-0.912 6.563 -1.032 6.048 -1.032 5.364 c +-1.032 2.196 l +-1.932 2.196 l +-1.932 1.393 l +-1.032 1.393 l +-1.032 0.324 l +h +f +Q +q 1 0 0 1 -42.1133 -7.3398 cm +0 0 m +0 -0.684 -0.012 -1.271 -0.048 -1.812 c +0.876 -1.812 l +0.924 -0.672 l +0.96 -0.672 l +1.224 -1.452 1.872 -1.943 2.58 -1.943 c +2.688 -1.943 2.772 -1.931 2.868 -1.908 c +2.868 -0.924 l +2.748 -0.936 2.64 -0.947 2.496 -0.947 c +1.752 -0.947 1.224 -0.383 1.08 0.396 c +1.056 0.553 1.044 0.721 1.044 0.9 c +1.044 3.996 l +-0.012 3.996 l +h +f +Q +q 1 0 0 1 -35.2134 -6.3066 cm +0 0 m +-1.152 -0.025 -2.46 0.179 -2.46 1.307 c +-2.46 2.003 -2.004 2.314 -1.476 2.314 c +-0.708 2.314 -0.216 1.835 -0.048 1.343 c +-0.012 1.234 0 1.115 0 1.007 c +h +1.032 1.571 m +1.032 2.075 1.056 2.566 1.116 2.963 c +0.168 2.963 l +0.072 2.23 l +0.036 2.23 l +-0.276 2.688 -0.912 3.095 -1.74 3.095 c +-2.916 3.095 -3.516 2.268 -3.516 1.427 c +-3.516 0.023 -2.268 -0.745 -0.024 -0.732 c +-0.024 -0.854 l +-0.024 -1.333 -0.156 -2.208 -1.344 -2.197 c +-1.896 -2.197 -2.46 -2.041 -2.868 -1.766 c +-3.108 -2.473 l +-2.628 -2.773 -1.92 -2.977 -1.188 -2.977 c +0.6 -2.977 1.032 -1.766 1.032 -0.602 c +h +f +Q +q 1 0 0 1 -31.2656 -10.5439 cm +0 0 m +0 1.393 l +1.512 1.393 l +1.512 2.196 l +0 2.196 l +0 5.328 l +0 6.048 0.204 6.456 0.792 6.456 c +1.08 6.456 1.248 6.433 1.404 6.384 c +1.452 7.189 l +1.248 7.261 0.924 7.332 0.516 7.332 c +0.024 7.332 -0.372 7.164 -0.624 6.888 c +-0.912 6.563 -1.032 6.048 -1.032 5.364 c +-1.032 2.196 l +-1.932 2.196 l +-1.932 1.393 l +-1.032 1.393 l +-1.032 0.324 l +h +f +Q +q 1 0 0 1 -26.2378 -4.0039 cm +0 0 m +1.008 0 1.764 -0.947 1.764 -2.267 c +1.764 -3.252 1.272 -4.488 0.024 -4.488 c +-1.212 -4.488 -1.752 -3.336 -1.752 -2.231 c +-1.752 -0.96 -1.032 0 -0.012 0 c +h +-0.036 0.792 m +-1.596 0.792 -2.82 -0.359 -2.82 -2.195 c +-2.82 -4.14 -1.536 -5.279 0.06 -5.279 c +1.728 -5.279 2.856 -4.068 2.856 -2.303 c +2.856 -0.145 1.356 0.792 -0.024 0.792 c +h +f +Q +q 1 0 0 1 -22.0376 -7.3398 cm +0 0 m +0 -0.684 -0.012 -1.271 -0.048 -1.812 c +0.876 -1.812 l +0.924 -0.672 l +0.96 -0.672 l +1.223 -1.452 1.872 -1.943 2.58 -1.943 c +2.688 -1.943 2.772 -1.931 2.868 -1.908 c +2.868 -0.924 l +2.748 -0.936 2.64 -0.947 2.495 -0.947 c +1.752 -0.947 1.223 -0.383 1.079 0.396 c +1.056 0.553 1.044 0.721 1.044 0.9 c +1.044 3.996 l +-0.012 3.996 l +h +f +Q +q 1 0 0 1 -15.1616 -9.1514 cm +0 0 m +0.78 2.939 l +0.936 3.587 1.092 4.2 1.2 4.8 c +1.236 4.8 l +1.368 4.212 1.56 3.587 1.752 2.952 c +2.7 0 l +3.588 0 l +4.488 2.903 l +4.704 3.587 4.872 4.212 5.004 4.8 c +5.04 4.8 l +5.136 4.212 5.292 3.6 5.484 2.916 c +6.312 0 l +7.356 0 l +5.484 5.808 l +4.524 5.808 l +3.636 3.036 l +3.432 2.376 3.264 1.8 3.12 1.116 c +3.096 1.116 l +2.952 1.812 2.772 2.425 2.568 3.049 c +1.632 5.808 l +0.672 5.808 l +-1.08 0 l +h +f +Q +-6.75 -9.151 1.056 5.808 re +-6.75 -3.344 m +-6.246 -10.22 m +-6.642 -10.22 -6.905 -10.531 -6.905 -10.903 c +-6.905 -11.275 -6.629 -11.576 -6.222 -11.576 c +-5.813 -11.576 -5.549 -11.275 -5.549 -10.903 c +-5.549 -10.531 -5.813 -10.22 -6.233 -10.22 c +h +f +q 1 0 0 1 -2.6694 -10.5439 cm +0 0 m +0 1.393 l +1.512 1.393 l +1.512 2.196 l +0 2.196 l +0 5.328 l +0 6.048 0.204 6.456 0.792 6.456 c +1.08 6.456 1.248 6.433 1.404 6.384 c +1.452 7.189 l +1.248 7.261 0.924 7.332 0.516 7.332 c +0.024 7.332 -0.372 7.164 -0.624 6.888 c +-0.912 6.563 -1.032 6.048 -1.032 5.364 c +-1.032 2.196 l +-1.932 2.196 l +-1.932 1.393 l +-1.032 1.393 l +-1.032 0.324 l +h +f +Q +q 1 0 0 1 0.0303 -11.8633 cm +0 0 m +1.056 0 l +1.056 3.623 l +1.08 3.623 l +1.248 3.323 1.512 3.048 1.836 2.88 c +2.148 2.688 2.52 2.58 2.916 2.58 c +3.696 2.58 4.944 3.06 4.944 5.063 c +4.944 8.52 l +3.888 8.52 l +3.888 5.184 l +3.888 4.235 3.541 3.455 2.544 3.455 c +1.86 3.455 1.332 3.936 1.128 4.512 c +1.068 4.655 1.056 4.812 1.056 5.016 c +1.056 8.52 l +0 8.52 l +h +f +Q +q 1 0 0 1 10.5073 -10.5439 cm +0 0 m +0 1.393 l +1.511 1.393 l +1.511 2.196 l +0 2.196 l +0 5.328 l +0 6.048 0.203 6.456 0.792 6.456 c +1.079 6.456 1.248 6.433 1.403 6.384 c +1.451 7.189 l +1.248 7.261 0.924 7.332 0.516 7.332 c +0.023 7.332 -0.372 7.164 -0.624 6.888 c +-0.912 6.563 -1.032 6.048 -1.032 5.364 c +-1.032 2.196 l +-1.933 2.196 l +-1.933 1.393 l +-1.032 1.393 l +-1.032 0.324 l +h +f +Q +q 1 0 0 1 13.2056 -11.8633 cm +0 0 m +1.056 0 l +1.056 3.623 l +1.08 3.623 l +1.248 3.323 1.512 3.048 1.836 2.88 c +2.148 2.688 2.521 2.58 2.916 2.58 c +3.696 2.58 4.944 3.06 4.944 5.063 c +4.944 8.52 l +3.888 8.52 l +3.888 5.184 l +3.888 4.235 3.54 3.455 2.544 3.455 c +1.86 3.455 1.332 3.936 1.128 4.512 c +1.068 4.655 1.056 4.812 1.056 5.016 c +1.056 8.52 l +0 8.52 l +h +f +Q +q 1 0 0 1 23.5493 -6.8242 cm +0 0 m +0.012 -0.66 -0.275 -1.716 -1.451 -1.716 c +-2.531 -1.716 -2.988 -0.731 -3.072 0 c +h +-3.084 0.756 m +-3.06 2.185 -2.159 2.772 -1.104 2.772 c +-0.348 2.772 0.12 2.641 0.505 2.484 c +0.696 3.229 l +0.324 3.396 -0.323 3.601 -1.247 3.601 c +-3.023 3.601 -4.104 2.412 -4.104 0.673 c +-4.104 -1.092 -3.06 -2.472 -1.367 -2.472 c +0.528 -2.472 1.021 -0.804 1.021 0.265 c +1.021 0.48 1.009 0.648 0.984 0.769 c +h +f +Q +q 1 0 0 1 30.9775 -11.6475 cm +0 0 m +-0.191 2.88 l +-0.839 2.88 l +-1.031 0 l +h +-1.788 0 m +-1.979 2.88 l +-2.627 2.88 l +-2.819 0 l +h +f +Q +q 1 0 0 1 38.1421 -3.5957 cm +0 0 m +-0.372 0.192 -1.151 0.384 -2.136 0.384 c +-4.416 0.384 -6.121 -1.056 -6.121 -3.721 c +-6.121 -6.264 -4.403 -7.969 -1.896 -7.969 c +-0.899 -7.969 -0.252 -7.752 0.024 -7.607 c +-0.239 -6.756 l +-0.624 -6.948 -1.188 -7.091 -1.859 -7.091 c +-3.756 -7.091 -5.016 -5.879 -5.016 -3.756 c +-5.016 -1.764 -3.876 -0.503 -1.92 -0.503 c +-1.271 -0.503 -0.624 -0.636 -0.205 -0.839 c +h +f +Q +q 1 0 0 1 39.4243 -7.3398 cm +0 0 m +0 -0.684 -0.012 -1.271 -0.048 -1.812 c +0.876 -1.812 l +0.925 -0.672 l +0.96 -0.672 l +1.225 -1.452 1.872 -1.943 2.58 -1.943 c +2.688 -1.943 2.772 -1.931 2.868 -1.908 c +2.868 -0.924 l +2.748 -0.936 2.641 -0.947 2.496 -0.947 c +1.752 -0.947 1.225 -0.383 1.08 0.396 c +1.056 0.553 1.044 0.721 1.044 0.9 c +1.044 3.996 l +-0.012 3.996 l +h +f +Q +q 1 0 0 1 46.9487 -6.8242 cm +0 0 m +0.013 -0.66 -0.275 -1.716 -1.451 -1.716 c +-2.531 -1.716 -2.987 -0.731 -3.071 0 c +h +-3.084 0.756 m +-3.06 2.185 -2.159 2.772 -1.104 2.772 c +-0.348 2.772 0.12 2.641 0.505 2.484 c +0.696 3.229 l +0.324 3.396 -0.323 3.601 -1.248 3.601 c +-3.023 3.601 -4.104 2.412 -4.104 0.673 c +-4.104 -1.092 -3.06 -2.472 -1.367 -2.472 c +0.528 -2.472 1.021 -0.804 1.021 0.265 c +1.021 0.48 1.009 0.648 0.984 0.769 c +h +f +Q +q 1 0 0 1 52.3374 -6.3066 cm +0 0 m +-1.152 -0.025 -2.46 0.179 -2.46 1.307 c +-2.46 2.003 -2.004 2.314 -1.476 2.314 c +-0.708 2.314 -0.216 1.835 -0.048 1.343 c +-0.012 1.234 0 1.115 0 1.007 c +h +1.032 1.571 m +1.032 2.075 1.056 2.566 1.116 2.963 c +0.169 2.963 l +0.072 2.23 l +0.036 2.23 l +-0.276 2.688 -0.911 3.095 -1.739 3.095 c +-2.916 3.095 -3.516 2.268 -3.516 1.427 c +-3.516 0.023 -2.268 -0.745 -0.024 -0.732 c +-0.024 -0.854 l +-0.024 -1.333 -0.155 -2.208 -1.344 -2.197 c +-1.896 -2.197 -2.46 -2.041 -2.867 -1.766 c +-3.107 -2.473 l +-2.628 -2.773 -1.92 -2.977 -1.188 -2.977 c +0.6 -2.977 1.032 -1.766 1.032 -0.602 c +h +f +Q +q 1 0 0 1 56.2856 -10.5439 cm +0 0 m +0 1.393 l +1.512 1.393 l +1.512 2.196 l +0 2.196 l +0 5.328 l +0 6.048 0.203 6.456 0.792 6.456 c +1.079 6.456 1.248 6.433 1.403 6.384 c +1.451 7.189 l +1.248 7.261 0.924 7.332 0.516 7.332 c +0.023 7.332 -0.372 7.164 -0.624 6.888 c +-0.912 6.563 -1.032 6.048 -1.032 5.364 c +-1.032 2.196 l +-1.933 2.196 l +-1.933 1.393 l +-1.032 1.393 l +-1.032 0.324 l +h +f +Q +q 1 0 0 1 62.5962 -6.8242 cm +0 0 m +0.013 -0.66 -0.275 -1.716 -1.452 -1.716 c +-2.531 -1.716 -2.987 -0.731 -3.071 0 c +h +-3.084 0.756 m +-3.06 2.185 -2.16 2.772 -1.104 2.772 c +-0.348 2.772 0.12 2.641 0.505 2.484 c +0.696 3.229 l +0.324 3.396 -0.323 3.601 -1.247 3.601 c +-3.023 3.601 -4.104 2.412 -4.104 0.673 c +-4.104 -1.092 -3.06 -2.472 -1.368 -2.472 c +0.528 -2.472 1.021 -0.804 1.021 0.265 c +1.021 0.48 1.008 0.648 0.984 0.769 c +h +f +Q +q 1 0 0 1 68.5493 -7.4346 cm +0 0 m +0.228 0.059 0.528 0.083 0.864 0.083 c +2.136 0.083 2.892 -0.541 2.892 -1.634 c +2.892 -2.714 2.136 -3.229 0.984 -3.229 c +0.528 -3.229 0.179 -3.181 0 -3.146 c +h +-1.044 -3.89 m +-0.54 -3.985 0.12 -4.058 0.96 -4.058 c +1.992 -4.058 2.748 -3.817 3.228 -3.385 c +3.672 -3 3.948 -2.413 3.948 -1.693 c +3.948 -0.962 3.732 -0.385 3.312 0.035 c +2.76 0.622 1.86 0.923 0.84 0.923 c +0.528 0.923 0.24 0.911 0 0.851 c +0 4.091 l +-1.044 4.091 l +h +f +Q +q 1 0 0 1 74.9331 -4.1602 cm +0 0 m +0.264 0.049 0.648 0.049 1.056 0.049 c +3.288 0.062 4.5 -1.199 4.5 -3.384 c +4.512 -5.314 3.432 -6.504 1.224 -6.504 c +0.684 -6.504 0.276 -6.455 0 -6.396 c +h +-1.056 -7.164 m +-0.408 -7.26 0.348 -7.332 1.176 -7.332 c +2.676 -7.332 3.744 -6.996 4.452 -6.336 c +5.184 -5.676 5.604 -4.728 5.604 -3.42 c +5.604 -2.1 5.184 -1.02 4.44 -0.274 c +3.671 0.48 2.424 0.89 0.852 0.89 c +0.096 0.89 -0.516 0.864 -1.056 0.793 c +h +f +Q +q 1 0 0 1 81.8696 -11.4316 cm +0 0 m +4.355 0 l +4.355 0.876 l +1.056 0.876 l +1.056 3.564 l +4.104 3.564 l +4.104 4.428 l +1.056 4.428 l +1.056 8.088 l +0 8.088 l +h +f +Q +q 1 0 0 1 -96.3525 10.9043 cm +0 0 m +-0.372 0.192 -1.152 0.384 -2.136 0.384 c +-4.416 0.384 -6.12 -1.056 -6.12 -3.72 c +-6.12 -6.264 -4.404 -7.969 -1.896 -7.969 c +-0.9 -7.969 -0.253 -7.752 0.024 -7.607 c +-0.24 -6.755 l +-0.624 -6.948 -1.188 -7.091 -1.86 -7.091 c +-3.756 -7.091 -5.016 -5.88 -5.016 -3.755 c +-5.016 -1.764 -3.877 -0.504 -1.92 -0.504 c +-1.272 -0.504 -0.624 -0.637 -0.204 -0.839 c +h +f +Q +q 1 0 0 1 -92.8237 10.4961 cm +0 0 m +1.008 0 1.764 -0.947 1.764 -2.267 c +1.764 -3.252 1.272 -4.488 0.024 -4.488 c +-1.212 -4.488 -1.752 -3.336 -1.752 -2.231 c +-1.752 -0.96 -1.032 0 -0.012 0 c +h +-0.036 0.792 m +-1.596 0.792 -2.82 -0.359 -2.82 -2.195 c +-2.82 -4.139 -1.536 -5.279 0.06 -5.279 c +1.728 -5.279 2.856 -4.068 2.856 -2.304 c +2.856 -0.144 1.356 0.792 -0.024 0.792 c +h +f +Q +q 1 0 0 1 -88.6367 6.9199 cm +0 0 m +0 -0.611 -0.024 -1.092 -0.048 -1.571 c +0.876 -1.571 l +0.924 -0.636 l +0.96 -0.636 l +1.284 -1.187 1.824 -1.703 2.796 -1.703 c +3.576 -1.703 4.176 -1.224 4.428 -0.539 c +4.452 -0.539 l +4.632 -0.875 4.872 -1.115 5.112 -1.296 c +5.46 -1.56 5.832 -1.703 6.384 -1.703 c +7.164 -1.703 8.304 -1.199 8.304 0.816 c +8.304 4.236 l +7.272 4.236 l +7.272 0.948 l +7.272 -0.18 6.852 -0.84 6.012 -0.84 c +5.4 -0.84 4.944 -0.395 4.752 0.108 c +4.704 0.265 4.668 0.456 4.668 0.637 c +4.668 4.236 l +3.636 4.236 l +3.636 0.756 l +3.636 -0.18 3.228 -0.84 2.424 -0.84 c +1.776 -0.84 1.284 -0.312 1.116 0.217 c +1.056 0.372 1.032 0.553 1.032 0.732 c +1.032 4.236 l +0 4.236 l +h +f +Q +q 1 0 0 1 -77.5723 8.792 cm +0 0 m +0 0.144 0.012 0.3 0.048 0.433 c +0.228 1.164 0.876 1.669 1.62 1.669 c +2.736 1.669 3.384 0.757 3.384 -0.575 c +3.384 -1.739 2.784 -2.735 1.668 -2.735 c +0.948 -2.735 0.264 -2.231 0.06 -1.439 c +0.036 -1.296 0 -1.14 0 -1.008 c +h +-1.056 -1.548 m +-1.056 -2.292 -1.068 -2.891 -1.104 -3.443 c +-0.156 -3.443 l +-0.096 -2.448 l +-0.072 -2.448 l +0.348 -3.168 1.044 -3.575 1.98 -3.575 c +3.396 -3.575 4.452 -2.388 4.452 -0.624 c +4.452 1.464 3.168 2.496 1.812 2.496 c +1.032 2.496 0.372 2.16 0.024 1.585 c +0 1.585 l +0 4.74 l +-1.056 4.74 l +h +f +Q +q 1 0 0 1 -68.7407 8.1924 cm +0 0 m +-1.152 -0.024 -2.46 0.18 -2.46 1.308 c +-2.46 2.004 -2.004 2.315 -1.476 2.315 c +-0.708 2.315 -0.216 1.836 -0.048 1.344 c +-0.012 1.235 0 1.116 0 1.008 c +h +1.032 1.572 m +1.032 2.075 1.056 2.567 1.116 2.964 c +0.168 2.964 l +0.072 2.231 l +0.036 2.231 l +-0.276 2.688 -0.912 3.096 -1.741 3.096 c +-2.916 3.096 -3.516 2.269 -3.516 1.428 c +-3.516 0.024 -2.268 -0.744 -0.024 -0.731 c +-0.024 -0.853 l +-0.024 -1.331 -0.157 -2.208 -1.345 -2.196 c +-1.896 -2.196 -2.46 -2.04 -2.868 -1.765 c +-3.108 -2.472 l +-2.628 -2.772 -1.92 -2.976 -1.188 -2.976 c +0.6 -2.976 1.032 -1.765 1.032 -0.601 c +h +f +Q +q 1 0 0 1 -64.7925 3.9561 cm +0 0 m +0 1.393 l +1.512 1.393 l +1.512 2.196 l +0 2.196 l +0 5.328 l +0 6.048 0.204 6.456 0.792 6.456 c +1.08 6.456 1.248 6.433 1.404 6.384 c +1.452 7.189 l +1.248 7.261 0.924 7.332 0.516 7.332 c +0.024 7.332 -0.372 7.164 -0.624 6.888 c +-0.912 6.563 -1.032 6.048 -1.032 5.364 c +-1.032 2.196 l +-1.932 2.196 l +-1.932 1.393 l +-1.032 1.393 l +-1.032 0.324 l +h +f +Q +-62.093 5.349 1.056 5.808 re +-62.093 11.156 m +-61.589 4.28 m +-61.985 4.28 -62.249 3.969 -62.249 3.597 c +-62.249 3.225 -61.973 2.924 -61.565 2.924 c +-61.157 2.924 -60.893 3.225 -60.893 3.597 c +-60.893 3.969 -61.157 4.28 -61.577 4.28 c +h +f +q 1 0 0 1 -58.2285 8.8281 cm +0 0 m +0 0.133 0.012 0.264 0.036 0.385 c +0.24 1.116 0.864 1.62 1.62 1.62 c +2.736 1.62 3.384 0.721 3.384 -0.611 c +3.384 -1.788 2.784 -2.783 1.656 -2.783 c +0.936 -2.783 0.264 -2.279 0.06 -1.488 c +0.024 -1.355 0 -1.212 0 -1.044 c +h +-1.056 -6.191 m +0 -6.191 l +0 -2.557 l +0.024 -2.557 l +0.396 -3.204 1.068 -3.611 2.004 -3.611 c +3.444 -3.611 4.452 -2.412 4.452 -0.66 c +4.452 1.428 3.132 2.46 1.836 2.46 c +0.996 2.46 0.336 2.137 -0.108 1.368 c +-0.132 1.368 l +-0.192 2.328 l +-1.104 2.328 l +-1.068 1.932 -1.056 1.344 -1.056 0.828 c +h +f +Q +-52.457 2.637 1.056 8.52 re +f +q 1 0 0 1 -45.9644 7.6758 cm +0 0 m +0.012 -0.659 -0.276 -1.716 -1.452 -1.716 c +-2.532 -1.716 -2.988 -0.731 -3.072 0 c +h +-3.084 0.757 m +-3.06 2.186 -2.16 2.772 -1.104 2.772 c +-0.348 2.772 0.12 2.642 0.504 2.484 c +0.696 3.229 l +0.324 3.396 -0.324 3.602 -1.248 3.602 c +-3.024 3.602 -4.104 2.412 -4.104 0.673 c +-4.104 -1.092 -3.06 -2.472 -1.368 -2.472 c +0.528 -2.472 1.02 -0.804 1.02 0.266 c +1.02 0.48 1.008 0.648 0.984 0.769 c +h +f +Q +q 1 0 0 1 -41.0688 3.0693 cm +0 0 m +4.356 0 l +4.356 0.875 l +1.056 0.875 l +1.056 3.563 l +4.104 3.563 l +4.104 4.427 l +1.056 4.427 l +1.056 8.087 l +0 8.087 l +h +f +Q +-35.561 5.349 1.056 5.808 re +-35.561 11.156 m +-35.057 4.28 m +-35.453 4.28 -35.716 3.969 -35.716 3.597 c +-35.716 3.225 -35.44 2.924 -35.033 2.924 c +-34.625 2.924 -34.36 3.225 -34.36 3.597 c +-34.36 3.969 -34.625 4.28 -35.044 4.28 c +h +f +-32.752 2.637 1.056 8.52 re +f +q 1 0 0 1 -26.2607 7.6758 cm +0 0 m +0.012 -0.659 -0.276 -1.716 -1.452 -1.716 c +-2.533 -1.716 -2.989 -0.731 -3.073 0 c +h +-3.084 0.757 m +-3.06 2.186 -2.16 2.772 -1.104 2.772 c +-0.348 2.772 0.12 2.642 0.504 2.484 c +0.696 3.229 l +0.323 3.396 -0.325 3.602 -1.248 3.602 c +-3.024 3.602 -4.104 2.412 -4.104 0.673 c +-4.104 -1.092 -3.06 -2.472 -1.368 -2.472 c +0.528 -2.472 1.02 -0.804 1.02 0.266 c +1.02 0.48 1.008 0.648 0.984 0.769 c +h +f +Q +q 1 0 0 1 -21.3765 2.8516 cm +0 0 m +-0.192 2.882 l +-0.84 2.882 l +-1.032 0 l +h +-1.788 0 m +-1.98 2.882 l +-2.628 2.882 l +-2.82 0 l +h +f +Q +q 1 0 0 1 -14.9458 10.4961 cm +0 0 m +1.008 0 1.764 -0.947 1.764 -2.267 c +1.764 -3.252 1.272 -4.488 0.024 -4.488 c +-1.212 -4.488 -1.752 -3.336 -1.752 -2.231 c +-1.752 -0.96 -1.032 0 -0.012 0 c +h +-0.036 0.792 m +-1.596 0.792 -2.82 -0.359 -2.82 -2.195 c +-2.82 -4.139 -1.536 -5.279 0.06 -5.279 c +1.728 -5.279 2.856 -4.068 2.856 -2.304 c +2.856 -0.144 1.356 0.792 -0.024 0.792 c +h +f +Q +q 1 0 0 1 -9.7007 8.792 cm +0 0 m +0 0.144 0.011 0.3 0.048 0.433 c +0.228 1.164 0.876 1.669 1.62 1.669 c +2.736 1.669 3.384 0.757 3.384 -0.575 c +3.384 -1.739 2.783 -2.735 1.668 -2.735 c +0.948 -2.735 0.263 -2.231 0.06 -1.439 c +0.035 -1.296 0 -1.14 0 -1.008 c +h +-1.056 -1.548 m +-1.056 -2.292 -1.069 -2.891 -1.104 -3.443 c +-0.156 -3.443 l +-0.096 -2.448 l +-0.072 -2.448 l +0.347 -3.168 1.044 -3.575 1.98 -3.575 c +3.396 -3.575 4.452 -2.388 4.452 -0.624 c +4.452 1.464 3.168 2.496 1.812 2.496 c +1.032 2.496 0.372 2.16 0.024 1.585 c +0 1.585 l +0 4.74 l +-1.056 4.74 l +h +f +Q +q 1 0 0 1 -2.6577 3.9561 cm +0 0 m +0 1.393 l +1.512 1.393 l +1.512 2.196 l +0 2.196 l +0 5.328 l +0 6.048 0.204 6.456 0.792 6.456 c +1.08 6.456 1.248 6.433 1.404 6.384 c +1.452 7.189 l +1.248 7.261 0.924 7.332 0.516 7.332 c +0.024 7.332 -0.372 7.164 -0.624 6.888 c +-0.912 6.563 -1.032 6.048 -1.032 5.364 c +-1.032 2.196 l +-1.932 2.196 l +-1.932 1.393 l +-1.032 1.393 l +-1.032 0.324 l +h +f +Q +0.043 5.349 1.056 5.808 re +0.043 11.156 m +0.547 4.28 m +0.151 4.28 -0.112 3.969 -0.112 3.597 c +-0.112 3.225 0.164 2.924 0.571 2.924 c +0.979 2.924 1.244 3.225 1.244 3.597 c +1.244 3.969 0.979 4.28 0.56 4.28 c +h +f +q 1 0 0 1 5.2515 10.4961 cm +0 0 m +1.008 0 1.764 -0.947 1.764 -2.267 c +1.764 -3.252 1.272 -4.488 0.024 -4.488 c +-1.212 -4.488 -1.752 -3.336 -1.752 -2.231 c +-1.752 -0.96 -1.032 0 -0.012 0 c +h +-0.036 0.792 m +-1.596 0.792 -2.82 -0.359 -2.82 -2.195 c +-2.82 -4.139 -1.536 -5.279 0.06 -5.279 c +1.729 -5.279 2.856 -4.068 2.856 -2.304 c +2.856 -0.144 1.356 0.792 -0.024 0.792 c +h +f +Q +q 1 0 0 1 9.4385 6.9199 cm +0 0 m +0 -0.611 -0.011 -1.092 -0.047 -1.571 c +0.888 -1.571 l +0.949 -0.623 l +0.972 -0.623 l +1.26 -1.164 1.932 -1.703 2.892 -1.703 c +3.697 -1.703 4.945 -1.224 4.945 0.768 c +4.945 4.236 l +3.888 4.236 l +3.888 0.889 l +3.888 -0.048 3.54 -0.84 2.544 -0.84 c +1.861 -0.84 1.321 -0.348 1.128 0.24 c +1.08 0.372 1.056 0.565 1.056 0.732 c +1.056 4.236 l +0 4.236 l +h +f +Q +q 1 0 0 1 19.9155 3.9561 cm +0 0 m +0 1.393 l +1.512 1.393 l +1.512 2.196 l +0 2.196 l +0 5.328 l +0 6.048 0.203 6.456 0.792 6.456 c +1.079 6.456 1.248 6.433 1.403 6.384 c +1.451 7.189 l +1.248 7.261 0.924 7.332 0.516 7.332 c +0.023 7.332 -0.372 7.164 -0.624 6.888 c +-0.912 6.563 -1.032 6.048 -1.032 5.364 c +-1.032 2.196 l +-1.933 2.196 l +-1.933 1.393 l +-1.032 1.393 l +-1.032 0.324 l +h +f +Q +q 1 0 0 1 27.4751 9.5723 cm +0 0 m +0 0.6 0.012 1.128 0.048 1.584 c +-0.889 1.584 l +-0.948 0.636 l +-0.973 0.636 l +-1.236 1.104 -1.86 1.716 -2.893 1.716 c +-3.805 1.716 -4.896 1.201 -4.896 -0.828 c +-4.896 -4.224 l +-3.84 -4.224 l +-3.84 -1.02 l +-3.84 0.084 -3.492 0.84 -2.544 0.84 c +-1.836 0.84 -1.345 0.348 -1.152 -0.132 c +-1.093 -0.275 -1.057 -0.469 -1.057 -0.671 c +-1.057 -4.224 l +0 -4.224 l +h +f +Q +q 1 0 0 1 29.2388 7.1602 cm +0 0 m +0 -0.684 -0.012 -1.271 -0.048 -1.812 c +0.876 -1.812 l +0.925 -0.672 l +0.96 -0.672 l +1.225 -1.452 1.872 -1.943 2.58 -1.943 c +2.688 -1.943 2.772 -1.931 2.868 -1.908 c +2.868 -0.924 l +2.748 -0.936 2.641 -0.947 2.496 -0.947 c +1.752 -0.947 1.225 -0.384 1.08 0.396 c +1.057 0.553 1.044 0.721 1.044 0.9 c +1.044 3.996 l +-0.012 3.996 l +h +f +Q +q 1 0 0 1 33.1978 6.9199 cm +0 0 m +0 -0.611 -0.012 -1.092 -0.048 -1.571 c +0.888 -1.571 l +0.948 -0.623 l +0.972 -0.623 l +1.26 -1.164 1.932 -1.703 2.892 -1.703 c +3.696 -1.703 4.944 -1.224 4.944 0.768 c +4.944 4.236 l +3.888 4.236 l +3.888 0.889 l +3.888 -0.048 3.54 -0.84 2.544 -0.84 c +1.86 -0.84 1.32 -0.348 1.128 0.24 c +1.08 0.372 1.056 0.565 1.056 0.732 c +1.056 4.236 l +0 4.236 l +h +f +Q +q 1 0 0 1 43.5415 7.6758 cm +0 0 m +0.013 -0.659 -0.275 -1.716 -1.451 -1.716 c +-2.531 -1.716 -2.987 -0.731 -3.071 0 c +h +-3.084 0.757 m +-3.06 2.186 -2.159 2.772 -1.104 2.772 c +-0.348 2.772 0.12 2.642 0.505 2.484 c +0.696 3.229 l +0.324 3.396 -0.323 3.602 -1.247 3.602 c +-3.023 3.602 -4.104 2.412 -4.104 0.673 c +-4.104 -1.092 -3.06 -2.472 -1.367 -2.472 c +0.528 -2.472 1.02 -0.804 1.02 0.266 c +1.02 0.48 1.009 0.648 0.984 0.769 c +h +f +Q +q 1 0 0 1 49.8296 7.7129 cm +0 0 m +0 -0.145 -0.012 -0.313 -0.048 -0.457 c +-0.204 -1.116 -0.78 -1.668 -1.572 -1.668 c +-2.664 -1.668 -3.312 -0.709 -3.312 0.564 c +-3.312 1.752 -2.724 2.723 -1.596 2.723 c +-0.888 2.723 -0.24 2.243 -0.048 1.463 c +-0.012 1.319 0 1.176 0 1.008 c +h +1.056 -5.076 m +1.056 1.943 l +1.056 2.459 1.068 3.047 1.104 3.443 c +0.156 3.443 l +0.108 2.437 l +0.084 2.437 l +-0.24 3.084 -0.936 3.575 -1.884 3.575 c +-3.288 3.575 -4.38 2.387 -4.38 0.623 c +-4.392 -1.32 -3.18 -2.496 -1.776 -2.496 c +-0.876 -2.496 -0.276 -2.076 -0.023 -1.621 c +0 -1.621 l +0 -5.076 l +h +f +Q +q 1 0 0 1 57.5825 10.4961 cm +0 0 m +1.008 0 1.764 -0.947 1.764 -2.267 c +1.764 -3.252 1.271 -4.488 0.023 -4.488 c +-1.212 -4.488 -1.752 -3.336 -1.752 -2.231 c +-1.752 -0.96 -1.032 0 -0.013 0 c +h +-0.036 0.792 m +-1.597 0.792 -2.82 -0.359 -2.82 -2.195 c +-2.82 -4.139 -1.536 -5.279 0.06 -5.279 c +1.728 -5.279 2.855 -4.068 2.855 -2.304 c +2.855 -0.144 1.355 0.792 -0.024 0.792 c +h +f +Q +q 1 0 0 1 61.77 6.9199 cm +0 0 m +0 -0.611 -0.012 -1.092 -0.048 -1.571 c +0.888 -1.571 l +0.948 -0.623 l +0.972 -0.623 l +1.26 -1.164 1.932 -1.703 2.892 -1.703 c +3.696 -1.703 4.944 -1.224 4.944 0.768 c +4.944 4.236 l +3.888 4.236 l +3.888 0.889 l +3.888 -0.048 3.54 -0.84 2.543 -0.84 c +1.86 -0.84 1.32 -0.348 1.128 0.24 c +1.08 0.372 1.056 0.565 1.056 0.732 c +1.056 4.236 l +0 4.236 l +h +f +Q +q 1 0 0 1 68.8735 11.2881 cm +0 0 m +-0.408 0 -0.696 -0.323 -0.696 -0.756 c +-0.696 -1.188 -0.396 -1.512 0.023 -1.512 c +0.443 -1.512 0.731 -1.2 0.731 -0.756 c +0.731 -0.323 0.443 0 0.012 0 c +h +f +Q +q 1 0 0 1 76.1929 3.9561 cm +0 0 m +-2.46 0 l +-2.46 -0.887 l +3.527 -0.887 l +3.527 0 l +1.056 0 l +1.056 7.2 l +0 7.2 l +h +f +Q +q 1 0 0 1 80.5015 2.6367 cm +0 0 m +1.056 0 l +1.056 3.623 l +1.08 3.623 l +1.248 3.323 1.512 3.047 1.836 2.879 c +2.148 2.688 2.52 2.58 2.916 2.58 c +3.696 2.58 4.944 3.06 4.944 5.063 c +4.944 8.52 l +3.888 8.52 l +3.888 5.184 l +3.888 4.235 3.54 3.455 2.544 3.455 c +1.86 3.455 1.332 3.936 1.128 4.513 c +1.068 4.655 1.056 4.812 1.056 5.016 c +1.056 8.52 l +0 8.52 l +h +f +Q +87.161 5.349 1.056 5.808 re +87.161 11.156 m +87.665 4.28 m +87.269 4.28 87.004 3.969 87.004 3.597 c +87.004 3.225 87.281 2.924 87.688 2.924 c +88.097 2.924 88.361 3.225 88.361 3.597 c +88.361 3.969 88.097 4.28 87.676 4.28 c +h +f +q 1 0 0 1 89.8257 10.0879 cm +0 0 m +0.323 0.192 0.876 0.408 1.403 0.408 c +2.159 0.408 2.52 0.036 2.52 -0.455 c +2.52 -0.959 2.22 -1.236 1.451 -1.522 c +0.396 -1.907 -0.097 -2.472 -0.097 -3.168 c +-0.097 -4.104 0.672 -4.871 1.907 -4.871 c +2.495 -4.871 3.012 -4.716 3.323 -4.512 c +3.071 -3.756 l +2.844 -3.888 2.424 -4.092 1.884 -4.092 c +1.26 -4.092 0.924 -3.73 0.924 -3.3 c +0.924 -2.808 1.26 -2.592 2.016 -2.304 c +3.012 -1.932 3.54 -1.428 3.54 -0.552 c +3.54 0.48 2.735 1.2 1.38 1.2 c +0.743 1.2 0.155 1.032 -0.252 0.793 c +h +f +Q +q 1 0 0 1 -99.6289 24.9951 cm +0 0 m +1.008 0 1.764 -0.947 1.764 -2.269 c +1.764 -3.252 1.272 -4.487 0.023 -4.487 c +-1.212 -4.487 -1.752 -3.336 -1.752 -2.23 c +-1.752 -0.96 -1.032 0 -0.012 0 c +h +-0.036 0.792 m +-1.596 0.792 -2.82 -0.359 -2.82 -2.196 c +-2.82 -4.14 -1.536 -5.279 0.06 -5.279 c +1.728 -5.279 2.856 -4.067 2.856 -2.304 c +2.856 -0.144 1.356 0.792 -0.024 0.792 c +h +f +Q +q 1 0 0 1 -94.3843 23.291 cm +0 0 m +0 0.145 0.012 0.301 0.048 0.432 c +0.228 1.164 0.876 1.668 1.62 1.668 c +2.736 1.668 3.384 0.757 3.384 -0.575 c +3.384 -1.74 2.784 -2.736 1.668 -2.736 c +0.948 -2.736 0.264 -2.232 0.06 -1.439 c +0.036 -1.296 0 -1.14 0 -1.008 c +h +-1.056 -1.548 m +-1.056 -2.292 -1.068 -2.892 -1.104 -3.442 c +-0.156 -3.442 l +-0.096 -2.447 l +-0.072 -2.447 l +0.348 -3.168 1.044 -3.575 1.98 -3.575 c +3.396 -3.575 4.452 -2.388 4.452 -0.623 c +4.452 1.465 3.168 2.496 1.812 2.496 c +1.032 2.496 0.372 2.16 0.024 1.584 c +0 1.584 l +0 4.74 l +-1.056 4.74 l +h +f +Q +q 1 0 0 1 -87.3408 18.4551 cm +0 0 m +0 1.394 l +1.512 1.394 l +1.512 2.196 l +0 2.196 l +0 5.328 l +0 6.049 0.204 6.456 0.792 6.456 c +1.08 6.456 1.248 6.432 1.404 6.385 c +1.452 7.188 l +1.248 7.261 0.924 7.332 0.516 7.332 c +0.024 7.332 -0.372 7.164 -0.624 6.889 c +-0.912 6.564 -1.032 6.049 -1.032 5.364 c +-1.032 2.196 l +-1.932 2.196 l +-1.932 1.394 l +-1.032 1.394 l +-1.032 0.324 l +h +f +Q +-84.641 19.849 1.056 5.807 re +-84.641 25.655 m +-84.137 18.779 m +-84.533 18.779 -84.796 18.468 -84.796 18.097 c +-84.796 17.723 -84.521 17.424 -84.113 17.424 c +-83.705 17.424 -83.44 17.723 -83.44 18.097 c +-83.44 18.468 -83.705 18.779 -84.125 18.779 c +h +f +q 1 0 0 1 -79.4326 24.9951 cm +0 0 m +1.008 0 1.764 -0.947 1.764 -2.269 c +1.764 -3.252 1.272 -4.487 0.024 -4.487 c +-1.212 -4.487 -1.752 -3.336 -1.752 -2.23 c +-1.752 -0.96 -1.032 0 -0.012 0 c +h +-0.036 0.792 m +-1.596 0.792 -2.82 -0.359 -2.82 -2.196 c +-2.82 -4.14 -1.536 -5.279 0.06 -5.279 c +1.728 -5.279 2.856 -4.067 2.856 -2.304 c +2.856 -0.144 1.356 0.792 -0.024 0.792 c +h +f +Q +q 1 0 0 1 -75.2446 21.4199 cm +0 0 m +0 -0.613 -0.012 -1.093 -0.048 -1.571 c +0.888 -1.571 l +0.948 -0.624 l +0.972 -0.624 l +1.26 -1.164 1.932 -1.704 2.892 -1.704 c +3.696 -1.704 4.944 -1.225 4.944 0.768 c +4.944 4.235 l +3.888 4.235 l +3.888 0.887 l +3.888 -0.049 3.54 -0.841 2.544 -0.841 c +1.86 -0.841 1.32 -0.349 1.128 0.239 c +1.08 0.371 1.056 0.563 1.056 0.731 c +1.056 4.235 l +0 4.235 l +h +f +Q +-66.041 19.849 1.056 5.807 re +-66.041 25.655 m +-65.537 18.779 m +-65.933 18.779 -66.196 18.468 -66.196 18.097 c +-66.196 17.723 -65.92 17.424 -65.513 17.424 c +-65.104 17.424 -64.84 17.723 -64.84 18.097 c +-64.84 18.468 -65.104 18.779 -65.524 18.779 c +h +f +q 1 0 0 1 -63.3765 24.5879 cm +0 0 m +0.324 0.191 0.876 0.407 1.404 0.407 c +2.16 0.407 2.52 0.035 2.52 -0.456 c +2.52 -0.961 2.22 -1.235 1.452 -1.524 c +0.396 -1.907 -0.096 -2.473 -0.096 -3.168 c +-0.096 -4.104 0.672 -4.872 1.908 -4.872 c +2.496 -4.872 3.012 -4.717 3.324 -4.513 c +3.072 -3.757 l +2.844 -3.889 2.424 -4.093 1.884 -4.093 c +1.26 -4.093 0.924 -3.732 0.924 -3.301 c +0.924 -2.809 1.26 -2.593 2.016 -2.305 c +3.012 -1.933 3.54 -1.429 3.54 -0.553 c +3.54 0.479 2.736 1.199 1.38 1.199 c +0.744 1.199 0.156 1.031 -0.252 0.792 c +h +f +Q +-55.937 19.849 1.056 5.807 re +-55.937 25.655 m +-55.433 18.779 m +-55.829 18.779 -56.092 18.468 -56.092 18.097 c +-56.092 17.723 -55.816 17.424 -55.409 17.424 c +-55 17.424 -54.736 17.723 -54.736 18.097 c +-54.736 18.468 -55 18.779 -55.42 18.779 c +h +f +q 1 0 0 1 -53.1284 21.4199 cm +0 0 m +0 -0.613 -0.012 -1.093 -0.048 -1.571 c +0.888 -1.571 l +0.948 -0.624 l +0.972 -0.624 l +1.26 -1.164 1.932 -1.704 2.892 -1.704 c +3.696 -1.704 4.944 -1.225 4.944 0.768 c +4.944 4.235 l +3.888 4.235 l +3.888 0.887 l +3.888 -0.049 3.54 -0.841 2.544 -0.841 c +1.86 -0.841 1.32 -0.349 1.128 0.239 c +1.08 0.371 1.056 0.563 1.056 0.731 c +1.056 4.235 l +0 4.235 l +h +f +Q +q 1 0 0 1 -42.6528 18.4551 cm +0 0 m +0 1.394 l +1.512 1.394 l +1.512 2.196 l +0 2.196 l +0 5.328 l +0 6.049 0.204 6.456 0.792 6.456 c +1.08 6.456 1.248 6.432 1.404 6.385 c +1.452 7.188 l +1.248 7.261 0.923 7.332 0.516 7.332 c +0.024 7.332 -0.372 7.164 -0.624 6.889 c +-0.912 6.564 -1.032 6.049 -1.032 5.364 c +-1.032 2.196 l +-1.933 2.196 l +-1.933 1.394 l +-1.032 1.394 l +-1.032 0.324 l +h +f +Q +q 1 0 0 1 -39.9526 17.1348 cm +0 0 m +1.056 0 l +1.056 3.625 l +1.08 3.625 l +1.248 3.324 1.512 3.05 1.836 2.882 c +2.148 2.688 2.52 2.581 2.916 2.581 c +3.696 2.581 4.944 3.061 4.944 5.064 c +4.944 8.521 l +3.888 8.521 l +3.888 5.185 l +3.888 4.236 3.54 3.457 2.544 3.457 c +1.86 3.457 1.332 3.937 1.128 4.513 c +1.068 4.656 1.056 4.813 1.056 5.017 c +1.056 8.521 l +0 8.521 l +h +f +Q +q 1 0 0 1 -29.6084 22.1758 cm +0 0 m +0.012 -0.66 -0.276 -1.717 -1.452 -1.717 c +-2.532 -1.717 -2.988 -0.732 -3.072 0 c +h +-3.084 0.756 m +-3.06 2.184 -2.16 2.771 -1.104 2.771 c +-0.348 2.771 0.12 2.64 0.504 2.483 c +0.696 3.228 l +0.324 3.396 -0.324 3.6 -1.248 3.6 c +-3.024 3.6 -4.104 2.412 -4.104 0.673 c +-4.104 -1.093 -3.06 -2.473 -1.368 -2.473 c +0.528 -2.473 1.02 -0.805 1.02 0.264 c +1.02 0.479 1.008 0.647 0.984 0.768 c +h +f +Q +-23.657 25.655 -1.056 -8.088 re +f +-21.738 17.135 1.056 8.521 re +f +-18.931 17.135 1.056 8.521 re +f +q 1 0 0 1 -11.2617 24.0713 cm +0 0 m +0 0.601 0.012 1.128 0.048 1.584 c +-0.888 1.584 l +-0.948 0.636 l +-0.972 0.636 l +-1.236 1.104 -1.86 1.716 -2.892 1.716 c +-3.804 1.716 -4.896 1.2 -4.896 -0.828 c +-4.896 -4.223 l +-3.84 -4.223 l +-3.84 -1.02 l +-3.84 0.084 -3.492 0.84 -2.544 0.84 c +-1.836 0.84 -1.344 0.349 -1.152 -0.132 c +-1.092 -0.275 -1.056 -0.468 -1.056 -0.672 c +-1.056 -4.223 l +0 -4.223 l +h +f +Q +q 1 0 0 1 -9.6548 24.5879 cm +0 0 m +0.324 0.191 0.876 0.407 1.404 0.407 c +2.16 0.407 2.52 0.035 2.52 -0.456 c +2.52 -0.961 2.22 -1.235 1.452 -1.524 c +0.396 -1.907 -0.096 -2.473 -0.096 -3.168 c +-0.096 -4.104 0.672 -4.872 1.908 -4.872 c +2.496 -4.872 3.012 -4.717 3.324 -4.513 c +3.072 -3.757 l +2.844 -3.889 2.424 -4.093 1.884 -4.093 c +1.26 -4.093 0.923 -3.732 0.923 -3.301 c +0.923 -2.809 1.26 -2.593 2.016 -2.305 c +3.012 -1.933 3.54 -1.429 3.54 -0.553 c +3.54 0.479 2.736 1.199 1.38 1.199 c +0.744 1.199 0.156 1.031 -0.252 0.792 c +h +f +Q +q 1 0 0 1 -3.4868 18.4551 cm +0 0 m +0 1.394 l +1.512 1.394 l +1.512 2.196 l +0 2.196 l +0 5.328 l +0 6.049 0.204 6.456 0.792 6.456 c +1.08 6.456 1.248 6.432 1.404 6.385 c +1.452 7.188 l +1.248 7.261 0.923 7.332 0.516 7.332 c +0.024 7.332 -0.372 7.164 -0.624 6.889 c +-0.912 6.564 -1.032 6.049 -1.032 5.364 c +-1.032 2.196 l +-1.932 2.196 l +-1.932 1.394 l +-1.032 1.394 l +-1.032 0.324 l +h +f +Q +q 1 0 0 1 -0.7744 21.6592 cm +0 0 m +0 -0.685 -0.012 -1.272 -0.048 -1.811 c +0.876 -1.811 l +0.924 -0.672 l +0.96 -0.672 l +1.224 -1.452 1.872 -1.943 2.58 -1.943 c +2.688 -1.943 2.772 -1.933 2.868 -1.908 c +2.868 -0.924 l +2.748 -0.937 2.64 -0.948 2.496 -0.948 c +1.752 -0.948 1.224 -0.384 1.08 0.396 c +1.056 0.552 1.044 0.721 1.044 0.899 c +1.044 3.996 l +-0.012 3.996 l +h +f +Q +q 1 0 0 1 6.1265 22.6914 cm +0 0 m +-1.152 -0.023 -2.46 0.18 -2.46 1.308 c +-2.46 2.004 -2.004 2.316 -1.476 2.316 c +-0.708 2.316 -0.216 1.836 -0.048 1.344 c +-0.012 1.236 0 1.115 0 1.008 c +h +1.032 1.573 m +1.032 2.077 1.056 2.568 1.116 2.964 c +0.168 2.964 l +0.072 2.232 l +0.036 2.232 l +-0.276 2.688 -0.912 3.096 -1.74 3.096 c +-2.916 3.096 -3.516 2.268 -3.516 1.428 c +-3.516 0.024 -2.268 -0.744 -0.024 -0.732 c +-0.024 -0.852 l +-0.024 -1.332 -0.156 -2.208 -1.344 -2.196 c +-1.896 -2.196 -2.46 -2.04 -2.868 -1.764 c +-3.108 -2.473 l +-2.628 -2.771 -1.92 -2.976 -1.189 -2.976 c +0.6 -2.976 1.032 -1.764 1.032 -0.6 c +h +f +Q +q 1 0 0 1 10.0737 18.4551 cm +0 0 m +0 1.394 l +1.512 1.394 l +1.512 2.196 l +0 2.196 l +0 5.328 l +0 6.049 0.203 6.456 0.792 6.456 c +1.079 6.456 1.248 6.432 1.403 6.385 c +1.451 7.188 l +1.248 7.261 0.924 7.332 0.516 7.332 c +0.023 7.332 -0.372 7.164 -0.624 6.889 c +-0.912 6.564 -1.032 6.049 -1.032 5.364 c +-1.032 2.196 l +-1.933 2.196 l +-1.933 1.394 l +-1.032 1.394 l +-1.032 0.324 l +h +f +Q +q 1 0 0 1 15.1021 24.9951 cm +0 0 m +1.008 0 1.764 -0.947 1.764 -2.269 c +1.764 -3.252 1.271 -4.487 0.023 -4.487 c +-1.212 -4.487 -1.752 -3.336 -1.752 -2.23 c +-1.752 -0.96 -1.032 0 -0.013 0 c +h +-0.036 0.792 m +-1.597 0.792 -2.82 -0.359 -2.82 -2.196 c +-2.82 -4.14 -1.536 -5.279 0.06 -5.279 c +1.728 -5.279 2.855 -4.067 2.855 -2.304 c +2.855 -0.144 1.355 0.792 -0.024 0.792 c +h +f +Q +q 1 0 0 1 19.3013 21.6592 cm +0 0 m +0 -0.685 -0.012 -1.272 -0.048 -1.811 c +0.876 -1.811 l +0.925 -0.672 l +0.96 -0.672 l +1.225 -1.452 1.872 -1.943 2.58 -1.943 c +2.688 -1.943 2.772 -1.933 2.868 -1.908 c +2.868 -0.924 l +2.748 -0.937 2.641 -0.948 2.496 -0.948 c +1.752 -0.948 1.225 -0.384 1.08 0.396 c +1.057 0.552 1.044 0.721 1.044 0.899 c +1.044 3.996 l +-0.012 3.996 l +h +f +Q +q 1 0 0 1 25.7935 25.6553 cm +0 0 m +0 -8.088 l +1.152 -8.088 l +3.731 -3.996 l +4.32 -3.048 4.8 -2.208 5.172 -1.368 c +5.208 -1.38 l +5.112 -2.46 5.088 -3.444 5.088 -4.691 c +5.088 -8.088 l +6.072 -8.088 l +6.072 0 l +5.016 0 l +2.448 -4.104 l +1.884 -5.004 1.344 -5.929 0.948 -6.804 c +0.912 -6.792 l +0.972 -5.771 0.984 -4.8 0.984 -3.456 c +0.984 0 l +h +f +Q +q 1 0 0 1 36.6885 22.6914 cm +0 0 m +-1.151 -0.023 -2.46 0.18 -2.46 1.308 c +-2.46 2.004 -2.003 2.316 -1.475 2.316 c +-0.708 2.316 -0.215 1.836 -0.047 1.344 c +-0.011 1.236 0 1.115 0 1.008 c +h +1.033 1.573 m +1.033 2.077 1.057 2.568 1.117 2.964 c +0.169 2.964 l +0.073 2.232 l +0.037 2.232 l +-0.275 2.688 -0.911 3.096 -1.739 3.096 c +-2.916 3.096 -3.515 2.268 -3.515 1.428 c +-3.515 0.024 -2.267 -0.744 -0.023 -0.732 c +-0.023 -0.852 l +-0.023 -1.332 -0.155 -2.208 -1.343 -2.196 c +-1.895 -2.196 -2.46 -2.04 -2.867 -1.764 c +-3.107 -2.473 l +-2.628 -2.771 -1.92 -2.976 -1.187 -2.976 c +0.601 -2.976 1.033 -1.764 1.033 -0.6 c +h +f +Q +q 1 0 0 1 40.6382 18.4551 cm +0 0 m +0 1.394 l +1.512 1.394 l +1.512 2.196 l +0 2.196 l +0 5.328 l +0 6.049 0.203 6.456 0.792 6.456 c +1.079 6.456 1.248 6.432 1.403 6.385 c +1.451 7.188 l +1.248 7.261 0.923 7.332 0.516 7.332 c +0.023 7.332 -0.372 7.164 -0.624 6.889 c +-0.912 6.564 -1.033 6.049 -1.033 5.364 c +-1.033 2.196 l +-1.933 2.196 l +-1.933 1.394 l +-1.033 1.394 l +-1.033 0.324 l +h +f +Q +43.337 19.849 1.056 5.807 re +43.337 25.655 m +43.841 18.779 m +43.446 18.779 43.181 18.468 43.181 18.097 c +43.181 17.723 43.458 17.424 43.866 17.424 c +44.273 17.424 44.538 17.723 44.538 18.097 c +44.538 18.468 44.273 18.779 43.853 18.779 c +h +f +q 1 0 0 1 46.5532 19.8486 cm +0 0 m +1.14 3.251 l +1.332 3.79 1.488 4.271 1.607 4.752 c +1.644 4.752 l +1.775 4.271 1.943 3.79 2.135 3.251 c +3.264 0 l +4.368 0 l +2.088 5.807 l +1.08 5.807 l +-1.128 0 l +h +f +Q +q 1 0 0 1 55.4692 22.1758 cm +0 0 m +0.013 -0.66 -0.275 -1.717 -1.451 -1.717 c +-2.532 -1.717 -2.987 -0.732 -3.071 0 c +h +-3.084 0.756 m +-3.06 2.184 -2.159 2.771 -1.104 2.771 c +-0.348 2.771 0.12 2.64 0.505 2.483 c +0.696 3.228 l +0.324 3.396 -0.324 3.6 -1.247 3.6 c +-3.023 3.6 -4.104 2.412 -4.104 0.673 c +-4.104 -1.093 -3.06 -2.473 -1.367 -2.473 c +0.528 -2.473 1.021 -0.805 1.021 0.264 c +1.021 0.479 1.008 0.647 0.984 0.768 c +h +f +Q +q 1 0 0 1 60.3657 17.5674 cm +0 0 m +4.355 0 l +4.355 0.876 l +1.056 0.876 l +1.056 3.564 l +4.104 3.564 l +4.104 4.428 l +1.056 4.428 l +1.056 8.088 l +0 8.088 l +h +f +Q +q 1 0 0 1 68.2251 24.9951 cm +0 0 m +1.008 0 1.764 -0.947 1.764 -2.269 c +1.764 -3.252 1.271 -4.487 0.023 -4.487 c +-1.212 -4.487 -1.752 -3.336 -1.752 -2.23 c +-1.752 -0.96 -1.032 0 -0.013 0 c +h +-0.037 0.792 m +-1.597 0.792 -2.82 -0.359 -2.82 -2.196 c +-2.82 -4.14 -1.537 -5.279 0.06 -5.279 c +1.728 -5.279 2.855 -4.067 2.855 -2.304 c +2.855 -0.144 1.355 0.792 -0.024 0.792 c +h +f +Q +q 1 0 0 1 72.4243 21.6592 cm +0 0 m +0 -0.685 -0.012 -1.272 -0.048 -1.811 c +0.876 -1.811 l +0.925 -0.672 l +0.96 -0.672 l +1.225 -1.452 1.872 -1.943 2.58 -1.943 c +2.688 -1.943 2.772 -1.933 2.868 -1.908 c +2.868 -0.924 l +2.748 -0.937 2.641 -0.948 2.496 -0.948 c +1.752 -0.948 1.225 -0.384 1.08 0.396 c +1.056 0.552 1.044 0.721 1.044 0.899 c +1.044 3.996 l +-0.012 3.996 l +h +f +Q +q 1 0 0 1 76.3853 21.4199 cm +0 0 m +0 -0.613 -0.024 -1.093 -0.048 -1.571 c +0.876 -1.571 l +0.924 -0.637 l +0.96 -0.637 l +1.284 -1.188 1.824 -1.704 2.796 -1.704 c +3.576 -1.704 4.175 -1.225 4.427 -0.54 c +4.452 -0.54 l +4.632 -0.877 4.872 -1.117 5.112 -1.297 c +5.46 -1.561 5.832 -1.704 6.383 -1.704 c +7.164 -1.704 8.303 -1.201 8.303 0.815 c +8.303 4.235 l +7.272 4.235 l +7.272 0.947 l +7.272 -0.181 6.853 -0.841 6.011 -0.841 c +5.4 -0.841 4.944 -0.396 4.752 0.107 c +4.704 0.264 4.668 0.455 4.668 0.635 c +4.668 4.235 l +3.635 4.235 l +3.635 0.756 l +3.635 -0.181 3.229 -0.841 2.424 -0.841 c +1.776 -0.841 1.284 -0.313 1.116 0.216 c +1.056 0.371 1.032 0.551 1.032 0.731 c +1.032 4.235 l +0 4.235 l +h +f +Q +q 1 0 0 1 89.4526 22.6914 cm +0 0 m +-1.151 -0.023 -2.46 0.18 -2.46 1.308 c +-2.46 2.004 -2.004 2.316 -1.476 2.316 c +-0.708 2.316 -0.216 1.836 -0.048 1.344 c +-0.012 1.236 0 1.115 0 1.008 c +h +1.032 1.573 m +1.032 2.077 1.057 2.568 1.116 2.964 c +0.169 2.964 l +0.072 2.232 l +0.036 2.232 l +-0.275 2.688 -0.911 3.096 -1.739 3.096 c +-2.916 3.096 -3.516 2.268 -3.516 1.428 c +-3.516 0.024 -2.268 -0.744 -0.023 -0.732 c +-0.023 -0.852 l +-0.023 -1.332 -0.155 -2.208 -1.344 -2.196 c +-1.896 -2.196 -2.46 -2.04 -2.867 -1.764 c +-3.107 -2.473 l +-2.628 -2.771 -1.92 -2.976 -1.188 -2.976 c +0.601 -2.976 1.032 -1.764 1.032 -0.6 c +h +f +Q +q 1 0 0 1 93.4019 18.4551 cm +0 0 m +0 1.394 l +1.512 1.394 l +1.512 2.196 l +0 2.196 l +0 5.328 l +0 6.049 0.203 6.456 0.792 6.456 c +1.079 6.456 1.248 6.432 1.403 6.385 c +1.451 7.188 l +1.248 7.261 0.924 7.332 0.516 7.332 c +0.023 7.332 -0.372 7.164 -0.624 6.889 c +-0.912 6.564 -1.032 6.049 -1.032 5.364 c +-1.032 2.196 l +-1.933 2.196 l +-1.933 1.394 l +-1.032 1.394 l +-1.032 0.324 l +h +f +Q +q 1 0 0 1 -98.7876 39.4355 cm +0 0 m +1.656 0 2.592 -1.523 2.592 -3.359 c +2.592 -4.967 1.752 -6.647 0.012 -6.647 c +-1.728 -6.647 -2.592 -5.027 -2.592 -3.275 c +-2.592 -1.571 -1.656 0 -0.012 0 c +h +-0.048 0.853 m +-2.196 0.853 -3.684 -0.803 -3.684 -3.251 c +-3.684 -5.819 -2.1 -7.5 0.059 -7.5 c +2.28 -7.5 3.708 -5.807 3.708 -3.407 c +3.708 -0.635 2.015 0.853 -0.036 0.853 c +h +f +Q +q 1 0 0 1 -92.7046 37.792 cm +0 0 m +0 0.144 0.012 0.3 0.048 0.433 c +0.228 1.164 0.876 1.669 1.62 1.669 c +2.736 1.669 3.384 0.757 3.384 -0.575 c +3.384 -1.739 2.784 -2.735 1.668 -2.735 c +0.948 -2.735 0.264 -2.231 0.06 -1.439 c +0.036 -1.296 0 -1.14 0 -1.008 c +h +-1.056 -1.548 m +-1.056 -2.292 -1.068 -2.891 -1.104 -3.443 c +-0.156 -3.443 l +-0.096 -2.448 l +-0.072 -2.448 l +0.348 -3.168 1.044 -3.575 1.98 -3.575 c +3.396 -3.575 4.452 -2.388 4.452 -0.624 c +4.452 1.464 3.168 2.496 1.812 2.496 c +1.032 2.496 0.372 2.16 0.024 1.585 c +0 1.585 l +0 4.74 l +-1.056 4.74 l +h +f +Q +q 1 0 0 1 -85.6606 32.9561 cm +0 0 m +0 1.393 l +1.512 1.393 l +1.512 2.196 l +0 2.196 l +0 5.328 l +0 6.048 0.204 6.456 0.792 6.456 c +1.08 6.456 1.248 6.433 1.404 6.384 c +1.452 7.189 l +1.248 7.261 0.924 7.332 0.516 7.332 c +0.024 7.332 -0.372 7.164 -0.624 6.888 c +-0.912 6.563 -1.032 6.048 -1.032 5.364 c +-1.032 2.196 l +-1.932 2.196 l +-1.932 1.393 l +-1.032 1.393 l +-1.032 0.324 l +h +f +Q +-82.96 34.349 1.056 5.808 re +-82.96 40.156 m +-82.457 33.28 m +-82.853 33.28 -83.116 32.969 -83.116 32.597 c +-83.116 32.225 -82.84 31.924 -82.433 31.924 c +-82.024 31.924 -81.76 32.225 -81.76 32.597 c +-81.76 32.969 -82.024 33.28 -82.444 33.28 c +h +f +q 1 0 0 1 -77.7529 39.4961 cm +0 0 m +1.008 0 1.764 -0.947 1.764 -2.267 c +1.764 -3.252 1.271 -4.488 0.023 -4.488 c +-1.212 -4.488 -1.752 -3.336 -1.752 -2.231 c +-1.752 -0.96 -1.032 0 -0.012 0 c +h +-0.036 0.792 m +-1.596 0.792 -2.82 -0.359 -2.82 -2.195 c +-2.82 -4.139 -1.536 -5.279 0.06 -5.279 c +1.728 -5.279 2.855 -4.068 2.855 -2.304 c +2.855 -0.144 1.355 0.792 -0.024 0.792 c +h +f +Q +q 1 0 0 1 -73.5645 35.9199 cm +0 0 m +0 -0.611 -0.012 -1.092 -0.048 -1.571 c +0.888 -1.571 l +0.948 -0.623 l +0.972 -0.623 l +1.26 -1.164 1.932 -1.703 2.892 -1.703 c +3.696 -1.703 4.944 -1.224 4.944 0.768 c +4.944 4.236 l +3.888 4.236 l +3.888 0.889 l +3.888 -0.048 3.54 -0.84 2.544 -0.84 c +1.86 -0.84 1.32 -0.348 1.128 0.24 c +1.08 0.372 1.056 0.565 1.056 0.732 c +1.056 4.236 l +0 4.236 l +h +f +Q +q 1 0 0 1 -67.0488 39.0879 cm +0 0 m +0.324 0.192 0.876 0.408 1.404 0.408 c +2.16 0.408 2.52 0.036 2.52 -0.455 c +2.52 -0.959 2.22 -1.236 1.452 -1.522 c +0.396 -1.907 -0.096 -2.472 -0.096 -3.168 c +-0.096 -4.104 0.671 -4.871 1.908 -4.871 c +2.496 -4.871 3.012 -4.716 3.324 -4.512 c +3.072 -3.756 l +2.844 -3.888 2.424 -4.092 1.884 -4.092 c +1.26 -4.092 0.924 -3.73 0.924 -3.3 c +0.924 -2.808 1.26 -2.592 2.016 -2.304 c +3.012 -1.932 3.54 -1.428 3.54 -0.552 c +3.54 0.48 2.735 1.2 1.38 1.2 c +0.744 1.2 0.156 1.032 -0.252 0.793 c +h +f +Q +q 1 0 0 1 -55.6484 36.7129 cm +0 0 m +0 -0.145 -0.012 -0.313 -0.048 -0.457 c +-0.204 -1.116 -0.78 -1.668 -1.572 -1.668 c +-2.664 -1.668 -3.312 -0.709 -3.312 0.564 c +-3.312 1.752 -2.724 2.723 -1.596 2.723 c +-0.888 2.723 -0.24 2.243 -0.048 1.463 c +-0.012 1.319 0 1.176 0 1.008 c +h +1.056 -5.076 m +1.056 1.943 l +1.056 2.459 1.068 3.047 1.104 3.443 c +0.156 3.443 l +0.108 2.437 l +0.084 2.437 l +-0.24 3.084 -0.936 3.575 -1.884 3.575 c +-3.288 3.575 -4.38 2.387 -4.38 0.623 c +-4.392 -1.32 -3.18 -2.496 -1.776 -2.496 c +-0.876 -2.496 -0.276 -2.076 -0.024 -1.621 c +0 -1.621 l +0 -5.076 l +h +f +Q +-52.841 34.349 1.056 5.808 re +-52.841 40.156 m +-52.337 33.28 m +-52.733 33.28 -52.997 32.969 -52.997 32.597 c +-52.997 32.225 -52.721 31.924 -52.313 31.924 c +-51.905 31.924 -51.641 32.225 -51.641 32.597 c +-51.641 32.969 -51.905 33.28 -52.325 33.28 c +h +f +q 1 0 0 1 -46.9727 37.1924 cm +0 0 m +-1.152 -0.024 -2.46 0.18 -2.46 1.308 c +-2.46 2.004 -2.004 2.315 -1.476 2.315 c +-0.708 2.315 -0.216 1.836 -0.048 1.344 c +-0.012 1.235 0 1.116 0 1.008 c +h +1.032 1.572 m +1.032 2.075 1.056 2.567 1.116 2.964 c +0.168 2.964 l +0.072 2.231 l +0.036 2.231 l +-0.276 2.688 -0.912 3.096 -1.74 3.096 c +-2.916 3.096 -3.516 2.269 -3.516 1.428 c +-3.516 0.024 -2.268 -0.744 -0.024 -0.731 c +-0.024 -0.853 l +-0.024 -1.331 -0.156 -2.208 -1.344 -2.196 c +-1.896 -2.196 -2.46 -2.04 -2.868 -1.765 c +-3.108 -2.472 l +-2.628 -2.772 -1.92 -2.976 -1.188 -2.976 c +0.6 -2.976 1.032 -1.765 1.032 -0.601 c +h +f +Q +-44.249 31.637 1.056 8.52 re +f +q 1 0 0 1 -39.0405 39.4961 cm +0 0 m +1.008 0 1.764 -0.947 1.764 -2.267 c +1.764 -3.252 1.272 -4.488 0.024 -4.488 c +-1.212 -4.488 -1.752 -3.336 -1.752 -2.231 c +-1.752 -0.96 -1.032 0 -0.012 0 c +h +-0.036 0.792 m +-1.596 0.792 -2.82 -0.359 -2.82 -2.195 c +-2.82 -4.139 -1.536 -5.279 0.06 -5.279 c +1.728 -5.279 2.856 -4.068 2.856 -2.304 c +2.856 -0.144 1.356 0.792 -0.024 0.792 c +h +f +Q +q 1 0 0 1 -30.9297 36.6289 cm +0 0 m +0 -0.168 -0.012 -0.324 -0.06 -0.469 c +-0.252 -1.093 -0.756 -1.597 -1.536 -1.597 c +-2.544 -1.597 -3.264 -0.744 -3.264 0.611 c +-3.264 1.739 -2.677 2.699 -1.548 2.699 c +-0.888 2.699 -0.3 2.291 -0.084 1.607 c +-0.024 1.44 0 1.235 0 1.043 c +h +1.056 2.676 m +1.056 4.02 0.78 4.823 0.228 5.34 c +-0.348 5.856 -1.164 6.035 -1.896 6.035 c +-2.593 6.035 -3.36 5.867 -3.828 5.556 c +-3.564 4.739 l +-3.18 4.991 -2.58 5.207 -1.86 5.207 c +-0.78 5.207 0.011 4.645 0.011 3.168 c +0.011 2.531 l +-0.012 2.531 l +-0.324 3.071 -0.96 3.504 -1.86 3.504 c +-3.3 3.504 -4.332 2.279 -4.332 0.659 c +-4.332 -1.309 -3.048 -2.412 -1.704 -2.412 c +-0.697 -2.412 -0.156 -1.896 0.108 -1.416 c +0.132 -1.416 l +0.168 -2.28 l +1.104 -2.28 l +1.068 -1.873 1.056 -1.404 1.056 -0.697 c +h +f +Q +q 1 0 0 1 -24.5337 37.8281 cm +0 0 m +0 0.133 0.011 0.264 0.035 0.385 c +0.24 1.116 0.864 1.62 1.62 1.62 c +2.736 1.62 3.384 0.721 3.384 -0.611 c +3.384 -1.788 2.784 -2.783 1.656 -2.783 c +0.936 -2.783 0.264 -2.279 0.06 -1.488 c +0.024 -1.355 0 -1.212 0 -1.044 c +h +-1.056 -6.191 m +0 -6.191 l +0 -2.557 l +0.024 -2.557 l +0.396 -3.204 1.068 -3.611 2.004 -3.611 c +3.444 -3.611 4.452 -2.412 4.452 -0.66 c +4.452 1.428 3.132 2.46 1.836 2.46 c +0.996 2.46 0.336 2.137 -0.108 1.368 c +-0.133 1.368 l +-0.192 2.328 l +-1.104 2.328 l +-1.068 1.932 -1.056 1.344 -1.056 0.828 c +h +f +Q +q 1 0 0 1 -16.3496 39.4961 cm +0 0 m +1.008 0 1.764 -0.947 1.764 -2.267 c +1.764 -3.252 1.272 -4.488 0.024 -4.488 c +-1.212 -4.488 -1.752 -3.336 -1.752 -2.231 c +-1.752 -0.96 -1.032 0 -0.012 0 c +h +-0.036 0.792 m +-1.596 0.792 -2.82 -0.359 -2.82 -2.195 c +-2.82 -4.139 -1.536 -5.279 0.06 -5.279 c +1.728 -5.279 2.856 -4.068 2.856 -2.304 c +2.856 -0.144 1.356 0.792 -0.024 0.792 c +h +f +Q +q 1 0 0 1 -11.8374 34.3486 cm +0 0 m +0.828 1.235 l +1.044 1.571 1.224 1.859 1.416 2.184 c +1.452 2.184 l +1.644 1.848 1.836 1.549 2.04 1.235 c +2.844 0 l +3.996 0 l +2.016 2.808 l +4.044 5.808 l +2.844 5.808 l +1.992 4.5 l +1.764 4.164 1.572 3.841 1.368 3.491 c +1.344 3.491 l +1.152 3.841 0.948 4.151 0.732 4.5 c +-0.108 5.808 l +-1.272 5.808 l +0.792 2.844 l +-1.176 0 l +h +f +Q +q 1 0 0 1 -7.4575 41.6455 cm +0 0 m +0.264 -0.722 0.588 -2.005 0.72 -2.881 c +1.896 -3 l +1.608 -1.981 1.08 -0.649 0.744 -0.085 c +h +f +Q +q 1 0 0 1 -2.1538 34.3486 cm +0 0 m +0.78 2.939 l +0.936 3.587 1.092 4.2 1.2 4.801 c +1.236 4.801 l +1.368 4.212 1.56 3.587 1.752 2.952 c +2.7 0 l +3.588 0 l +4.488 2.903 l +4.704 3.587 4.872 4.212 5.004 4.801 c +5.04 4.801 l +5.136 4.212 5.292 3.6 5.484 2.916 c +6.313 0 l +7.356 0 l +5.484 5.808 l +4.524 5.808 l +3.636 3.036 l +3.432 2.376 3.264 1.801 3.12 1.116 c +3.096 1.116 l +2.952 1.812 2.772 2.425 2.568 3.049 c +1.632 5.808 l +0.672 5.808 l +-1.08 0 l +h +f +Q +q 1 0 0 1 6.2583 31.6367 cm +0 0 m +1.055 0 l +1.055 3.623 l +1.08 3.623 l +1.248 3.323 1.512 3.047 1.836 2.879 c +2.148 2.688 2.521 2.58 2.916 2.58 c +3.696 2.58 4.944 3.06 4.944 5.063 c +4.944 8.52 l +3.887 8.52 l +3.887 5.184 l +3.887 4.235 3.54 3.455 2.544 3.455 c +1.86 3.455 1.332 3.936 1.128 4.513 c +1.068 4.655 1.055 4.812 1.055 5.016 c +1.055 8.52 l +0 8.52 l +h +f +Q +12.918 34.349 1.056 5.808 re +12.918 40.156 m +13.422 33.28 m +13.027 33.28 12.762 32.969 12.762 32.597 c +12.762 32.225 13.039 31.924 13.447 31.924 c +13.854 31.924 14.119 32.225 14.119 32.597 c +14.119 32.969 13.854 33.28 13.434 33.28 c +h +f +q 1 0 0 1 19.8657 39.9521 cm +0 0 m +-0.275 0.132 -0.888 0.336 -1.668 0.336 c +-3.42 0.336 -4.56 -0.853 -4.56 -2.639 c +-4.56 -4.427 -3.336 -5.735 -1.439 -5.735 c +-0.815 -5.735 -0.264 -5.58 0.024 -5.424 c +-0.216 -4.62 l +-0.468 -4.752 -0.863 -4.896 -1.439 -4.896 c +-2.771 -4.896 -3.491 -3.899 -3.491 -2.688 c +-3.491 -1.344 -2.628 -0.517 -1.476 -0.517 c +-0.876 -0.517 -0.479 -0.66 -0.18 -0.792 c +h +f +Q +q 1 0 0 1 21.1021 31.6367 cm +0 0 m +1.056 0 l +1.056 3.623 l +1.08 3.623 l +1.248 3.323 1.512 3.047 1.835 2.879 c +2.148 2.688 2.521 2.58 2.916 2.58 c +3.696 2.58 4.944 3.06 4.944 5.063 c +4.944 8.52 l +3.888 8.52 l +3.888 5.184 l +3.888 4.235 3.54 3.455 2.543 3.455 c +1.86 3.455 1.332 3.936 1.128 4.513 c +1.068 4.655 1.056 4.812 1.056 5.016 c +1.056 8.52 l +0 8.52 l +h +f +Q +q 1 0 0 1 33.3657 37.1924 cm +0 0 m +-1.151 -0.024 -2.46 0.18 -2.46 1.308 c +-2.46 2.004 -2.004 2.315 -1.476 2.315 c +-0.708 2.315 -0.216 1.836 -0.048 1.344 c +-0.012 1.235 0 1.116 0 1.008 c +h +1.032 1.572 m +1.032 2.075 1.057 2.567 1.116 2.964 c +0.169 2.964 l +0.072 2.231 l +0.036 2.231 l +-0.275 2.688 -0.911 3.096 -1.739 3.096 c +-2.916 3.096 -3.516 2.269 -3.516 1.428 c +-3.516 0.024 -2.268 -0.744 -0.023 -0.731 c +-0.023 -0.853 l +-0.023 -1.331 -0.155 -2.208 -1.344 -2.196 c +-1.896 -2.196 -2.46 -2.04 -2.867 -1.765 c +-3.107 -2.472 l +-2.628 -2.772 -1.92 -2.976 -1.188 -2.976 c +0.601 -2.976 1.032 -1.765 1.032 -0.601 c +h +f +Q +q 1 0 0 1 37.1455 37.792 cm +0 0 m +0 0.144 0.013 0.3 0.049 0.433 c +0.229 1.164 0.876 1.669 1.621 1.669 c +2.737 1.669 3.385 0.757 3.385 -0.575 c +3.385 -1.739 2.785 -2.735 1.668 -2.735 c +0.949 -2.735 0.265 -2.231 0.061 -1.439 c +0.037 -1.296 0 -1.14 0 -1.008 c +h +-1.055 -1.548 m +-1.055 -2.292 -1.067 -2.891 -1.103 -3.443 c +-0.155 -3.443 l +-0.095 -2.448 l +-0.071 -2.448 l +0.349 -3.168 1.044 -3.575 1.981 -3.575 c +3.397 -3.575 4.453 -2.388 4.453 -0.624 c +4.453 1.464 3.168 2.496 1.813 2.496 c +1.033 2.496 0.373 2.16 0.025 1.585 c +0 1.585 l +0 4.74 l +-1.055 4.74 l +h +f +Q +q 1 0 0 1 43.9741 37.792 cm +0 0 m +0 0.144 0.013 0.3 0.049 0.433 c +0.229 1.164 0.876 1.669 1.62 1.669 c +2.736 1.669 3.385 0.757 3.385 -0.575 c +3.385 -1.739 2.784 -2.735 1.668 -2.735 c +0.948 -2.735 0.265 -2.231 0.061 -1.439 c +0.036 -1.296 0 -1.14 0 -1.008 c +h +-1.056 -1.548 m +-1.056 -2.292 -1.067 -2.891 -1.104 -3.443 c +-0.155 -3.443 l +-0.096 -2.448 l +-0.071 -2.448 l +0.349 -3.168 1.044 -3.575 1.98 -3.575 c +3.396 -3.575 4.452 -2.388 4.452 -0.624 c +4.452 1.464 3.168 2.496 1.813 2.496 c +1.032 2.496 0.372 2.16 0.024 1.585 c +0 1.585 l +0 4.74 l +-1.056 4.74 l +h +f +Q +q 1 0 0 1 53.4409 36.6758 cm +0 0 m +0.013 -0.659 -0.275 -1.716 -1.451 -1.716 c +-2.531 -1.716 -2.987 -0.731 -3.071 0 c +h +-3.084 0.757 m +-3.06 2.186 -2.159 2.772 -1.104 2.772 c +-0.348 2.772 0.12 2.642 0.505 2.484 c +0.696 3.229 l +0.324 3.396 -0.323 3.602 -1.248 3.602 c +-3.023 3.602 -4.104 2.412 -4.104 0.673 c +-4.104 -1.092 -3.06 -2.472 -1.367 -2.472 c +0.528 -2.472 1.021 -0.804 1.021 0.266 c +1.021 0.48 1.009 0.648 0.984 0.769 c +h +f +Q +q 1 0 0 1 58.8296 37.1924 cm +0 0 m +-1.151 -0.024 -2.46 0.18 -2.46 1.308 c +-2.46 2.004 -2.004 2.315 -1.476 2.315 c +-0.708 2.315 -0.216 1.836 -0.048 1.344 c +-0.012 1.235 0 1.116 0 1.008 c +h +1.032 1.572 m +1.032 2.075 1.057 2.567 1.116 2.964 c +0.169 2.964 l +0.072 2.231 l +0.036 2.231 l +-0.275 2.688 -0.911 3.096 -1.739 3.096 c +-2.916 3.096 -3.516 2.269 -3.516 1.428 c +-3.516 0.024 -2.268 -0.744 -0.023 -0.731 c +-0.023 -0.853 l +-0.023 -1.331 -0.155 -2.208 -1.344 -2.196 c +-1.896 -2.196 -2.46 -2.04 -2.867 -1.765 c +-3.107 -2.472 l +-2.628 -2.772 -1.92 -2.976 -1.188 -2.976 c +0.601 -2.976 1.032 -1.765 1.032 -0.601 c +h +f +Q +q 1 0 0 1 61.5649 36.1602 cm +0 0 m +0 -0.684 -0.012 -1.271 -0.048 -1.812 c +0.876 -1.812 l +0.925 -0.672 l +0.96 -0.672 l +1.225 -1.452 1.872 -1.943 2.58 -1.943 c +2.688 -1.943 2.772 -1.931 2.868 -1.908 c +2.868 -0.924 l +2.748 -0.936 2.641 -0.947 2.496 -0.947 c +1.752 -0.947 1.225 -0.384 1.08 0.396 c +1.057 0.553 1.044 0.721 1.044 0.9 c +1.044 3.996 l +-0.012 3.996 l +h +f +Q +q 1 0 0 1 65.3335 39.0879 cm +0 0 m +0.323 0.192 0.876 0.408 1.403 0.408 c +2.159 0.408 2.519 0.036 2.519 -0.455 c +2.519 -0.959 2.22 -1.236 1.451 -1.522 c +0.395 -1.907 -0.097 -2.472 -0.097 -3.168 c +-0.097 -4.104 0.672 -4.871 1.907 -4.871 c +2.495 -4.871 3.012 -4.716 3.323 -4.512 c +3.071 -3.756 l +2.844 -3.888 2.424 -4.092 1.884 -4.092 c +1.26 -4.092 0.924 -3.73 0.924 -3.3 c +0.924 -2.808 1.26 -2.592 2.016 -2.304 c +3.012 -1.932 3.54 -1.428 3.54 -0.552 c +3.54 0.48 2.735 1.2 1.38 1.2 c +0.743 1.2 0.155 1.032 -0.252 0.793 c +h +f +Q +q 1 0 0 1 73.1938 34.3486 cm +0 0 m +0.78 2.939 l +0.937 3.587 1.092 4.2 1.2 4.801 c +1.236 4.801 l +1.368 4.212 1.561 3.587 1.752 2.952 c +2.7 0 l +3.588 0 l +4.488 2.903 l +4.704 3.587 4.872 4.212 5.004 4.801 c +5.04 4.801 l +5.136 4.212 5.292 3.6 5.484 2.916 c +6.313 0 l +7.356 0 l +5.484 5.808 l +4.524 5.808 l +3.636 3.036 l +3.433 2.376 3.264 1.801 3.12 1.116 c +3.096 1.116 l +2.952 1.812 2.772 2.425 2.568 3.049 c +1.632 5.808 l +0.672 5.808 l +-1.08 0 l +h +f +Q +q 1 0 0 1 81.6045 31.6367 cm +0 0 m +1.056 0 l +1.056 3.623 l +1.081 3.623 l +1.248 3.323 1.512 3.047 1.836 2.879 c +2.149 2.688 2.521 2.58 2.916 2.58 c +3.697 2.58 4.945 3.06 4.945 5.063 c +4.945 8.52 l +3.888 8.52 l +3.888 5.184 l +3.888 4.235 3.54 3.455 2.544 3.455 c +1.861 3.455 1.332 3.936 1.128 4.513 c +1.069 4.655 1.056 4.812 1.056 5.016 c +1.056 8.52 l +0 8.52 l +h +f +Q +q 1 0 0 1 91.9487 36.6758 cm +0 0 m +0.013 -0.659 -0.275 -1.716 -1.451 -1.716 c +-2.531 -1.716 -2.987 -0.731 -3.071 0 c +h +-3.084 0.757 m +-3.06 2.186 -2.159 2.772 -1.104 2.772 c +-0.348 2.772 0.12 2.642 0.505 2.484 c +0.696 3.229 l +0.324 3.396 -0.323 3.602 -1.248 3.602 c +-3.023 3.602 -4.104 2.412 -4.104 0.673 c +-4.104 -1.092 -3.06 -2.472 -1.367 -2.472 c +0.528 -2.472 1.021 -0.804 1.021 0.266 c +1.021 0.48 1.009 0.648 0.984 0.769 c +h +f +Q +q 1 0 0 1 94.2778 35.9199 cm +0 0 m +0 -0.611 -0.012 -1.092 -0.048 -1.571 c +0.888 -1.571 l +0.948 -0.623 l +0.972 -0.623 l +1.26 -1.164 1.932 -1.703 2.892 -1.703 c +3.696 -1.703 4.944 -1.224 4.944 0.768 c +4.944 4.236 l +3.888 4.236 l +3.888 0.889 l +3.888 -0.048 3.54 -0.84 2.544 -0.84 c +1.86 -0.84 1.32 -0.348 1.128 0.24 c +1.08 0.372 1.056 0.565 1.056 0.732 c +1.056 4.236 l +0 4.236 l +h +f +Q +q 1 0 0 1 -102.1719 53.5879 cm +0 0 m +0.324 0.191 0.876 0.407 1.404 0.407 c +2.16 0.407 2.52 0.035 2.52 -0.456 c +2.52 -0.961 2.22 -1.235 1.452 -1.524 c +0.396 -1.907 -0.096 -2.473 -0.096 -3.168 c +-0.096 -4.104 0.672 -4.872 1.908 -4.872 c +2.496 -4.872 3.012 -4.717 3.324 -4.513 c +3.072 -3.757 l +2.844 -3.889 2.424 -4.093 1.884 -4.093 c +1.26 -4.093 0.924 -3.732 0.924 -3.301 c +0.924 -2.809 1.26 -2.593 2.016 -2.305 c +3.012 -1.933 3.54 -1.429 3.54 -0.553 c +3.54 0.479 2.735 1.199 1.379 1.199 c +0.744 1.199 0.156 1.031 -0.252 0.792 c +h +f +Q +q 1 0 0 1 -94.2153 51.6914 cm +0 0 m +-1.152 -0.023 -2.46 0.18 -2.46 1.308 c +-2.46 2.004 -2.004 2.316 -1.476 2.316 c +-0.708 2.316 -0.216 1.836 -0.048 1.344 c +-0.012 1.236 0 1.115 0 1.008 c +h +1.032 1.573 m +1.032 2.077 1.056 2.568 1.116 2.964 c +0.168 2.964 l +0.072 2.232 l +0.036 2.232 l +-0.276 2.688 -0.912 3.096 -1.74 3.096 c +-2.916 3.096 -3.516 2.268 -3.516 1.428 c +-3.516 0.024 -2.268 -0.744 -0.024 -0.732 c +-0.024 -0.852 l +-0.024 -1.332 -0.156 -2.208 -1.344 -2.196 c +-1.896 -2.196 -2.46 -2.04 -2.868 -1.764 c +-3.108 -2.473 l +-2.628 -2.771 -1.92 -2.976 -1.188 -2.976 c +0.6 -2.976 1.032 -1.764 1.032 -0.6 c +h +f +Q +q 1 0 0 1 -91.2393 48.8486 cm +0 0 m +1.14 3.251 l +1.332 3.79 1.488 4.271 1.608 4.752 c +1.644 4.752 l +1.776 4.271 1.944 3.79 2.136 3.251 c +3.264 0 l +4.368 0 l +2.088 5.807 l +1.08 5.807 l +-1.128 0 l +h +f +Q +-85.875 48.849 1.056 5.807 re +-85.875 54.655 m +-85.372 47.779 m +-85.768 47.779 -86.031 47.468 -86.031 47.097 c +-86.031 46.723 -85.755 46.424 -85.348 46.424 c +-84.939 46.424 -84.675 46.723 -84.675 47.097 c +-84.675 47.468 -84.939 47.779 -85.359 47.779 c +h +f +q 1 0 0 1 -83.0674 50.4199 cm +0 0 m +0 -0.613 -0.012 -1.093 -0.048 -1.571 c +0.888 -1.571 l +0.948 -0.624 l +0.972 -0.624 l +1.26 -1.164 1.932 -1.704 2.892 -1.704 c +3.696 -1.704 4.944 -1.225 4.944 0.768 c +4.944 4.235 l +3.888 4.235 l +3.888 0.887 l +3.888 -0.049 3.54 -0.841 2.544 -0.841 c +1.86 -0.841 1.32 -0.349 1.128 0.239 c +1.08 0.371 1.056 0.563 1.056 0.731 c +1.056 4.235 l +0 4.235 l +h +f +Q +q 1 0 0 1 -72.4956 51.127 cm +0 0 m +0 -0.168 -0.012 -0.324 -0.06 -0.468 c +-0.252 -1.092 -0.756 -1.596 -1.536 -1.596 c +-2.544 -1.596 -3.264 -0.744 -3.264 0.612 c +-3.264 1.74 -2.676 2.7 -1.548 2.7 c +-0.888 2.7 -0.3 2.293 -0.084 1.608 c +-0.024 1.44 0 1.236 0 1.045 c +h +1.056 2.676 m +1.056 4.021 0.78 4.824 0.228 5.341 c +-0.348 5.856 -1.164 6.036 -1.896 6.036 c +-2.592 6.036 -3.36 5.868 -3.828 5.557 c +-3.564 4.74 l +-3.18 4.992 -2.58 5.209 -1.86 5.209 c +-0.78 5.209 0.012 4.645 0.012 3.169 c +0.012 2.532 l +-0.012 2.532 l +-0.324 3.072 -0.96 3.505 -1.86 3.505 c +-3.3 3.505 -4.332 2.28 -4.332 0.66 c +-4.332 -1.308 -3.048 -2.411 -1.704 -2.411 c +-0.696 -2.411 -0.156 -1.896 0.108 -1.416 c +0.132 -1.416 l +0.168 -2.278 l +1.104 -2.278 l +1.068 -1.871 1.056 -1.404 1.056 -0.694 c +h +f +Q +q 1 0 0 1 -64.0957 51.6914 cm +0 0 m +-1.152 -0.023 -2.46 0.18 -2.46 1.308 c +-2.46 2.004 -2.004 2.316 -1.476 2.316 c +-0.708 2.316 -0.216 1.836 -0.048 1.344 c +-0.012 1.236 0 1.115 0 1.008 c +h +1.032 1.573 m +1.032 2.077 1.056 2.568 1.116 2.964 c +0.168 2.964 l +0.072 2.232 l +0.036 2.232 l +-0.276 2.688 -0.912 3.096 -1.74 3.096 c +-2.916 3.096 -3.516 2.268 -3.516 1.428 c +-3.516 0.024 -2.268 -0.744 -0.024 -0.732 c +-0.024 -0.852 l +-0.024 -1.332 -0.157 -2.208 -1.344 -2.196 c +-1.896 -2.196 -2.46 -2.04 -2.868 -1.764 c +-3.108 -2.473 l +-2.628 -2.771 -1.92 -2.976 -1.188 -2.976 c +0.6 -2.976 1.032 -1.764 1.032 -0.6 c +h +f +Q +q 1 0 0 1 -61.3716 50.4199 cm +0 0 m +0 -0.613 -0.012 -1.093 -0.048 -1.571 c +0.888 -1.571 l +0.948 -0.624 l +0.972 -0.624 l +1.26 -1.164 1.932 -1.704 2.892 -1.704 c +3.696 -1.704 4.944 -1.225 4.944 0.768 c +4.944 4.235 l +3.888 4.235 l +3.888 0.887 l +3.888 -0.049 3.54 -0.841 2.544 -0.841 c +1.86 -0.841 1.32 -0.349 1.128 0.239 c +1.08 0.371 1.056 0.563 1.056 0.731 c +1.056 4.235 l +0 4.235 l +h +f +Q +q 1 0 0 1 -48.1714 51.2959 cm +0 0 m +-0.804 -2.329 l +-0.972 -2.856 -1.092 -3.337 -1.212 -3.805 c +-1.248 -3.805 l +-1.356 -3.337 -1.488 -2.832 -1.644 -2.341 c +-2.436 0 l +h +-2.652 0.815 m +-3.492 3.359 l +-4.572 3.359 l +-1.824 -4.729 l +-0.564 -4.729 l +2.196 3.359 l +1.08 3.359 l +0.216 0.815 l +h +f +Q +q 1 0 0 1 -41.0088 51.2109 cm +0 0 m +0 -0.144 -0.012 -0.312 -0.048 -0.455 c +-0.204 -1.114 -0.78 -1.668 -1.572 -1.668 c +-2.664 -1.668 -3.312 -0.707 -3.312 0.564 c +-3.312 1.753 -2.724 2.725 -1.596 2.725 c +-0.888 2.725 -0.24 2.244 -0.048 1.465 c +-0.012 1.32 0 1.176 0 1.008 c +h +1.056 -5.076 m +1.056 1.944 l +1.056 2.461 1.068 3.049 1.104 3.444 c +0.156 3.444 l +0.108 2.437 l +0.084 2.437 l +-0.24 3.085 -0.936 3.576 -1.884 3.576 c +-3.288 3.576 -4.38 2.39 -4.38 0.625 c +-4.392 -1.32 -3.181 -2.495 -1.776 -2.495 c +-0.876 -2.495 -0.276 -2.076 -0.024 -1.619 c +0 -1.619 l +0 -5.076 l +h +f +Q +q 1 0 0 1 -35.8008 53.9951 cm +0 0 m +1.008 0 1.764 -0.947 1.764 -2.269 c +1.764 -3.252 1.272 -4.487 0.024 -4.487 c +-1.212 -4.487 -1.752 -3.336 -1.752 -2.23 c +-1.752 -0.96 -1.032 0 -0.012 0 c +h +-0.036 0.792 m +-1.596 0.792 -2.82 -0.359 -2.82 -2.196 c +-2.82 -4.14 -1.536 -5.279 0.06 -5.279 c +1.728 -5.279 2.856 -4.067 2.856 -2.304 c +2.856 -0.144 1.356 0.792 -0.024 0.792 c +h +f +Q +q 1 0 0 1 -30.5566 52.3271 cm +0 0 m +0 0.132 0.012 0.265 0.035 0.384 c +0.24 1.116 0.864 1.62 1.62 1.62 c +2.736 1.62 3.384 0.721 3.384 -0.611 c +3.384 -1.788 2.784 -2.784 1.656 -2.784 c +0.936 -2.784 0.263 -2.279 0.06 -1.487 c +0.024 -1.356 0 -1.212 0 -1.044 c +h +-1.056 -6.192 m +0 -6.192 l +0 -2.556 l +0.024 -2.556 l +0.396 -3.204 1.068 -3.611 2.004 -3.611 c +3.444 -3.611 4.451 -2.412 4.451 -0.659 c +4.451 1.429 3.132 2.46 1.836 2.46 c +0.996 2.46 0.336 2.137 -0.108 1.368 c +-0.133 1.368 l +-0.192 2.328 l +-1.104 2.328 l +-1.069 1.933 -1.056 1.345 -1.056 0.828 c +h +f +Q +q 1 0 0 1 -21.0884 51.1758 cm +0 0 m +0.012 -0.66 -0.276 -1.717 -1.452 -1.717 c +-2.532 -1.717 -2.988 -0.732 -3.072 0 c +h +-3.084 0.756 m +-3.06 2.184 -2.16 2.771 -1.104 2.771 c +-0.348 2.771 0.12 2.64 0.504 2.483 c +0.696 3.228 l +0.324 3.396 -0.324 3.6 -1.248 3.6 c +-3.024 3.6 -4.104 2.412 -4.104 0.673 c +-4.104 -1.093 -3.06 -2.473 -1.368 -2.473 c +0.528 -2.473 1.02 -0.805 1.02 0.264 c +1.02 0.479 1.008 0.647 0.984 0.768 c +h +f +Q +-15.138 54.655 -1.056 -8.088 re +f +-13.218 46.135 1.056 8.521 re +f +-10.41 46.135 1.056 8.521 re +f +q 1 0 0 1 -2.7417 53.0713 cm +0 0 m +0 0.601 0.012 1.128 0.048 1.584 c +-0.888 1.584 l +-0.948 0.636 l +-0.972 0.636 l +-1.236 1.104 -1.86 1.716 -2.892 1.716 c +-3.804 1.716 -4.896 1.2 -4.896 -0.828 c +-4.896 -4.223 l +-3.84 -4.223 l +-3.84 -1.02 l +-3.84 0.084 -3.492 0.84 -2.544 0.84 c +-1.836 0.84 -1.344 0.349 -1.152 -0.132 c +-1.092 -0.275 -1.056 -0.468 -1.056 -0.672 c +-1.056 -4.223 l +0 -4.223 l +h +f +Q +q 1 0 0 1 -1.1338 53.5879 cm +0 0 m +0.324 0.191 0.876 0.407 1.404 0.407 c +2.16 0.407 2.52 0.035 2.52 -0.456 c +2.52 -0.961 2.22 -1.235 1.452 -1.524 c +0.396 -1.907 -0.096 -2.473 -0.096 -3.168 c +-0.096 -4.104 0.672 -4.872 1.908 -4.872 c +2.496 -4.872 3.012 -4.717 3.324 -4.513 c +3.072 -3.757 l +2.844 -3.889 2.424 -4.093 1.884 -4.093 c +1.26 -4.093 0.924 -3.732 0.924 -3.301 c +0.924 -2.809 1.26 -2.593 2.016 -2.305 c +3.012 -1.933 3.54 -1.429 3.54 -0.553 c +3.54 0.479 2.736 1.199 1.38 1.199 c +0.744 1.199 0.156 1.031 -0.252 0.792 c +h +f +Q +q 1 0 0 1 5.0347 47.4551 cm +0 0 m +0 1.394 l +1.512 1.394 l +1.512 2.196 l +0 2.196 l +0 5.328 l +0 6.049 0.203 6.456 0.792 6.456 c +1.079 6.456 1.248 6.432 1.403 6.385 c +1.451 7.188 l +1.248 7.261 0.924 7.332 0.516 7.332 c +0.023 7.332 -0.372 7.164 -0.624 6.889 c +-0.912 6.564 -1.032 6.049 -1.032 5.364 c +-1.032 2.196 l +-1.933 2.196 l +-1.933 1.394 l +-1.032 1.394 l +-1.032 0.324 l +h +f +Q +q 1 0 0 1 7.7466 50.6592 cm +0 0 m +0 -0.685 -0.012 -1.272 -0.048 -1.811 c +0.876 -1.811 l +0.925 -0.672 l +0.96 -0.672 l +1.225 -1.452 1.872 -1.943 2.58 -1.943 c +2.688 -1.943 2.772 -1.933 2.868 -1.908 c +2.868 -0.924 l +2.748 -0.937 2.641 -0.948 2.496 -0.948 c +1.752 -0.948 1.225 -0.384 1.08 0.396 c +1.057 0.552 1.044 0.721 1.044 0.899 c +1.044 3.996 l +-0.012 3.996 l +h +f +Q +q 1 0 0 1 14.6455 51.6914 cm +0 0 m +-1.151 -0.023 -2.46 0.18 -2.46 1.308 c +-2.46 2.004 -2.003 2.316 -1.475 2.316 c +-0.708 2.316 -0.215 1.836 -0.047 1.344 c +-0.011 1.236 0 1.115 0 1.008 c +h +1.033 1.573 m +1.033 2.077 1.057 2.568 1.117 2.964 c +0.169 2.964 l +0.073 2.232 l +0.037 2.232 l +-0.275 2.688 -0.911 3.096 -1.739 3.096 c +-2.916 3.096 -3.515 2.268 -3.515 1.428 c +-3.515 0.024 -2.267 -0.744 -0.023 -0.732 c +-0.023 -0.852 l +-0.023 -1.332 -0.155 -2.208 -1.343 -2.196 c +-1.895 -2.196 -2.46 -2.04 -2.867 -1.764 c +-3.107 -2.473 l +-2.627 -2.771 -1.919 -2.976 -1.187 -2.976 c +0.601 -2.976 1.033 -1.764 1.033 -0.6 c +h +f +Q +q 1 0 0 1 18.5952 47.4551 cm +0 0 m +0 1.394 l +1.512 1.394 l +1.512 2.196 l +0 2.196 l +0 5.328 l +0 6.049 0.203 6.456 0.792 6.456 c +1.079 6.456 1.248 6.432 1.403 6.385 c +1.451 7.188 l +1.248 7.261 0.923 7.332 0.516 7.332 c +0.023 7.332 -0.372 7.164 -0.624 6.889 c +-0.912 6.564 -1.033 6.049 -1.033 5.364 c +-1.033 2.196 l +-1.933 2.196 l +-1.933 1.394 l +-1.033 1.394 l +-1.033 0.324 l +h +f +Q +q 1 0 0 1 23.6226 53.9951 cm +0 0 m +1.008 0 1.764 -0.947 1.764 -2.269 c +1.764 -3.252 1.271 -4.487 0.023 -4.487 c +-1.212 -4.487 -1.752 -3.336 -1.752 -2.23 c +-1.752 -0.96 -1.032 0 -0.013 0 c +h +-0.036 0.792 m +-1.597 0.792 -2.82 -0.359 -2.82 -2.196 c +-2.82 -4.14 -1.536 -5.279 0.06 -5.279 c +1.728 -5.279 2.855 -4.067 2.855 -2.304 c +2.855 -0.144 1.355 0.792 -0.024 0.792 c +h +f +Q +q 1 0 0 1 27.8218 50.6592 cm +0 0 m +0 -0.685 -0.012 -1.272 -0.048 -1.811 c +0.876 -1.811 l +0.924 -0.672 l +0.96 -0.672 l +1.225 -1.452 1.872 -1.943 2.58 -1.943 c +2.688 -1.943 2.772 -1.933 2.868 -1.908 c +2.868 -0.924 l +2.748 -0.937 2.641 -0.948 2.496 -0.948 c +1.752 -0.948 1.225 -0.384 1.08 0.396 c +1.057 0.552 1.044 0.721 1.044 0.899 c +1.044 3.996 l +-0.012 3.996 l +h +f +Q +q 1 0 0 1 34.3735 54.6553 cm +0 0 m +0 -5.004 l +-0.804 -5.004 l +-0.804 -5.807 l +0 -5.807 l +0 -6.084 l +0 -6.899 0.192 -7.643 0.685 -8.112 c +1.08 -8.496 1.608 -8.651 2.088 -8.651 c +2.473 -8.651 2.784 -8.567 2.988 -8.483 c +2.856 -7.668 l +2.688 -7.74 2.484 -7.8 2.172 -7.8 c +1.284 -7.8 1.044 -7.008 1.044 -6.12 c +1.044 -5.807 l +2.448 -5.807 l +2.448 -5.004 l +1.057 -5.004 l +1.057 0 l +h +f +Q +37.783 48.849 1.056 5.807 re +37.783 54.655 m +38.287 47.779 m +37.891 47.779 37.626 47.468 37.626 47.097 c +37.626 46.723 37.903 46.424 38.311 46.424 c +38.718 46.424 38.983 46.723 38.983 47.097 c +38.983 47.468 38.718 47.779 38.298 47.779 c +h +f +40.59 46.135 1.055 8.521 re +f +q 1 0 0 1 47.0815 51.1758 cm +0 0 m +0.013 -0.66 -0.275 -1.717 -1.451 -1.717 c +-2.531 -1.717 -2.987 -0.732 -3.071 0 c +h +-3.084 0.756 m +-3.06 2.184 -2.159 2.771 -1.104 2.771 c +-0.348 2.771 0.12 2.64 0.505 2.483 c +0.696 3.228 l +0.324 3.396 -0.323 3.6 -1.248 3.6 c +-3.023 3.6 -4.104 2.412 -4.104 0.673 c +-4.104 -1.093 -3.06 -2.473 -1.367 -2.473 c +0.528 -2.473 1.021 -0.805 1.021 0.264 c +1.021 0.479 1.009 0.647 0.984 0.768 c +h +f +Q +q 1 0 0 1 56.8149 53.0713 cm +0 0 m +0 0.601 0.012 1.128 0.048 1.584 c +-0.889 1.584 l +-0.948 0.636 l +-0.973 0.636 l +-1.236 1.104 -1.86 1.716 -2.893 1.716 c +-3.805 1.716 -4.896 1.2 -4.896 -0.828 c +-4.896 -4.223 l +-3.84 -4.223 l +-3.84 -1.02 l +-3.84 0.084 -3.492 0.84 -2.544 0.84 c +-1.836 0.84 -1.345 0.349 -1.152 -0.132 c +-1.093 -0.275 -1.057 -0.468 -1.057 -0.672 c +-1.057 -4.223 l +0 -4.223 l +h +f +Q +q 1 0 0 1 58.4233 53.5879 cm +0 0 m +0.323 0.191 0.876 0.407 1.403 0.407 c +2.159 0.407 2.52 0.035 2.52 -0.456 c +2.52 -0.961 2.22 -1.235 1.451 -1.524 c +0.396 -1.907 -0.097 -2.473 -0.097 -3.168 c +-0.097 -4.104 0.672 -4.872 1.907 -4.872 c +2.495 -4.872 3.012 -4.717 3.323 -4.513 c +3.071 -3.757 l +2.844 -3.889 2.424 -4.093 1.884 -4.093 c +1.26 -4.093 0.924 -3.732 0.924 -3.301 c +0.924 -2.809 1.26 -2.593 2.015 -2.305 c +3.012 -1.933 3.54 -1.429 3.54 -0.553 c +3.54 0.479 2.735 1.199 1.38 1.199 c +0.743 1.199 0.155 1.031 -0.252 0.792 c +h +f +Q +63.319 48.849 1.056 5.807 re +63.319 54.655 m +63.823 47.779 m +63.427 47.779 63.163 47.468 63.163 47.097 c +63.163 46.723 63.438 46.424 63.847 46.424 c +64.255 46.424 64.519 46.723 64.519 47.097 c +64.519 47.468 64.255 47.779 63.834 47.779 c +h +f +q 1 0 0 1 66.1255 50.4199 cm +0 0 m +0 -0.613 -0.012 -1.093 -0.048 -1.571 c +0.888 -1.571 l +0.948 -0.624 l +0.972 -0.624 l +1.26 -1.164 1.932 -1.704 2.892 -1.704 c +3.696 -1.704 4.944 -1.225 4.944 0.768 c +4.944 4.235 l +3.888 4.235 l +3.888 0.887 l +3.888 -0.049 3.54 -0.841 2.544 -0.841 c +1.86 -0.841 1.32 -0.349 1.128 0.239 c +1.08 0.371 1.056 0.563 1.056 0.731 c +1.056 4.235 l +0 4.235 l +h +f +Q +q 1 0 0 1 76.6978 51.127 cm +0 0 m +0 -0.168 -0.012 -0.324 -0.06 -0.468 c +-0.252 -1.092 -0.756 -1.596 -1.536 -1.596 c +-2.544 -1.596 -3.264 -0.744 -3.264 0.612 c +-3.264 1.74 -2.676 2.7 -1.548 2.7 c +-0.888 2.7 -0.3 2.293 -0.084 1.608 c +-0.024 1.44 0 1.236 0 1.045 c +h +1.056 2.676 m +1.056 4.021 0.78 4.824 0.228 5.341 c +-0.348 5.856 -1.164 6.036 -1.896 6.036 c +-2.592 6.036 -3.36 5.868 -3.828 5.557 c +-3.564 4.74 l +-3.18 4.992 -2.58 5.209 -1.86 5.209 c +-0.78 5.209 0.012 4.645 0.012 3.169 c +0.012 2.532 l +-0.012 2.532 l +-0.324 3.072 -0.96 3.505 -1.86 3.505 c +-3.3 3.505 -4.332 2.28 -4.332 0.66 c +-4.332 -1.308 -3.048 -2.411 -1.704 -2.411 c +-0.696 -2.411 -0.156 -1.896 0.108 -1.416 c +0.132 -1.416 l +0.168 -2.278 l +1.104 -2.278 l +1.068 -1.871 1.056 -1.404 1.056 -0.694 c +h +f +Q +q 1 0 0 1 83.3101 47.4551 cm +0 0 m +0 1.394 l +1.512 1.394 l +1.512 2.196 l +0 2.196 l +0 5.328 l +0 6.049 0.203 6.456 0.792 6.456 c +1.079 6.456 1.248 6.432 1.403 6.385 c +1.451 7.188 l +1.248 7.261 0.924 7.332 0.516 7.332 c +0.023 7.332 -0.373 7.164 -0.625 6.889 c +-0.912 6.564 -1.032 6.049 -1.032 5.364 c +-1.032 2.196 l +-1.933 2.196 l +-1.933 1.394 l +-1.032 1.394 l +-1.032 0.324 l +h +f +Q +q 1 0 0 1 86.0103 46.1348 cm +0 0 m +1.056 0 l +1.056 3.625 l +1.08 3.625 l +1.248 3.324 1.511 3.05 1.836 2.882 c +2.148 2.688 2.521 2.581 2.916 2.581 c +3.696 2.581 4.944 3.061 4.944 5.064 c +4.944 8.521 l +3.888 8.521 l +3.888 5.185 l +3.888 4.236 3.54 3.457 2.544 3.457 c +1.86 3.457 1.332 3.937 1.128 4.513 c +1.068 4.656 1.056 4.813 1.056 5.017 c +1.056 8.521 l +0 8.521 l +h +f +Q +q 1 0 0 1 96.3535 51.1758 cm +0 0 m +0.013 -0.66 -0.275 -1.717 -1.451 -1.717 c +-2.531 -1.717 -2.987 -0.732 -3.071 0 c +h +-3.083 0.756 m +-3.059 2.184 -2.159 2.771 -1.103 2.771 c +-0.347 2.771 0.121 2.64 0.505 2.483 c +0.697 3.228 l +0.325 3.396 -0.323 3.6 -1.247 3.6 c +-3.023 3.6 -4.103 2.412 -4.103 0.673 c +-4.103 -1.093 -3.059 -2.473 -1.367 -2.473 c +0.529 -2.473 1.021 -0.805 1.021 0.264 c +1.021 0.479 1.009 0.647 0.985 0.768 c +h +f +Q +q 1 0 0 1 -102.1245 67.8838 cm +0 0 m +0.469 0.301 1.141 0.527 1.861 0.527 c +2.929 0.527 3.553 -0.024 3.553 -0.853 c +3.553 -1.597 3.121 -2.04 2.029 -2.448 c +0.708 -2.929 -0.107 -3.624 -0.107 -4.752 c +-0.107 -6.013 0.936 -6.948 2.509 -6.948 c +3.325 -6.948 3.936 -6.757 4.285 -6.553 c +3.997 -5.699 l +3.745 -5.856 3.205 -6.085 2.473 -6.085 c +1.369 -6.085 0.949 -5.425 0.949 -4.873 c +0.949 -4.115 1.441 -3.744 2.557 -3.313 c +3.925 -2.772 4.609 -2.125 4.609 -0.937 c +4.609 0.312 3.697 1.403 1.789 1.403 c +1.009 1.403 0.157 1.164 -0.275 0.875 c +h +f +Q +q 1 0 0 1 -92.9917 66.1914 cm +0 0 m +-1.152 -0.024 -2.46 0.18 -2.46 1.308 c +-2.46 2.004 -2.004 2.316 -1.476 2.316 c +-0.708 2.316 -0.216 1.836 -0.048 1.344 c +-0.012 1.236 0 1.116 0 1.008 c +h +1.032 1.573 m +1.032 2.076 1.056 2.567 1.116 2.964 c +0.168 2.964 l +0.072 2.232 l +0.036 2.232 l +-0.276 2.688 -0.912 3.096 -1.74 3.096 c +-2.916 3.096 -3.516 2.268 -3.516 1.428 c +-3.516 0.023 -2.268 -0.744 -0.024 -0.732 c +-0.024 -0.852 l +-0.024 -1.332 -0.156 -2.208 -1.344 -2.196 c +-1.896 -2.196 -2.46 -2.04 -2.868 -1.764 c +-3.108 -2.473 l +-2.628 -2.772 -1.92 -2.977 -1.188 -2.977 c +0.6 -2.977 1.032 -1.764 1.032 -0.601 c +h +f +Q +q 1 0 0 1 -90.0156 63.3486 cm +0 0 m +1.139 3.252 l +1.332 3.791 1.488 4.271 1.608 4.751 c +1.644 4.751 l +1.776 4.271 1.944 3.791 2.136 3.252 c +3.263 0 l +4.368 0 l +2.088 5.807 l +1.079 5.807 l +-1.129 0 l +h +f +Q +q 1 0 0 1 -81.0996 65.6758 cm +0 0 m +0.012 -0.659 -0.276 -1.717 -1.452 -1.717 c +-2.532 -1.717 -2.988 -0.732 -3.072 0 c +h +-3.084 0.757 m +-3.06 2.184 -2.16 2.771 -1.104 2.771 c +-0.348 2.771 0.12 2.64 0.504 2.483 c +0.696 3.228 l +0.324 3.396 -0.324 3.6 -1.248 3.6 c +-3.024 3.6 -4.104 2.412 -4.104 0.673 c +-4.104 -1.093 -3.06 -2.473 -1.368 -2.473 c +0.528 -2.473 1.02 -0.805 1.02 0.264 c +1.02 0.479 1.008 0.647 0.984 0.768 c +h +f +Q +q 1 0 0 1 -72.2314 65.7959 cm +0 0 m +-0.804 -2.328 l +-0.972 -2.856 -1.092 -3.337 -1.212 -3.805 c +-1.248 -3.805 l +-1.356 -3.337 -1.488 -2.832 -1.644 -2.341 c +-2.436 0 l +h +-2.652 0.815 m +-3.492 3.359 l +-4.572 3.359 l +-1.824 -4.729 l +-0.564 -4.729 l +2.196 3.359 l +1.08 3.359 l +0.216 0.815 l +h +f +Q +q 1 0 0 1 -69.0996 68.0879 cm +0 0 m +0.324 0.191 0.876 0.407 1.404 0.407 c +2.16 0.407 2.52 0.035 2.52 -0.456 c +2.52 -0.961 2.22 -1.236 1.452 -1.524 c +0.396 -1.907 -0.096 -2.473 -0.096 -3.169 c +-0.096 -4.104 0.672 -4.873 1.908 -4.873 c +2.496 -4.873 3.012 -4.717 3.324 -4.513 c +3.072 -3.757 l +2.844 -3.889 2.424 -4.093 1.884 -4.093 c +1.26 -4.093 0.924 -3.732 0.924 -3.301 c +0.924 -2.809 1.26 -2.593 2.016 -2.305 c +3.012 -1.933 3.54 -1.429 3.54 -0.553 c +3.54 0.479 2.736 1.199 1.38 1.199 c +0.744 1.199 0.156 1.031 -0.252 0.792 c +h +f +Q +q 1 0 0 1 -57.5205 68.9512 cm +0 0 m +-0.276 0.132 -0.888 0.336 -1.668 0.336 c +-3.42 0.336 -4.56 -0.852 -4.56 -2.64 c +-4.56 -4.428 -3.336 -5.736 -1.44 -5.736 c +-0.816 -5.736 -0.264 -5.58 0.024 -5.424 c +-0.216 -4.62 l +-0.468 -4.752 -0.864 -4.895 -1.44 -4.895 c +-2.772 -4.895 -3.492 -3.899 -3.492 -2.687 c +-3.492 -1.344 -2.628 -0.516 -1.476 -0.516 c +-0.876 -0.516 -0.48 -0.66 -0.18 -0.792 c +h +f +Q +q 1 0 0 1 -53.9556 68.4951 cm +0 0 m +1.008 0 1.764 -0.947 1.764 -2.268 c +1.764 -3.252 1.272 -4.488 0.024 -4.488 c +-1.212 -4.488 -1.752 -3.336 -1.752 -2.23 c +-1.752 -0.96 -1.032 0 -0.012 0 c +h +-0.036 0.792 m +-1.596 0.792 -2.82 -0.359 -2.82 -2.195 c +-2.82 -4.14 -1.536 -5.28 0.06 -5.28 c +1.728 -5.28 2.856 -4.067 2.856 -2.304 c +2.856 -0.143 1.356 0.792 -0.024 0.792 c +h +f +Q +q 1 0 0 1 -49.7676 64.9189 cm +0 0 m +0 -0.611 -0.024 -1.092 -0.048 -1.57 c +0.876 -1.57 l +0.924 -0.636 l +0.96 -0.636 l +1.284 -1.188 1.824 -1.704 2.796 -1.704 c +3.576 -1.704 4.176 -1.224 4.428 -0.539 c +4.452 -0.539 l +4.632 -0.876 4.872 -1.114 5.112 -1.296 c +5.46 -1.56 5.832 -1.704 6.384 -1.704 c +7.164 -1.704 8.304 -1.2 8.304 0.816 c +8.304 4.236 l +7.272 4.236 l +7.272 0.948 l +7.272 -0.18 6.852 -0.84 6.012 -0.84 c +5.4 -0.84 4.944 -0.396 4.752 0.108 c +4.704 0.266 4.668 0.456 4.668 0.638 c +4.668 4.236 l +3.636 4.236 l +3.636 0.757 l +3.636 -0.18 3.228 -0.84 2.424 -0.84 c +1.776 -0.84 1.284 -0.312 1.116 0.216 c +1.056 0.372 1.032 0.554 1.032 0.732 c +1.032 4.236 l +0 4.236 l +h +f +Q +q 1 0 0 1 -39.7607 64.9189 cm +0 0 m +0 -0.611 -0.024 -1.092 -0.048 -1.57 c +0.876 -1.57 l +0.924 -0.636 l +0.96 -0.636 l +1.284 -1.188 1.824 -1.704 2.796 -1.704 c +3.576 -1.704 4.176 -1.224 4.428 -0.539 c +4.452 -0.539 l +4.632 -0.876 4.872 -1.114 5.112 -1.296 c +5.46 -1.56 5.832 -1.704 6.384 -1.704 c +7.164 -1.704 8.304 -1.2 8.304 0.816 c +8.304 4.236 l +7.272 4.236 l +7.272 0.948 l +7.272 -0.18 6.852 -0.84 6.012 -0.84 c +5.4 -0.84 4.944 -0.396 4.752 0.108 c +4.704 0.266 4.668 0.456 4.668 0.638 c +4.668 4.236 l +3.636 4.236 l +3.636 0.757 l +3.636 -0.18 3.228 -0.84 2.424 -0.84 c +1.776 -0.84 1.284 -0.312 1.116 0.216 c +1.056 0.372 1.032 0.554 1.032 0.732 c +1.032 4.236 l +0 4.236 l +h +f +Q +q 1 0 0 1 -26.6924 66.1914 cm +0 0 m +-1.152 -0.024 -2.46 0.18 -2.46 1.308 c +-2.46 2.004 -2.004 2.316 -1.476 2.316 c +-0.708 2.316 -0.216 1.836 -0.048 1.344 c +-0.012 1.236 0 1.116 0 1.008 c +h +1.032 1.573 m +1.032 2.076 1.056 2.567 1.116 2.964 c +0.168 2.964 l +0.072 2.232 l +0.036 2.232 l +-0.276 2.688 -0.912 3.096 -1.74 3.096 c +-2.916 3.096 -3.516 2.268 -3.516 1.428 c +-3.516 0.023 -2.268 -0.744 -0.024 -0.732 c +-0.024 -0.852 l +-0.024 -1.332 -0.156 -2.208 -1.344 -2.196 c +-1.896 -2.196 -2.46 -2.04 -2.868 -1.764 c +-3.108 -2.473 l +-2.628 -2.772 -1.92 -2.977 -1.188 -2.977 c +0.6 -2.977 1.032 -1.764 1.032 -0.601 c +h +f +Q +q 1 0 0 1 -23.9688 64.9189 cm +0 0 m +0 -0.611 -0.013 -1.092 -0.048 -1.57 c +0.888 -1.57 l +0.948 -0.623 l +0.972 -0.623 l +1.26 -1.163 1.932 -1.704 2.892 -1.704 c +3.696 -1.704 4.944 -1.224 4.944 0.769 c +4.944 4.236 l +3.888 4.236 l +3.888 0.89 l +3.888 -0.048 3.54 -0.84 2.544 -0.84 c +1.86 -0.84 1.32 -0.348 1.128 0.24 c +1.08 0.372 1.056 0.564 1.056 0.732 c +1.056 4.236 l +0 4.236 l +h +f +Q +q 1 0 0 1 -13.3477 65.7109 cm +0 0 m +0 -0.144 -0.012 -0.312 -0.048 -0.455 c +-0.204 -1.114 -0.78 -1.668 -1.572 -1.668 c +-2.664 -1.668 -3.312 -0.707 -3.312 0.564 c +-3.312 1.753 -2.724 2.725 -1.596 2.725 c +-0.888 2.725 -0.24 2.244 -0.048 1.464 c +-0.012 1.32 0 1.178 0 1.01 c +h +1.056 -5.075 m +1.056 1.944 l +1.056 2.461 1.068 3.048 1.104 3.444 c +0.156 3.444 l +0.108 2.437 l +0.084 2.437 l +-0.24 3.085 -0.936 3.576 -1.884 3.576 c +-3.288 3.576 -4.38 2.389 -4.38 0.624 c +-4.392 -1.318 -3.18 -2.496 -1.776 -2.496 c +-0.876 -2.496 -0.276 -2.075 -0.024 -1.619 c +0 -1.619 l +0 -5.075 l +h +f +Q +q 1 0 0 1 -10.3369 69.2871 cm +0 0 m +-0.408 0 -0.696 -0.323 -0.696 -0.756 c +-0.696 -1.188 -0.396 -1.512 0.024 -1.512 c +0.444 -1.512 0.731 -1.199 0.731 -0.756 c +0.731 -0.323 0.444 0 0.012 0 c +h +f +Q + +endstream endobj 724 0 obj <> endobj 729 0 obj [/Indexed/DeviceRGB 255 730 0 R] endobj 730 0 obj <>stream +8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0 +b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup` +E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn +6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O( +l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~> +endstream endobj 711 0 obj <> endobj 712 0 obj <> endobj 713 0 obj <>stream +%!PS-Adobe-3.0 +%%Creator: Adobe Illustrator(R) 13.0 +%%AI8_CreatorVersion: 13.0.0 +%%For: (Administrador) () +%%Title: (Green_Art.ai) +%%CreationDate: 10/23/2009 1:07 PM +%%BoundingBox: 0 0 842 596 +%%HiResBoundingBox: 0 0 841.8896 595.2754 +%%DocumentProcessColors: Cyan Magenta Yellow Black +%AI5_FileFormat 9.0 +%AI12_BuildNumber: 406 +%AI3_ColorUsage: Color +%AI7_ImageSettings: 0 +%%CMYKProcessColor: 1 1 1 1 ([Registration]) +%AI3_TemplateBox: 421.5 296.7754 421.5 296.7754 +%AI3_TileBox: 123.3447 -123.2324 718.3643 718.6279 +%AI3_DocumentPreview: None +%AI5_ArtSize: 841.8898 595.2756 +%AI5_RulerUnits: 2 +%AI9_ColorModel: 2 +%AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 +%AI5_TargetResolution: 800 +%AI5_NumLayers: 1 +%AI9_OpenToView: -301 710.2754 1 1414 947 18 1 0 44 75 0 0 1 1 1 0 1 +%AI5_OpenViewLayers: 7 +%%PageOrigin:0 0 +%AI7_GridSettings: 72 8 72 8 1 0 0.8 0.8 0.8 0.9 0.9 0.9 +%AI9_Flatten: 1 +%AI12_CMSettings: 00.MS +%%EndComments + +endstream endobj 714 0 obj <>stream +%%BoundingBox: 0 0 842 596 +%%HiResBoundingBox: 0 0 841.8896 595.2754 +%AI7_Thumbnail: 128 92 8 +%%BeginData: 22736 Hex Bytes +%0000330000660000990000CC0033000033330033660033990033CC0033FF +%0066000066330066660066990066CC0066FF009900009933009966009999 +%0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 +%00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 +%3333663333993333CC3333FF3366003366333366663366993366CC3366FF +%3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 +%33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 +%6600666600996600CC6600FF6633006633336633666633996633CC6633FF +%6666006666336666666666996666CC6666FF669900669933669966669999 +%6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 +%66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF +%9933009933339933669933999933CC9933FF996600996633996666996699 +%9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 +%99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF +%CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 +%CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 +%CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF +%CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC +%FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 +%FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 +%FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 +%000011111111220000002200000022222222440000004400000044444444 +%550000005500000055555555770000007700000077777777880000008800 +%000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB +%DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF +%00FF0000FFFFFF0000FF00FFFFFF00FFFFFF +%524C45FD8052285252522852525228525252285252522852525228525252 +%285252522852525228525252285252522852525228525252285252522852 +%525228525252285252522852525228525252285252522852525228525252 +%285252522852525228525252285252522852525228525252285252522852 +%5252285252522852525228FD345227525252275227522852525227525252 +%2752525228525252275252522752525227FD2E522E5227522E5227522E52 +%27522E5227522E5227522E5227522E5227522E5227522E5227522E522752 +%2E5227522E522752527D527D7D7D52A85276527C52A0527C767C52767C52 +%527D7D7D527D52A8527D7D7D5252285227522E5227522E5227522E522752 +%2E5227522E5227522E5227522E5227522E5227522E5227522E5227FD3152 +%A87DA87DFD05A87C7DA0A7A0C9A0A7A0A7A0A7A0A77DFF7DFD09A87DFD2C +%522E52275252522752525227522E5227522E522752525227525252275252 +%52275252522752525227525252275252522752FD077DA87D7C76FD04A07D +%27A0527D52A07C52FD057D52A8A8277DA8A8275227525252275252522852 +%525228525252285252522852525228525252285252522852525228525252 +%28FD31527D527D52A8FD047D52A07CA07C7D76A17C7C76A07CA0527D7DA8 +%7D7D7D7EFD057D53FD2B5227522752275227522752275227522752275227 +%52275227522752275227522752275227522752275227522752275227FD06 +%527D527D52525276527C5276FD045276FD04527D2759527DFD0652765227 +%522752275227522752275227522752275227522752275227522752275227 +%52275227522752275227FD04522E5252522EFD07522E5252522EFD235227 +%52525227522752277C7CA07C7D7C7C767C7C7C76A0767C76A07CA0767DFD +%2B5227522752275227522752275227522752275227522752275227522752 +%275227522752275227522752275227522752275227522752275227522752 +%2752275227524B52527651525252277CFD0A522752275227522752275227 +%522752275227522752275227522752275227522752275227522752275227 +%522752522752525227525252275252522752525227525252275252522752 +%525227525252275252522752525227525252275252522752525227525252 +%275252522752525227522752275228522752275227522752275227522752 +%525227525252275252522752525227525252275252522752525227525252 +%2752525227525252275252FD80277C587C7C7C587C7C7C587C7C7C587C7C +%7C587C7C7C587C7C7C587C7C7C587C7C7C587C7C7C587C7C7C587C7C7C58 +%7C7C7C587C7C7C587C7C7C587C7C7C587C7C7C587C7C7C587C7C7C587C7C +%7C587C7C7C587C7C7C587C7C7C587C7C7C587C7C7C587C7C7C587C7C7C58 +%7C7C7C587C7C7C587C7C7C587C7C7C587C7C7C587C7C81A581A581A581A5 +%81A581A581A581A581A581A581A581A581A581A581A581A581A581A581A5 +%81A581A581A581A581A581A581A581A581A581A581A581A581A581A581A5 +%81A581A581A581A581A581A581A581A581A581A581A581A581A581A581A5 +%81A581A581A581A581A581A581A581A581A581A581A581A581A581A581A5 +%A581A581A581A581A581A581A581A581A581A581A581A581A581A581A581 +%A581A581A581A581A581A581A581A581A581A581A581A581A581A581A581 +%A581A581A581A581A581A581A581A581A581A581A581A581A581A581A581 +%A581A581A581A581A581A581A581A581A581A581A581A581A581A581A581 +%A581A581A581A5817A817B817A817B817A817B817A817B817A817B817A81 +%7B817A817B817A817B817A817B817A817B817A817B817A817B817A817B81 +%7A817B817A817B817A817B817A817B817A817B817A817B817A817B817A81 +%7B817A817B817A817B817A817B817A817B817A817B817A817B817A817B81 +%7A817B817A817B817A817B817A817B81A581A581A581A581A581A581A581 +%A581A581A581A581A581A581A581A581A581A581A581A581A581A581A581 +%A581A581A581A581A581A581A581A581A581A581A581A581A581A581A581 +%A581A581A581A581A581A581A581A581A581A581A581A581A581A581A581 +%A581A581A581A581A581A581A581A581A581A581A581A5817AA581817AA5 +%81817AA581817AA581817AA581817AA581817AA581817AA581817AA58181 +%7AA581817AA581817AA581817AA581817AA581817AA581817AA581817AA5 +%81817AA581817AA581817AA581817AA581817AA581817AA581817AA58181 +%7AA581817AA581817AA581817AA581817AA581817AA581817AA581817AA5 +%8181A581A581A57B8181A581A581A581A581A581A581A581A581A581A581 +%A581A581A581A581A581A581A581A581A581A581A581A581A581A581A581 +%A581A581A581A581A581A581A581A581A581A581A581A581A581A581A581 +%A581A581A581A581A581A581A581A581A581A581A581A581A581A581A581 +%A581A581A581A581A5817B8157A57B57505757817A817B817A817B817A81 +%7B817A817B817A817B817A817B817A817B817A817B817A817B817A817B81 +%7A817B817A817B817A817B817A817B817A817B817A817B817A817B817A81 +%7B817A817B817A817B817A817B817A817B817A817B817A817B817A817B81 +%7A817B817A817B817A817B817A817B817A81A557F826512D5757577BA581 +%A581A581A581A581A581A581A581A581A581A581A581A581A581A581A581 +%A581A581A581A581A581A581A581A581A581A581A581A581A581A581A581 +%A581A581A581A581A581A581A581A581A581A581A581A581A581A581A581 +%A581A581A581A581A581A581A581A581A581A581A581A581A5818157FD05 +%F851517B80A57B817AA581817AA581817AA581817AA581817AA581817AA5 +%81817AA581817A817B817A817B817AA581817AA581817AA581817AA58181 +%7AA581817AA581817AA581817AA581817AA581817AA581817AA581817AA5 +%81817AA581817AA581817AA581817AA581817AA581817AA581817AA58181 +%7AA5A557FD07F820517B817BA581A581A581A581A581A581A581A581A581 +%A581A581A581A581A581A57B8181817BA581817BA581A581A581A581A581 +%A581A581A581A581A581A581A581A581A581A581A581A581A581A581A581 +%A581A581A581A581A581A581A581A581A581A581A581A581A581A581A581 +%A581A581A581807BFD09F87B56817B817A817B817A817B817A817B817A81 +%7B817A817B817A817B817A817B817A817B817A817B817A817B817A817B81 +%7A817B817A817B817A817B817A817B817A817B817A817B817A817B817A81 +%7B817A817B817A817B817A817B817A817B817A817B817A817B817A817B81 +%7A817B817A817B81AB57F8F827FD06F857817B8181A581A581A581A581A5 +%81A581A581A581A581A581A581A581A5818181A581A581A581A581A581A5 +%81A581A581A581A581A581A581A581A581A581A581A581A581A581A581A5 +%81A581A581A581A581A581A581A581A581A581A581A581A581A581A581A5 +%81A581A581A581A581A581A581807BF8277A27F82751F8F8577B817BA57A +%A58181578181817A817B817AA581817AA581817AA581817AA581817AA581 +%817AA581817AA581817AA581817AA581817AA581817AA581817AA581817A +%A581817AA581817AA581817AA581817AA581817AA581817A817B817AA581 +%817AA581817AA581817AA581817AA581817AA58181A551F827AB2DF826A5 +%26F851817BA57B7B57817B817BA581A581572DA581A581A581A581A581A5 +%81A581A581A581A581A581A581A581A581A581A581A581A581A581A581A5 +%81A581A581A581A581A581A581A581A581A581A581A581A581A581A581A5 +%81A581A581A581A581A581A581A581A581A581A581A581A581A581A58180 +%7BF8278151F8F85727F85781815657567B567B7B817A817BA5262780817A +%817B817A817B817A817B817A817B817A817B817A817B817A817B817A817B +%817A817B817A817B817A817B817A817B817A817B817A817B817A817B817A +%817B817A817B817A817B817A817B817A817B817A817B817A817B817A817B +%817A817B817A81A57B27F82726F8F85104F851AB7B5757817B81577B7B81 +%7BA5A55151A581A581A581A581A581A581A581A581A581A581A581A581A5 +%81A581A581A581A581A581A581A581A581A581A581A581A581A581A581A5 +%81A581A581A581A581A581A581A581A581A581A5818181A581A581A581A5 +%81A581A581A581A581A581A581A581817BF8272726FD05F8517B7B507B57 +%7B567B577B578181A5267B81A57AA581817AA581817AA581817AA581817A +%A581817AA581817AA581817AA581817AA581817AA581817A817B817A817B +%817A8181817AA581817AA581817AA581817AA581817A8181817AA57B817A +%A581817AA581817AA581817AA581817AA581817AA5A57BF8F8AB51F8F87B +%27F82781575757817B817B812D27F85151277B7B7BA581A581A581A581A5 +%81A581A581A581A581A581A581A581A581A581A581A581A581A581A581A5 +%81A581A581A581A581817BA581A581A581A581A581A581A581A581A581A5 +%7BA581A5818181A581A581A581A581A581A581A581A581A581A581A5817A +%81F8F8807BF8F87B51F8275681565756817BA551FD09F827817A817B817A +%817B817A817B817A817B817A817B817A817B817A817B817A817B817A817B +%817A817B817A817B817AA57B817A817B817A817B817A817B817A817B817A +%817B817AA57B817A817B817A817B817A817B817A817B817A817B817A817B +%81A58126F87C51F8F8812DF827A581812D51517B5127FD0AF851AB81A581 +%A581A581A581A581A581A57B8181A581A581A581A581A581A581A581A581 +%A581A581A581A581A581A581A581A581A581A581A581A581A581A581A581 +%A581A581A581A581A581A581A581A581A581A581A581A581A581A581A581 +%A5817A81FD07F827F8277AA58126FD0FF8267A817B7B56817B817AA58181 +%7AA58181578181817AA581817AA581817AA581817AA581817AA581817AA5 +%81817AA581817AA581817AA581817AA57B817A8181817AA581817AA58181 +%7A817B817AA581817AA581817AA581817AA581817AA58181A57B27F82726 +%FD05F826A5817BFD11F857FD04817B817B817B8181A581A5818181A581A5 +%81A581A581A581A581A581A581A581A581A581A581A581A581A581A581A5 +%81A581A581A57BA581817BA581A581A581817BA581A57BA581A581A581A5 +%81A581A581A581A581A581A581A5817A81F8F85781F8F85151F8267BA527 +%FD12F827265757817A81577B7AA57B817AA57B817A817B817A817B817A81 +%7B817A817B817A817B817A817B817A817B817A817B817A817B817A817B81 +%7A817B817AA57B817AA57B817A817B817AA57B817A817B817A817B817A81 +%7B817A817B817A81A58127F8577B27F87B57F8F8818127FD16F827AB81A5 +%7B8181817BA581A581A581A581A581A57B817B817B817B817BA581A581A5 +%81A581A581A581A581A581A581A581A581A57BA581817BA581A581A58181 +%7BA581A57BA581A581A581A581A581A581A581A581A581A581A581818126 +%F8267BF8F8517BF8F85781FD17F85181817B8156817B7B7AA57B817AA57B +%817A81577B7A817B817AA57B817A817B817AA57B817AA57B817AA581817A +%817B817A817B817A817B817AA57B817AA57B817AA57B817AA57B817AA57B +%817AA57B817AA57B817AA57B817AA5A58127FD05F82727F8F87B57FD04F8 +%57515126F8F82727FD0BF827817B8181A57B8181A581A581A581A5818157 +%8181A581A581817BA581A57B817B8181A581A581A581A581A581A57BA581 +%817B8181817B817B8181A581A5818181A581A57BA581A581A581A581A581 +%A581A581A581A581A581A5817AA526F8267BF8F8F827F8F85157F8F8F827 +%81A58051F8F8F857F8F8F82726FD06F8517B817A817B8156817A817A817B +%81567B57817A817B817A817B817A817B817A817B817A817B817A817B817A +%817B817A817B817A817B8156817B817A817B817A817B817A817B817A817B +%817A817B817A817B817A817B817A817B81A5812DF851A52DF8577BF8F87C +%51F8F8F82757517B27F8F85127F8F82757A58151F8F8F8277BA57B7B81A5 +%81A57B817BA581A57B817BA581A581817BA581A5818181A581A581817BA5 +%81A581A581A581A581817BA581817B817B817B817BA581A581A57B8181A5 +%818181A581A581A581A581A581A581A581A581A581A581A5817A8127F826 +%A527F82D7BF8F85151FD0FF87B7BA526FD04F87B817B51817B817A81577B +%57817B7B57817B817A817B817A817B817A817B817A817B817A817B817A81 +%7B817A817B817A817B817A817B8156817B817A817B817A817B8156817B81 +%7A817B817A817B817A817B817A817B817A817B81818151F8272727F85151 +%F8F85126FD10F8272627F8F8F827578181817B8181817B817B817B817B81 +%7BA581817BA581817BA581817B8181817BA581817B8181817BA581817B81 +%7B817B817B817B817B817BA581817BA57B817BA57B817BA581817B818181 +%7B8181817B8181817BA581817BA5817BA526F8F827FD06F826FD04F85151 +%5126F8F8F826FD0BF826575681577B7A817B7B5681577B56817B817A817B +%817A817B817A817B817A817B817A817B8156817B817A817B817A817B7B56 +%81577B56817B817A817B8156817B817A817B817A817B8156817B817A817B +%817A817B817A817B817A81A58151F8278157F8275127F827F8F8F82781AB +%A557F8F8F857F8F8F8272627FD05F851577B7B817BA581817B817B817B81 +%81817BA581817BA581817BA581817BA581817BA581817B817B817BA58181 +%7BA581817B817B817BA581817BA581A57BFD05817BA57BA581817BA58181 +%7B817B817BA581817BA581817BA5817BA52DF8F8A551F82681FD07F85151 +%7B26F8F82727F8F8F8277B7A51FD04F827817A817B817A817B815681577B +%7A817B817A817B8157817B817A817B8157817B817A817B817A817B817A81 +%7B817A817B817A817B817A817B817A817B7B7A817B8156817B817A817B81 +%7A817B817A817B817A817B817A817B817A81818151F8F85757F8278127FD +%11F87B517BFD05F85181817B817B817B818181578181817B8181817B817B +%817BA581817B817B817B8181817B8181817B817B817B8181817B8181817B +%8181817B8181817B817B817B817B817BA581A581A581A581A581A581A581 +%817B817B817B8181817B7A8151FD06F827FD13F8272626FD05F8517B7B56 +%8157817A8157817A817B7B56817B817A817B817A817B817A817B817A817B +%817A817B817A817B817A817B817A817B817A817B817A817B7B7A817B8156 +%817B817A817B817A817B817A817B817A817B817A817B817A817B81A5817B +%F8F82727FD08F827A57B7B26F8F8F82727FD0BF85151817B817B817B817B +%817BA581817B8181817BA581817B817B817BA581817BA581817BA581817B +%A581817BA581817BA581817BA581817B817B817BA5818181A581A581A581 +%A581A581A581A581A581A581A581A581817BA581817B7A8151F8F8578126 +%F87B26FD04F85181818127F8F8F857F8F8F826F827FD06F856A57B815781 +%7B8156817B817A817B817A817B817A817B7B57817B817A817B817A817B81 +%7A817B817A817B817A817B817A817B817A817B817A817B817A8181817AA5 +%81817AA581817AA581817AA581817AA581817AA581817A817B81817B7BF8 +%F851A526F87B51FD05F8272751F8F8F82727F8F82751518157FD04F82681 +%7B817B817B817B817B817B817B817B817B817B817B817B817B817B817B81 +%7B817BA581817B817B817B817B817B817B817B817B817B817B817B8181A5 +%81A581A581A581A581A581A581A581A581A581A581A581A581A581817B81 +%7B7A8151F8F8275727F87B26FD10F857578127FD04F8277B8156817B8156 +%7B7B817A817B8156817B81568157815681577B56817B817A817B7B57817B +%817A817B817A817B817A817B817A817B8156817B7B7AA57B817A817B817A +%817B817A817B817A817B817A818081577B7B817A817B817A817B7B81FD07 +%F827F8F8F8272627F827FD0BF827FD04F827A57B817B817B817B817B817B +%817B817B817B817B817B817B817B817B817B818181577B81817B817B817B +%8181817B817B817B817B817B817B8181A581A581A581A581A581A581A581 +%A581A581A581A581815758515757A581A581A581F82726F8F8262727F826 +%FD05F8817B7B26F8F8F82626FD0BF85181817A817B817A817B817A817B81 +%7A817B817A817B817A817B817A817B81517B7B817A817B817A817B817A81 +%7B817A817B817A817B817A817B817AA581817AA581817AA581817AA58181 +%7AA581817B572D512D572D577BA57B817A8127FD04F826A551F85157F8F8 +%F8277BA58127F8F8F857F8F8F8272D5127FD05F82DA57B817B817B812727 +%518157817B817B817B817B817B817B817B817B817B817B7B5781577B8181 +%7B817B817B8181817B817B817B817B817BA581A581A581A57BA581817BA5 +%81817BA581A581A581572D5751572D5751577BA581817BFD05F8267B51F8 +%5157FD04F8272627F8F8F82027F8F8F82DA57A81FD05F8517A817B815681 +%7B51F8F826F8F87B7B8156817B8156817B8156817B81517B51272D817A81 +%7A81577B56817B7B56817B7B56817B8156817B817A817B817A817B817A81 +%7B7B56817B817A817B817A572D512D572D512D572D7B7A817B8127FD05F8 +%5151F8277BFD0FF851578157FD05F851A57B817B817B818157F8F8207B7B +%817B817B817B817B817B817B817B817B7B2D7B577B7B817B817B817B817B +%817B817B817B817B8181A581A581A581A581A581A581A581A581A581A581 +%A581825157515851575158515757A581A57BFD0EF827512727FD0BF827FD +%05F8577A817B817A81577B5127F827FD045751575157515757575157517B +%2DFD04577B7B817A817B8156817B8156817B8156817B7B7AA581817AA581 +%817AA581817AA581817AA57B817A817B8157572D512D572D572D572D5151 +%817B8127FD05F87B51F82751F8F8F87B81A557F8F8F8272DFD0BF8265781 +%577B5757515751512D512D572D5751572D5751572D5751572D512D272751 +%2D512D7B577B57817B817B817B817B8181817B817B8181A581A581A581A5 +%81A581A581A581A581A581A57BA581572D7B7B57515751572D5751577BA5 +%81FD06F85181F82751F8F8F826515751F8F8F851FD04F8517B5127FD05F8 +%572D512D572D512D512D512D572D512D572D512D572D512D572D51265126 +%51275127272D572D512D575757567B577B56817B7B56817B817A817B817A +%817B817A817B817A817B817A817B817A812D51517B2D572D577B7B2D572D +%7B7A8127FD05F8577BF82751FD0AF82727F8F8F88181AB51FD04F8275157 +%2D585157577C577B57817B817B817B817B817B815758515751512D512658 +%515127585157577B5757577B577B57817B817B8181A581A581A581A581A5 +%81A581A581A581A581A581A581A57B5751585157515851575758515157A5 +%81FD07F827FD12F826515127FD05F857515757817B817A817B817B817B81 +%7A817B817A817B7B2D572D2727512D272D272D572D5757572D572D572D57 +%575756817B8156A581817AA581817A817B817A817B817A817B817A817B81 +%7A7B2D577A7B2D572D5751572D572D7B7A8127FD0DF8512D51FD11F8277B +%817B817B817B7B577B577B577B577B577B577B578157572D572751515751 +%5751575157515751572D5751572D58517B57817B817B8181A581A581A57B +%A581A57BA581A57BA581817BA581A55757515751575157518157572D577B +%817BFD06F8267B26F826F8F8F857A556F8F8F82627FD0CF8267B51575157 +%2D572D512D512D512D512D512D572D512D572D512D572D512D572D512D57 +%2D512D572D512D572D512D51517B5681567B7A7B7B817A817B817A817B81 +%7A817B817A817B817A817A572D7B57512D572D572D572D512C817B8127FD +%05F8278151F827F8F8F8515157F8F8F85127F8F8F8277B5157FD05F82751 +%58515751585157577BFD0A57515751575158515751585157517B51575158 +%515751585157515851575781818157A581817BA57B8181A581A581A57B81 +%7B817B817BA581815158577B515857577B8151572D5781817BFD07F8812D +%FD0BF82627F8F8F851818156FD05F82657507B577B567B5727267B7B7B56 +%7B577B567B577B568151512D572D572D572D572D572D572D572D572D572D +%572D572D7B568157817A817B8156817B817A817B8156817B817A817B817A +%817B572D572D572D572D5751572D5151817B8127FD06F82627FD10F82651 +%5151FD05F8517B8157817B8157812DF8F87B7B817B817B8157817B817B57 +%51572D5751572D575157515751572D5751572D575157515757817B817BA5 +%81A581817BA581A57B817B817B8181817BA581817B8151577B7B51575157 +%57572D582D577B817BFD22F82681577B567B577B567BF8F8F857577B5681 +%577B567B7B7B2D572D512D572D572D572726F8512D512D572D512D572D57 +%2D5156817A8157817A817B81567B7B7B56817B817A817B817A817B817A57 +%2D572D575151518157512D5151817A8127FD21F8515757577B5757577B57 +%2DFD04F82651577B577B578157585157517C575757582627F8F82D575158 +%51575158515751572D7B81A581817BA57B818181577B7BA581817BA58181 +%7BA5FD04817B7B7B5851575158515751582D7B7B817BFD22F826572D512D +%572D512D5726FD06F85751572D572D572D57515751575151F82D5127F857 +%2D572D572D572D572D572D5151817A817B7B7A817B81567B57817A817B81 +%7A817B817A817B8157577B7B2D575151518151512D5157817A8127FD14F8 +%267B7B51FD09F82D51575151275751572D272727F851517BFD04572D5751 +%57577B51575157F85157572D57515751572D5751572D5751572D5781A57B +%A57B817BA58181578181817BA57B817B8181817B81817B2D7B7B572D5751 +%57575751572D7B81817BFD15F857265151FD0AF8512D5751572D572D2D26 +%57F827565751512D572D512D7B57572D572D57F82D512DF82726512D572D +%512D572D512D572D2D2C817A817A815757568157817A817B817A817B817A +%817B7B7A815151578157572D5751572D572D2D56817B7B27FD13F85151F8 +%2727FD09F8272781FD047B517B57815757518157575758577B5781575751 +%58515727275757FD04F82758515751585157515851572D5157A57BA58181 +%57817B817BA581817BA5818157817B817B817B572D5857815758517B7B58 +%51572D8181817BF8F8F827FD09F8512D27FD04F85127FD0CF82D27265151 +%57577B577B567B577B56815757568157572D572D2727572DF82D5727FD04 +%F8262D572D572D572D572D572D512C577B817A817B7B56817B817A817B81 +%7A817B7B56817B817A8157572D57577B2D572D572D572D2D57817B7B26F8 +%F82727F82720FD04F8577B7BA551F8F8F87B26FD0CF8512DF8277B577B57 +%7B577B577B577B577B57512D5151572D2727F82D2727575151FD04F82657 +%515751572D5751572D572D512D8181817BA581817BA581817B8181575781 +%7B7B7B817B817B8157572D577B7B2D57577B575757817B817B277B26F827 +%5126F8F826F82751F8F85180F8F8F8578151FD0BF8267B2727567B51572C +%572D572D575127F82D262751512D512D572627265751572D27F8F8F82727 +%2D26572D572D512D572D512D2D57817B817A817B817A817A81567B515751 +%817B8156817B7B515757572D577B51517B51572D5157815681A57B7C5127 +%57F8F8F827F8F827F8F826A526F8F87B517C7B51FD09F82D5157F827512D +%26575157515851572D582727F851275157817B272D815757515851512751 +%27512D512D5751512D5751582D572D57A5A57BA57B81817BA58181A57B7B +%577B2D817B8181817B58578157585181577B51575157577B7B817B27F8F8 +%57577BFD0451FD05F8277BF8F8F851272D7B27F8F8F826F8F827F8F80451 +%2751517B515151572D572D5751572726262DFD0427262DF82D5751575751 +%5757572D572D512DF8272D272D2D27272D27272651272727512751275127 +%512D512D515151577B7A817B81515751FD0457812D572D57505750577A81 +%27F8517B817B57277B577B5127F8277B51272751A5275127F8F851FD0427 +%26F8F827267B57512DA57B572D572D512D575127262D2D512651512DF827 +%F82D27FD04577B2D5727270427FD1FF827265127512D512D57577B7B7B51 +%585757577B57817BF8275151517B51515127F85151575151F851F827577B +%5127267B7A7BF8512DF8F8F826042627F826507B272726272627042DF827 +%F827F8F8F851F8F804F8F827262DF827FD11F82727272651272726272627 +%F827F826FD0DF82751510427272D2D575051262D2627F851A57B81277B51 +%7C51512651277B7B7C5151F851A55127277B512651517BF8272727F827F8 +%F8F85157FD0AF827F8F8F827F804F827F827FD05F827275151585151517C +%5151517C7BA57B7C5151FD0427F827F827FD0BF827F8272D5151572D27FD +%07F826FD05F82651517B577B5757517B51275157572DF8517B2651A55151 +%27517B5157817A817B7B5157577B5757577B2727F827FD13F851517B5151 +%2727265157A5808157515157517B51572D512751272D2751262727512751 +%27FD04517B7B7B51512727FD10F8817B8181A57BA57BA57B817B81517B51 +%51517B57A57B8181A581A581817B8181A57BA581817BA5818181AB81A57B +%817B7B517B5157FD0451275127512751277BA5AB515127512D7B81AB817B +%5151275127FD04517B7BA581A57B817BA57B817B8181A581ABA5815127FD +%0FF827F827F8F8F87BA57AA57B817A817A817A817AA57AA580817AA57B81 +%7AA57B817A817A817A817A817A817B817A817A817AA57A817AA580A580A5 +%A5A580A5A5A580A580A57AA57B7B577B5757517B51512D7B7B817B7B5151 +%26272627F827F827F8272627FD07F8262651517B577B515751512D512D51 +%2D51265127272627FD04F8A581A581A57BA581A581A581A581A581A581A5 +%81A581A581A581A581A581A581A57BA581A581A581A581A581A581A57B81 +%7B7B515751512D51FD0427F827FD0DF82026FD04517B51572D5127512727 +%2627FD0DF827275127512D51275127512627F827FD07F8577C517C517C51 +%7C517C517C517C517C577C517C577C517C577C517C517C517C517C517C57 +%7C517C577C517C5758515127272627F827F827F820F827F827F827F827F8 +%272027F8272027F827F820F827F82720272727F827F8F8F827F827F827F8 +%27F827F827F827F827F821FD07F820F8F8F827F827F827F827F8FD312752 +%275227522752275227522752275227522752275227522752275227522752 +%275227522752275227522752275227522752275227522752275227522752 +%275227522752275227522752275227522752FD3A27522752275228522752 +%28522752275227522E28FD34275227522752275227522752275227522752 +%275227522752275227522752275227522752275227522752275227522752 +%27522752275227522752527D537D527D527D537D527D537D527D527D5252 +%275227522752275227522752275227522752275227522752275227522752 +%275227522752275227522752275227522752275227202727272027272720 +%272727202727272027272720272727202727272027272720272727202727 +%272027272720272727202727272027272720272727F8272727F8272727F8 +%FD0727202727272027272720272727202727272027272720272727202727 +%272027272720272727202727272027272720272727202727277D597D7D7D +%597D7D7D597D7D7D597D7D7D597D7D7D597D7D7D597D7D7D597D7D7D597D +%7D7D597D7D7D597D7D7D597D7D7D597D7D7D597D7D7D597D7D7D597D7D7D +%597D7D7D597D7D7D597D7D7D597D7D7D597D7D7D597D7D7D597D7D7D597D +%7D7D597D7D7D597D7D7D597D7D7D597D7D7D597D7D7D597D7D7D597D7D7D +%597D7DFD7FFFFF +%%EndData + +endstream endobj 715 0 obj <>stream +%AI12_CompressedDataxk%uÙD`TE3\=,B0D,.VŢ=_߱VϊX{$ë́DFΌ|ro~vŻ|pG?W/?{ś7|uן܌e__{s[듭/|Żo_x߿z^|IoWW07χ_k}owPcnsg^08s+Z_LK}͗~_}7߿|{˗+yy__yn޼_:X?{zUf_ps8}v7_7_a3}]V6lgtՇSuRï_w6W_~'4Xos{wd4e4O>/m?i?NK߾{J߼iM/7o^?8yYw/} }7/}O2+~^}(ݛo>8;Vv߼+;xW_zw?a,jos|Xʗ_ڰmŶw;L#xgpQϿ‹I[>zJ◿w}|/@|@o`ywkk&J_;>+چ?oߎ>}M_WxO>./F}ySW)_,W|&/ݛ_{Ȧ~{?7߼lSmo~ݛz_ ٺ7?q7~͛׿{~sF߲yrն>qS1?~EYO}]Lo~/n{S^S^vc{7@,o_^Zo^}v2}V6~Vٓg?mOο.۷/|u3G>,OPq2KYu+˾e9rޕ~}(>m6e,eYrݶmߎmYo?L$0>O0dH/%n~zyyyײvq~ZrY/1MkCr,r-۟gw{$O>{jxl5(QZ{v%[|9 8?k>g[G,#Gd:nTY>w[5Tp?ZwžvB?vԳϿUgYϺr&YWϸIg۶~t xL|,Dߎ;x:s(g>v?ݕ,QNo4e*4< e,we-X6X"Wc"A|y,q|?ܖ|(| TUβ2HƇ8 XirmC1mY,Ól2ƹeGD,x;ޕ`GqHe2Z5^5b.cr-wKwJ60=;zU>oYh;1]/Nl,u },>8F :(}(,> F:~?H~^y\swww{wwVa|7[Swq+zηx;x>y{qV9c>CeDޗ嶜4Qt9r*a*C9)wSo/ZNŹ>s汜5^,^;%k8o^}A οae@(> BP}. Z o5(ׂ\rm +\/u\?rd]!Cc} Nn|Bi) +BB²´m+[ +Ba|*|(L+t< %˵s. eҖR7/k3?;=7x=8Y뗏-.!v9Bb1.,_\ty)_hL؅.5q/9墣ˎ]x3gKPY>H/FG\nT/Kvazc\]25^JZM.kNn=ƽ8cwm2h^g-UerjrK9n\(m^Y79n® ݶUu;lX 6~R_#sſ}o-'-Q),'0΍o)G}=/9'>OuVpNOrV<>>>=h Ok*=XvUXJ.9e4?T"WV*) Ͱ|mo*w,_W]_w)_Gb^^wwW?zG ;]re-~ww,e?zX-W2?=3?8^!Z)'(^!Z)q˟(^!Z)0xef_}כU^}ͷGَmַfۻmVmymSqwhhV9{h /xa{qw/[j(j%lOkaj\òc+<²:l#ѣP^#ӣ|c_' eՒj}xDlf/|zj2͗>sO;_~Wmse4àPC>uDE +qvٮ(ZQJ_ĒZP)CO&w()D@1hQٝDcߟ<|~/?w*ߊJ{r-*/""R㓊"{s-z~R|Z6֕ނדz'>v|K~:}WZF4Rw%n3֓y~?@ 3lGJ4% woûL7_wL%\oǹ-^n-;75d}m{7߆/>]c_Z_~ÿi]zڕ?HKWgOC짺n_l*۩ +6mv2 R٘[>cqsT}G륹6wAV븮}:M.!ESFNk~Q2JLc n#2ACpJgJyM1 ~BP?Xϼ'J% unl ީ򝬽gc/q1~}-UP.qԍߨ|^4n4웟V9߇(zF,~T{ bz,q[tu?,RkJy8nG]ʾғߺb@uNs0КXC W3sZΎmo l﨎feǵ1zN-h@oݽ1+@o N߇bϩ16Z@nl ,ILl ,ȍ]Jcj3 \MSK{ m8Kk13|v `` o]r \^%h}`u[cx=NN,mK; J m hyushovAN 9!hs? 6VeP~׵B4- / huK( +'ڐ-A' y6y47: A#y6zhq>I,h}].AkD4Ct۰= G@KX`ˡZ{;eޮ.}XPߢϲ:0Ͼ>A?g+mLy Wg;APWb*t~o5j+2/Axa~=˚W:WEf=mi++쁿>qdi:Jÿ,#:J%矵:4ֶg% +gy3sѦۈϚa߽&#?kοҞ5Pqճg?ۡcWRgjgwmW +>-#u֞%g{.~sb8ό?_s8[ŸCi Y{|<[gK%]l4t4}\Wiipi4~GJm> m\ץ`o_٧`pWi}> ]ץ}gNK;y. ./3i24S\Wi24S\i24SϤOKOy[g[K4~ܥO^4Wiuip'L9 ui/``. ^;4؎kPJmLui|4؆UKݧ6i]4;4}44]ly|& . ful}<9 el4}l4xRl`o_. {2 > vF`}컮K4xs\) 4x4R\ipLipi4xҗNi* .) .u\:4x^Rlͫ4:4] v[* c`kiHE\Rl> U<ܧ޾HKݧ־J9. Ul_O}uipi_3iNK_fu\:S<9 .4t4S\ipLipi4xڞI-ӖҼNOݧLl}\) Ul}mgͫ4߽Kݧu4;4ߨKm\C4mCۨii4Gj[Omx_Ӝ`?'4ۗiwvipOKNiCKݥ~> g`liK}Δ[OK* > .߯qMiӧ44}4:4}l* oݧ4qipi4xܮץq& .) .U\R\) .4x v3ipLi4Wiuip#5iK}{guim* ٧v\4؆UlcK-6`]l> }kN`o_٥`o_ק`k?/{FaYB +A xX5[<5Cp4x9 {f) - `;L|x<Ao,8v<^?`.vj << 3I,O - +<<3,}IgK+7,X~TS_18X"{I,*rƔ} O+g×,eL HH8=; +;GeecPwfQ`=`vE^|xo?eӻ?_OyWv/ܷO~_o^ 槟D_~9yBP?nbL!Ԁ p6tϫBsd慟jBh=Oah=)u!B+:. A +3%LPk^BPg]!vYT5T]BTj_틫Bj ~L. vB'e!Nžj$*V:;? +#6*]B/:Bj`cb_U!:BodؚWP uBwv7 +[ph +žj_jý¾#+Z/ڱ*Q +>tnNL!;>uNy]uuPowPk?!t9/ᯅgg@>2A|>_eY "i*:`K8Ի]_G32ϥ 'e>)ПD"q5H9c>?jq~99jчsGpֻ8ۮD +׽Fε[R~f /5m!c<+S:0k(tEmo}U _bm=%]jA9R cp.W1>34-{uV"?ZU_ 9<·r8/#|Vcᗸׯ! qj!5! uڹ/uՐ.|ږ`ט|5crN2Gՠܸ>L-("(KEHn>9<#B$$/GSCr{xH^!y<&Xb.yLnm]"&ch%.&br?d1K[crq.-(lKoϘiDZ| mG[o}Zg|yg~m%KK+1& )ֹAmc'ڌo&EL^T>Z 1uQOn ș[}ʭbHrQM<(v,, gI(kӚ-(kS傲fqSOmƠErY;a.0kjì\kM?jY9 ƚ͊Z&s(΍&okkm-ScmZ;֖b8kM_` YFvk[;@vڡJvvDŽX>+yi݇w5)uh4< Z3@>|㬫˘9>yg+u4Κb>sp749SzL Q-c˔7߶ z6e=( +0f\1 [` Ⱥ ͙nW":e7em.em.3@vAvJ+=kq`쨣c]7\kCo@~#k%b1{:طjJ3st]a+Ӝg~3gFxė֘)D>؏1ym7׋Ms [Yo:s5|a|֮?Ѹgr%gB9fY|4}G@ x3gQ#Jުl zC\i GI3Zqxmg-.[=s)ҶIG{\v ߾dY{ޮمs^s9|meD>Q;6"' 5rՕD>|>: |SÑ?)P#6' +-'s2L8֥|p3|S.g{yr;8LӑWcO}Ջ}.kbyx-Y{ٱq3}swMu+~o2gcO kgrX?яc{"o$=gm /sz\߯`}'{nn4QxQdCa)^X9ic[ Dxh&1f+Ns.!Lu hA@a .>?h4 ~Y iB=#YKMW4ئ@B~3pQeH\ȱ@wk6hF0@kt-~2sV@jj} 7Z`Ôh;T3Џ|mhjr.Y Q8>T Znh{_@e +:p5Hݎ@o-;PӔ[`=p@7%1piK}g]]Recu!1" 1Х֞$h5Ntz{H~}5Mr>oZ{9Ock{K~K1acSK~g&E:qt<@7 ;Mߦ.6;2c1[e;mJjܗ %mڒ_7^d,owٯYֲ_ki"pT4!6-_뵮c`\.14^0,ܩ%8<߾Q"U- Z`%_}h +/' ]ŀ sm|Vi/_5csi lH^$6Zep:oΖR"^@ꂀeZL<=p8_v5+Y[}zqSc +@ʷnv@;IնpnJv@rgRhNg3 +\NZ~l_c=2VZ˵n-s!ig[VM6Iyg_L@Z_]8rgDo`A@s}FΉ m5<p& 8SgR!]zӈfSZ{m `U `lDv - >1djX8.YlPV %yZhwe5h38 +@ͱ7ZG78*&tݳ- 9@k=ƖF@kE^3)%94߃@7ϋ4r4؏kŗ3Zo4ڨڗO@" ԟtۡ" sȂ99ZF@?mY8O!I`p>p\p\Ml#`ڞo\?Qu'AfA6B/&M^L1n}-/:=\L N?-, $j}kmրϾt㸘i3z!s}v~1ƴf9lJ:+DkNq\zu5όh3mah^ˀM?Jp>h5MqΙq/=F7H$@>duM-EG2 Y06v1~"[b<6ƍMhuŸ9& ޿hY6fbգdyic~w1ʹvM̩g:6ʹ;=<3v'NahwB?޲&{Y!mǍ[$|l7,Vtn $+m2[!ۏϞEx2]\IiMӭ3eflI k(8[%~  +Z1V*G5*oYǖ +- 6}F^{fC˂c#P'S_@޽fRUr}s˂ǟ[eilر!}N_ +i_&2fGS:҉Gc3 +~; ~*GcmJl¤Σ>&-uxvJnq ^A&>MUq"ʽ5.޺u\bD7!XۦJo.ї"OJ^eq?6"oqfw£{:|OOi>M> ç}4}5a$a>:Q؇Q#!? 5}5!$>PP8jjD?|O'!?D?$@?|OOjBMB CƄXj?4&?|k!$a?RRj VM" V ĪX5=j@ R DI DH50&1"@A T# +$ A8S8( ԀAA0S$ +b@A(U(Uj VM V0Tj%ՀA0T0j RM R 6DIDqj NM R DH5H5 N ĩIĩ85p&qJH5 H5` "$ "85p&qAk"$bA8V8Vk fM f Ĭ5=k b DID5p&q"AĚA8X#$Ay5`&adʫ V&0(&sPfMpphWV&cPfM`PfM`PfMƠ0(&0(&cPbMpPbMppnA91A5A5km#k2%Ay512kk.d +Jj ^M ^ īx5x5P "$ "րA0X0X# ՀAy5x5P +i$i % %dJ J QqH(G&H(&H(&PMm6malc$>%%%dJl M<0gll DID6`&aFijx6 l ij`6 a0ma6ڀm`a6 a@ðma6=C!ۀĐmm`b6@1dDŐmb6p&q1dMB#MĈrm"FkcD6#v QMD\]Ȓ,6$˶$YMNm9l(ʶɥB6 +%DPM.JR%*mrPLT +%DPMJJaHJɥB6 +DPMJZdJɵB6$rm"Bk#D6!ʵQMB# h 6l tD!$pm õpm!/<͒dpml.vGX{殍M=2S$m=OnX߸HahO\,\^6U Ou9S$m.L'xq|97mJ>@}7mJ?TMӞ"i3&)6ms6OrfCǀ'Bz6>ioO\,WjL0&I4ēZ$0 V:I+IMzȪȰz)6cCO9S$66!$wղic>GEvYE<IrH!cɑ0G{0m|2qI<.f$D)>lO<)6שm|Ԟ"ig66#J.ls f9Sdج!;h 7msmM)N.|,sH̓T]xHL{6>=iB`3mBY5dXS$M>VTB=E!7/>&SV gg)+&(+&(+&aVM`VM`VMƬ<X[5@4ZY6ZY6ZY6lll2kekekdJ Jh6=klZ$`d`dn EIEH6&JYod@dheوH6H6p"$bYI6`VM, 6_>Ia0lM ð|2l0lZ V&Qo [UMUJzu`͐`V 6{nTtܸnčYElV 6pF``lMn C|6|6k  CI`lM"*2l *&oa#l *&o`V 6o`зJ}طJI[%6oaӳoD2طJ}f]&Fï}5}5C5&FC5=kv îIîa$b@^^k` zM@5B r DID/"JID5&"@@䚄@]#"$J5 &!/"׀@䚄@md5 5 "$"@\\k` vMb v\kk`\\k@ rMB r Į5=k` r Df rMb r[qkqk` n ĭZQk` jMb nZQkzb@YY1k` fMb f Ĭ550Pj DY#"%dUV&(&3PbM0phvV&#PbM PbM PbMFĚ@(&#PbM0PbM0PbMf`Ě`Ě@5]˴_$x5@5;kk.dJ ʬ55" b DID5 &!"ր@Ě@YeD5JXWW(&(&(&PZMPZMPZM(&(&PVMZ5?Y5jjj2ddư?Ӄ@|vOZMj5jhs!$!?TTj RM" R DH5H5FDIDH5&"@@D@mdrjR CICH5&ʪ ʪU1T(&(&(&PRMPRMPRMiKD@I5A@I5A1 "$"Ո85 &!Ԁ@(5 $(5 GQ1j1j@ F ĨIĨ5 &!Ԁ@5b B DI Pj@ BMB B D5 5 "$"Ԁ@!&!PB Djqa B DIDFvi/vӀ@PPj@ BMB B Ĩ55bPP%@@D@Pj%@Q"@k"$"44 4ɧNi:M|æ~4&a@?lD?lMii: CM#I~4 ~4!$!@?dL'!?d?dM#!$!@?dLi2M2 6Ky1&!Ӏ?d?dLii6 æILii2 DID4&b@@l4"\&Fĥ440D4@&AP. JQizbAL I I1i &M & Ĥ4 4@Ĥ%ADAD HH!(& 84`;%dJ +J +VIDL((&SP"MPP"MPP"MDDLA4A +/<<`icKw%%%dJ40&1P&Hii@ " DID4 &!bҀ@Li " D&`4 !҈hãhzF C$G#FCyEâa$"EâX4aŰhbh4 h4JPM e6PM%Dj\%Ȍ%̸J4KXMNDy$( JɥAI4QECiPM. JҠ#*hrePKT%DePM J`^UIsҠ Jڠ$\DAI4QDkh"1E,ʢ!2D$E$ \ &1$ȈDCIh 6bưhz8DC!\/_ݣM 7H4 FR>:ա)GrBS ")4v*ա1csh CS踍ssh0wYr]\Xo>Cܢ)c'sUnVv_>0%/ .6)$էnD3h@>,.!XYM霌hMAYgxY h:4 !a /ZEch AC}i]¢Yy >4!%eDcrR[4f[4vL防hJgR`X=Tc¢'ѸE3GV%A?E3]i4trKh,7딀8-"uܢ[,͢ShũhfєoǢAc4TnϝF3ZlX/m.u,N ]X46a8E3X4 +YZ4kE3aьI Dkhl%84PFšIšeQh, +M, š84 84V EIEEEQ+&P+JɬDDDY+&X+"$bZ4`F`Fl]h4=ih - ŢIŢh-MZ$@+b$bY,8E8geрYY4aшaш F F lX4lX4,MٰhٰhٰhzʆE#ʆFeheX4@&AV ŢX4X40ʢVW5Fbh.MǾphľh*¡Me/|84_84_84=¡Bhza>,EEh>I4{H4pO ؓD'!=$=,Fhh C鹇EhM ܋_dC{H4OM â{X4{0pa$E#E#Fӓ/4/4/4|X4_X4_X4=¢¢¢MOh*¢¢MϾh>,/,}ш}ш}\oAطЈ} Me߂Cӳo[PhD߂C#-84߂Co$oAȢ,ȢI[dxXHI\$E \$$.h`".hjрeotۆe0рj4=Y4pEY4,ȢX4=h@  ĢI Eh@ MB  ĢX4 aa F F C`X4b`X4b`X4=â,10,aшш M2pE#.X4=,!p}MF\hfz]hF \Hg%Ј!TC30PhPhzC#C#C3010$10$X40b$b@,1&1"@$@$("$b@4`4!B6U{(&1PM0p^WV&#PM PM PMB$@$@,@Y4@Y4@Y4`hh2h`  DI Dh40pl)پRh`  M"  Ģâ MOh*ââyK#0,!0,!'`h4"`h4"`h4D C%84 84 + DIġ84&"@$@Y4jddɢ ɢh2$$xxPzI V&OMojh3$?4Fb@,D@,Ehz"@@$EEh M"  ĢX4aшaшh4=hĿhĿh:E#E#E0,b$b@` hh@,EEh M  ĢX4h4pJ4áC G`84 F F G`84B`H4B`H4=e@@,EEh@$@$Dhh@  DI Ģh40&1P Dh4=h@  ĢIĢX4 G  DX4 h  Eh M`X4`X4`h4=CCCMhhhzE# "F '`X4"`X4"`X4=hhhzF#F#Fsh:C#DS(_84(4=¡¡¡C#D#Da@?,D?,E#!$!@?$DDhM Fhh CEhM,Ehz!?$EEhM âX4 aa F F _X4_X4_X4=¢¢¢7_pF#F/4ʿhhzE#E#E0,0,0,шш\0$Ј!TC30PhPhzC#C#CCF F  ĢX4 X4@FDIDH4@&A"A$A, ($A44AY4!]!(&CPMPPMPphJK4AAI4AAY4hhh2%eed +b@A,((&AJcx s˂âX4X4b`X4b`X4=hhhzFSMhhhzE#E#E#F F 悀M1I4͡)ЈMA94Bashz6F4lp$X40,M"bX4 1,1&11$ T &a&bCI4Jɱ, ;yp(&h"5MdhRf\-HeDb,&'բXMeѤҠ$( JҠ,\EEePM Jʠ, ʢɕA,*X4ThRi  A46ECm M bѐcbѤbh4͢EӃY4"cX4"chz46Fl FllMǦMFC&c%I4V7*Y49KX4Vyh4VVrͬ2<΃h*?x4sX5UrS"\{F3ջ+LEh4S-Rhiŧy4Nh.9є]ݣьO>44Kє3\A ch4v=-;34gzjPφF3gEՕh7Num!!<:єGc8G3kHx4cJգ{ çIOi>M?tN$N#N#N/t/tg_4b>ME:MO>t/t!Ć F F _4_4b_4=§§§>Me_4:MϾiľi?|}ӈ}ӌqS5O#O#Ouq$ӈ~4&O> ç~4~4: CIC~4&Ӏ?|?|Oii?4&?tk!$a?RRpj NM" N ĩ85=Qj@ J DI fJ]ԈԈ F R0ԈԈDCC1J0JO}s`85`85`85=é F `85`85`85=qjpj[c[Mééé1SԟWS0S# ԀAA8SqjqjA8R8RQj JM J Sqjqj N ĩIĩQj JM J D(5DIĪX5p&qAA8WjzA8V8ZV3Ŝ F F `x5`x5 FDpZMjj ZM Z VjjР}ȵ?.1W# ՀAA0Wjj ^-׻x5p$ʫ 33dʫjcd>ݫW@ADA(Vjj ^ īIīx5`&aFDjj Z Dyk2eGRk2dʭ ʭ ʭIZZM A ($@[[qk@Ԛ@Z3p`50p +9Cj% QpjMy}qk[S)8ֈ3jMOF F `5`5`5=ííí)[ԛխI[#@Ԛ@[qkg5ԚԚ A5A dJ ʭ ʭ[[[!(J JZZ# rM v Į5 5` $ ׀A0_c ׀AAQ0a0 F b0 a:T`5`5`5=kkkz _# _# _c0kkz _# _# _cF Fį!~ ~ ~MA m!_(_# ׀AA0_kkA8^8^׀AA0( +$ +@A(_((FD5 5@\C&Ab$؀@(&@@Rl DI F F Ŧ'`(6"`(6"`86=ññ Ŧ'`(6"`(6CU b#b000؈؈ı F Ŧ`86`86`86=ñññM@mcc#؀@@cqlql@bbQl` Mb ı86 86@$؈(6P&Q +@Ak +"$ +b@A,(f8fl M! ijx6=B4@f@@@Mlllzg#gSAMlli6=Cllzf#fllа ijI ij l 4@B<HgHg l !M"! $ijx6lm#ճ gf#8)Ay6Ay6lA48f8xH DIDx6`&a ـAB붴Z^8ll 4+sI6v"`{\t l9{X6©R쇌s!LdS~hct H6VO?$a-B?AMi Ju6wuΗ0${ͨgؼ1g|ZM&$ $ $LXI6AXI6AXI6 rlrlrl2b%fdʱ ʱ ʱɘccc9+&8+&8+&sVMpV E5,z EIE@YDY(^ekek _ _d׈550ƢEk@DXm>>ݚDX\akak ,r \\`k,vM",v\\ [ *_%_%dJ q5 kkk\\krMr ܫr C'=ܚ=AɭIíz5@&A=5y50On ȓ\'!<W#<<Vjj`Z CICyx5@O^Mb^ ë$<V3!a=D=m>;ƝD=Tj$=TTjVMVTjjR CIC{H5p&qɪ{X5=jR CRMR C{H5{H5p!é}85&Fé}85}85$x؇SSpjRMR CH5O>Nii: NNi:M: ç~4 4FCINi:M?DMii_=6|. C4 4!$!?lMM#!?d?d'!$!Ӏ?dLLi6M?dLiLLi2M2 C4=pi. åICpi.M. å44$?\LL4{y4MOM +*:dI ʿdI ɣ ɣGGdI I IIEE'&6{4&OMOMol)N$$K4~h~hMDhh CICH4&aр?Y4=h Cy~h2$DD'&'&'&OMOMOM$'&'&OM:4>94}rh}rh}rh2Ƙ󱻙.D?>K6}??>gg ßIß~3L C~3=_ ÞIÞ~F4 g3>؇<؇<g`L =='{K .dI I I<<<'y&'{&'{&OLOLo~DI $πiвo<&3oȇ3ȇ3pf L" Ù|H3|H3i!@>|O CIC|2]&]@>tD>2_$>|]&]@>t}.[a$aˀn#11$ʀ>dЇ,Ї,e@LB Ö}2 }2B CIC}2Y&Y!?d?d-ez!@?d,sM?dD?d,ee C+qe LdW$@\++qe L ĕ2 2Y"@<P DIDh2@i2dCPL4 < E:ʓ ʓd2$Jp ʒ ʒ%lCb.$['I&'I&'I&O ÒX2GS#$$$dL CH2 H2Ka$d5BI I$CI I$C!  G #8H ( G&G# ÑI4 G#qdÑ@Đdd"&$1$ vPzL +D:\ȆlX?@2,=&a19zLc(?&D)P~ @1(=&JJJI@-Q @1(;&*m#xRܘJZ\ @1Q kc"ʎ!"䘄Dr L 9&A1CIX 9.bŰcz0C!\/^)ɁX8XЌԵXrx63qČnk[봋7jLi% 5%_5ӥ~NUU)xwp!K:ݘ.G}2wc\Cvdi^sTA!cm=b -J^WN6[L#3ڌelK.u<ڃhl0ORMuLb)E!;/ϡ=ORKm-xևgI +Ώ/#!䎌mCgrGj-ӴHWd9Գ+,ө\ry +EJmW8Y<"cmPdl7ۖ G[}OsM>E&qVLpV 9+Aʏ ʏɠ   A+A&h[ [2[2[22d2dL[ 2A[ 2A[ 2dd-L- Eh2 h25X&ӳKbZ,Z,X+Kb$bɀZY2M&MҢ@Z4DZ$H+OʓIœ qS&qSbYL2pRvu޽m`n4ٹud1d$A`KR>uOWu;RU'n6{,SEP: PY2Y2Y2Β΂9u6H,@,@Sf9UTT6@SdAHdAHdAʼ,A]P@GL ̡}d(}d(}d#&C#'C'sH9J9J9CP@GLLL=`2=b2=b2쑕9d e ̮|de|de|dek ̡|de|de(}e#,C#,C#,s(A*iJiCHP̡}e}e}ev.C#.C#.shqjqjqCPPG^G\G\?2T?2T?29ԏ Տ Տ̛Տ Տ ԏ̡~e~dede#,C$,C$,s a + i + iCIPIPI +HZ +HZ +HX@2@2@2 + +9T E E!Df(Df(@fv $1C $1C $1sh j j CIPIP*d"Hd"HdA.A23A23 CIPIPI"Hh*Hh*HhPAB3TAB3TAB3 + + +9T T E!f(f(f$4C$4C$4s   7$4   C@IPI*Hd*Hd*HdPA"3TA23TA23 + + +9T U T̡DfDfDf$2C2C$2s   S/-B9T܌TZ*)f$f$fN7#7#8s   SHAHAΜ*rF*F`gN;#;#Lo<UTљSHPHtHvHv@3@3A39D E E̡dgdgdg5̡ gAPΜpnFn@3@3@39%$0) șSAHAPΜ +pF +pF +R@3gN=#;|P@3T@3@394 5 5̡ggg $>C $>C $>sh j j CP4! h$h$hv$@C$@C$@sh j I SH)ǔ@49%$$)h$h$hN A# A# LvHf@3@3A39D E E!g(g(g?C $?C$?s   CPt1~ 9T U U͡h(h(hv$CC$CC$Cs   CPP "H@0@R4@R4H 9CPP"H"H"HAb4Ab4TAb4 + +9D D 5͡hhh FC $FC $FsH 9J 9J 97 $FK )J )H`@49  ͡h(h(h#EC(PHGGG?B4?B4?B4%@4Կ@PR9/ -\P_J_ICK i $͡)H_ICK iK i( $ /q_%4&`0P090 Ii(i090&4(S'N +@@`J`LsH`LC Li( 0! 0 50&4&4ܧ5, 5, 4(͡Di$i$i$JC$JC$Js P Y YCPP2H2H2HA4A4A4   9d< e@ e@!j(j$jO#f 9eB5`k:XshuFC500 +2 +9Q` ` Q`!kkkyYCYCY&k`N抣e5&eCk.W}5Wn؊h 5Wx7\槙bk,hq;Xs +A +ynZQky, yn$ =D+WۀGdXcGjkk9u]\Ͱ{q5ט{b 1cGlܜſL W؆B%BؗaL[Ϊ?g1LLGk,`biY`/kWskt]hC=Pwʫl :2ZTߐX!Kٵ=-wYg!08<_DUG ou4Q8lWn&[[u%zWbX:N5 +)m5{:56* u\~?\e붸e_GP#ƞ 'k%YC%YC%Zs,,,ٚChPhHhlB Fj``)k$k$kNX#X#Ys---CmHmPmjKjKrK[r5[jj%XC%XC%Xsh-j-J-Cj PjPi JKJKJKPZR1TZ5Z5ή&Bg :K:K:KfY5Y5Y5 + WW문謸,CgPf Pf 2 *K*KPY5PYr5TYp5WC%WC%W&kvYYZhؚ]@P,#XC#XCXsHJJCPP@Gf=p5=r5=r5쑫쑫쑫9d\ e\ \͡|k|k|kkkGP>5T>5>59L ` `͡}k}>5ٵ` O` O`ͮ}kB@'''f>q5?5?5 O`M]@@ռ]D@D@'''f?5@5@5K P P  +H +H +HP@5T@5T@5E\ U\ U\͡j(j(j$WC$WCWsh j j C P P "H"H"HPA5A5A59D` Ep5"HA5A5A5 + + + + +UP`M]@@*(*H"(fA5A5A5 + WWs]p5PAq5PAq5o*(fWA5PA5PA5 + + + +UPl Tl UlRA5TA5TA5 + + +9T` U` Ud͡$kkkv$WCWC$Ws   SH9U`TdC9E`D`D`)k$k$kNX#X#Xs   SHT`ͩkkIkkHH(f@q5@q5@q5 + +EP` DP` DP`.kB@\'q7oX\ 4P\ͻ94d 50H@5@5@5$`$`$`)k$H֜ +F +F +T@5T@5R@5Ws* )`9\ \ %\!k(k(k $XC $XC $Xsh j j C P P H&HH@5Hh CP P +( +( +(f@5j(j $WC $WC $WsH J J C P P HHH@r5@r5] +5Pd 4Pd DPh.Bk bk bkv[$[C$[D` E` E`!kk(k$XC$XC$Ys 7"HfAr5Ar5Ar5 + + +9T\ E\ E\!k(k(k$XC$XC$Xs   CP PH&HH@5HH C P P "("("(fA5!jjvW$WW h]@@H&$P\ $P\uw5 W W&kv Y Y Z  ٚ]@P,XC#XC#Xs k#XC#XC#Ys]PPGG?r5?r5?r59` ` `!k(k(k#XC#XC#Xs  +  + C PW#!k$XC$XC$X ]քP\ P\ P\ͮj.H@(((f@5jjv W W Wkv Y Y Zؚ]@P,XC$XC$Xs   C P PI2H2H2HfAr5Ar5Ar5 9d\ e\d\)k$E2A5A5&9u`t`t`ͩ`bkkNX#T#kNX# F*Y*H*H*HPA5jjv$WWW]@@"(&DP\ DP\.jjj4p5.*Bkv\h p5PRhؚkk(kE$XCEXCIXsh"ɚCE@PHFCp5Z9ׇj>a\ 9!S8mh{ X1֜.B5r`"X#al!XszCFB5`B5` j:$WspuHCr500.ʸ]XmXm\`. uWu\\.@. +g5/mVju>b?X#h-{ҭVEC[1d|mdAx_}?? S)薨a,,RE?~bKTW _WS ?~Mgb];(!z8E%?6ԚP;W*#8kccY66V +dml|fUƁe["xO +֖I=۪q6ϯ=K@g<:>ӪdFkq7~pq湢Gl~' ïe3ٸǶꯢ35\yb6"7vBWYBi+tX`#dB׷>P,Xo4A>'27x`Eg56?_+OG :G :G :g9g9g9:Α:Α:9|$Nm+FfCY7hV9$o(Ѭ|Cf]Y @gi444Ωt.:i:HБH;#&s*4!)4))41]IHHMP:kjY%pv}V J@Uggկ>N3+ +8PB*LPLNLNPg:Tg:Tg:8ԡ8ԡ89ԙՙΩAPHΩu uA RG RG RҼ$u%$C%$C%$s(!I(a:A:ԡ ԡ 9de0X*d]U*"8PAUPEp*dMU"VGX@:R@:R@;عH  +HXG +HZG"H\A:A;A;ؑؑ +9E̎DPp@U "j8 DPpBU gAÁ 6BU gA2;A";A"; ١ +١ +9dee!v(v(v$$C$s  +! +!CPP BHhBHhBHh] BHhBHhBHhBB;B2;B2;١١91v@ Ubz8Pp *@ HzHrGHtGHvD;!CH HArHtGrHvGrHxgC;C;C;ߑ0PqvAda +@UgCմ0Nz8FzH|zHzzHzC;D;D; ߡ ߡ 9!w ww=$C=$C=$s!! +"CPP "CPPH~H|H|D;D;D; ::!* J@U8 4Qq J@IHHGGzHCr<%@xNA óϋ YxI!zg!8y^$CȡFΡCAd +"k6Dȁ D!,CATD9$ 1qS "H "H <9(((!Dy $y$y=$C=$C=$s!I!Q!PCPP|!@yN9#9 GjTCD>C>9|||!{||5$C5$C5$|5$C5$C5 B>B=B=90$%T] U;R9B٥Ps )TM +U;RHGRHBR>Bb>Br>H JHGRHGRHBb>Br>B>9PEt*+@ +UDR":*JhjZ:FhjZH'NC IZHԇZHԇZHB>B>B>9ĐĐՐϡD}D}D}5$C9$C9$s!Y!Y!Yw9$s!Y!Y!YC@PHԇHԇHPD>TD ЎȊPDQet*ET]UF2:PDQet}}NE$#E$#E$s*"ϡ}}"yQD?K/)8ϡ@~$YxeDP?KYOz:$%t(sH"KPYOr:$%Qm@QNPDP?TĀ~${]AHAHAꇂH쇂HD`?Cb?Cb?9ϡ~~~=C9$C9$.~=CAdt=QϡNܾ~wIt'6~Qt觺4TQeQϡ~#qٵQuqѸ!Qu6}\u ~N$`֢ˢKq\5Ygm3c=YD?/D? +wdYͪsxYYDH3ug"MkGaYW%}͗L +@G?&9M#o"ӛGg?Z19Wb#C!@!P")"OtJ9dfȥN!bCC&_[{{70oao~g_o/?͟T8hszLدk~/__`qc7_>_<ϯtn\`_~'6_%\o|kYxkDD#Ϭbg -O/竽Fk;?v~|h8T HuGz>Kkm;"?UP`[ty3g# TfCVnԂμl2MiFFavf1=χkQ*pm;q?b'/n%އĸmBF\|{wiY|vqCh`rY-Yd>^ˁVWq9@ܸVWhus$,GD 6BڔУ?i%ssmwi-ๅЂm!nf|kf7wFׅױsHhOG~364 %DiH=fɆ-Væ\F$Uuivf~Z@YK{˷Ϡ{O%H3?fBcc u%ξ(_b=٦ZXo4.}ccs77-.xz,068"W g!bqqTΌ]Ʊs6/ėx#ۀT*|7Ac[-pyKN*1n tZ$ A Tr;~4XuO훻&w#O ~P+^u-$&[$cwa 276#{*.N/!T<4vEovMθ>aF8kTŴʾ5ˇ(cǂ7n#-1ӂ5[9-HAS +q 3.dlC{ïa?DXWD`mu5C%v/5cHqo"|料 W񊪞&4̴f11 8vέE +3Θݚ7njch`]|[y_Tpcϲ 9yO=pF ͓b˱Ơk|f +oLnYeC"f;ऒ{>9KחF'L3lsFOwVbwc 7>-3be3'g},`kWP,O݂mPy֍2T] Ls PF?i!Z0}rPۅҨzFNcQb.ff+\0e>fq 7[k~[Ʋc]^ssU~ٲ%sMڮȡV;fmŕU~z{sgTE0nH]s'͆1qq 8d0"z:3Q?s Ŏc[k׼9h3\ l@m9An>i!`\Xr7 vwdqj 1%׽]^<#fuk[m9oϱf.O)Vm2EXjQ^w/YUvJKTpΜ"R-[vUn|󁖯89_$i^ނg|Xv1j{-|4FǫsOj`D1a(o1]rrNnq6^a㞤4?:3XoN޾p'm6cϯX96^\ 29;So5SƛC. ٢L /'pIszq:Ƹ~y>Qg&>lLja0?ޏ<uÂH\n[a<ˌ>dGm7i=c?FKL{آ& F h~SL+z@Ӂ[Np?Ft7ϼo5,ߙ~i=؝$Yo#ˏ1m*4Jbc؂mg(awϙZij ,m~FТ,Tqk>~ߪ?3v#vŬ@b%q?B%gpD-qRI"[3ȉ#˝eq lo!W>':K{}pXXgN퍴:= 0\ldm [a)"?+reqGs|la\U;Ϫ5us v#X:6OJ"k9f`3x^Y}bvlbeC_h@cbal+QL/xD^lJ ggxuhǷ?g7Fێxnw9/c\R~sgosk^-vkfrgv?ТGe'+_up,Ȼ'AFSRXf8x]О.qĀq\7qŗV>y'Kv{M>>[@mW;7I%QFxi 5k ߗxv7cH8'N_+b[-~0;$ʿ3z&EY mX=guSwO=]E)ME{ZG쓆#4ԁ|\Uj#m_Q11Ʉ)K,-|Lf#}VfZ|74w>NNvVW9&fB㟽-O~t#Wc"T̙9D;^3޿z;~XE/WaKgᔸɘ N_Τ ٍN +vvcyGd%x5SEزôH߹%2ڒwY3xM6_XYOM"_۟ES+q؛9X;EKg"p.N $f(V\,#bAGvT9dsޗކ=n٨ |iWz%Yz^^ 3_?/>*4ZHvb/$-戎x`3|chK g{O_d ]3 w +9zM摵Tx?~>$zl_1cmCWe|o[^iv#(ёge'"kqnhw—W,|L6i7>~9[ljjjJ5O_E\|9mdl6u}K#G89.6 8x5wiQ#8i╲+\)j7f;hIr̼<WQn&82B^|*c_a x +ժiv71=%׷PwF70!nFۉgaSߖ2e?UCwy֌#ۜba+.Аn%qs{EPk){ciт_ύR-lvX +>Zj%/Mw519bٍLnt N}l%ڋD>KO*DE"7czF;|oN[yYQz)e3XaO^؍Zi'uXpQ7um£<"=ܕ>|3z~\9aF%nٌh<=\s[Z-j|Ş/CRUUޫڽv5C]d +/eƵg鮲/ <[.>WZMf6sb;NƁn +F݁|GwyGhYol M{1r갳P q=V(~ +#6nDPZ=!rčwHn+_Փv>.ezO|_ acVfG:sߞzG0KM _bDqTa+f3fWՂGL^0xdM˃N0fD~G{t ;ʮ_/Q1_fFiom7~VoYz#iqk#.З#`'KN4Zu}hwẁ!|FNc'=ib_DbOBfWx:#1F1~އhv㽇'%zWnm_3Ε7chaNE E Ga]&5S<ءiNdH讵Ղ얘Y 3ͱHں=oϻb{. Wo+p M[ܶ7%OfB#vD][;s/eۗ"`,n-%B\i\~r$$PpOZ(.]y5a nՀW՛SC\FЊ΋kXTK99hzQ' q ~\oD6>l:^LER+:U#"VJt竦4b7 Οvu|/C 4Mtq4f + xcN%N@=+8"*߅lbRpۑ<|͕7B[YO h9 JA^|Q3^Ѱlq:x}8;,Qx1vTcHq_VOipXvԞM} q)]˸$65hpD@`Î%ecæ B-,]6{W86JKv'NsgCkh5CЇj$M~?.ߌ=6D'1O07P3g Eq2w3zb5Pe籣{\(+}o{4.uPrqhn d-;kh8tE/5=8g/. 6":yGSEp :H'W,o!:x;] ?qHѷzy8j0mJK}3MZy6 ':mٗk]=kbKs3͓Fk< ^0. E.өG%F=U?/h;D&gts| ]a%2}n-:w.ڄ.#a~uqrmFn7 zm%\l'3η{zJ(jO<#ט90l!ϋo兀wrStk8xJ-lvdԞ ce˘vD +nF4/x8;/g.oKj3C$pl Iw'xFmmֹq?}wqgS[t\T6}DKkDJċ^Ʊml7s<a{akd))&0}S5۽)܎/\Г(&*$50Dv?T& +,`gΧQ36)nK3"-n3Lr" +"1X#[/b׶b+j "ӬEnh,N> l#, +HwQ&RMtGȻeRX{r5$xy?!y̯Z3Tb#.}3b~i\i9ϞAc naxMcǯ5.6Oy|oFK^I+[WV w5Yw=|_jd"垝͛I:ތ8Q[S\!}^ 6+neUXq!)%(|q̖2'$Ou-C!Rykg +9~ 'Wc]%_oZxSœ#h>"(-(g _55׏fj^57kM=B}{jOb$$i8EǐG=A0!L'gK!d6w&0;+'f75ٽ>iorQűҷZ(RLekM`d3ZGq^G )SlW,U%ѿVL +YRYjm?+|ǩpQ5~$G;0υ +>V\'+!R#rax@ishk|RodY?,e˘0zz-/apM^+4-ܜnьH&(7G:/ko60Zu=Nb,+f+ˌ~cH-01M.GR $KϮ) Cdh5ޠŬno{] vXd"fZ7G' [CJ6px[7Ɍf,Op5mRXN#NM-Jl8fjhoo=IZ0̦\BElj*+ D:Յ~ R4[H 37jb\v}`xw%bօ.*WBe`ᱺ7cT\`噶TRF?PӍ8||5F*#4@죵F Zxik_,n2»c9.jJ̨iNVJZA})HR!}M랧v [_Q_D޶MZQB}joFGΡb_\ +#È4>5EkƳ-6,DoFDC1U%GUTpo(x2iAǭTT\Q~قtw"/ܧ̵~,FQO§U܀#m^|?GSg#f0ץ>вX ucG_,`=7?[6v:ŋ4Bgocq(S +;dr4EX1G,]0vVFF}+U{>7_e?}\\:-۱rgLj3B-7yo+KY2V(Gvh!8V*`#(c 5|[/m[lZFq3Ru +*ZPol!O^LYkPkkFm|R wF}PpzMBz}_,Z7swEss?2{bxbm-1 ;G1E#2;yfg'fMN#v4+wӁZRAn s<#RMۂ8 +#JX>#k;0_ǧaG%ֲ3O78 +_+/k5hUֶ!,}ry +^YJ`jj Zk +wag<އ|C2i&lÅns{-Les|zrgmLg7cOڵH~;ȠBŴClLp63qV}$Ö7E`Z!9xv={+: -=zqwL_+UЙ;?"x{jS6l+rcYVʐ.t:إIA௷J: srEx*v6l o]p XinI- ]N /uL)aorc`;n)Z8qI{YE4SUB:_g5OZ0z W궬d}adAKȷn[ $m֍LWndVaTۣe9*sCnD8| (uᵨȳ}l4jɹ[_+tw;k.ٓf&Y~~dhNQZdw`` t$ay/FQ +X4z>c V(=#nnl-ܓA ~ǐ럮f-0>TYl/f#cz҉C%ܽ[5RЄt$Mq6 +[`WOJWļۀ{|jlg伦)ίゖد9isZ +d +g@f[m.[ϊ{:Y-03kf;:cۓ1V7~M,tTz2bY5V +zӽ4A݌j\M13@ \aٺ:גi^;#('d m˩80QXoFj6B?]r[W* Zࢳlb_V_f[#Kwg#9`ȠS+#t3v>'Q+ܖuzWQ ~W2, *pg-agt!c; x{s2t%B ]#g܆d=љɌ(J~[]aD dXAďPl_vωc8c9)g[2 +1KV;I)G' ZkTSFp+oWh ,Y-0+[ZP +Z?98Zlfd>;9DpBok +ټ[57]\|-XH: V'+\]ʴ}(\W]+1cg|U^:dmMҶ鐖x6Ce +3pHL޼5;kie60O|EaZyJc۰j =O)vzݲ#B-xVg4:B~~K\'Wf{,Oر$^};8$:-k#\kˏS-QD=3ѓӶ<޳bM4b+MgRIV~LjhDS'yv<5_?3GmP!%bB86URQUw͝6W&q`it+b˜˪# Xg|lc[w#,Njq(T!{쇌\V_m(6*e"ԩ|͖!|k(gnTȺ8<[-N`V<8w'X8g,l_c^~@55h%~DclAc?ڌzsEYˍ-Rj#VQِTrfTU{:n/YtٛH⻟YA׼=+.:rÊ |W;!ˊ˷!3"0 /O0m-~>x ;IN[Së;MKß0wo]]v`qCibIiv߶3EdpbE*V(7xz+i{X#1V?dO{͕j/{B+FUQkhzVԪ_l%cո=ތLvC}-jԺZěՓDuK3τ.,OYY*p1Lթ Jyؒ*rjs3/=-rXڧWc/2FdSs,kȜZDA)b#0kdyDewjW 7!^\< +]} FYT=Ө*m66գS9霱Rӹl[~2<،%X5nn+*CqnU@!VTmHq~q +1#f_ V)"k+C숚BiLy1buƪ\ʍ?[B)!E= vjx>fL댶5?؇FgD?PHEnc5GBϏm&e8.86ՌT[Ѷxm;lEG*W4#7V*L+oFNh81V0mYVRU +$km[ ˤ2ƭh۲oE,ƦUx[ؾizqH(ùsstx1vEX +9ʯlx Kz=!m=*5_DujP ˎ96_<@j"1o*5_+ܱ9[!w%t=9oP;7}u*KG9'Z^P&$bxNľE,tcܫnt~\/Sk]0/3V]ZvqiF{3Mn1fFxk;;̟wΐ#|pOvfAWYW6{bHjTVO1!M8FJlةdVsi|j=qiVDw5*'W9^g-ae/,ƕjԘL[q^iH ѶSb*k~Xk]u +컴؅Q*m{dL}9pX+Q*ڙ&Rm4wsM52mF+էn󹠜פ) +gŖvDo&Hԉ"EU*ψȊM~cz_/M[?k;A䧢Q|V'Nk}!VmYمFIqϋ4wm14Y៻Lu.l?/ڞq!Fu$|ŝIOS̢/M-^R jE(r5 /ykDy.#zaXt5GNh%- &fN|v)lwA5}[ٸbǪNu:WHIКBKW~f*rQ~BL܇C&3ǻ+V۴Ι!2 "l6O_Zc:j1 C׽tAo1IG 4h 1^.܌4nI}O\ƚVwެ*x-k" -o] &)1d@|a!}n5GM] u{b¹ rb<[Kߖ@$5Pcے _[d9SG!R#=LWNe@ygj8w_zo"p=*o +Mވӑo u?aKwyݘp_ +e;\lu'SϗOV8nU9''$Q~#e+v⏅MX́{`(}'L* mFeFȫZ28/w>PwG8"I ,{9 +QP#ZΆ,'&P I\;m8mmѹR{w<(,FOQU">S#M=%]#ќO^1&by* xJ[MNSCŰ7=W.i5/Ʊ,2`*,sV,- YUWт񻭼\(7p-0z(bbUS ?!8@x_DYoTbj9xƈXQ%f iOsb!F&Kq\w} v$ˉG4Eⶋ7Y@i/}}j1?hF2<^}U^wQɃI.JL(}=J5i3Zņpw-X}6/4~iʪv0~(-`IjYVm+* @O>_+wS;ђG:e+nxkaZHe|8F`Ho/葳vc]Þƪ~0.|"Mlfd0ytU'ѱnɀc + 1#Z9\ls( f_QEUl-ŀ 3q)>0 e2graKRn"a3ppK*Muw2.\_f[^b^.vyKSn8dޢx9<#GZ1EQ̭_جaDݟ#"\ҋ͎qƖrN%h-3G;h z~0.;,[\ _jJhk/F?8Mi%J@\Yģ}c0]MLn0`ZnթSut)2-ƴ-{p )ݙ%ZG>Ŧvq'KIqV.4^lu%ݍ{|n)\\*ޙ'AO۲+USI/>0ڷ R8nWNI(56..JUy3"Z}l@0#JPk'omMV$:o;iC" +6߁啽+b[|lD0Z0jaQfqW";A<$mZֵnwt`@ (%ӿ3wmMP`ANdrltׄAkS"W3)+jMHAf^-jcc+!͈8颖B_vsTvz4od*4VDDzCi 4RZc`Ɛo@dZaCnW!PWW)ӀD]BP%zKKVriǓ$i01ەNtTT;teIɲio%TF@<^K^#B_s^ݮT$38ю@7TTu"OMv:PErWVeSa$:6avn y'0I$ap5GӝVY9MlIJH7sN eUJ5GEx'DއtBw.>CKQ{`13fym‡Q6ǎդaωݰtCwaDFˠ#2PHq8g(G2l*'JfC2]^0e6H<RzٍZ 4 SjjA͚ثѨN8p3L"ZX$p3x;'0añLT$q^хFE46FnEqW!hB/$ȁ*QRD mQF@ؐ7KJZFd #S65Tv{e2]̇ԝ봩Z!-#;;#bqU5\?b( Ee֪=tTZXrmB\5,{NfG"k@h[ lDNx6iYXH9Ufc`Wp!w Z 4G"MWߍJ'Pu +zlR/c4stߍ{T) +|,u̮J[28 MgiI)J x`tW$:gF&^O;]47}{4AÔԺB9ڍl/$!KπZ;VLWT +q +}8*! wwV<8j]'* 7; eMUCD q'4#O,D>Db@h W*(#P<ɧk.KK Tu56%X^"jt;Lh0Ҙ!l]?Jtβ-귓^8UY13hq6wcC+F#& +ELk#de,[]mvrD(E+g53\&6;4fa +ݒr+i,<5f&*5ULڸPi$rl М ऩ,%aXNdxLg<ab Id1AWnStz%!27gh5{۷rH nb.FPNe4ok4Bi [S.N+>i^s@IhH "Y5ڸgA%61 D +"иTP8 J`ܭww7 +uCcPBwM4[P8[.$em66E,-3DjBOT<% *qzKRQQO'z)Hʉc@jW<[]N\]2LEqQ{NEJc/\ L GaDHg'^Rэ1ȵTt#'-k:'cCbkQ8έ|=n8ʖT4ĉ<ǹgg i#E-ؽԣ@zvm99QzW#{ =t:d:c4%A'B8N{J/[sz&;]YHQ[ *DC +MWVT =~zLB6. 8HY!g N}z 6X)p<7t$'*ԓ$DStqa! +(4jvy ١,AhxZwPB, VGӫܡD% :a T*sK :Wvd8qg`hvcT.F#M)~BD@A腼ԭrP`^5`pC%i6R8Wƕ0b#aaw"CU0I#R&h˟!t)2Vyۄ/RvlXE!bzxDNeRr*8Pи_ʔ8^Lruq5q&ߓ8I'{gVifvɠVg3Pŋ#(TPk qsu]&@Pg^ 0Z,ظJ D3w:82 ;1ZUw<@s +19EWZ$fӵ6V {`ڰL +q3!&QTxjO(U^Ї1JIbO#kP;MsųL'ҙmMD+pC71a=؁6ǝowyrBUJ/R$7 pN,dP>f(&qBEm?"P,R߃ӊLnjbJ" 2\f-c|zWRmm!nr} N:}`,%τWTxl/PlR#Ͼcc ˧|YC?u'?_ӯouqvwqs}z=>??.6ۋ^ߝѿ_ޞ95pG]{?o~닳F}OpP[Hۇ|w5 &'M?=xޚoJh6fw}yy79Z^ lm^?z{h w/ϯ^+uIjg[{b^_n~H&ܬ=N__6ٮz}{a..7Г?Ts~}ӟ67kMþk|#=z3woNۛ׷g=}B{uq'=v:+ioIݼ<=]f/v|Nn^޼[bB@W/>?=zA}rE4}V5Fywzg?ؙL 37W lY 1|Xw+)_^\`tvzy̳Ůx}s+wzvj&[9ͮ&y{ORbnK;rGz*G`j{f9#=\ &n_xG`jb:@>ƀ U"v/WDODH`jX"!"A=jSD"6?ވ gf}Hdg=ވ /䃌H>FVmr~76عIa_Iٖ_t*ş}uwmhbC[ {Vq +H`y~ā>FF9p9<%l < gnu[o[\76|ȑ7;/Ms!uOaޞ_ݼV?ٟGbA{[,~̑ۿ}8z_\\_lV=y~z&Wr]MtєLz&=CɤP2aLzhbcF}~s86ܾdq,;6;{4uM7v{wsDr/&4nݙaU7Q؋g;'3;? 85T=Ƿ#=nq(g}H:v/߰0C6=Ktpcۃs~_{ޙ7L'?`[C7{j:8COŋ7@L7ulv3Ljo//o~^{pAwKP\;6pgz/MNj͗=8َ(1v}#Sן7A|?=~x00wn`DN8XVƃq|VF[C{F'##֟elVß`*IOi0W=wk/ģ4zy~ރǜ$ߏ%vij4onj>*|لm`t:F01щ-Ph{:FiyN1. +8\_j ߲׶mNbR{)Ps *J[F{Rf=ړ0Ghy//9x}CUyc|(㍙J/Ѷa>gvc+o~>lȼ1TX⡎@ô&1`Fo6ћ=ao^xu~fl~G% cTDH>j٢ҸJ泵ϬKi&|WS;qm PGT[~}@ޡP}0L2I&L2{$Gg`F$s0<~CۧIfE>d@;$kq%{q{zvwz.N&xWfJM`jm_QW=g(Z+^|{wqwĘvqIjE~}bd+=`Bw77/vjw5׺<:U_׷߽l\b?u{?C<~TVeks&ۯ@ &7a~|R`~ێks7Jg{.o:֏Et9k9UTу*@}TE=*=G_堋.tу.zEAյuM&_}Req#<(lؓܯ5C]sCC]}olF +b~,Ôٍ)g77ُݛT~n3~7nمѽon=ׯR/Bbh\Q"% zOƦߞn"O +endstream endobj 716 0 obj <>stream +hWsM.\58^CwGL\;}jXq^߾8=;NjW7֯Lz/S`WG㫰/u~k3';徺@v<)sdP$mM$_~r_lW26yHvٲ|˷}Em}wX wP@] fɌ>?dFv/h㎣?77S>|Ԣc1~3ړxS'L}_[|x-Gv?D_؟EG̾6/o"š4[V/0y=UF]G}!{v˳ׯa^j7;1fE2衚>]Wx}`_Z6L/;I%0Q1w'`{1lu[?v߮}AA{O7ܷ8탱<ݿ;n';Zue'c.gKx|d>#|XG~lL˝ =~bxE(6;iL94_K=sƜ{C<^n.G!"g_ɚg~tH@z7,1u/Ͽ|/훫n.mx~~js?ixsG Oz#pۿF(9z~syRG%$ꏮ^TJ$쎀8Q=I%G wd '&{t=$^Hs1<5F@ IL9PRRy=yِ g 1 ]{ܸ{!:pIi!t_ /B["$"T}y)'FfmtL63Ԭ+d[pQ +<[&DW֬Dd[K[.vܦzjcaK\3.%q؈myV "[O?yÙuP֥W X9K a*I 4g*t= -D(@#OBklY$%+'5 +>"Iqdhm c9=I01Zl<6=CK^Cm "Y>ښvߐX_-VpM38'B;0v8'/mAj@=v?vg#_65gиĶ.p%a tv"ltk.2Jh;Mwm-Oh;P窧m)j誸vUL" \N,3ظ mU1݊? ?mn@ xzlh@ZX`W F>zPv+7Z}4>X;`QMr&Z$rS]R%ϗ=۝3p̌9VzbRrty|{{=ĶE|ޒ1g>bJF;_`sHoJtl}pĒw,]Rw[FګQ[D4Ez(LGzҘ <qKBrZ +꒮7on{i*6 Ǜ"5$HWmbCN -z@%s6~P˯˴*-H˯K +-@˯˳|=!P}xSRXL_LH\ 2ާB!8@_d?zFFï}hBvPRmR;?4IsJo'`[G2жFچu;7+cAe:5$Ol +l"znO }=r[g"hVlC $ -+CL'"qK'XCe qN" +M`ϋ߄<@&dar1[`1$`oH۳טb^#rv={P2Ɏ|<M xAioh`Ãg%Fn g˷V^3̷UkX\z1YmSa`гx`Ona` +wN';f'79ymAu +P +KM2>}x+&Y?Tm]}6 +’luqqfq[ +=^wMBl߹Nh!º >ǤEjLtm=QP': lE54xiYBWW{![?M@g%z"s zGƜ"9äTwiLJc/&#oZZd>ˢ0 +bIU6O+,gûwƩѺg)g Qo^.>>ղn~vz` J߭-r=X-{htFLx5H)|EY#Y81b!S@0˖nd^M*6Q| i̕l/ufF̢9HE*0+OXڶ SũUgpv SPnɿ䚊"=@"=xT>(MYnL#$b;0ajϯtNZ<0l  \e%CX7i̠QИX'z(x*tdNVi@LE DsR(PE]Ǿ~]X4Eh1M]B8(RF=Pl$P 4]g3J-qeBuU{h/olbۑ +ΓFU/HG&~DJR=hc0NT7n=]""p8člI{`ubOw =y`ȧBOb +ŗg5fH !lJ'X$kxA΀vŁ5Ί#' z0/Tua5NGn*ThecEvDdsPFy*vbEvɉ{h@8 +i0a0JAICk纉PBlKl<Ջ5AR94P$Z7=ZN,H8yx2i(Cv^BY{h8Dp+ABĺbS%z%*g"낈gN}K쑉#bGK:󟨓f R:q(w/.QYI#IXByf}*OoMN|/nrG_޾~nn.O>3ɔ<`җ#JO'θyj@1]igz&3o~ 6Mq?$S;yǕɴ +t䲁Dv4]c/ LR敘qk&dx3+.W푈R%Wϙ# d bEzMّS;hB?J*W 'DGlԁ處2 j ]9#BznL*0ukg +M}쑇x.2P`( +~BDڞɃ*Ddxb \((3YD0'bb1KzOOPO%+ V_R»Kj\p̵0wtoFvOn431j S@I TƙwvilB/1">ηf\N{dKI5cҤJQřϲ}{pu虉FC( ^8+1ǹyCWG9 +I&FZmE%b?\1H7h1NfKK0r&Ahtz.' h}*T%:=AEjhȑpID֨D'u$vNѱai$9X8ȀA{_k\?=broBG$2YSc!ᨅ(Ԅ\: ¨ox6txnX,5eIӴRNG!QŸPY8+wI^oWWG9E:%.L-4e.AJ" +\z;x;겴BE|xkT) &M+ߋ*ZkHCQb2T+ݍRz0sVEBY+Lm2zvXeh75H6G y/Ɵ>ArYUTG$썃|E ٧3v;U*Lbpzr|+0iǸADON4TDEPژ'#z(0dK㦹$9J1z~JIxȐmNys!fhkDHi_@OImvxfn5ήay-m/a=t.NɧoipbQԔ^ux4v%oу-~9}{6m\'h dEoM * ״"vsE%/(t0G xD͍qLՂoy`'`0>5`ZΨj4y?jW{ <6kNdd&`KKBUˀ33L@ +bCC4bX/A}J{f.AY25( x9ACE/瘁 uRd|ىIN1C'+*Θg&^Uɒ|M")rF3cOxhHle9f=wA4bIwna/5G %a+ F.a4{\Zm=*f9G=YYIV&IƠs]e% hK dIOJ0KO&{ eQ~ҲN'(ˑJ 'oL>zL`bqhȱ[`62jҟ:\^{زcV{S&.ǤB!Kx%$$>lƎ+WюxeGuXa;ByP8:ã\ zrԾIjOdY4u +c7EtPQիQ`֒OrA.h&bʪ+Pऋ*bƍ18 ݾ%M=XICe( w|\|厲7Ê!0H9ɌKG$vHř&pK>Ysa8- wUttCT_tG5xy({-R0p B!%Ld6d9(j T<hrYnt'c(yLWkYH!$o`G7_MOJW0!ARӼɃ 81A35iI;VOn+R4'%G$;ubԑޭd% twE 3.q0yI\Hj Y {cG!Z%\HQ A֨1h'EwF+wa#]dBN|^"wfNRHcX^FFq\4HMK=wKuC,V"8<  E~C/"U +A`Na1|UJlv2s>I)u7Y9FOꈻ2KD=i\:;jIv q^gL3eb$+F>3T;pXܻ88g;O)@̒|&˴^IrhEIiEͦmfAP$PY.":XHX\-XD%r ͬP țu6*yŒ +fI-IT@IRB +ios.2<H%E,ݴQ OXPUsDG swrT+#<5̼1z.2P\=EwJx}{nzRbp)g6tRVpdf} +'^9XG"dT{ +ӳn1 $ghT(ңF)k5ЭЃ>k.Wg$g׳<7P^W􊼆E!Q kF22 \$jo\#{u @%Ke(rnbrB5;tӎY;'!JSK- Nニcj,3;W jAБ!j=XIt{ͫDѥ7RaӨC2AyK/YFKs%h^42KNE1Gš.ZnEAmgS-#岐(p'A:x_!2cH"#wCXv"aA00;1ϙPA\<Ї%EfTtM7gQ nQn $BJkB8c7j0j+01k#ij/lkgݨwQ1`4#8wTX-V5EN^QgjU"ZU*Lli웍+@#m? +b}e+-Cq%82$Wck,Wa+uϕ:X*XCC;Cv9+}YJ.EYJ 6gʫW.y>"8` +|WYx!~z@}=n +?֋5:s[pc`_Kx'N,ݐb+Vb[*CpPc9n|Y;=V,Uy{>{QdG-3ُd Oka[#ʊ +4ǔ/PeB݇++_ ˊ[VX.K},×>!ҫ cX2KwlάiV5sYQJKfe2Y98KPget !Ͼ/FVXZaRPhE[C+]lDK=,Vh2DZ}I+TZc.dZ96-}YN+|Z^e`Ԋmr JZVLsZ1/@.í/Kk{cJKkE[_+},AVFV5l9l4[cg_ ][iK},CVٖl[-AK|[-A>a>GVfXu+V[_ o1o[ފVX}+b'3[2G/ 0pE]K}8]_E•/s,\ WKp%2L\G,Ať>b+%ظn\+/Cȕ1r\>\,AE~+_5j{\',A9̕/KPs܇+_ȹe+qzn&݋_V0tEw݃۵,;V6cg-}gSHϞ LJpCnau>jNiZ?>,jZK\HoTjc.푩eKE^NןQ#?h~~^p/ꗎc_V$^hKKp퐴UXCi~Y},GaU{as\m=a +=3te{|.Rm c[%(mVUVam+[^mU4V=u[U.mR"*ѷ [ nc­_.Heŭ_hC֭^-}Ѻncحzfe{nէfevwI.1/3-S}Xe]D>L.zo|`Jyŭ/UnKUeB~gwvE޿yW̷$ kJaWO8 X'OVYY)cKsrrUWgpe\?b1\l+Aqx|`$M)A 潏s5+-2׵]e;73W8sc ;xXjyll.hsWU\e|YqZ |e,Ѳ\U[v\sm yE `ϻε +|oy}\{_}lR. mޭà8f@eA] ! +@Xts`t]ptc._K u3t2I_JW;tc_w* UX.},MW 8]̡ <]l." @]?,PW DWa H],RW; L] P]D9Pu]g*~Y.}z멟A˗% ¬/@@+^[ ܿr]ߴ~vګoB |]]Z`/3n95k?8f]~]ekmM|$5QRݔXs7u,_fe.ަ/}e20[,klwn?>ev- |X/y{zcA/ e^?̀{guo>ps@zIA`0~A/e^?W _^l SpP.X~YX/zcZ_Ksz^p^߿˗%ama'r}1M]d/]le ̾ok8.k ~h1/3x>U5} z fXet_@k3{跅g|Oi{oryY99ЊS}Ya4yʳ/>OA/`D0N1~1*pL$/E8YkV-'vE$Qs`N +e=o~6[&_-(Dv/dͳ_?w7Bk!Gǁ ƸMAy >|t5ՓHd~ :4*kIS4\b49A1,pf#t2ِ{pb.""`Sʁ!V"zv 1JPhPX]JԈu*@h{ؽX +?1y̻"Chc(l6KRedaXh<$oy:Aj +Pˆoݡ.e ]jܝl^5_x>J0P8k,# &5̏.d,2D7k(b[5T +E7X`lł&n[M;ZmKVx/D0\G4ђ1Ũ_S{Do$a__K5T,"?z>j%{@94QARc~5 XFV@U_F҂wn5?ܞYvuܢHJ;DkAf@BI̺ސiIx9Of+ˀyp2s"EK>A~0NNAdE<8JuS@L/!i:ɚW) bA ` d$D=bēLO$S2g1Kb*.BmoGƍh@oɜ1NE{"Ppw*Ȑ-Eͪ"@z-F/h(и/|Ñs2i.Q""xyK9=NTsՐtcOl1",%^*O~NA#cR8ҷ ^5 )[a'D/%`(jއ̅`k MEtAtV(E4^}PqG4'[Y2"p=4%j)J&JKFKEx'<0؅N2|dV?9U)co5!"jsS7D,ⶪYHu\9Bx0n)Byr%a-A #~9ȪdsC5[40JЂ(l{A +񕢥io "psǔg Y KHkF&cZ.Ԝn${ŷ^6Z*XVL4Z[y&V@[udʖm|a{5"~hf%W6Y"\h%UTx\~Ҟu!הUf N~+,kR1ߞR rAf~, %v=T'hɺi9L^HDSJ13IkWB|89Ob_Ű7b#%Ծrj`a/ЛYq'DSQ@Fd~ , E04x&C@B@rl0k\T;ALk< +3GE:"^Hˢs2 +).3'X.Nx`.bi$0obda%J?XQ\%p"b  }Kqj*h^ b͙JFm$*5 3"!zb8Z|ɷH5w ڨrd}R(GauG/!i_Ybw&C%t0 p|y1pl+ap4)<̐`ߺ@^D`'G*TՀz72x#2JO;$NM'KE$dH + +>ɍyrY0^%r@"7VCh ن'BDWQ U)i|w3U +-/Tn5GrkIU#dE'QR€/phfNDAdxXiI3I1Z,-aĪ#0(dA2J^sy;D@T V.pY>vK.]cSRZY泒bc$ abPJT7Vj/Vm(W_-23J?lFJˤYjNN&hC6țHZu*+ey/iy5eЧhUR㛋GKX`'Ex  A`(2МELo-0+y\X#\L4$<"ؓ'&Fa Z׈9J] A&=`XD X(݁WY/n$'0*]O=㿿5 ˛K4yw|}wQI[޼nqC˭BO.o^?_ RdB6 C' MIʆu.74J)Yj{r KTKVhDIJK IDxܝA (_*/#-S *t2u1 ߔ@X/*ȩ&j{ zRe<; (o=AQIښ$PS5!0ԃ.]9JU %Yk=tFLdexdHP2'\\ ~Dp?8m<{VʿKTcͳ-#C6iiX(h(#Pcwyɍ" dC3dɒ )GT:{D pQ 8٣g +'鞴a%HwUXbBUƠ ldUF=0 +Dot1``\YdgEVXbdKQ?<>id\2#OV,^qIX6ӓ gMԷ`t|D@ƙJO(L>(À6ٵ-(6Rb7I9Gl ˨w+lr\9#DM 5$$k {Ȩ̣tdQP@kJ F:!'9,Tρƌ o{E-< 0F\tYģ'X Ferણ %|!AS#E@(l +Vx#,V!$T>#& B(֤qa$%`u*e4rY=TrD8H|Z8wPXhXBZF:4p0\HkT+pWDP\A%X2#|.xPJ/ʡ ݍ@H1^3hz +$Fq QJLFHJb:o]OoHCj;.Y`dqIL|@٬)kH MgSh(Q%AfZ#p _ nw!sY'*R -O#(Pܜ JvApMJ0|Rض^+p& -sdF&1vlKVS741qM.Bk0c]dwC)רtpaq$9wRCe"c Y=Ɲe +nGÀ7?cX >Gx0ɟ5#`4&e˚Q4=spqpPPHw +,!gLeY +]G?*^y$*rcx* %D莊DH+%}ozP@[ +>ٯps*^U+7Sð2:fIMg&Z^e +=QtD Gwl,k@/ih9;@NHb;E⩇=䟡1TBu8З8xgvۂr6cd_*P cc`.ΐU8\Nh5D'MUXe(#O=)O2қVFx b%+5 2mE~kr/`R'R8)6a(Feᚍp%ý}GT!՟@IDN@Lln~&r!IጙՄZQ{Ա*Qlr+cB0BPTSb?4n<qD;X ]qP(inU, H89.!7EneNP̓F'̌"XrJ d;?Y[ft;Dyv/`8/e.Qq@7(ZRHD= sXa`وJ +PUXiDj gbxd d \E.KƢ!RdF􃙠łdpI}T@ 45z%8Yz4% K-%/?di~dbwh5pVC DH l +,GLaQuj\SbC~H>[4ˀ?UHK@fxs2[<RKB^Gwۭe"ø:~rXQZŶ2GȀ7xsV,A.q_ϸmU +і3=DTcȭ堐 : Y,͹ ;\W<"%'n 'Y=R2= 돷^S:@!UB)j)*Bz~alwI@.b i>GșJ&xrRQ0k`>_Xd dƟBcj7qя_釬>dhe/]+Xc.#*zAPdXp1^T5̆VY⥰[*e +9ӠR=?*g[[00Y'y\Ց'r[Bu{{16p.Jd'ǭ51$Ay `ޜ`J6c 4 8K'CF0@8졒B9Ʌ 0K B^ʑ`ġ?RYdFՇѓEVs%c^SC*KtF #rA2Җ/d\ 0h(d^IKPY]kAľJxDdwQBm MSꡐ>IeSb`~i8y^ަ6+ !C% HDÐdM{}66ZuPvo*dҀE\"]QG 3O-Fuy@”'GQGpsYB0oer~!囯,Z~ JZ 9s/uEXL\0BhRQzUPǸE߄$x DWft\Ff{h)z3!dm7Y0"F CIEy>'4s$%%GKp0D/J!e +8 |^Oų i95UM8H!ES''@ Z*X@'/b ða 6NƇ)Luch߇3Z| ݺ7F'T솂89x&+[d5*.c)P&U"&rV` +1&!1 +#^ZD=oeXOJY9Knᦒq9FT2'&+ IðfTNh +j:E_5 F]K=̼* HƁ/e F@o2g)͓x$ +Xl{ 8Ԇ)3(qZ9:J t`,@Wt}sB HeqSX%XBT3QXٔI•-YKE&B[I&)Y.0Z֥I$[M9&*.LC> ꣈ZF@G0GA+}Ț nrDLlY0-MԂ.'/vR oh~C?{FOHЇ,;d5e ‚XK} M^z2 2A B=XxVo_}gnрAf{+51E@aBߌ >d + "ףȕ(OY*jF>Ր!NY@wC*H(d/1Vfu̱V"!iKߣ  x\kVy}"G'ngrr&z! rJqAedyHLr0Iӆ5:ޠ)AK 1 *s;DA'Sa̖ +w܂&0^ȿa,-O$-D@hT8C1CpSzj P Q*(2 9ӡNgA4gA*X1Pn,ADPhȕЭZ2Op؃e<;\9 q,=p3 +ˌr`kq!xDpQKİ8P -oΑ C+b1ٙY dz H]CNe6(VWYdP[%qN(TzEx 9GuON/Bo 66X-]4Rke U.E ADLSJj$f(gx* 9Y;Q (:Fel2,t#P/{gln'WڡDv%mf㨾]L,b;_jj/LŠC@~+ۊIde#=$nUW&Ucł%*u}T8!y9!VnNx3EdJLFK"Y~PX)K%AW$m6 eh1SPR4o(Gɑ0}@zh46n -Ge?P[^שp(S)IӅ@Z?W; +!%.ef_E8"7kV%<' Pn)(ŋ{fm^_%Ni6$4fl!z(5eB;ء(]IPSѨ` * S-S zQx[lV*F`\55^ڍWqA ![BTYil"|t{] qjCRRF䌭+|ؑ 5ؒB~6vO5!cre |6sW76D?Pg|m4'HŻ-1un=z.UV$(4Ck@ 1a&9X%FDw"9YCd0w@\nhb?w}4:>i1ZvKC "Q!怌:rѧ2U7*wl*Tki ۔vUUz1.wƛ|x3~TEWje Z\?dIo2T'Ҿ)|3ϞGr!Drk(ח% J{b5©4 u58HueKuTS MV9}-MÍs]MK<(fJlMh WDr=t9s4}9*.sL:;=Yz6Ц3%d rAl7XP`l Uf̎ X6Ez0wf[Q|uaij ڻ/%@{iD s Z4\ uVqS#,Ա EZ&eU%Q*D>{7>A?]l#X"3VXafRC;ys[Ty+DY!7}i&o&i|FWQM.$Ė(Cz>{# ~Ei_HXhqpsq;xIi2$M~'^,HpfbQ.h@$JO`@Ccxc%ckbaf :"n;Ar%4~ejfʼnvdw} *^n\4'N3l+{*|p(7VaB9oYK#`prɑJ\ohtEQW2ECJa׬񣧢dDg`Wi! 4֥ ]FoTp=t"iC*%OR`"le/ř*Ha+"nP8hBA=bB`քB&vnwW,2 LD @WR24VQ@OVp{vRGρZNʉb HYi^RR'6`cܴ@W5CCNȚ#:$3(MiAf}`:SȆT9Wu727K)d~vFVf)d}* 0d=CB)@vL7NtFFq/beލMjx=eQb nE6"H6a5!.Hʗ0-;]wDJsB,vڷdR*G}H<[|8[42lSBe4pVQ^] O`,rEʼu0cXf=}&É^gnJKn9BqyA~Q"4(CЅ0 N>6Wr' PUh6 +4GPLXXFM%o(Fw\*H*w)8e W=|8Q/ܗ6u6UXڰh/ܪ~yza9M=Hrp]jkqI'hL)9vOr"+,Sd1 +H/hjSh؆H2VQ&\ +ɧ2h#Wd|9;%L=@tdUWˋ [[,H=獦9Q)8<Іvs J `rUGH5znUu5[|8Ƹ,m! ++&u|9\Cz Fu;)n b`K{!kQdj 4*rև͊V5'DQkd]Ym원V3P(h'Ea-%HN\ -T +P˄LrOd/Ћ&akV(e]0JJBx0#$4AS C~rc`>HW7]͐j_8x +`QF/' +v @G%yu8!1 2H1澙~ ,lhPEWǞ-JYHuoBgy#5g<>*ɨnDaԐ,܃<:BtZ{!pJ a +)zuf˨܍S{NįZYJ72P&EĤx ޤMd#WL#T| ԅt& w)Q.6􌭌VG:%'tN2kQ XW_ی8^~"gٕy!fv;1!Ztfr(@!Ly>HXlo"/'z˒\S{YRr`H͢3])}^/0#hkh€Fx xM eM5*+ =Y -`%}JCaMiPQ_eukB|qyΉ8uS'{_ c+aFh\@ ЙbiuܓFMi%b&,$Gsm5 /LWJr?a'iZx hMritڏn.<%\+jymCn/Sg(6U;5Gqߐ 2[p)"$mVU&! ]Y>amUb+I`V6C\N$)zI R:[ Gfg48.m{`l!?(s.Lp(;ҙ gӜc!#q7WdE٬=2+CH*U(qg$3eτ@"L,E$U<g LU\ڋpՔmίۣ`0f2P R +&{R(7փu.aՑ*U +`آ +@3Ʊ9 4´$#NF#!ZT!?Ϝ>'m͒"{ʲs:KpWD ,4ɐPqzK;;b!;|ǺP- :g[p99(4PwQU!.̿j8-G6̮22g|&'Dơ-B~C ٺu'Ca+SG[jT6X#DAցw#bhRʩV:}.csoHx2̆'ڈTu%OuJh]*hew=NɋĭGOϠ.։Y'n?h=02%> bhϜčr&iĔuOo @|lGtp (U׀S +%4ti#E+āG=r>B8;Gsq.Ib6"EvJ*JshI >ˊ|AZԄQ)x z7ګ@$6L0TYCcuTSO603s{h+– jӼ8=BAxɵlGH۱r{3 v#2#jj‚-ѽV9 *EUj26\ĀFMPb88Jmj1>@ib{֫J::1 J3ŶTPCmqOyj* ?R ޙ,} ‰/(`ֆ]8䴽奦K VR@M3E[Ό@b|w(Jwm$.Ƶ<wJOG5..x5YXȕ9CMx>(,[l* +  !3/aJdA(HxÔȂ즲q >Qי[pd 흯ϊ!)p:H|Xo26,5JP:)iZD.[sUsF,2SOclClJgALBXD-]@.l$r tgӓ9ciZrcdra4D}u.ιqhQ6y7rz;hȞȒFшkq'(J+[Υ RY}/,Dg{*6!z Eω4]b3ąPpϑ!GgpTzĵh4EULѶ:7wl2M*Jg S I#+ASحBי;qy}5AZһ 8_OK΁ IlU݌ ;6[K"N)'eW沞OlJ==$YnfXEP]$emvi=cK7S|Z r~"Bw9Xm:Er{ɸYSr}*6p;ӑvnD!qnr%Tz!Ic,Q8-isb?Ofލv%D, *qP.Ĺ#T_dJqAfR`v28\;ghj,a|S ˸/+7w.n;udmLJ{r*(rȈ#a5n,APxTzrh5!}"=Be u ՅpB2\Gf5KdAa\WLMAϪNҐsĠ8tr*ĥ䳲S~;@,Ń kfuW9xTS(y(tD/ +1Un@3.VVLyӛ/glt"$Sʊ.]89lwg9Sߏ^( QDVqP,C>9yjF +CTuE25&}=[4ª̕nZذqWjb!@(Q JM!ĠYˁA'}W0'E$h*+ԋЈԽ c\%o\G$]4BlCfQ* BlŎ|>2]!nH>Nݒ@ RESB3yMu( p PgnI)ܩo@zGC!Ex,WUɻSK" +C"F +T^C| )cM7 yN^ָT)`+0MVaƎKIۗ H83Ppׇ5|9VǀjJswlv; c& ߖ1o\ݍ.v=jyr3afZ8W15 =ږ|c (Pez 䓩; L&H^TJM8u9{TA9Ai~*R8-%V̻Ƕ .=E;⦲ʵ\Է+6nf4Tryw"!s[yYO(1 :h i(T [B'_VNh;3b}3݁biYXCXy[8\wqBE0kt!C{k'cc$fjXf̬ +>u" %L(mi{;c](a,2m-@@!} - S[[z(TZ1_}ȵ*X9EhllKtzf@t{"U;ʬP]g]a! B ̦1>7Gh*BG$v3؉+E^PxKit/IeCld ޵C{I,!DQN܈oS+6+x U"HlZ2 e{Kvl#Fn"TbQjQ2)^ Rx(VшG. ذZOC0s{ۆ Iȩznq#1"2zulrQڄ`{q$-iQhؚSo$d,`p10*QXɉx=$}c4z^hI;1%ԅX8sEB߂A I$Z ]aYwIT(xRpWY}M.0qO25AqϗeHR\(2r>4b#^-57^.VD*}1KI +Fa=W&o?jVRgB+NCaj%ӰƼbKWÞ[ _f`}PxIӼk+gFN 1٨Uη͠.uk6"LSK^O J5MHw<ј;CT29| LfҌz>Y+aϭôDuܓbF'?BLҩX!i\W~e\QF>r;)O+1Lp* Ҙk!"iM)(+8:Tuqs{lSf-g=CҰg_$E%0 bް;-RmEO%"!ηD_K;&ʪh%FLdHG>pRAWWa_4(C+%@XKȑteT??[4 a5%#4Vrh+UlN.rvhǕKmFb Sa{:_^8CD&;Tf/f1>n$|4ʝ|\8l!zHWa8E,59|n7~MOw"Ɍ1cjRyhWSܩ0|)XIф^wv1|wc_b3#\:9 '  7R)G4 `_`4yWB(?w 8@ !T _Td "'XǑBcbh#ȅmBUʠScE J91[156Q%KYP!ZtVk:^Gʨ~=f:6h- U+/g,}>4xTgTSaJTw\Dv+#,{>E2h#eJ/#`$i}Mz (O:}o&a9!6)ʜD4!SNh XZ+p{G`,C*jr`VʲPIWN_F^eyޖvCݮg냬V؏uQUh,\R ۟i 2a9j`ݡ,d6TKn}԰,氡KҤc\IkWF2x +-|bM.R: W3{M,ǓljU Y4@BSwS0AVOxx]X +*<1eS >#뢖ք\4QB *,=#I+BW f6/xyp2/<7N3.ȁW2E$l L;v]9Lm_|l +g.osR'QO)SPXA{T:.(ETp:CnЈ]܊o;C +5&E7tGʹꮼV `K)6.\YH[*aNYɋZiԗMI|7N'bP@W%T*ӶK~ +C9@c,D ʠsIĢ)\ȥۼK0-pTFvӆn9=;Oz;.vmU缎2[ zj,|'.ux!?mS7P~ DNhae^$&:Ol%īWGK*> |t.THerMI:Of+Ө;EI{+d{P@㉺`%Z-p״T8{mF5I_3p aT4M3\<^bDu=!-$ڊ>&sYzPq+Sm31bD V.ݶvx#nAk͕h 4ռ( +\"EqScDpȹ]eBonh=cЎ4Y$QsHA+h*jR'ej_g$],T,~:DR )F*5hYӬ8,(pQI!7M',f+ +$MCmFVggئ\$w❆IR iܠaސ,&uJ>Y8`Z'I& Ot1BuzOu.R_:Vc28zWŗM/ 5E,8u]D}C-*m!KMZO`6\ESxwL +& +(ЈUc|NPLHj]5d +d 38=ͮm}QtQ+y"EoH3oy +gĞ\ eu&-J+{whq7)*d7vTӫ.߉k{y\NN00WCTi z*v* ̰1U%3r +t\I#Od)\xwH(@K81)QbOVy]QķC!Q^LT:^2 Ta{'IĦtpgvi) L*P ċOUC}暈VWBv%(Hr)'Hk[>U[ \ML +ݏIw]ҹ$ ɒ;vW5&%3ǐ ڒ6NF HUYMȈmM0ipli] A$,\̇EL!jJ?6}W=jb1kq!ѩ C!ڳ6b8]FI†ĉ;xqD $:bRnNf/g6:P2r-ݔ/V(Tr1<g&$tbcN!pbL0]3~gTX&%0u$-mr +}(κTnՑ4Rf$Y29 3zY-ӕo\\hjTY29)`Q~%i֯&dB WB2ȐʓEq8ʠu3$cq+=JJ*;wWW>epn*,Nہ0 )9Sy/=g|Z;VN,A G[l%O$w*eiXy<°% H \!SYjj8SЧ`"9r-N&>V-/U;jVL›gՅXl[1 zVS1acxuK(.m^2ZRD43vg'Äj~̰,:з H hbA_S7GDg҈L"w]+WfoWHj`}9"5%ǻ]D"械󓸛RzoyūCN]_Į%X0ʋӟ6p +j\Ox7Q5 Qp{WNo'<z۲K6@n +:\;hµLD owXs qj@L`*9֓hrC*Z]O"E/TR973f>iGH.'n>"fV?ތ.3B@ݜNi;]j2 W ܴ9wK5|~5DWi`Nk+@TUNsO܂U85 ՙM`a%IenAfUGc%&B˨hUKN49LD)"AuȋMT2_C/V7rmsî*R}N +c:߮%" >w[$i@B|{Mxn)7{0kLOʪ.k uƗAIQ;P vަv|j wpOHr"Lwr;/I< B> D/E#2=dt;fF*xvDI(XQ0N9i>s^=3'\XbrYb !w[#!^AEebօdBq53@jS3૮eAK+ʴ5cJi6fPc‡ UJ l`.Y#ӡe/\F,X)m YȔujO +lF꣟ap#)ϔn5! !#Mk=qvwNtȧyN%#9Hjj8U6;808zA&%Ǎ#Luv&s.@:5ݬ(!a5PMva[6E[9`QW܏ K4"؏nPD>ٔswմ&9$OnZؒV* (gӆ J9(đ:$l,S+o`U8h. uIsLsh3IZPsTT7֬;sKZ"Cؚesx=Az +Ueedy1(R{9A{idXƲ~b̥Np?*tItN<{uzK`=؀T]$){l#c Q$]kLxK Y%4edj@#uFtse#"";?s>* _.U +4!q䡘ʊ'$Ra WU8|*xƻu授,L( pI*&e26"8Ы)"i2ci%9y\=;իZN;&k9IFLf0\e3;īO`HS2bfxU + -'DҢ.{lQXIHzWvqA.A6R[s>% ;Bq]A8Lә39 W B1]lG c҈Q4vD,O6mTA'JUGj5x[c|5xR5|fblz[jUbByjpQJzEA$/Ue3c,t`j-BLFY$<ɗRH8 dZɵY%N]s#9:s]d6#D4j(`APWJQX7RRJ*_1;PE +ãSPZ刑7+5= ޖY\7ۥpXod&q(#m Džu?\I!MIaWyPD9A/ :qۦ!jtɜ.FoMo>Tm|}8>Tґ@YP % +'LtFWK`1C*/bGd"n7'Ffe,dQ ?EH(Qlx]6tZP 82_Ks +UP-,̳*24a +=z@\n:H5t29⤕@Lcu#p!1)1Rq;,~mrN7*_G~mZ51xp3RGb-BTĢW}ȪtzP%ߦ7^ ke8ZЋmJz1& {MQT;'JV|pqKz/!b.fl2O7Z*JLnj1k&Q˴g +Upȋ|"[B?ᐊ˪SEw}@"qYз}hvȘnm&/>UtJGr<|GoCRz/oJbߔ|&2N"]2+]p:Dlx#AκDe17JWqT%tL"2*@SEB~,FS#IANiu.P:Q&1tMnPdv|[߆MDr$65 Z'!3 n<{p̹Up. zsKv:K;].U?״]T2[@8VJE*"οoL6*T}==SvN#fc'evKj9PtKżԷK<7]eܠ$? QU,qBQʎE>sΩ*aVRU- 9N mdVm!S %Fdqw ڧ'T+9M$k⯅3WdJo +2YfKXM.*6Eo#X&?c߬:1ʋ.\!v=PYF &p8ReP*Gi aSZBmu:VmF~VcUdm%SJXݗ8ExMTՙ.$TuvF8R6Tm߫F)sLcD:zMD٦ W&Դ!TBR֊ш.Y ʅM5^ ̹E V3Q' ტ(U|M1IA=8]0 y*^Tm8{ TCtFWgI6I9_*!R@jT n)$kSp>K 6|'݉l.`RLJsҔ4Wd.wW^A'1s*'bcr\VHh2yT2&#ȷ0¿LҦS Q bfr)F&y x~%&RaC(3=۩BqƨD m\@ uy)sh!eF(5OT9Dx7h +~iFs~0TĭP4ʧ/yQҰ2 Tn +߶,X׫o.X6匐@PUķ +hOb)c$ڴ9X`/^pS/K`pWR*DtTfBL/"txva/qVJHKfYeW!p4jˀx85f<'"HK[~`ú6 hd1^6["`3SJxfudD1߹i {nEC nPƩ!Ob@gl tg#Sb%h cNf6 S:cbg@4l4eo{VY6 _UR(B,}})8+cK$ ɭA`ΈH@dAeAR 3" j'2OU3\L7yv(pI3j? }s.cZc޺fx٬ؕ8e*ԥZe-gX%">XB&$ ghvTCԥq}Еs* ?fm׻ȅٳ5 }Y쪹-6A4>9Ӫ:h ˣ( ]*f7U:g~×e<wCdB&>Q{!%v8+0;tzZV?z~>/9|X8>;yڽx~|xwmZ;:vﻭw}umc5j;{[ƾƸ .Kk8rHڵMϮ>ѳ?xr/~2(KV^;eƓK>u}k}_nuͻ??xq6puB}r>!? Σҁ߹5LurWמnf͵+|mמi54k_] űx퓃ÝEnm}eQ.wpvhqsrO;UtxwVM=!Fb_24TUGQ/LJۛkk2~ 78glnkgF|OÃ`?gn1+,n8C؊gh!v̕7#;^Akeُw.fhq{w X|VW~{1FXXyvb s9ŷڨ7/WU7_o8Eś-7^/n7M7K|u54c-?~K\߶F`]GS~p=vܸv9\W!7-Uʐ ]V>upx_z,n[%Qw빷4͝oNN{EDϾ>rGr[=G?X<+C`F{/كQ"߱쭭' Go*ǕծxR g?Ls5:*kl{LKuEAXc;Z K[wՃ0>͟7hM&캜l/'28fN{whyrx#mݕ>-OݺٿY>s? zѣ)z?^.%Fx^)Q(~spzfyD*sS~$_/ƓsյT=};g߯}uϿ~h}z?9Vy?z3zvo/AMÇ{?ckŶOsQ{G䨾X^?_.gyE|Hq5ɷw/Zrvg;OvsyV8WOqi̗\(?v3~ ?xS<=>|td]/+5|g_W({t7X9+j UK[W UTD7~h&?j3@a/[^tq]A>jߟsCWӾ{#z~\rl:;jL>?ڛ.=Rtib5}34|66BP|cN_;}~]n/o/ڕϞ.yu.%7?k-vf?Y{!_=^,f,~k[l6Kp1/WKDCV^ջl56}\P}04vSV1vsly~XpǞCvL[WB/<~ mW<>YecGM~_5_½V|5-uKuu~x`y{-w<-7:|+-uE*]=+t|DQRqI7hת14Kiu7scV6ioT,wnAzqսMIMڦj:ɴhV޸|M=[iϥ[Z +qNr}Xg?0?Sf]sH?N?ZW^>8w['o}ۂ9%~/yV Ey<]_Y݃qg,NϷyw17?(bwku2K7 +o|b}}vrz3-Cs;87~n:'3dowG R,v?y3,7wIӞW<|?}g>9,{<<<uGwٯwG?5b?=b~_n +֏0៞K;<מ}ahhqv7N .[ǧwֶᚍ9|bglޙͫ 77+˵u#R.Ƨ?^ gf8Φ60hJ=[i}wl."a N.N}urً'mKeVȟ.wvz4N}cɗ:om[j/kUSZͧ̾{G'<?9t>gb<(X;{Qk7|ۧvq3|8H8fZn"꾽c3|dwG{o޿u :>Ytwv)|{fRDTW依]xv(;ootg}%R}ötؖ.?uGЀk[;nYQaJWꯞ{!)+q=Kmb%[~y_^i99NoK.6?{Qt^ \=,K +!?_,\=<~|t]Hx{hou>8yڎNw&%NGzciɋguu՝saw/zս%||N>Ş}z㭙vIeimOJǓ'[_dąݠəwee;AW{Gۇq`dGxx\up/tvSiGn.U|rյG?* ;k{w'5s$mo2[4^lt/%?mQYPX/_XprwuGCRiz3fy~t;xy<~O.>ze?nxt|=O@ϼxv>zOo,\zf'.6w'zK?;=No=M":#F M] +ODW=k(esidfvj4%ڇܟCMqǧ)0\F}NQA*E}}x]tvG;Yg*TYSzu|Ty5=[z,$st>^V}I~\,kۓ?Giy>*.<8S;[u\\q+J[QEω]u}\ +J9ufIf$ا+DgBeRmړ<|t?U2N=tʏW3,t?p/o7_s2QY{ڭQ?~8XJ/gY܌sDs\G(.MbK>tuقT^eOs̵N|{}_wk?{7>{7_W_yeswɯO~w};o+8>ܮ>S6KEqr[_/~}?_86>ƛwu?x?ܽqY<}[Wܳq|`wox7_m4_~s>y;_+xᅣu?x꧗?un\W78ڭsvʻ;p|{g}Ywc7m<9|}Q./.w^L>?_oޯ6o~}K[;gco}{;/o^6~ׯ|;߾/=ys{{[k}Gŵx_w6~+ǯ//7Ri7_߼ݮvy^ڷoy[|:\ʢ.vݼ5~÷x776߻g_ +ooe_ΗoT_}n>˗gToo|j ݢo]ۺ_1(2>xk/}g?PoѪ?V;⵷߾zuoy޺73f8޽{m4[עa[ƭpo-Ni/oo~|n=_oom'OnܸуݿWo<hAhwO7o޻sM{Gy|goq >9?7wnV'ZG[^{ӭno~|n~[YJW1ԃDm/v~;;Qs\[m^^ˋ;1}yrru̗%׎ΕG\Y\_f"GΕ^|~^3ly|^|I'쾨>[?.,÷6ÝFapkh:G]ջ͇;{έE~~tm>Z[׾>7;{<_yyG2쫗7>|֣W~׎=|"x}|_}\߹!￾yO~}}^uJ^c7r{啙n͗e[f,ݾ_?nbC_9\<8ס7;y4Fy_7qwW._:ʃ/o_想 68~e}A<^*?Fl;7OF+=mݘ7 r1bEfo+7쭽Ӣ#?0 b\td|23V峾A4\r#Ǜfg kxW{O|%gカבu99KfΒ%9眣lw~+IeiqVIP(T.`,V+C=p $=*WǓ<ĚRt< OLeoar|5(d[@0͝.2vC-=FNO'zGNP=,ߎMq-0-C:Z^\@uH XwA͂Wk^m Ycb}笀A"]۞Yqݱ)c#Z 3=$bau~>Row6W{U׋9-+wo +j%dI[t,bW.G*_lOm}s}O:Jq{k~K9~ېC=(lo$>{}_7ho{Fw4)2_=s aJܻ ˁ7?KE㷨=u8(S9*k<>_]Yĺ{'qS\(^ŋ-CVQX4гetqZ\?O> m7^F&z#0SgioSW#/wxz*&MkV~F޳lhdPo欣ŅGTZ(RC uF]tuͬ+r!/zJW O&ѷYx]#$^~?6'˫SϠZ.6=<_˹nt^MO?ϵ=. @Zd|-ՇOÚ[쌥QR(lw7rK}CeaagF]./W +{וO`8ėz'g]1`(cֹ +/جyu2u쯖N\ 6oYk巔߹On<4wgꭼ/Ѝo +B7%zx^\yA,m.K7H>QWmsl$^g/'@M&J{5V\>Z.~J| ݺ$GF:,m7loU?z~R4屴8;P%õw2|w T!;V0>:<☵CR jQ'k|e~z/)= R?fr̭ne1`-EEۋRNs|  +Vp?$1LJ0>lɴeC/&i~{凶{A*DZWJf>?Y_9c3+'+:/liȀȄ= ֱ3s *֗FUPZ@ߔo顠~/ău1>{Oi|S^~a ,Zd*egg)|-9=u68HUWbu9-D}ϰ7, :43,kkttpR}m_>ϰB['U_ ԅre})ODmkplPW8,=/T?Gxes輜'bqWoH:+'7'1]~,*L >Gsqf)N~~*NyviqcϜfL>h>&] :T9uoBCWer}4@mO"!WjKsl HuM4^:#;$冲ONJg$sk]跊e{wnK/{$}W^-:C$< Dw$X`GJDKf~ju*esۤW,-:eCA?t1P+}%ko0qGàBQׅ!#h/Le~M ˱o23l> ;pq62s0?ڝEWK[(Zte] zP$4Ps6OzFٗ b|fps4_Ok3V^sI-/>%0rgV!l +f _ۭW7l [e[bm/[ߏ^z>swk鄢6VqP( ө-M|YNw`z?er>W޽|f#6:uen>*-Ecq)OLX,i D24[-8f/8J*F:G\4:&4౼਄y>D&_n}>U- wqutg C:*!i!/߾fPrmgUgtCMS&\ȴBzsToʺ=ݼ_?x(r-^<>cF ׵jc[jt oA|c=\O\w%ݟ$IUA3@pSx7?}.nO|0up'߿{K#h$~ituOݿ/*m+Q׷&UB{5`B=#yK6˨xׯI3 + 'DŽ Zl[+EfFs~w]k "jM}CkOsfPiT%~!GZuoD H֤fr=lvĈO;,׎x1T.9 #˫ސh>Tys4 zqj kzBLiocctFE{YpOqۓMJpmX2mQׯ?5=eqo˲ʹ=R]) <-5oZcISn<.iMXe-2an;~߬qLZXXCq4,89ϼh6?=Y/M^mzi> *qrs.Su7֙qx`,t2a zx"ȕnF']l?alּn&ѱi?SOt.,\#{lX=#LOnb=c/=#'n=NzrʺL3|gad־itcnjV/:cXo'Kԟ*KUIj37ύU{Y>W>NrD)T*3[u`K0zBntYISkn?w6GPƗ cWL>os]| *w*ެ[;Wswlvg.֦v k~.ǥ0ԳvO_j|4ԚRP"PM5@e}/eWXsW;IP ۯOOwﱺu{adm-l*&LPs'Pc*P{+vΛ(Jt$Az2Tt&k;PwD]]ʂ8)#q]]n B=zH}uP LtOxk~e~z&JoTph>\I@r8Z>[>?zPY4Nؼ$@=s\<ԍϷM#P_.\IzVb:ޞ1q78y2`18#̍Esv}:"0S}"Kj9% ? |qP\)B H;*ZJO<)%v>ޏ՞lHV//>&ooIOFA@49؂7yj9|4nO(!':U닡:_(h'O7SQY}7K ;KKЗvS[F߬ǽ 8^i쥖pf'YirGZ Wv短ϥħ/Zqio/ًiLSf͘=JAZ=gO˅jmWMAZGLxj.)Qo֓Sc{sB~a{.]-/NF[PN$+xOiV.M0O~a.VcBOm9dϷwjC3[9 +i}}{P`gL;Cd$>bTU>⽵^ȘPTvi74볂:6pV~k G>~IIf?G?.Yw1.]Z) < _X; ԵNoSqV,NZR,! +"(_?jdFwTJXOh!cč,?f< 4{{S>&m܀ו0}Az^.h:ӱ@6MWH *U{ fFbfo9T'e crL;ʅ^%Um28?b +G ,Tr0׎G]gn%YznehLɊ|BzfXF?]hj} Uha ]In [X:0?X,{;N7rZfPkiN"-Zggse۟?j h)`LuceV +A $?HEIlb9IFYNEľoE"h$W#ZH䚓j]؟QA4%Kx +0zqsĿy="\ +r6xP0),@#ʷ-onW32$/mVI^Z.7hCr2)lQlm8^]tVgCQ낰-hlMCB+ ۑA7-z0A@h&lɯXr fnΔWm|>[sV 4As\<wnsuKQJ8i8G&MJivv0;N2hk7˛dQc[5KqHbL_ZHT"iQ6?᳡Ϫ^h yG~Ft\({넓[GaH͹v^|ofu~%} ǖVuXXCGh^|؆Xb^&nBGYcpmѐCIu+g(5ڑ\R%,!v> ,f7NAZ&M[Md2т30 %c/2DYCMYTR-s bj!? ]b`rj\_ƦefMv^-1Sw eյ8SivkzGB#V2s+zKǩ,8X̔iXɢayBZl⺀5c \Г{߾?( +*q|]⠙b+RrZ>;PΗkK[,K +.SΗ^V(K +.QΗ^וRR9_z-_.v/.Yv9_ 6kּ/R5TS2"I4 .5VUZN-E'Bfi)|Ha)gTM6R,EiA3K$R[zW +`"38bMvO>ت +ѕ6=MK{N9_j맥bҺ9,6k۽uKo5+ '\JM ߮Y%}"fTd#6=ZdJ)G(Bz 'xi!f6u]i238ѓS5[*z*5}x]Ok558 +}b:XThMձ5M3[)iZq%%ᤫ1C3}IÚM^^5ZSԚQD$%ufV$ƴ[soialHkZ}u7Z8NZweSs\ōvw|{>:x> \ `(lb>,e:hȄCqVUT2Oщ(k'՗;Ӫ")IghJxkMÜn(!.dz0D˭bl)@5k`@eY?/zrֽ_zrk䊿?걉KB tТ&4}aixJRZ +cj߼-MYЕ ޒkUi8BDWT8c (abq20yu,]1⢾qx@|e\q/*{[!ޜ}׭Nʵ’P.,'Vq'^ÃpEX\QP4ٞ +Ynvs=@5G{>㣴DJeZ7~*b @VM}vѲ;qͿ1R;Vv#PN%Y}}_YRiZ[bDkǻ$&n%C-o.&b]L +aiiʉ_'37t?^52uil0]bT ߌFel9C~oX{ E?:S\!6`I+Øa5˃m<"MN^wJW 0|Qi;-elR)Nl!wz鹃٩z3lߔ2\unWȌfwDOkRb5>ڬ5q>V m¾ٴYEl7Uu3/΍Na_1r#+몫a&(n/.S+싫 :SW՗5k¾8/gv/n}q*O(K3}qU}] GQ8Ǿꯅ}qj+#}qkjfa_70/Qla_ Hk/UCC}CZ˅}^+Kta_\¾`K4}q"bvY`C}M' +P;U״3}qU})#Ksu1ΜJӬψɶHR9ڸůaL?&-~)*G6T : (mmdPiDWB9vҰ"chTʰ±?Fj1f$3TS?YM9u;b2g]yXKgter_" Ǿu%e˔s$~_{xٕ嚝TV;!/4ھKT5/[sgtsjBPڱպ:Γyr3#Y49Lkf86RNwg˒A 03C2;LJrc Ij@P0B#}Cђ&8뮸SF[o/ Zi5AϜjE]5O˜jEeL"OϤfFؙB#)k1s2vܸ^c؊KC}WlsJ{-Sojjo:rﳠ4of܋fʹ0S Kg +c[W/Zͤuf)![\`˽CxmC>BG8@t ߢo3֥gKEgP,ُJ(9۾˫L1&_9OȽɪMre(Ľl:q_`1޿pmTꔿוv/.k޿Ƿj߳hfޚ+?լվ f&;iQ;mrpG)0]W2ZkM;JύR +Tr-nl>*^o؏M#˻ /c-UBkYjlpwU_ZL}}{NgٞY +fޣmϣ +pdmMc<꒕mBc}<:`:RwO>Vk2LfvܹSECuljjAn2fN6QԎ niL˱nftB-^ԅa`inlwoj̢԰rtVӆf{KBG8s7vPCyx+ +_M7pzP7\XT#:R&"Z}yR=Y"Ț'NWY;O:|Hn/(#yP5rC`E=Rt92U7̯}aa(=LN}T߿.s17ԯ/Z#}Yu\m:{ГƦyR\-*eܩծڷswa|7[~D0^D7}Ba'שKp ۿ m^8|1:h?6zOtrzwp7^ jo]j[Wr՞Oc q\!S (;+mDZսĬ5IzW./Y w?+wSe|8_g_ +KV ut+V^lV_Ox^&Xh ќ^(%y\cJF~f~Kf3~("Y3 X;QB2Q)k~+K7Ց0|6vK`g ?>\x/5loeAgƝF~TH~u)W@%.v1 w{\roOckAdE67 Osܶv{Et>gSH΀m@627?%L!|S|?nJ=uCmeZF*ӑNAi6[4'Ca|rS^/<]O۞Ciξ0J >8TH:uj=Ld_+,Q+7j+X7͏aq}8ZaRYA!AOMy2&HxtL'z~GQۗ/gP~#jl?'h >stream +AŔH ѻV5h"Pt88hm]Ɉ$08c;A iQ|EpJCm[XG{W"GmJtSHۺ9#5XߓH9Cqs[?SJp@Ő]Ǥcp K}H8/mPnS!O!DCfDl4Ҡȍӣ-ĺ2t vSF7"炒eRs˪T p +Jbֳ:s䊤K+jf̄|\pJCvM^33Y\us {#w:s29)\٨K|B@p$%GrGBJsp$,Z R'#E+˼"YDSIM%Ak)T +*P[KT:d-%J cRlZJ4<_ggDSM% iozn(Xaw 6bfvG9 H5+`yAiKLZ/ ۀ#Z)w:{_9%ހzy-4Dڏ(I#b.+pnxT$zW'Q:;V^2?敞ލ +]RWI/'c1i/S*X`kIBa-')`sȟ>*҈6C_8~iLmC?Y+^3dx.eNȴ'~\fgf PWϴly@z(x"[vG-zqa<$B_\Cn>c/=Q>C?_JGV7("UR6i}6' W?~GMl/`{c &㵜Ä&'އ+e9^lض/. }e\24s9Ȋ:^inG#՗HK$DB8^I";6_f3"Cj^7ofmy:UfeKձ؞6i+gNpx೘E&sRmY׊Ӥf"dm̮Qō8Ôf4'Ӽx o>mpt[ػ1BN7s]:v{Ah+ 7?`4F@_>ȀCdZXr9m0 +H{D]ʶ1髫wu3.hUnz``P{=T%6\٥9Zi#Nj6u؍v,x^;Iŝm9IE-gbDVΞtV}\OZ!q`x]qD]k}i"I L!e"b=1>T0{GeuSJ7>ȉc#즑+r|3(RfΞS_Y翫3HeE/]L_ym!fp[:S쾾^زRA&]|I*؊׽+ǼWhB}D(M-v"CN]C/oW0sRϸeKA[4RW_̟z_7Dx,~_opK W/;W4}D*"XTuOEs &]#j'b^OkֺQnUs5Ӥ&ZH添ՑwrP)=<}_=mGk˥n%==LcR.uz4NK/ҽXkzvݧJ]=3oyuC;kŶ__cxzP{MUn%e4K;jFuJw럜WT;ghchnEnix}[}.Rvlʫ46gy—PbO.$$Y@B 1h*yh(m.f34]\hyC0rrUˠAjw\n]yM90\5O6' +ՈFO,GTh6O:slm2iXA-;mZ;5ojΈPj1@Z:t) ͫ#yM?$6Q!aƠauK`, n0p4dbhJ%0| X6= 4'4Aē3QuK|hKub:c[6cC]$UfxBC#|.xO:?ۡ=Y!4.b4V_aDy=aDc:OȲiw 9"ެnln0A4Vbߪ-Ǧز:L*FHC%THU7y +HZHB` s0 )Bm=K*^sho;j[-U1hlB')ܓ-P6fL.Abw. 6CE疊:Ҋf!f9D@t/6 B-bIisYuC(JKBl>]}%n^A4gB\*2z@"MWqR kv  +}B$-o%$8L v]WHa$k`iklvOЅS4Kl_j,釶 ѩBP4t!&Ah@PҡQ!sEuua`@yPd +Rge`&!]JUatH33aU`* dn6,6vH)L2lW4 m)If- Iʭ`ۖ9悘70v@CW "AY ԙ,EuV-Aq M"V&BEZh$ +B5buB?b./ :&9` - љ}h&#VͲc"i7jsMSE];KfhD!w7αRFHFrqoX̣F9Z}V ڠV]3s\Y$ @0E+P=B5 ;3@0?]V>xs,!TO$3kf)ZLэS Ee_B$kD <(lWحgڒAET9b2ʚ$-" фcg%+6ЪYca}]CfH с:7bq:|+zcOwU@-DB+&,A^ÓƊ"݂>KX`Ũ0p3*a2܆2q*F0NS,^(O`ĦTtfedGT%:qEHH2Gr)O3cdHjS"d`MP᠔ź PDԆdf~,%?(VM 9ֺfYH/r0s%֍w vG(Ma?:Lc"+ݍ3oFciVxk (pن\h BFP}a vEA/e +A% ԀKO1ߐQ5'y>c==b +=NG8LjI@Sb$ԳOTw`f2Bd +k$LMt& +a -.MaƨpjȠ KEFaI[ѐt]:r@["sLUě4 fML%jPWu!D4 Z +! 'ffC OBc2"W8uv І%\| iymtGCKX>,`ir:!WaGi+#?0\鑆*.IMD2\i*]6A 62ۋ3Y"ľaԹEu+B2AW|h@s;2DB(~sE`.#0N>MH|v01wͲg0%,gfADhy3W*GmR9r2b@AgCȀbl\B* 5sBq;uxūҙojl40`X̛\u{=, ELA ,Dh!Y.$G9+ӅK@DQ-D˄%8Navv:4bpNf,gucutrjЧ" +x7 X>:D~(B%뛸!|݄I+mcNNHro!IT9pp"ĊV cP 31[CK# o$%uC"i[auߒ2nq]iQ㷄(>N(T?Ǯ7Zq&KDp7/O*6M" +ҝ#YHTbє+W&4$`X9c䛐jpȺ %9 1_4}9D-6hf.Uv4*~*˄Jξx}~v}UdQKC1SR^)DDĺdI 5rV"hn@hl#܀c1M:mRGzIc㷀Aoh> +~Na gv=N$lδ$a)qhQ t+FŴ}-*{9!B%u"sȕJH 8.;k?"N.iA[gs;7Uo6 Qr䶮 7tUĉ]dNG8tl@+[ މ4Se#)! +,[U-*HFvDR*Q&$9hn&B|>1"7&)|&NӠ)0cP@:iZ$%ԐOr!.\C,6 +9 +"gPS?iHڇ."G*z*)ds6(Fd upH@ -h[bIi-Xj$N#s&=.~W">5%8l:DTkqNB:< &9IbB<0TLQ1Ǥ6^`Xp OE_O\tNl9921 ULζ!b -.4M8 R?HoM A +QDBh &p+2 W \[h-1"k(qTuC:0HCU.@R28 +4*l>=nHF܈ngetA#\,h!mdzf +YIt[QA@:<'oIHgpɼN$4"--^hB0&'"O` +bCMp`gFQ x!bIGGNCDDEth-E%#[p6dOw w$1n&qcUc2dfj0;<1 AW3مFJ"3,UE6'Y:Rw2+KC {iUIS`lnTcvSc}"mNvG#]F_Jx@^:#kbz~MP:^(,/JֺwQ{Cι,UQ#Q1TGM*#шI 66 +)49HGaw\dk;MPA3T]1R5.܈ 4e9Z/`S*QEҿc ]2GvdK[>ԄQ܋|HN&L*ցe@:77hJ5aqrn Kz|6ҿ$(H<56D1H$H1LE`*(h +n%nLЀ@:T:bt+D "'nѕ>vm(w!Cn*rd]NcŒF:.bGO8E0fg^qEE71HOF 87@E8ǸBǥ­o>SN4dE>K<_n4#W4"[P5F#S)utdwԓ…<4$vqyA`q5[24+ $KX87"ЊqɁEZ 30)Y"N6QK!VZt^XC8Jj.iɢ2 ?1@AI)$dω\ եa6vh BD 8*5Ym0U2G04UÊDLi a5tVUyg 0Zr1 L9S-26CTi8q]i:܂*r,:1.qO C0OHXq7b]1 +Ϊ2p[Ų"D ^-1sO +]ؓ&~Ӕ@@\ř&d-I4Uq#'6wdC`Hh]1){^ h$k>QB1oԎ(w5߹-_sY?iT)PcG+r {TF6:Pm- O@qxXrO,8!"+y,6څтU hتEZ#+bFGUg"6cnV2uU6rµ=!'Zĩ|ó%j9YH+0Ff\788 +EY4"Ϊ#k m5Kh)~[t`"k`4HE; V C#S*$u +>nt b0,$rWv,۠pqU46kQ|2=d֯bchn:HOx#\EJ7X#h$H*9e] Ax YWUXcUgY HРky) +NgwCqoс'D#{rW%V|({[!jY#G#P̙Y ~~VcxXP/E$c&F^#0JDle^~t$NTh߂YROdx\-Ԗ]NH`jrO|ZL(kFN^C7 vf9Y4r+8 EDrT]4;A5(NnS>oig|, cÇFYZ`8>z!"GMpN =,./,E0p 0 +~+ZBmT.?ia *149 ҅ v<P s -S%}ɍ(dQ[@91\tl4t6-*ں@^#ƅoIPHr*:NkӮ.sliwd1=Ր 64>Qr>FK&#)qb* iFHM0DĂ77pJ8DxH[j^)P\:Bq̍$T92]^9G`6 d]ٜK+u"p1IDf؜dԩLn4hp9rc&4:N Jx@nht@:\qcK-,擑o9bHT +nM@Īp<id%Y3Wz5y?v>zliK2P65TEh!8iVd7Nr [bTLO4SRQ3n<.8Yǔ,Rh+6c2D`NC l@_]q +ץ$@R1_2r+RQMQ̷@X8AS`6%aWqj\&8 *C.ٺ8+Y.\rldd+ښܮ!_wd' G4C +L/N8q w1#Rو*4S͎<~"ဌlfST_aZ)mag1ea>#*1f1f }Ik:5nxElȘDy:q~?0YSr +mM% !%&S6g`)%X:9>:uaCMq'V4pPWudJ.QsP,KaB9g_gCguMpYӒ8Oج(t<0 _h תK>#=pCM'#Bd53ײ!@d`q>OY(EHEQB\>5-:?8\C*؟a a56t P Iwkˑ\ pت[siF#b$hzy$@w֊̪%{Yb- \KVü48؅u\s=wة]sT4.fI*KW`]i<#壋q|/e֪ eh,S/J\f*Ipr;Ouii>eS+9+mJέ*-"dH, +DdʒᇎmevZhs@0U /ڑ0%KPT2H*kXƒ( R=*i@t)&iakDь9Z1"mAtGY?lB=jb+b"%i'p^k ު԰+QWGUMA`Y K~dS8U$̉sdCK-TLK5ܴ=HMZtK~8ͤsx֫K-k*B-7ݵT0hjmo[@Jb$,DY=:e +VآBŒx@ +sQ/#/.;~rΌY1b#| uLqaLA"CF)M$~KK)BIowDdWGH%~ J)[r70G.}+BQ3aU:+ÅR"+fPc6̕)$ppI}@[=H+BSJSq2K@4%mUK2TAb}Vq5! 4צ&=t+گcSG Dp%σXTn–1*j83$gugߵ(@[ dɲ|T>(Jh)zAҾp$&_R($,,gr$8;T[EgCnnqe F|Q<@mW7O`ȯS_9[]bel@ChQѾ"X8G'XpT5Z4~%:W<愞ֻ:F@&&5-1bR]eJCWQ`>q +c w5 +-F(dtD_"qFsY2*od{Q,-%'ʹNmh^lJ=^4Ze7T揢tTX8IcZ D +q Ł_ js](k< CDJ!U%T[Ťi@B8' xLΝD@]z(E; nX׊AL'0X@ &7.)T@_AZ`a'~*lظLMQYB*C +h&/jOJF輧'hHU ,M"/Q"̯BΫb0µ nQR.,qsX>ݸJ|=<s *U=L1Gj7pH~>83ZQdi5e$ !Ue,7`i[oqJX$2%=㔉XYPǠCFzRfV_ g%ȥI~gO9 H0+o +u ] L-X;%cmph' +ý4Js8؉6Ed?o/qs62wI$SUH:mcX| ԂKXh(;'݌Q/˷WC;Iw_oVel)f"k]uu}4B*vaLp1B}E|o3@EvzܚZyGpPW^Mf㴼):.F4zc=,6jSҁ`ONZpZ\2f[)eNF&+JEuy|/K(LV"O=/z"U˒.WSI_]w7Żce1؉V>ɽߍșNPqexsIyE\u,voQBhcsj eM˛iVݫ5=pIH)'ܔrtO˛E. R[Sw|sAjcs5iPuiysժ;v]:zc05;FͳThU. rCf~Բ0JVnl3GSYݼė.ߚz;a.e?I86MˠW0.#\coF \̀UelqVbĔ* `e=j9\/+=oM-K.#@z]6至ce1lNe$W-rdFNG/ƾiH:zv̂h,re"QY%X?8dTBU%3E ]2by՚Ce r%OCճ1C~ Ş>(w\=@{䀕)W여:(=@yU˒zq`-I{oZn]EWCÛ +k;0,ú"E8&']M˛ׁieE؍IZB݀8uigVWS#OLGN}m "tMKG.K0VF%?}B~Ӣfvk]Zztńz`l#$v56W*E /d{Sw|36[S9Ns:.[_M=quEW\7%?h<޺` Ze2Kko6uY⎱ԇXTжx:6/:?z5-t#K\O1 ]E[K%n,)rp[4nVgKO#du>)WWM>B䞬j˵r|]6eޢ6o 3n8eEeWǣ|b6L5DMQ_PUI'쩂r:gzl3I୩e˸[Tuǰā':.>. )>cfYZZz# ĭ6 1nvU3M뭥i7KD\+ s5`f3OÛz{I`Ye(zS京ՔoM]eLr5BseWgSOc#Of,c[g[S8L $O¢,WvvDWK7|sS nMAxq|կujqWM!"ݢ4OV: qԺ]jiZA8B,i5% +ep"\n]WG8c)as^2iM(!?t OKDP-$/ǫwDAn5ź, qe]vu~64S-8]%ILIrpȪ&8.R7Jtrk*5vǰhZWϖF8GNdc7.¶.]:jܪ{/c*$X.Rǫc󳩧1^3>!N*obˊc*2Z|Zbc]~?_[Zߛ,KlW;`ɳnѫy +c&;eC(؂5W:-uº,tpkjZ Ҳ9/j 7epZL~]̆Q QDSt i_*>pB~c3OzFT幔IWB@ +~AHRd;Ug(6+ZhP|JQ&OOYd02~yH"K/2D:$?4K*4w,=y?,lSĶZ^(N5B~)A/pޅ7ڋ EG|z5G}\ +4p|t?.(]`n^3-?pY5 oߥaNC:ҤΆxrrYR Љ +-S^)CKZUXPć'NM)Ʊ~Zq+rY[h*!Т43d_bf {t{n2nuP+侼Y)msc3Vt%V>J/_krj SBnF TR(_W<j89U&mq;ttX4 wdrBr|Z^`нZЖQ7̈ + տm+Sl0Om^xK{P]خM/h=-R{Ds!8b`g6I闵~e~ M󢝱v]FqN(@j7 9 ~~e*]u}O|DkeB@OʪRΠAuw u׆Tǭ/.6HԶdiS;Ct~͖sţ=?~b?c) y_` U&k{VᱞmIܼJ\5he|y!灟7N ` 6199&1fw/e)dth =yƔpص#3퐙@xɜU1bvNLxZFV82aбV ?T=rE.UQg`XaS:[,K~FPuB<S잹OH{#a)HH>'pV6'RVevXqc.ֱnBgWndK늪Uɷ.KQ?\ݠAMM;N ?\.[M=qP quSadԸY`ZRYߚZm勗QλtfST,F-ź,:kq1` 2Ǐ癃tLM˛H#(6cpWCwM?&LKlD +S^ˢEnnGL ݌[aV40?7,ofu,JnM-KQ?\nҚsTQuigǮVWScc߄ت-9BP I_E on!~%nN8|¸UӢΎfWKqs!Q19\hs=,S)WOK,]Zg<˲-مCQO=ui:v]6;589,(9ڽ9s5 Iđ]']vEzAZᯢ]|?<|'jX8pR60ttBU/;MzKظRhc +=҈O[,oW${ԲelFdޚR1,غlu~589yvJ+?CIc8iX,$YM]㛟v7oMmq8VgSOc>qaɉXk=gY';.~q4 qXk*AQ4M˛?pl|588.s8.c6W,*gM<kumܚ,bInMmfq[ˮϦ8_zN1cuĺ9XHnm2CN{۵߯e,qu!=/=z.[= ="k8H<6m&L'֦[ܺLBԴ;8\Fv5@Ѓ[n]FzF/Jj]t f3! \7{WC`\MK,pulqluOV 9ۗxż !N]Wͮ%n.#Nū]7 CLֺju}89\!r`NtlEZ->`SH{Za%_Q-Mo_MS +;M~7;u]:z\ڲtb +W +RIS-nY"2'uչd9nܢH(@QْvS[![ˢ뷖G8MoJ8;+B '4-.Sjhwӏ*F0bDeWgSO1!uMތM$'Ss7sݴQE e(ZZJn\aJk]6bhqsG8X-b~Q=қKN_ގ,tZP`Z؃9QX՟uٍ7z5q b'O)Q.`e$# %$Pz)q1~ ]JXePJUKhHjZ T 9 Tm󭱜JHyC %%[wz{\t#|dk#xnw Ek$O> +(Nyɉ*9sb U6+>/oӨ^Dyn6}7,ڣ0{a)pU aYU^4g( awkݼK#LAWn,-z|/huwdK6ğg&-8AuU rrwE D,h3VNhTv(AUL2 +,6QAS5T XM-Q%q"o墝5tnhà^J`ԽAv* 'H] U(Dٚxߨ FGT#y無~$ +YQo|U(͡܊t&O04.Ӡ^pדT$wY0444!Q&dA13Z+/QjW(h-;*;@hb^)b.jfZ k-:A@3YF=Ŕ~Z9_"BA0]04P%q)Ab9ij)=0R+ n&so 6'gg4ͩx~]aDeCB6cFݻ6A{l#uRIfEjvݺUI3<@'1H@̭2{,:jnx; Żko:'~)=pjQR(Q> "(pq~ưs,bБHH%{P߻|iPd4ұ PʵI# +/'#q,xz8q㱪\|];th=dPOu!S +Us= O %=$UVh +{B!oЃcu7X\LuDQ0IRu1-;¹ ᡡӛS(Tm 8\CPӨ^k%c Ԯ5zhW:!? 3 +Hr$LŻ8 #_Cg 'Au"gCֳ_ÛFI'~=Փl7k2ז +[sҎ )?xLqdA/eꊝO!x%6ћsFҚp 6̑O +0 Yk0WG1'gb=9'@rkd*.g +3}6̓tNŗ0h)BVH%Og-Um{Mkx^gS,S9#^Sv䭆[ϗFAvK+ y8IlŕI/Dut0WIײE)tϪ2,aڍXK1B@bB:PUJvBM<[x9{*Ł'q(h4"G}S -9eRr-Z_P=:/)<)PbaLfs^V^d(28-YÊZ~z|[- NGN{lI26*&(8J(5 ;EW-iX^̂+~_5` { +0#(&A“W(H:QR&?ZM%'Slź/W*B8TIJ5_;bAf!z9K tT;Rz8%"T2+r,@ep@Fв3tⰒ_/TTdy/~fD9n{kayEQ +n ')ǧQ= ؜Cewd7c@6aPE.&I>CSDLڃ䇰p)G U~ڹ)ܖD*_ ^=3/~b"<[TyvGڧ@Z8!Ԡ!'Qz=@FV"az_飔O4HM!\k69X'hH-gT4iU^-8 J +u|Ӌj,R$oY)zAeY>NrܳzwUNsƲ{c1h]cz$j txOvΝwh%L, OyvD\P9pzT$OK9mSl2SKc@@od >tb'jDU+,x[6JẺ߻4OzY7l*g`n)2 %A)rqe|LlvCQW2ʼnm d5g7M +|̡3x7g2Q[?=aa3hAkN +! ٯ$$ 67sF[4?N> 3V9ɗsj* Vkur|ͫXÒ31 cC"# ޺]p ;&ʠ :H(0 ۘe;'Qv&x)*ѷkы셊t7 G,;EHkU m1ѵHَ<`rbche;ͱ=/cghSA(޿hKv*A ;|d0hSnM*DlEhdf>W?U|,:y+ߝj ܣ$yCo(Ca#c b7D K )LG'ğcz)'Hw{JӝR?2w-N篔Pi失i4_YvafzMڃ\e>".{7S4Ġ)lĚ@W+Q0Ocz FU`,T3r< V/<!l F X8B&FڒEH/~*UQnE? [f*yig'Epj1/!)I)up6 .,T +PȄ +(YI;Dc,dJZ9VC2a=Myѻ+9A=pB34-w BiT~hٴ_ :Z%`4js`AXUpO +Jr|zN2y9K_4ҡdrp0vR4707 +b$mkRe! ҋQHU :MYZRf&쯉{D)ghHs$U`N,U^Z  +2ͰUW xAS>bIAAҝ?^J-; 'Xw7{' MTCf rO) Z= &)I!~#-)74]ϒ=S^pӼ乢*%&nh@ Ff <a=kf5z͔^A!IopܢHGEs34 +:A? +=ݒPL.8 K|NF|r@N[ "N=`g*eP0]i=#G3Ьt1K3Y\~i,9S"Kg/|Oy>u WP BAN 8pz ]0GaQe󖣏|-ala_5r- ]8A=qF`1 фN>p.@&qٮm0]i&@@pӔZ>3;ͯ +l*qF7*0+Tک?nnR<`Z#!ʤB\ ˬV=ܤARwz :dFXEj/,Jj+*|Qvv@ Pq4O D=&UF4Wc{܍P,v0µ [|Šxi4#a畐J2P!x +: 3hl $%YQ98קPZyZb $pP.3GcQLGte#YA +]04WtX3Nt|+) :8]B9[p.~uPRDR"ba,y^,üxVMf$:uiнCEsq-#.3׽ll,m/O4YBK#ћ5&m`VHdT武j + kV+c2B\ '+T/qeyt|2+%0[ZyFjiMtlg58·l>9[\U^o 6~jdgKMEW3PVpk6F2uix:.Z]<+>ʑg~0q_oVѺ1ۈ?m[8e(6o&r]3ZyP3gaμ6UzEg LCv|ۗb\G,Cˢmwpޫкhvy68T(=tNmL5vVQ MqKެ;6uf4ĭb9s5^u?èUAsN_j/MtuQߚ ü~5mC' +׭¿uQa6qHsUNJ`y٨%k$ u2Z[-B9{53 b6l3V@DB 6ϵK!͑e@U'Xu$xIK?6ƀQ(ܪUU[I&bTy8&74@9[lX1W0 tK+wk 8>9 +g~90 V-Al}`p"xXHiov8֭ey cF>HA[ZWG8'械02A2:.$p6`{`fF!+l-W3*hE_7%1yR#YGӶL{0M~?x-.5vY{oM-K10!ʣMӠEVW;\*RV M]|+šAYO }h0[XF}]7 +n4{.]< Ϯ 9'%~tP@²y[TvzMep>&نՇVC&[ujapOk.A*0Hgn6s򡒓P-KOD. /ԲI sm.Z2OujaӢ;n硁U2032ۂh"w52 q'jڐD^i6,ޯ{M~\FCd{8qm|*kfKeY|6n5QZZ\lBT{5E-fu}4<=>nqYRR|Hm*4IPeˢo uY9Bps5uY|]؊׷ַ%&Ik]>zlT4%hyQhSy': lYHOݮڜ,fl)5_nvlu}8¹%K 0qLC͹. a63 ² #2W3HO'k⼪0f3> +~5b-t.R3_y@躤k5174W# U;oC3Ðf\L0H+@ad=b]톓]LC&gͻ ^R:.jݗ+{ :8M[uI4vM$V!nMJT_w +~4.^OV+b2yEJ&X-̿.ۈM%l5*k'ꮬkf_W+A!׶2>FJ"b m[Zk=6u G/V!1B 99HT3ej[EwkCz{.HT ~7eipF*V +vg.BjiY L^g)o7~Z\< moI ;D%:9&}>ߐ} CVΨnqg\(3S" |vR)S/Ǚ+`탠:7uyB]R{޽TnK (* [!,<޽RҮf`&{fP6~FLEɩs6OTO+AAsl ),,N؃*=SJ+*`Aj(@!`%}F)Hj%,SC۠VL֥QE\1O_ShqKi!vc\m9§GStv%gR=#i 5:͚zwW'n$ϐ(W ؼxh>)7ֶ??'eG=bc>}TGLi? wo5_w@[Pd1>2rp+{uΨ!G\>}~3ooѦaU\n?{c8Gu3C[MfHq>!G$~ 0bFMmη!ʳ'2Ma(gT!W×ib +6ͫ@ȷ &BGsrX8HxpL?U~ڂ`)SJK,q(\+5@,~؉Iy8%VߢEE*<# sP񼀔y5!P,w* +ڔL5E W;oAv)%K,|IMAUM Iˢ4F/UII(hƍNe"AWeU!rP9JLA!opԶtѴ6'AUL#Tɣ4,*~NM.P#4A]`]3VN".x6Iç6sc>ߴ)x8B((< x)Pih cJpESe pGP~P\x*?捶(< +d#fR PbcMa*㾲*TR4i90MmE,غ$VK`w9jKH?is$+j<_IwYZǞ~; @Ϻo+"؃w6}ƶO˿?˿K]_y_˹ӿ?O?C?թ?kټYk7e9\3?O&$FZ̞I)vxH/V)5GʔYiK| +"Z|!0ؙaDdFjB%D"XV>Sدm(~. kfp[%mHɈ*A ҡtU9`tpoۧ&"[]!'+"$1\ ܕ@ d(Fd\L9GP(F[V7^"ڃmC⫁TERe#Psu lҧ-NL2/b6*"Jׁ{ީbZ⏣zC#!UÊ^2չxnXm4qb'h؁]6q`R+B + 9zn`d|8TĢ[tDOzLFP=BGQ\< zl{1gvW/HӉ`K5K4@hESG?fmCӔ苚X%PǠŀх<MRdAs#1;H/? Ӭ~mIYm s*@j͇x[i@VuHb)0gdI[i%ޕ4fʾcZ;2^<ϴ +j -`vHQzYxTbFq⤵JoO^ݡ>habSL5&"}|Vl #dFꇶ F⡙K"mZF կ$"a\>7*J+!lِ͌y}lĮ܊rXɤZbyү?17~k(eba2XUy-)N8B%<҃M@R~26H=fιm.mDm(#I 4'pT"E!vu"2x6cH#}m>luO+btO*wsD"@<(OzS)@o_cO"\8${(zSP0s_G7'Ͷ9_d-ہUs%Az1#R݈'m* +y @T&[gwKC8MBK.ʯPz#TPfiH^HU+#34o0.m^`Sv/}0pI"k(6HQjř<IVNx.U3ɅR{)ˋKQ6٪ԭOOzDa ?&(zsg NDIkٝ͆hIgKXQũfj%b`!) +eC6WOnEumYVRaW{c% +<9S0UD-8S$.eBWNH]oL~ynFF^U !7i+DƁB|Rt7LӘ^ȐU,hR@!T(X'SN!;Q͂fmwvwsN*4\$\(D!ΆIDԌ3{Wɚ'$ۮX>Ӱ^i p=b9,TjRY)'(2sb/b5m|6]x:D@ +9r<\}4WObnȻm{0v5RoEx$_:=A- +E\r0RpHn)Mx IQXUe@[l2MNuhg|x3|?աq}w^84v +nf[lZ(ee!(UBxC7q''Sӌ&Ħ{PP9b +?dbyTw6xX74Wi -+ilDjA8w!48i0w`0:%"Fͺq+C*UGiy`$0vQ쉣SL(8;.g3W?g껷/ٻSg{J2ŧub&}xbWg+>/( +%MU_5l,}QɎYƍ3ُXu~OCzUHmǞxns>_A94xu}.)n6lȒ!oNEK8]dۀ +ְiGhJNAǮm +Md˴ +͏feI4$EQ{]R(BK<_)̈́z*3\}!tP"$FmAz( \jI^*5 +V+n/ ia)3nIJ6[E$Z܆%Ƞf GӠ^ 4q7]PKИgRp+A."ZϮ@%X{FgơͰ 6^iB\PI~pIok## +xDU̚IKzb!,".fd»4nj~#F^IS0^F](E4m(oNjJHg/L$57ɖ8蹣 9xk#2]L#D^6Heifln#Vrz3kQnȜw%iS$WoQufL/Kӊ2xg>,*g䟚 f2PZWM5Oy~֫gZP;  rCn67襨/t&,vg6Dr!-Y=c!T3nOz\KQŷ9%n["AO:-4{B[tL&53bpj$ɸ[F@0_xҌ"V ltS耜7>x%8W + 9E9Ŧ`*K`4{be-zr8)BM z |f3} +86ѩ`DBl)PRd)94?z`}۟Wv͛i>#{Ԁ-,!l-0G)@IB@BWqT`(q{ Pn6*UA{֫]p=TEvO qQRg$1hKĺ\f,'o̦WN @TMB`OƺaPۉxKhl~2i+^F+xkxm HaConH +KG+q&S- 1[vS 9J ^)ϔ)PG Gn9#AUAT5A\(1/b!ĹK;'+[²Ys(N-UW*1Hm"b'+R/RԘP!̓==i .a#!!O|ek',1?(X3G,BDiPy@z6ԉF(rP$K ð^ڸLz]4pL@$( rDlĿu@ϨW'e*IpmbP|PֶjikTFP> Ϭ;wwJ5"ݼZ=T}.v݅3d0  cf QA4gEif%PExhˠ\nvAǶpD=лmZU[ Gv)}/-5fYYH#a(ꡥ9mTzL4R Pi[s(-&Wcz"zCi +#%J'd1 R 'N(>J.`[h);mGn%D]蹐(:B|_Cd?2m t>bD40}P@F!. HX΍rJR! 2\.F$Pxd'/)ij)oO;U@%B$-_~H~ W,sDR?_ =]?ҍ@.8d! elfR +,o'VMxJP(';U,Uva*e NQy.r\Xyhi@$;p nծ\ڛ+_~0+ZP A(G "e7ojR L c) IP5PǕ"kx?uT5 p"cK`5+Qi "#.fSγ<쬭狊ǣ7E 8d/<_P&E4"8Aпoj1L)ut'hyTMe1mʼn{ $(K͍,RS֦sZcyYh eZJ-XP,bs;(uY3#E-UO 1F|a|Lن?{=/#!ieRī/qfJlKWp,,\ XQ<64W q5ǢcM].VAХc] +dqdH,WT9XD.,alNv|u>;QM _74tƢwʰYي^ʎb1|?۰~UZ0K5GY s܋{Ҥ'kB 3W},szbhTtC +Rs*'fP~Z_>ej}Q{59FPlfxzL?R[RU?T+5xމ;) }Dp#'qyn-8N@vɴ#m2 ?y$ҁ>yCOh9q*EdELmI'Ϟ!$%O +^ŬsEİ bVs4hK^Ƚ=`eE2 ?Q(|讨7PAaM'lu%,48%BS??UؚHM??k#~"G΀(Uyt'7SYG?Q5Yj?|ǑDbp h~!u8TB2Oàɛm#_n3m';`r#~S< u4 Bv_:`bw`ٓxHne}rIp!}sQ*R&_[p].9=OO/&*։@hvɺMV.ϔIT"'1MMlsC sWdSLO&x{E%>Z}t.,= s0hPr73i,QNiHz7GnMEzA-[@@ +p>U +l91w6-Mp18ɉ&Z5QJchVd"Ff1Q= b:a՘.x aؾ{{wX-s#}BՀډt#G;#0{qUr0HlbUή P2>(T^~U^.ìQ#ydAgKS=@D&ґ6E~MhWG4=Yz-@3TO!Ƹ>h;X8NA>OR 1ۯ:\" RYK$"A@ wI4X%S`?T2WR)6I%َeD%c'4;41D2kV⧻,Lay{:k{Y.z# W^; zJ@JJM/@UȖ ] Y@z;֎Q:=:BLv +v\xji*jO +نÓ)Ŷ]>3k{H5aC(ӐI=_gfoH6h?li9#B?At'9ЏLCn-@x ^!&E^`zqpcKJ3P9d$Yc{ccQ0䟋!s[o!tIPZ2WK..uJCcApGی`2 eYFx2܈{N+8ߣt:lUAr<-:s0 "Ǝg TQ@MmYy|| +ҭWK9%O% :vpDo=h?>ѥfW U+QJԧi4r}tQ!|"*:p фu\]7wlo/ IU'^,B"Ԅ?z -g?lʰeMI"rW.pk1H툴5䎎F!7ʔ}!6.Eb]Y=Mfi+n{ByFI]cWfk"KyܕRnޱ+PXpn(MO/вN0PDs?4 !5}LWxTLt:B~k"Roȕ[v!e/)iU:Au@y9_^%;Xw~ U{8Nμ#ԣ직[`XM~ +>&A\]lVn!8Qu>Ys8͟[Qs5m?maaSq"~D*AjR{reUepi3.lMsVܬsLG{7PB h,HXrztCU5[^(A~4Aލ>˜8$tE'텸1 le4RQsu\Fr?CgګHhqL-c !%$hR{L (`j*3="[U 4Ͻ".@bz̔hSǶƽF,*,8"_r\K Q~WR^5Jc5}W)HpG2Lͥ?rQY")lT$Lܾ?W=|&7HRx-لs{ 03>_d$Pal Vr\M&BgQCjeIa}|MJy2( +NR8q{OiAwRY.%HHmZ-{0.?=o ~5iq&ՠ,A cUت갊u1;VEȬSSGY/K;x'"c"MО2moЈ *6 ]I.佴3e!Q[$B +J5$~Leaf RqA+ΉF];+:Q JByjo1g7xardŚnaC) .>օZu9s[-W}zvam|W^$e K*yNR>άP*gW%&ˢ\x P֗t-–k Y_ lUBXS 2I=맮?eFQy@7[]2g jjEٲAJ$U%$B)kUYR6) R"9MДPYp**}1G@U4ʉ]*9UAI'Ĥ%43 +idt%@ANi؉O!Dxȳ%I ok|3#^K[d<AAQJni\}ч$#5!evOXr:ʷ4eC_XTٷej)Ypr(f 5%k5*c#wiG@vYN6K[KrR!y+Au"W~jq(׎__nYj#˫ݿN *%Ns#J(Mſ@5_t]pr?318*:ނP`0)~1D +ʜ2nEEzK&I};ryGP5=IEZI -喛?5G68DZ;P2LchW*Rġ +b\#i wK~&$K6m$t5WN3_ŒBVye3C̦rA \?HG=# "ߚcHFL: ";Edp\Ps|Cßa1ISHٗ'!q." +I`Z5b +SLdXNT}E +pUuP9u4#1k2FI}IP~Roh9p?}⫖ykKڟ4Bϖj.ӥ--_nYiz\~E)^*|2TlbgA˧$#/<~%RIzB\$7;䖠L!F$r'7ث)@E)% )m4~UHXzE8?w r@cs]R$~3dt8ol/K0нi ) #GrlZ@^nY%vInpïKF/V! ڭIFD ^􊑉0dE!&ץE\/c9OCK60L5cz-G+" ~Ͷw)`eIق5Z;Hsx*of̑:%#dĎg/ݎyOp߹W< +r Hs֠%.^tYL$y+D~2!#ȑ#)rd +,2!Ҿ]ʧ}/_B)>~#&[F?sN;~r슈}Z|gWOĸ-c+ Z|ɾ|8@)awFseb +u) h(qIIQ}^UME~9E#+\%rEO^]$|}'k+/WHŪP*m{F\>*gWFs4ۋB8QЀ PSz ``%wprY%ScڔTb 53&6m۹Jj1Or̎6s̶H}Bش2Zj߈Xc'f^~,Iu+VFqq^yג>lr5[r9V1q6Wz,HHK o}W] A2ʣ_ƭP2-c&D#B$XB%Q!G"9=~ +PP2osI9@QO8XMv/]<unud; +nރD%ρdAyW GJjTE) 0*Q=v4L+’,Qd-QR@m? :l9)Qf/20Pʙu50KSDمw` &x~ㇻ!8vJrD^Y +hSثFќ(zA*E%Dd4D. lh7m^vиa O4-<@5vz9.n0T\O5U+Wcs-.&lb|)) )O#mlUU!iuY&8"JbbҶP0I$ 4miG']979e=*O)(՚^X/.}|- ;vUzyx!`u +&!hBHRid+ǟFґ lCuNR+&Rh*s$ޫQ],_ سnF4RWDkT}/JR #·GT%.?8pg^~_"9/? "-e -0c"DG6g @twܰ̓0,\Vҡ*o !Sԥ ~ OMvÃ=};/Owj RTUqOK..ri,+ -͇y%Ӈ,+X /n9-:9y6r +ޖ@+(8It{a."g"ܭ!zqz7)zƊ/ꏼus5/UQdBCWO5\n[sx/n}st~V`Ec7K\J 9`KT|*r[$mC'?g?լcU6ul̋gspR>V\^Ep5TX Tv@ZڂP\sALu1+躪|OJl]uߔ ;@˓]r(zTgg%LK|TG/=jhws;Cu֧}3j^}߭^>*K㋙)NU)-, X*9Uj&&h0T)PB +7=EfOD5TqiJWɝ +9ސ79cE*A=Vs/_ۗƧ|'^}_]B+. ~)' n +|tits$NZ`HZ HAV##=[ yJ:OXUfiTh) !:t&(F0(Y $*TQfM33jXrEؕp %Mv=3:,AQ:K79bQeHJWňb8Mť/+㓽>UkW8JH|@ FTQŸ]W'{}۫<_u"5ݦ4D8A!F;&~Zƞ=YtS=QX_ ݠ4<paaB܆*NgRO;ܞ8bAڷM'Bb,Ɲ]\녡j[/~^_i\"ңt ܽ]{\l:lf/E P5G]BG'Р~Q/2{$Ĥ\WAדz1e2k^=_xaiOYw{˟zW䯖TapQrTEG`q:ًuaG0B*"Sv@"G6j%\L.0n# 0(G9F#ń~΢ vMx GB`Yբ(>^H +*b{y6(t\vx4F4XY$zERE1C`ʩ J`qD9eȭ`T/V~9Ey2 Oetc\ 1\F,S5- 6W;R*l۲O +%{{Fl x6FbjJmb:^d^MK㖞*ϨJOQSass)zqi ^^ Uux=dTٟ&6DG] EW&Htώo*-sҰ.,ʄ}qorh$G,8)k4x98;pgI+_|_~Ӯ +IT-!S}SP }7缆8#nfBJ+ĈHvb| IR +W(ڴR +q^@zNdpo݊,Wx?@ŵ_}dp_}b֭ׯ`̮A^ jj +"{>P [ꯂBk +/Wb.,T]M㔸)z=usQ4ĥ{40MSF.nzc_4\n{".6\GXvb%T:u݋LN:3HP`Ќb} l1ql%ir|){Im#9$<&>W qhhtg3H)Id5j&֚8/S@R6?pomj (G: b6$kލgMpыo*o\Ou`Ig6 wܠBׇ姫>_]ef㰟&-&)83ee5 e'NZjqjZED .\!hpo%˶EMLΝt57  +~r9?GU:L+s_i/Op_}Z/: +bW+p1qU;y@#R~ 0gU@%rij#DX9/ ~ڴ+!,jՂ֩^87].U5 ڮIչr[/۲ Xaq_gлҗ&h #<"rV><Zg2 ň.w|B?S!rE\ѯFҒzwoȮD.ڑQuQtCQ>1}w;e*%%s jpklJ6*Q OSP3$x~N|;5}:fpucno5%?wl9Vz@-v5b{ЖM҅rx S*-R-A;dt0(+fb{}MC%(m2A)JŵHBmpoڹsOGm7m o[{]ڇD0ڸА) +֑y!v@0/i|3-n@:)6^ԣ {8TL('O-tѿr{3Ĥ$dU6.Q|֒Z)g<µ9Wc!?vqMN"'/Hؤ0]QJmx%Cx_ q6`|Y]*J(tMv +jC} $]y x_ gуW1 hDcn.w& : 4x<ÈAy!`T!!|)7qfE&OFk5߲Q $>ev{JA*=qqڕ9E @ <ɻ,Y}[5-EzBO1 N)?#IsxCǻf/)A7Ox?|ӏooM/'~;[# x+Oc抛s=/I͟Jxx+čO֏kMR7t Dh= 'tGy[ 5Rhst}+JSѿN݇Q{ʾ9 [ɱnؓ~!o yv̷!OÐ*NxX7VVέ9v[0/vl'Vf=wԷ_+u/J/J/J/J/J/J/J/JWQ2>3mUT%U^%UZ%UV%U^%U^%UV%U\%}o U_W%m遰&ήԞz~{~P7lR?O4s-\:>/us\9׋6BGJ׉>ws_W)oν3<=?{?;Ǚrt& OC;5s-cps߹1ut[aǟQ+M3||z?=ߟ~TD?=&ޟ)O= yu蝟OOg^"Ng +}?s u/?-ޟ!O=x:l=쾯F⹑scxk#}|Ki_C\+XF_wɟ KZ+y[뿼oӛ^*K0/a_T~ S%L>_c{C~3ћWi9jXg`o$#yƓܗHN>wqMy>!t}oC)7dWsЁ!Ёޏo@7rTE[sa`oߒ'wMh/l6u4h"Q wQ.hb,9%{P"-$F[Ulv`:'4r,[h:spe1ʼn.E6f5:-#/U!a :6/8UڲuL^1mje8BiǻmȵKB;Bn +D fCdwLEBxHcrGd6m{w|%Κ QFTYO;Oȷ\a5Ϟj\Hf:Q#[ J,>o|xH +A:2cCA =Pp +6qa, +DLe%\ZhnZVakiok~,>,{Z ,lJIYn^9 gZ;!jmT<}a0^hYM7SӬ=8_ojGZ@kU5Tc$`Mmmm"wPn;G"%!5ŕv1V7h(9_M18p#5"9(ՌjMEescPq5R]R8),>c,jrB$Ӣ ,ѫ +\#G(r&8'>xej5儒?/S@ h? yYGZjO*4[]w#iY5 c(zttBVTD\|!)n=!ePzŢdԃXqVkȑ@yr%<c]tGJ#[FVx ۛpiϱ%*BI5B]bYtCH# cm#/py7Y@L/~өZb.H]g*J7Yv`9+Fq.vH{<!7(*i`J!tӢ dI}&bʄH.oP tbm*MvGj&dz>cSSa_aDhkklRuj4~R%o6P(P W9;${k>;ZIjU։wy}Jg'zFG9Vhg*+g]QG9o:9Q(>NJzw64A~ 7:IׇQo>Ύ|vQyaܛ=;ԣn}bȑ/|_g wLĻ| p1]'=g^6= =xj=z3=힅w<c[o]힉w <=gtҺgZqLR5wsy);6ùaOw{fdߵZwS]3Qy=Wc]c~пgE@V,EN@(Ņ@Rm9S?#ij_I&`~ssTpZ]iPgus,B}YV +6vSnSQusF3T%v6 9פʪznHo^VAEv\-Q"-{>)8nn^ǣǨC{ 4`j*PR+6@hjSxn-1MTmj}uvSfym3".A qzYM~¬/-7jRiyhchceܞ) A\&6oMVTCpKRCjSg@QJ.* HTn% +n[ ,O`]3d,[]ʀ{&UJK%&UzgoAX6"i+KkP@qR%QG="ِkZKQKf# )+#sBw %1+ - 0P'ek.YQں+fsȕ_4Gɽ bf*7L)L&*Lўr1mz(ybO*f<0R T85Oȩeu 14uz_|l-lsB(MMG +# UyDFc d /@#:M\}![śyk/+Ŗ igM e rjeFmkIAQd:) !1v;)wV4[:w(ܔTAc]1Y'hFT +O^ˀEBl65%%?wɎ ^]g28. gƢӬ}Bptf dxlf~vۚ bn#aYHZK م|щ ŢA/i~NQВ\P^'>ǜZ}M9G}ZYR4Jyk S+/ 0 ($HֵJ*[5lC-5rQ6-t~l(}%?\묮o7x]7W\3>ͬ^ QT޶Λ:bj3SvƓ?v6$"%bB8h,aF^X+ꅈpBQP5J#q= 3g[0 }Nz}Hf9x2Ű%-M.KJ" [aj4ZjCJmZ ̩N.,i\(iFI_-4TV0ܟ:u4V z@bofFKuwLYȆ1Pn:@M." ;\`a#5W"=-v`otvv-.PVڄV^Q`skit!OV}Bg=y]X;oKl*O5ȯ8=dS98ʁj'HLho433C $mɊ/a釥K4zBPŝ ȷ>wp۸K +W{k]~+fa3^ r=,m?ml+|6I_g 5:.GOjguI$q~+_Fosʱ92afkA붟r= +E "iOFje&~*܆ɛtG;;{S&Esj8GIh0~7UWzKR q0b p7J*v7zp28]%&o}e%{v^4sfyоo/G3GH5˳EVyF%Nx$$9IQ-J SэFϖnk(JI8)x>")v +*sUЄPEګU^G@{Z&q~;ًIfK,Vlh) ^]-j. +ljiz&j|gqⱲQB zVqiU8SW <NƬA QiMw0h +N΂~Z}sЋzUj&`!U].B"TJ%pҟj?UVT`HrK&~g MK|QeT"MKg0.IO%J +]WxeZ 1fZ6Nv!ww4j#,L[bST^Pu6mOz% 84V ~Jlz,3^SF tb",ŋæXDwQˇŚAɇGAB܂8ŁL(hԣeUaBnu? +NAbe r/ qWIHI1뉼kjfkʼjm^9Ed7W_,xӒޓ#XN؄9`䜛MC94NIPf?84oA T2MӀPCi uu=ሟPkpfץNk]A%@uNmCH.>ɰ n=:9H', eg5c2.E'? +-V~J>lrCMR5 i1'ČM?5WD1TJZD^y`MHY}V[IWV%ud2BaCbIîxFj6EI}8HK8Rr ^ ɦ>?-]`PANuw +Js۩BN =L^YE3$}VS[4b0(AO3##!Ù;3i +Kfj*6^wOz.Xx}1WWXf%r| +3ɀY,sSky!,Nl"pPX%g 4ΒK:q6.\Ru6{8 H#Lx5}M83tYMM4OYU@"zT?làif6ڂğW2{|H2UIß'^rSm*i^fLX-1vdj1lJ 9Vd$Uho֝b^d3O3*0/Fdp;'K`eolq մUR*"[\bbU}W;HRjzÊ-l0zINGipp$;VI ΐTF!~5_$xECj:Ǖyq + D$q񱏆 ؂ +RtmӋ`[` ݨ GeT5*E+jqr/1 )ҟ&_gʶНk'%39/ii s|C tg^ze)Jz@,'b"),WB ؃.mI{M-vJaAKL^96ל] P.V6ABv +C+C!ԗ&D$skrNTx xW9cbu_Vʁ@K(=Ip R%KN%o%"&I8H9jIb"6[t4cOq#9S'e84J,$}(CU .gB}4=pOD +6WɽspN&Д!]uY5-nQ4e=َv= Ēujmm{Vcݯ'i~yVπnj 9r𬑈4W'h.EtKI9"E)vNY϶b@dc';qsˉ[< YwD6%j\M啅yv$Qjڥ1{?:6 TZMoBNUrvԫ(?F**;CVx Ƚ}sGDt*T뙼6J2? Wvę\I1j(pVVz%-!ҒF(at Y!RQQt;n$oU%! JopI}V1Q{(:fi!*aKOuَQ0iouyw;RV7U$[Õʀ qˎb6[aѲ"j~ F +47u^uz(˨@9o .Xu kz*΁'d)cʑaD.Y^h$X}T9 ޜ:*p_ٗAk= xG%R7Fp9"`/g)߭X;2ta7Ab,!/=myx2xTV+$C +ɚH"f?_IU! ^Դ\b&ɸw`<.pC|$ 4#aѶOw?tW9, +QHӰ@ z& b^f/{$lWFA Bj&T$DK5 G{]XKI|CV :bK)f1e%i G*>K#I(@1QfP^O䕗SnW դ2=pg_M|Е%. +&7<jݒ[坜k@6̺:)-U\U2P`\*KYPb*nvhoOVEI[QSMlMCL:*oF`XId!,]G:U^*͵C ZЊ9`Q[&fʯ)2LSTZXTєƬḌjMmlh;mFVPl[18eU + ?,Ѽ:85vԒ]s&U 04-xZ䕳)~6vd;F4z_0}7m]\; :f|hIK89b7cŗ.w6X4]̋w\~2Oқ.%K617ÇrB:XA.M M˔,K/ջ8ܶ>N$f|. k`_i$rzⱙ?t`k]`",^mPP(n n(Ey(X[m01 pCf7g[U*)R|Oy- KQM~?*r~Z(>5B{!2!uAkmŧ9˘ZL<5LHW12Y9bv n'Xïh=d}cU^`a +7.#nͩ*]!%"(QQ  <6_g, urX܉hJ6*iέi^,s6c]58CEk4SE d,]'ԼQTxm̯_Cj`Uj-w֠Ğ =DLPؠ`]X*Z5v(C/W՜H %JS)H1[Ki~5 pP$b`( +59)Aw(sruTX-Q) 6Do06 #=yW,($~aA s5!tD!yWt&ȱDطlbZ% K5͆D_0nMb.T`IMA޴2,ɐlUۧuA[O&DSFZʈMjՆ捠^;"fNEsԿmkaq9 U:T@qBk+BG7ӾxTR:9Ͼ*`S)6XL徱&m_aYUB p#5,rE:D)`R"2g{@،" '_0SA]Ba1J*FCOGCo,;{=ʄCo*Yy>$VQH`~m <21&~ށ&s (aӎv$,RI1U)ȪڃzNf )XeTz(],GX4x8S$tMAEEL?N2'F)ahda"n}d+'}wv +$3Yt!Ӂ3`1&s񹎿E+ PeN.p:7!W\}ed rIf +ȘDmlȐ!'/*KEd#!@*a0 2Qmt[R"޼KT{ H|6TMU=.wquQI>sw%33SӻF\lxyͶEUg6mq +yŻ)N\#Px̴7y[ hjĬVѰ$\1 . "Rk1W2 "iB$6q Tw&S ʄ#r8&CjΌ!zMU;2@D"$dH䛟]Φl-ҤrpuEɑ2حT,3BkS Tj^ +̧2d|.3&Uk%,aam%l7E"=4uk%Ga**)Gu$4T!WuB-Czw2~/;1\8XDPRg UMyT ʮ֪rAջpG+8f"Sb7%KXC,p׈@qu⎱lXZ2UL=d8UG=VdΌ!Ruirh7*DݚM޻2tNj\ Mѓqf4wmrԪ- +%DDPޡ|btQ6QgK*&Y4jb3-;DOuY@R +pdVC += 9sf@/w|d䈲 +?rsBeb>UD"a`WϔnE7Qp1Т:v." \.6m;7;$r+;"%JЫZBOf2|'Db(8qJ +wm!h#=:QXW(Ǝ[Ew1 +5l4., KHD泎hX8x?V'ShwTc&s*F!B5Jx%v+U: |C^e#T/x11`U|WW}`vPT ?aTG;8AMD^ Ȃ`t#p%)q X +SqԎ&,#&,ڄJ@5'8H5ͪ+"6Mi5.Ks`M?h1Xqd¢ PX0]~`ě;5RӎX;\ko+˱7i,&zm5ZnqCJmmry,LBXAxxͼ„*27,kkSLl8^DޠGX.MocV ں!zw/\b&2.v&^cY>1Kπ[55t)mP.Kg&1/3/;w+!P;|sgttܩ\t'ƒ\$@ݴ PgyxXp9$8ܧ80\ TL7T.m>T-A0T] YZ`ZJ%rXXEPԏF=GzrW3 |@ =4fC!T#Z@+TB7V +?2sw8D!%l D@~c ˆ,[E[`8)d]gY{u'( o j8~Rԏ'r\$a8(,uRp&N*-fTt?fÕ6][= $90t{e|M=z턝H1ÛKaB`˫: @}MV#)FZ|=uJOd.bUJ)5I.L[kPʼngá~'n]C}U6_f8yDm=x1KXJaOCUm*d- mٿ;R&kฉ>A+e)G$ܸXշK&{zIjB_w& ?j>?fz&t +i* 7g{CXãKbhi<\84(rtEfHozنW$~>ز=x=EuXXr82 d<ѤTlnRDdʯ1xSG.vH5Z;!%9l 4 IоE;`>\28%$I+tL@ 9!OTcGr((gj_TB(Mr=K_OMfR/ǫ;L 4xip4XCA=Z՗G?I7)9O!?iچ嬶6Mf[!ɭ?uoxKv*G@ٞg +(BuVJ)Abz/Q+B!:L$yzƋ6#+gsQc{ltSn˽ X` +)٣ y/iS^gN$'/O]uhb ~tL6M:)ʒY %ddS&r= /!z3l(pvž~{ pOJ`P_} Qv]`Uqn +CyPz|->wD؅lj S'>DCӣ\MaDCDOfbB?8j-gUm/dh!j+hhMeXtZ⃴hK`u so!-]9O딮e7̤ǚjL~;}SYͼ\U[hkkڞῢ|w? l2mp&?]\e(9#bg8c;Yw +$e`g5x{Xߓ۶~Qӗ*YK1_?z_Ϲuu1Amu-gW?|l}_ x_}_w0,qoYXצgDɆ;?_~x?} +endstream endobj 718 0 obj <>stream +?~<^PQA₿?u?߼$??>y?}7@\t~?wt}_|c^qE^CC[oo=?JpW짔Hj:ܙ-'w@4j = +^FPM8::Х +1c|ɹ؄ +4BŖLNM+(紒f`MT2``EC}(QъH?M'$=fuR;I)bIcprXip<ڞkv;El)Fc3fy[oHz81; `PiQSW羽m;gj6r6}/ +̈́tt˵/t/vK6^[]ۯOE[Qxy +DyB707OZ㏊ړx޺|k1쨗vSwo|^W?|ArKdŋ}l׾xaO +OD߻ܨ^G\D˛AԶm=;_- LNT~@|g|eT] +tF7z ax"K$e+ V#APEɰ|`tX?TC0T6=,MKicTQAj5vԂ YHG￟bއoʡt\kk +HaxC?vqәwk\E%[#Irg}>? ӤpJ &tUe_^V:qf~A|r[2{T|"8Yu4*YYw(hrqc>t)?xgOհ #{Ulh|Yzy?9 x|Ѐٍtਂ^DwH-AEiY}YtE7x@8e Ac*&@0skэcb& ?tER{]F|."N rY1zӸHy\ ?\fϲ{RJCu`TݖH 2xvR6\Ρ1G]aqV b5t'qo% +"6ޢwC,܃&mAxIph#E!Yw[OoK +M!QNΒXL3Fi0B :-+AFEK& *dpͱƘǡ+oz#Ĉ`Aۣmm3T ?N!z0NyD {K /2Hk+$' >릤7f$-VYΆʕzr8hK7^kM)8ƪO&|-[gɔvVp( -Wenc'UuqDLOe0oZ; b(-."gѻQPɯl9l9!D&eK(*Y}W$(QhY2ZD鿷wHۛCGWQxC-r뱳0.ri#a1D?z#ST(ZYvNuJSJNmi#gEZa!$,6 QΒiY埝;b,ZH5 Hs[~kԻuT~ ܫ|ٵ}Xuwк<{U6te}1[A&}牫l>1Ym5Y.21#u)^i H#iIon#Aaw̟;%N2iM#+挶)ga=UO8Py]ţI,p|/E[jpV)H4mjP > պD=mTn(#WGiF"MHOnUhb &z;f)H S):`-Õ)/fɔ:BL=FEsLA&r9f,&fﺋ PAD%]5-4. +%wySȐm(:(3td+=Ok?QR1ݽ%71^E|3[}ݟ'&%(P6Oo|OEۭcrf͚xt(ާ|IiLV,rxWRgExi2YI` s=?A4ۍ r1# s(}k;qr=pq{ +RZ|}xYoZ{}cl_ (" Jxh9Eԉ$+9*+8甘庠E'v8trLt{ #2uY+w4no+F曋Kd>6%Q)5--d R8[%.CNPKT{2%dZm-c#n]x\)?g^/{{Kƾ]⸗xTfɤM|Po*أ|g{Df[0[[wo$` Ra%j3t XJ^D/ל̩ޢq_^ci]<1]]`n5S;EvzS8T)\lM ʭf%+ʜHbG-˒arԖ|9">uܟ4}՟}o9pk3u)vS5a7`Ц6l>.^#=|6S_\E8}9\|ڃߚ ;) J+sFgXRmKum+VpKЭ+4`}c6ߟ ٯd ي{ZN,Q5Pƪĥb{וK"OfpJZV`ģ ?|MkQj?&)G.O"˒go1)ވ)ZF1(J3Ȱ9(2;@,6)|Oy/Pc98Sh5џ16jqCĶqaRF{4>^4ָ<.)|k"B .7 m}XWu5&LLBZ.*ZݯL,Y#o;ʎ_ Pآ]|HB7M$ +2Ht)QP4eBVްQ[S8m +T2ӷ=xִ~7b 6 bE=~*݇[T|ky ^=.b6g~[DJ.j+ ITuQT]0ph.QeMDzHNv@4b՚Y )1$AJ +8D^~ K>3-1kԵ q\+j#O~T? #$ bXX94N˾hd+bmioJBE&lz%q)5"-`ry9bbiX2GP b:mյU l~h;uobu+QK--] {*sGwN]1BrkTJePjT/:^Ͱ*~Y99RpTXˤXּ 'uh# Osϓ2aqSH0ƌ(lR@=Ztc592vVjHJAN):Zz".[FLF7ieSW" +~+]AUL&:1+~\p$ G#3UL"+ 9獿 p5=QޕRtwx] Bء#R.+4nmke AnެBTU6q^<.ڑc?ݏ%e϶6VB +T0JuN'S2` ͓V*LQ@;1r'BRllGQ<$x +s?Ċ{K6Sо1q2c}F@dہe\@զi޿ٿ#Xgg-ȆxѸGqHX , +M^|`?\lF車Xu=^E&Rv>Lr"YW88-c y*V?(r8fW0ZU3i2&դG"O69ԖQm\ h(K[L4K#t3{ KTK4YK kRyQkޖdəA,r1߼7{C -T7&; ++W(?/!Ѳ +*<\(Q@eDC/fSR4CT>lTW.>l9L:eA]<^4viI)k{Mmd6&Q7 ED96.lv8B_A$.'|<=zI}fJCpoIoLuFI:%vn<|q;e=ب)uO|}2|({K_ze]n=uyy1^Smy_R[/Qp+^jFOgG[thQoӌXPe-\H=u\94^>p8v+Hȑ +Z{G*kN..*}}?EM.=Y78Gu6FdQt\`{a io[rkHc4i7 iѦ!m?hHTn**=BEھئ"m"1y)*xP~lÙt< QWrQE"5$lFע64yqBD +*_k"a{Ka)2 TV} pJFZClSt1y_C7il z2>йsU|xIlѻg\ ?K^,,Ŀ$RcKK/Rrb_,ιGw;e5Yt 2gLru=]fNS<9"l ڈ.vFǖq>%F_p?FљpG0[zKQN xAZTz6'%FB|dyqSfw?|2z $薞m]K&@7w2u+o[)2EV 򨻌ke$bfSaU~I"V/֓Iu@jw;*-d ܦnYϷM=UĽ(L⩶|MO{ +L9d&ҝͥdn0PsLn͵}ǩf`LX ;v[G/*4qK4sوc4{clŇ`eVC k:I!l]a}q_ڹXiR\PA@<}#۹x\ Wzm =԰)t!|e/KKG4W̺nvsGv Dž:ho$S]]tm{)w6} -ޓޙeMϾ,p=jtE|ղ^k=Str^Յk@^v2X&ֈ]9'OC{I;|t=*XE&iwC%Ա*;>XeِtGn~)=Vkz)!7ȫȖvnB}5ѣȍ$&ꦌ CjGxH 2M|KZ$(q TL.+Nۏo_y#b sy`(*7 >!A޴Ԅ?7-/7{[&蛐ME߄v$ i?*/n=oCiD"pZ`fp`P8NB]{I6Jm_#7>m?(I/?q +Jztӳxh:6 i?~ɤ5,^;29k5ջf;>7'*L~4/ZNtǣh N]̡m$)w L:Z +5haӱ8cxyY|INE˸3O"pph'vcpBsaε'uQ{Hhݮ`M|O=v76S]87#@sҙ @Pۥ2c~'`i]cnTX$\8UT/;v?^y銩_2hHlIsue_iƷYY + EF > lU/k|QSa< ⦊ل$mcP76~>Ӕkq|gPݰ@ew;t &wf3hWA6wWE23p:dԥyEH R(5Ne|(hDS70Wӳj廜AN#ԦO򙁊Dfd㡠\wv;K_F×4c sֲ}&R$dL6[3F`w5}& h8l3AOks3H~ߋ.QbH[>V0]&xPz`j9\&Z $ ^i[o7 7hzK^sw_@K_CHޒv`yK'ib?ϙ5;`?>RLb6QlĬnذbFOJl$E%#3j@Ĵ`%О=Kq ȎKdLqKKqw弫Fg:^fBmz +%&"^(,;a%&vTM7١iE%6TTb=)RP%+  +JPQ +J%GsP" +JrA ,na%^?{4g>)(B@@r~Wvϣ:yG\H9-#\}+ Ai{#d:F+b dGuv^ټ|cWBbse+C+E̔S 4[ i:l|%VzZEb11{SQf̭>-ǖY6~Y4 Z^0gkAz 98hiGIX۷.鵭X .s $>qqUkV4&blV@S+U TµlWÔT%S`6A W87rou9FxR/3Om+RvTG9wF Fs!I QI@Mc}OiA$4+i9/Wч-C ֩Fez'IbpA$XuIڢl"ɯeur"ߨgT2<ʣ`kId'`>fo xf?xsяN/(+۲v} kX[zDv1R=.؛?| +yp&RTuVeG7uj#PwSɋXW !)gn9gn(FpfsnF*Kd;ו>w>"v‡No~|D -lvE̬|ϙ)\}+ suRX똑BMp~V17JJ7x+%m?52$kNrU"V"ZPl@dD@F4VEf庺aqٿq"=Rf)Uv)M4m DN +9*+RGpVޯ:54%?id쇄-H>B-# ]++5sJlj 2?< k+1d\NdCvSvv_s82tQ;aF` @7%;ݦl\gxVM`#4*R H_4SSSgtA>]gQzC:OcSH dtdp}=x$$ g"?YqCdbX,peW±Oǟ +WK36@;,`;_==}@/)O N"jO`s~>}Q#KC ?<>-GXA0*:";G6 eO>?(1S8KH32~PH@:9%gHw_`G+ӛϗ+)0 9vJ!,_;,jBU!ěÊXHT˚4irqRiʸ=U;͖#3l}њ[ksV06O`lyJD rȣ"byȑi* D]]?OԃDZA Syu}h{ T,/`@qLMOH!av3G!%,ѵE < >Na}"Oa}х9P'qyvC<@xȒme (t;BOnO5mA 'jupX;,z!eInlxUF2M]""EHb0*hmw`ppwLU,"Ei*>cW0h6<>]gd+hЕ&WS|u?5phS5 4e]cAW=2gpc3ffx-鰲HEW=_4߁l;z`s`ȟ+LQmpt/ҕ^o,98w;`a &x$A$GI9MJQ"+(~?ăJ%NRICdUX €=2OJ%<|lyCvwTtGp=&}}N.lSLi1⁎|YQ,wSx]Rw4hiIBȽ x֕x=VH2+ۋܥc`+I?l`k&1%]鵂>BOD 75+(sB v28Ey`oW#Q)>Fc(%uo')ھWׯ*ۨ.|{yeFK4a`C>%}#;V/E93:hboqgɏΘ#-&G*'$C)/-y} 魼tm| "K%K.u 5X~ZibZG+>iT3N4MhY^Bg6}Hf&N2B]dK婉a(@)#N^*eU)QOSĩH4H)._dc*) MFIVҹ2]IִɈ91 @.0u܈MI8Uw.^u mTu+W?lZwi]M 0Q3f{Щ.Ȳ=3۶2it,k [eeYo0,k<).eYXe ~n,kMf/odЙ/k:˲~iY &QT5 -YTKd5r<鑎=. +ۺJ a5`5pۺhIFmAxT|֟VMζ0?[Snf~(,VоdǮ+ևk1rQfR",AxmEBn@.[Xq|ʖع()SVqq !f$ 9V2fZ`lI}C^-ecA-]l75qK5} +΋t=/&!6MuVaM+Z)☱:@SweVoLf[`$(Ro^A@NԂXoÀ}~,n,B0#$Y$~d%aYx#F38Fxvu`dI#pk*J#,H4V;SAU)jAvDi$6C4h vI>K[ѷ%0+3I#ˆL1/ipzЅ4 +V,vFLh!@H? |iwFuq89S2ޕ8!CGaɣ[h#Imp˞1G׹ᩕ(H-n#ؿI,8l659{C<(bkLm3|jmP$i4Y$XmV!_H,-r&HSParT!Z2X+ot>AULQ9$pdc,#%8'b + @C!إzz.ZQT&7<~kgMe|F4*_r0Obu֐/ܔ϶R{f%'xw=$YfsZ0 @XJZ+p"Osiuw||.|WrG>)l-EgƑ>1]gGwKf*/}Rc9~Lʕ>xk?-ј[B~&~HH&4Mw[d-NmpK7Enic~KzWs8* V1nJzxQ/'"ξrFξ 2핮s{P8NvJ'NiwO@;K*A/|l8#>5fn)_m-iʆ!C2#4e疦͢$N٘ͭȕ2]EI18e@|$Nф츫Hn6$L^ޗ&\(wJSbǕ)VBvS$iɔl4%Ka +YbWu)眙ľ^@#%\jIf~04q/S5>C|uK;P߶ +l?JS44>)̶ie + +n9ilܵx+^~/{^2E +[wx#^܁cq,=!e_f3T)2{_v[S7 R/R;uYwO/eEd_l1i/?ծz2gH[\xEOe|n([$wټ=\IOk9\I@T~$v%$6.خ$x  SOχ'(Nd6H*~$gG2k-ۏGJ#%~|0)^.EZoH|R؉D&NElMH<6H 7ҢD7MufӲF"%H:ZrTSˍnMEH~6z6H\.MZ`u`&gn$cԇEuv#iKv#흗HIF#W#H@˲~l?RÏ GJ~=U'IΏ?>uп70BKv»ھjdT_ z t +uVF 2f}EFkѡmeܟnNET|3ǿ7o+ϯY?ǷϿwo?ϖH& +I|'oÙ% p!i_]a*+JM]UC@A׹:lF[P{a]b4>N>kx,*yx,ul٭ltD9˨ 1ӶoS tcPpQ'bB&mHLl-8oِND;D=#uxnT*juč0:664r`,522ShBhGsxŸPD)$dQ@qBpIIÉw5J-zֲTP4~s&y&$- ya(gU B%:؁,Y[/6.0-ʗUӻc9RA ;;!)߷0tp|H +4, c`?b=Ƌ&{3g>ECuEY^5F!̌tiYtJ}K(T.r.i %Y#Xyílk-iӱc2٢0#Q4KZ_l|4!QHYrq5Dg#ݒ󖻅4s>1Vʄ)`~W<^n%1!YdyK X9w1Қ42'Q'Ex/3qГ)SU+ޚgOX? hsgnɭrlM).A!JBLܬ|E}z?OA;Z5}!x78@TLjx}=Z\܈v^Q窒ͼ\,KnF0 cDߨ}Yuix^yAVZ| InDr`^' <)"v: .> +*5mIO^q]QLǏ B3?$_}khC¹G/VGo@dA$Hgez uYKfT3S0/(i:K07^̾n~'"HWQ{i.4zef +9z r#rO!" $v7 eju 错>Rڅ[21mrJohbэ]iӇm)!t;IkjXw=v/;N%]&[(#/S']$(ThVn$]IF_P(f > 3hc=w+.' xC+3vzXӶ1qHHE\̵D543cu0{U ? 3ԽyUx fv$H>Ebt]ˊk) y0*xJ +H-E.{P +}<@s)̅vqM O;ER*D)n8Ib8g6D̬BpQ~$R$t@zCPPkǣZRȭ6^:1)4zчS5y kֱ`,Ӣ؞b`9PK"kR%Yۇ%E*  j HH+~| +ɢP +Ҹ{T e\e@_{Wv82پiJc¶붺/bb } ܾFV! +9+msA^ $?+dw:q _%zRSYui3:9.G0qGG& +(犽݌9"} [p\H +q?i(L?]wwuk~;0֖_MMdlbI}G+rIE$Ycי:_ֲɆ,W$ٻldV^g(^?8i3L,n -gumK'Oʟb>m14c1qz7u^SgM$_kkx.C" p$3-mR[mꩋ„#s5ӯse0hY_{֓eRe`dZJe;,ěm32 ]&v8&U'H^enFYE%oǑAoRc\SAebݔb#yeGR "ݻ$dp}R '`I?7"A1fa_ϬbP 9*^Qmv?f-m^h1'AށFyC[@DD\; $\}(Κyb^YtŌiQ%DCUx}\4 a-Ã伟ߛ;YYbؖ-F1 'kpIH-dR>?:t:yt:5VC!a<ȎLf˗9D&\[[{i5-N-cF@Q@ߨpKr3;'$ifcṪ֟xaR)FLrI˥&BfQCK `( 1;w'I/PP%存 1n䟒2EM4F/F +{. + *G"i;Q4_E|Ł\",!IDcT% GW"mmFN k eB\2ҹF@M}UM6U0d8[gj|ϟ!Yw3w>9?3m9zHA16Uyqѭg3Zws{J3;}z>gNKߏ,HR[ s|3H&>rMgH?}䙍lqh2S|

t#<>jdʜjdc42ݫ+K+wlUW,N g.bzBXCXӎnPCͦI0C#R-e'-B7e5݈y謜H~2H'U;hMxS*&oNuX`>|$P;`j 2+t m EiY +*wN.8{p=K# ` CYα֎hJϮfC`͎5RR=;?rLJvaEt~CB֡ڝW']=A0qu)=><:\ק{U[p)/fe23HA# }"[7Yuɕ34ˌ3BY +]vO*e,G6~KPnbWU6cS%ﷱ8U+OV .`o/|Wked h@Ҙ>D\@ +?M$ +;Gc qn#D_ġ60'W-iHC:4݇/c4Sw,\ݬ]#WE*!≴hAz19ҵ0DW/1tijicĸ:C*{{= &%hͤ/x+Š9mHsSl#€qN_5Bbi> |cD%Zpuq>55eQ7\'Yy\4lwF/k&YT4)',[!QҮCn{Dlso:\i2yxuwqѸijÂ߶˕C GؕD%Sw(f9&r4ù $_&\2 9;H2#Tx߷@f}d0;G^CE:rNsn[&paB u*jRT\(cUrdVS9}:i{(I4G.C^#U+}KrDCNħ%/| ޮL UN!I:zJqA)7׽d3P*NW=!` ȌSef\)K_QN^s3/|Y ='*MI^.)9gy}_gTFOaJlYAtP!@" )&}$e9f3\ +Av7I.4]&F*l xxHޛu{ HDeR~۵:/C g bqFK(1u4s gu,yPDB|]Sg/5wa)_ߒMVmWٳĸ-'bl-AˆmeͳIл־&6@nrwRup#(cb69/+%4KrȜ`f[r42.vj/͔DDUHu:a kcHM<`:F,ӋKE7(..=Y4.^EvS SR0*J\8J簃 uUTبy8k KnAa֎vZgEjDݿ[^3>i'E1/% TdE=O=U[)7#]E:51/YTⳟT2.>9gҎ*Bߘk<5fA .`V'b.]юb~xQWhǥʴUr+9MN" g$EGYP5E@hGfF4hʈz[gp)x0x̹j<6R3Ȍ>>xZ8{d]%z0/G/x}+֦&fУch惊G83#I_:Ie'EVF֟gXez]W#zww1#mHM{NMt'@x?@,1v&I̢cOdn-+Ψ'5X?ȃ]@bأ=z<5Ϡc,VO+w,57vʃgo9EieɎ[VuQYb6}čwx4L K8lyas)U9, +2eG%c"bxbNQ#}!Jxվ.fY"ʤ+lHmAEiˎ,+>l& +>"\"t*(cnA #*9i3#ȱ2gu3"o|ÅHLX^^/4[1ϼ$H]ZRު0iv\%9t ȳvc҃̿7VPz ^ bZW1Ptw_]V̹It9:HNW*L $251x6 jD::D=" |N Wѱ"x9D/[ NÚq!es2sŬv:'L 3$|sr SF:'AɑϙEB2M\}*? .'P ">v +'",:~F@ȇH|0& Sym8}ꦑeIBn@P5WykIge?އq櫜e'q\뙀qŖ\,>1]usD2mW +7P~@JpV#nT!SU7`*9O_ ȡ;QԁQ۷ۛw tN#(H=ԷkT{}"E15H<$>;e~E43@fD񂍆/ޞ3^K-ǍthOoɏjA9߀D!{ySo2ڢD*tr/쌜EY.5à-8 0r_^Y7]X1rr?3veN=0p\\!sR(⥥@|mB{aq4rP.x.yIPARHJ;ЩuO/xlZ,1qDS,LyKBeLuCkm.?'7z׽)}Du_)އpi)4p-Ku޼Jej D-V.#;  +-<.'(8@9ϸ+ <{Ə=*\,Dя:^@uPs` $_<> +*6WR\lzqåb3EsĉC e1ώсεZ`xY3g:!c<@yP\س̮+~%rhfPD`W>e+G3!q8ya{SHѽ߼ {QI$.}!'U,J~W5H96OOiCfY;9G?Gf}o㭽h`hՎY)>Fs8R-#N<9=L]F=~ S%b\oT"z<90TE͓ecpPy<9w#,%ɑmc-r?pabm}^XQvݑ~^E}4 +9e*Q{<9kWp\4["|~_4p(>diRui; wɠB Oa9v :ށm c%di.S`@ B5XO]Np?k͝hK@nƨl'Uҧ}KHԶ'0iQFr1ԫ2VQIТׅ,$ɛ^UUEw +\Q%- +C +bop׶?@E>&?\˻"(YCֱz4։v.񑸬c^-/`Ϭz)v #YRqWDAA~ɀn밐]ڞH(U6UE͸\RӮa}kkMNw\k ǼFH[kd r.j_XuQ;EpPy-)9j-T9S>7e.pn6]$7!aU;?DcU;v~>oI\$AZjrϷS: +Wh@oϢrHDmՠ E_{V_܈V᪺6kp?˦5Q_\:P4labML+\]+\`jsq}/|ؗZIkuL`g=ڱר"UYh5y-kF݊Vc]7&LJO59ΠkUWI)\]g21~F=?u%\)"3/Y +_\?2#㢖p5fd(g)PT`x5z[%\WыCjPjqZ9s +Q3r3jAwT^o^};{4,XuUה v%[Dng .A1ݎHޠ ˑPŮ]#-*8Zd.ҋ5^ܙ#|[jĽ>x] wyBiȕxV#'?^'%>c $Y+W eȡY.mѵv5P {6^P,°*n9*><ʢh+(ߔ,7\:[4Zx m._PȽHM> {Qp " pc +FΦԡyHӪCѵ0,6loKDFRC0ymஞe$}xB薿Mʄ4(@;} =TfNTQjn K@4t(RDZ1 +ͯw4.;wvK,|W vS+i-}w,]bkn?G9#P\r$bx/?.^SRMkw*>p/ݥS-Dl۷+0[fMdqVP,47oG j>1 p|<4u0T@1Um| 556/ِ>k!M/!;uM1J4qoX FQۀ+Y ;e iJ#]HQ{ t/1 L`(Ow,$ i\7fw#I^Ҝ;/|Xt/e&De؟2A3yQU ɟV2*~a-B k8i*==cXy8o}t=y-w^Tשd;:yJ +~$%y84^s,|\حD!K +ڋ{ƨ?0"sXPP%gϩU9%nrQo"_a2z2雘\fZ:\X~іOFRF35U z7 +lOcX,qW2>ʀrpk̘w5Pqf`$_`e:3< 4#\Eu3EXF)tkc#ZBhɳv,x* ]lF*+ wEfd Mg:jJT io > 4>W?w>GM0/k;5<`y r^nr`@]-]Ss^AoɼK3)J*b} |r&9Ccy éOaLoJ pCEMWs:-/05I>}ld7mLQeA/5S|c"o$ ?ɼlR0Wtư68 7j} ~z28 +a/~Ą,nѰӗb*2ZO h奆5*ًky/.Xf_TfZZ\ktY5%E~;ژWyUj:X]o籺k(BWX++X]u\=Z˫)X^o?BٙD EsܢHIu-ԭVY3y*(ړE1d|'I$q7R؏.wot~z:g=c}TIyL,o}FDf2qqއM }:F,N`p"6˜؉HGc}XH5H؉c];2㺨dm; io(k ߿Nކᗶ!2+!)k!WDކ¢_ݪVm< Jn'yș__$g2V~ʼn0S a2yS`&2M]&{8i]JMޖ#f%;1 [^>Hu_hND|s :3I1 d{Šإ2ýJئ{|xqZ ꞕ|UZDu`sH0AГ!Q0kM빜?uK4ڤ{k^J_>&Ay"z%?L6t|U|E$Af^؅:Eekk>SZA/њ]+HRJ3cEN.?]Hׅ ;]pͬ&A" ,,yp_bwOf( mX1E=񘳡fEVI]YX+mltK +,nSL5PX_++,m(‚td/A~dh}erGH} /E)PxbW +z7l޷Wv|G4co)ڥlE5#auNo-ʁFif[YW:WJ:5:ESduQebNd+sYL)[uzwu9߷ +V4b!l~XROCi9,"z~-y*#ph=ߠժ JEl Jw= |gs6ݡ$&s^>STz">#@ݯC^}QSz{<+:!_m&VCiGpH>՘hPP eڷtZ2J; }>_-._$f ,A^۝|HSlX?HYH[dI,,dH7O˒j" 5SaK }6ɐ_3S=XXhp $ IۺX,,O܉#gM [ĀC;x đNaN̬ c"4[=X'`a aOqq$`m&[}9۔+όdhWWͺf105{(@I'F='؞<5t-s +݋ˊj{FҐIov0V4t9+Vs,vAÊo*N6.,mhj?ga$ + U>"G2$Zwi_K؃ +]XΝ8K q11!ʉ5l$:3o[%n{7{du3o6]5td-} +ǿ,׿Ϳ7/o_?_?}_wu_/oF +endstream endobj 728 0 obj <> endobj xref +0 733 +0000000003 65535 f +0000000016 00000 n +0000057809 00000 n +0000000004 00000 f +0000000006 00000 f +0000064203 00000 n +0000000007 00000 f +0000000008 00000 f +0000000009 00000 f +0000000010 00000 f +0000000011 00000 f +0000000012 00000 f +0000000013 00000 f +0000000014 00000 f +0000000018 00000 f +0000057860 00000 n +0000064060 00000 n +0000064091 00000 n +0000000019 00000 f +0000000020 00000 f +0000000021 00000 f +0000000022 00000 f +0000000023 00000 f +0000000024 00000 f +0000000025 00000 f +0000000026 00000 f +0000000027 00000 f +0000000028 00000 f +0000000029 00000 f +0000000030 00000 f +0000000031 00000 f +0000000032 00000 f +0000000033 00000 f +0000000034 00000 f +0000000035 00000 f +0000000036 00000 f +0000000040 00000 f +0000057931 00000 n +0000063944 00000 n +0000063975 00000 n +0000000041 00000 f +0000000042 00000 f +0000000043 00000 f +0000000044 00000 f +0000000045 00000 f +0000000046 00000 f +0000000047 00000 f +0000000048 00000 f +0000000049 00000 f +0000000050 00000 f +0000000051 00000 f +0000000052 00000 f +0000000053 00000 f +0000000054 00000 f +0000000055 00000 f +0000000056 00000 f +0000000057 00000 f +0000000058 00000 f +0000000062 00000 f +0000058002 00000 n +0000063828 00000 n +0000063859 00000 n +0000000063 00000 f +0000000064 00000 f +0000000065 00000 f +0000000066 00000 f +0000000067 00000 f +0000000068 00000 f +0000000069 00000 f +0000000070 00000 f +0000000071 00000 f +0000000072 00000 f +0000000073 00000 f +0000000074 00000 f +0000000075 00000 f +0000000076 00000 f +0000000077 00000 f +0000000078 00000 f +0000000079 00000 f +0000000080 00000 f +0000000084 00000 f +0000058073 00000 n +0000063712 00000 n +0000063743 00000 n +0000000085 00000 f +0000000086 00000 f +0000000087 00000 f +0000000088 00000 f +0000000089 00000 f +0000000090 00000 f +0000000091 00000 f +0000000092 00000 f +0000000093 00000 f +0000000094 00000 f +0000000095 00000 f +0000000096 00000 f +0000000097 00000 f +0000000098 00000 f +0000000099 00000 f +0000000100 00000 f +0000000101 00000 f +0000000102 00000 f +0000000106 00000 f +0000058144 00000 n +0000063594 00000 n +0000063626 00000 n +0000000107 00000 f +0000000108 00000 f +0000000109 00000 f +0000000110 00000 f +0000000111 00000 f +0000000112 00000 f +0000000113 00000 f +0000000114 00000 f +0000000115 00000 f +0000000116 00000 f +0000000117 00000 f +0000000118 00000 f +0000000119 00000 f +0000000120 00000 f +0000000121 00000 f +0000000122 00000 f +0000000123 00000 f +0000000124 00000 f +0000000128 00000 f +0000058218 00000 n +0000063476 00000 n +0000063508 00000 n +0000000129 00000 f +0000000130 00000 f +0000000131 00000 f +0000000132 00000 f +0000000133 00000 f +0000000134 00000 f +0000000135 00000 f +0000000136 00000 f +0000000137 00000 f +0000000138 00000 f +0000000139 00000 f +0000000140 00000 f +0000000141 00000 f +0000000142 00000 f +0000000143 00000 f +0000000144 00000 f +0000000145 00000 f +0000000146 00000 f +0000000150 00000 f +0000058292 00000 n +0000063358 00000 n +0000063390 00000 n +0000000151 00000 f +0000000152 00000 f +0000000153 00000 f +0000000154 00000 f +0000000155 00000 f +0000000156 00000 f +0000000157 00000 f +0000000158 00000 f +0000000159 00000 f +0000000160 00000 f +0000000161 00000 f +0000000162 00000 f +0000000163 00000 f +0000000164 00000 f +0000000165 00000 f +0000000166 00000 f +0000000167 00000 f +0000000168 00000 f +0000000172 00000 f +0000058366 00000 n +0000063240 00000 n +0000063272 00000 n +0000000173 00000 f +0000000174 00000 f +0000000175 00000 f +0000000176 00000 f +0000000177 00000 f +0000000178 00000 f +0000000179 00000 f +0000000180 00000 f +0000000181 00000 f +0000000182 00000 f +0000000183 00000 f +0000000184 00000 f +0000000185 00000 f +0000000186 00000 f +0000000187 00000 f +0000000188 00000 f +0000000189 00000 f +0000000190 00000 f +0000000194 00000 f +0000058440 00000 n +0000063122 00000 n +0000063154 00000 n +0000000195 00000 f +0000000196 00000 f +0000000197 00000 f +0000000198 00000 f +0000000199 00000 f +0000000200 00000 f +0000000201 00000 f +0000000202 00000 f +0000000203 00000 f +0000000204 00000 f +0000000205 00000 f +0000000206 00000 f +0000000207 00000 f +0000000208 00000 f +0000000209 00000 f +0000000210 00000 f +0000000211 00000 f +0000000212 00000 f +0000000216 00000 f +0000058514 00000 n +0000063004 00000 n +0000063036 00000 n +0000000217 00000 f +0000000218 00000 f +0000000219 00000 f +0000000220 00000 f +0000000221 00000 f +0000000222 00000 f +0000000223 00000 f +0000000224 00000 f +0000000225 00000 f +0000000226 00000 f +0000000227 00000 f +0000000228 00000 f +0000000229 00000 f +0000000230 00000 f +0000000231 00000 f +0000000232 00000 f +0000000233 00000 f +0000000234 00000 f +0000000238 00000 f +0000058588 00000 n +0000062886 00000 n +0000062918 00000 n +0000000239 00000 f +0000000240 00000 f +0000000241 00000 f +0000000242 00000 f +0000000243 00000 f +0000000244 00000 f +0000000245 00000 f +0000000246 00000 f +0000000247 00000 f +0000000248 00000 f +0000000249 00000 f +0000000250 00000 f +0000000251 00000 f +0000000252 00000 f +0000000253 00000 f +0000000254 00000 f +0000000255 00000 f +0000000256 00000 f +0000000257 00000 f +0000000261 00000 f +0000058662 00000 n +0000062768 00000 n +0000062800 00000 n +0000000262 00000 f +0000000263 00000 f +0000000264 00000 f +0000000265 00000 f +0000000266 00000 f +0000000267 00000 f +0000000268 00000 f +0000000269 00000 f +0000000270 00000 f +0000000271 00000 f +0000000272 00000 f +0000000273 00000 f +0000000274 00000 f +0000000275 00000 f +0000000276 00000 f +0000000277 00000 f +0000000278 00000 f +0000000279 00000 f +0000000283 00000 f +0000058736 00000 n +0000062650 00000 n +0000062682 00000 n +0000000284 00000 f +0000000285 00000 f +0000000286 00000 f +0000000287 00000 f +0000000288 00000 f +0000000289 00000 f +0000000290 00000 f +0000000291 00000 f +0000000292 00000 f +0000000293 00000 f +0000000294 00000 f +0000000295 00000 f +0000000296 00000 f +0000000297 00000 f +0000000298 00000 f +0000000299 00000 f +0000000300 00000 f +0000000301 00000 f +0000000305 00000 f +0000058810 00000 n +0000062532 00000 n +0000062564 00000 n +0000000306 00000 f +0000000307 00000 f +0000000308 00000 f +0000000309 00000 f +0000000310 00000 f +0000000311 00000 f +0000000312 00000 f +0000000313 00000 f +0000000314 00000 f +0000000315 00000 f +0000000316 00000 f +0000000317 00000 f +0000000318 00000 f +0000000319 00000 f +0000000320 00000 f +0000000321 00000 f +0000000322 00000 f +0000000323 00000 f +0000000327 00000 f +0000058884 00000 n +0000062414 00000 n +0000062446 00000 n +0000000328 00000 f +0000000329 00000 f +0000000330 00000 f +0000000331 00000 f +0000000332 00000 f +0000000333 00000 f +0000000334 00000 f +0000000335 00000 f +0000000336 00000 f +0000000337 00000 f +0000000338 00000 f +0000000339 00000 f +0000000340 00000 f +0000000341 00000 f +0000000342 00000 f +0000000343 00000 f +0000000344 00000 f +0000000345 00000 f +0000000349 00000 f +0000058958 00000 n +0000062296 00000 n +0000062328 00000 n +0000000350 00000 f +0000000351 00000 f +0000000352 00000 f +0000000353 00000 f +0000000354 00000 f +0000000355 00000 f +0000000356 00000 f +0000000357 00000 f +0000000358 00000 f +0000000359 00000 f +0000000360 00000 f +0000000361 00000 f +0000000362 00000 f +0000000363 00000 f +0000000364 00000 f +0000000365 00000 f +0000000366 00000 f +0000000367 00000 f +0000000371 00000 f +0000059032 00000 n +0000062178 00000 n +0000062210 00000 n +0000000372 00000 f +0000000373 00000 f +0000000374 00000 f +0000000375 00000 f +0000000376 00000 f +0000000377 00000 f +0000000378 00000 f +0000000379 00000 f +0000000380 00000 f +0000000381 00000 f +0000000382 00000 f +0000000383 00000 f +0000000384 00000 f +0000000385 00000 f +0000000386 00000 f +0000000387 00000 f +0000000388 00000 f +0000000389 00000 f +0000000393 00000 f +0000059106 00000 n +0000062060 00000 n +0000062092 00000 n +0000000394 00000 f +0000000395 00000 f +0000000396 00000 f +0000000397 00000 f +0000000398 00000 f +0000000399 00000 f +0000000400 00000 f +0000000401 00000 f +0000000402 00000 f +0000000403 00000 f +0000000404 00000 f +0000000405 00000 f +0000000406 00000 f +0000000407 00000 f +0000000408 00000 f +0000000409 00000 f +0000000410 00000 f +0000000414 00000 f +0000059180 00000 n +0000061942 00000 n +0000061974 00000 n +0000000415 00000 f +0000000416 00000 f +0000000417 00000 f +0000000418 00000 f +0000000419 00000 f +0000000420 00000 f +0000000421 00000 f +0000000422 00000 f +0000000423 00000 f +0000000424 00000 f +0000000425 00000 f +0000000426 00000 f +0000000427 00000 f +0000000428 00000 f +0000000429 00000 f +0000000430 00000 f +0000000431 00000 f +0000000432 00000 f +0000000436 00000 f +0000059254 00000 n +0000061824 00000 n +0000061856 00000 n +0000000437 00000 f +0000000438 00000 f +0000000439 00000 f +0000000440 00000 f +0000000441 00000 f +0000000442 00000 f +0000000443 00000 f +0000000444 00000 f +0000000445 00000 f +0000000446 00000 f +0000000447 00000 f +0000000448 00000 f +0000000449 00000 f +0000000450 00000 f +0000000451 00000 f +0000000452 00000 f +0000000453 00000 f +0000000454 00000 f +0000000458 00000 f +0000059328 00000 n +0000061706 00000 n +0000061738 00000 n +0000000459 00000 f +0000000460 00000 f +0000000461 00000 f +0000000462 00000 f +0000000463 00000 f +0000000464 00000 f +0000000465 00000 f +0000000466 00000 f +0000000467 00000 f +0000000468 00000 f +0000000469 00000 f +0000000470 00000 f +0000000471 00000 f +0000000472 00000 f +0000000473 00000 f +0000000474 00000 f +0000000475 00000 f +0000000476 00000 f +0000000480 00000 f +0000059402 00000 n +0000061588 00000 n +0000061620 00000 n +0000000481 00000 f +0000000482 00000 f +0000000483 00000 f +0000000484 00000 f +0000000485 00000 f +0000000486 00000 f +0000000487 00000 f +0000000488 00000 f +0000000489 00000 f +0000000490 00000 f +0000000491 00000 f +0000000492 00000 f +0000000493 00000 f +0000000494 00000 f +0000000495 00000 f +0000000496 00000 f +0000000497 00000 f +0000000498 00000 f +0000000502 00000 f +0000059476 00000 n +0000061470 00000 n +0000061502 00000 n +0000000503 00000 f +0000000504 00000 f +0000000505 00000 f +0000000506 00000 f +0000000507 00000 f +0000000508 00000 f +0000000509 00000 f +0000000510 00000 f +0000000511 00000 f +0000000512 00000 f +0000000513 00000 f +0000000514 00000 f +0000000515 00000 f +0000000516 00000 f +0000000517 00000 f +0000000518 00000 f +0000000519 00000 f +0000000520 00000 f +0000000524 00000 f +0000059550 00000 n +0000061352 00000 n +0000061384 00000 n +0000000525 00000 f +0000000526 00000 f +0000000527 00000 f +0000000528 00000 f +0000000529 00000 f +0000000530 00000 f +0000000531 00000 f +0000000532 00000 f +0000000533 00000 f +0000000534 00000 f +0000000535 00000 f +0000000536 00000 f +0000000537 00000 f +0000000538 00000 f +0000000539 00000 f +0000000540 00000 f +0000000541 00000 f +0000000542 00000 f +0000000546 00000 f +0000059624 00000 n +0000061234 00000 n +0000061266 00000 n +0000000547 00000 f +0000000548 00000 f +0000000549 00000 f +0000000550 00000 f +0000000551 00000 f +0000000552 00000 f +0000000553 00000 f +0000000554 00000 f +0000000555 00000 f +0000000556 00000 f +0000000557 00000 f +0000000558 00000 f +0000000559 00000 f +0000000560 00000 f +0000000561 00000 f +0000000562 00000 f +0000000563 00000 f +0000000564 00000 f +0000000565 00000 f +0000000569 00000 f +0000059698 00000 n +0000061116 00000 n +0000061148 00000 n +0000000570 00000 f +0000000571 00000 f +0000000572 00000 f +0000000573 00000 f +0000000574 00000 f +0000000575 00000 f +0000000576 00000 f +0000000577 00000 f +0000000578 00000 f +0000000579 00000 f +0000000580 00000 f +0000000581 00000 f +0000000582 00000 f +0000000583 00000 f +0000000584 00000 f +0000000585 00000 f +0000000586 00000 f +0000000587 00000 f +0000000591 00000 f +0000059772 00000 n +0000060998 00000 n +0000061030 00000 n +0000000592 00000 f +0000000593 00000 f +0000000594 00000 f +0000000595 00000 f +0000000596 00000 f +0000000597 00000 f +0000000598 00000 f +0000000599 00000 f +0000000600 00000 f +0000000601 00000 f +0000000602 00000 f +0000000603 00000 f +0000000604 00000 f +0000000605 00000 f +0000000606 00000 f +0000000607 00000 f +0000000608 00000 f +0000000609 00000 f +0000000610 00000 f +0000000614 00000 f +0000059846 00000 n +0000060880 00000 n +0000060912 00000 n +0000000615 00000 f +0000000616 00000 f +0000000617 00000 f +0000000618 00000 f +0000000619 00000 f +0000000620 00000 f +0000000621 00000 f +0000000622 00000 f +0000000623 00000 f +0000000624 00000 f +0000000625 00000 f +0000000626 00000 f +0000000627 00000 f +0000000628 00000 f +0000000629 00000 f +0000000630 00000 f +0000000631 00000 f +0000000632 00000 f +0000000636 00000 f +0000059920 00000 n +0000060762 00000 n +0000060794 00000 n +0000000637 00000 f +0000000638 00000 f +0000000639 00000 f +0000000640 00000 f +0000000641 00000 f +0000000642 00000 f +0000000643 00000 f +0000000644 00000 f +0000000645 00000 f +0000000646 00000 f +0000000647 00000 f +0000000648 00000 f +0000000649 00000 f +0000000650 00000 f +0000000651 00000 f +0000000652 00000 f +0000000653 00000 f +0000000654 00000 f +0000000658 00000 f +0000059994 00000 n +0000060644 00000 n +0000060676 00000 n +0000000659 00000 f +0000000660 00000 f +0000000661 00000 f +0000000662 00000 f +0000000663 00000 f +0000000664 00000 f +0000000665 00000 f +0000000666 00000 f +0000000667 00000 f +0000000668 00000 f +0000000669 00000 f +0000000670 00000 f +0000000671 00000 f +0000000672 00000 f +0000000673 00000 f +0000000674 00000 f +0000000675 00000 f +0000000676 00000 f +0000000680 00000 f +0000060068 00000 n +0000060526 00000 n +0000060558 00000 n +0000000681 00000 f +0000000682 00000 f +0000000683 00000 f +0000000684 00000 f +0000000685 00000 f +0000000686 00000 f +0000000687 00000 f +0000000688 00000 f +0000000689 00000 f +0000000690 00000 f +0000000691 00000 f +0000000692 00000 f +0000000693 00000 f +0000000694 00000 f +0000000695 00000 f +0000000696 00000 f +0000000697 00000 f +0000000698 00000 f +0000000702 00001 f +0000060142 00000 n +0000060408 00000 n +0000060440 00000 n +0000000703 00000 f +0000000704 00000 f +0000000705 00000 f +0000000706 00000 f +0000000707 00001 f +0000000708 00000 f +0000000709 00000 f +0000000710 00000 f +0000000719 00000 f +0000186609 00000 n +0000186685 00000 n +0000186932 00000 n +0000187928 00000 n +0000210856 00000 n +0000276446 00000 n +0000342036 00000 n +0000407626 00000 n +0000000000 00001 f +0000064176 00000 n +0000060216 00000 n +0000060290 00000 n +0000060322 00000 n +0000185930 00000 n +0000065419 00000 n +0000064557 00000 n +0000064791 00000 n +0000440368 00000 n +0000186044 00000 n +0000186094 00000 n +0000064858 00000 n +0000000656 00000 n +trailer +<<1C3773F6453D6C4B975DCE320B064888>]>> +startxref +440512 +%%EOF +%BeginExifToolUpdate +732 0 obj +<< +/Type /Metadata +/Subtype /XML +/Length 47687 +>> +stream + + + + + + application/vnd.adobe.illustrator + + + Vector_plane + + + + + + 6cd77771-36d1-4291-ba83-acbf618fcdcc + + + + Print + Document + + + + 2009-10-22T12:35:57-03:00 + Adobe Illustrator CS3 + 2009-10-23T13:07:52-03:00 + 2009-10-23T13:07:52-03:00 + + + + + + + JPEG + 184 + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAuAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8AimFDsVdirsVdirsVdirs VdirsVdirsVRy6HrTRLMun3LQtE1wsghkKmFPtSA0pwXu3QYqv8A8O+YPrUVp+jLv61OnqQ2/oSe o6fzKnGpHuMVQ66bqLPwW1mL+o0PERtX1EFXSlPtKOo6jIHJEGiR/byZCJPRv9F6nSE/VJqXH+85 9N6SbV+Db4tvDIfmMe/qj6ee429/cnw5bbHfk1Jpuox09S1mSr+kOUbD95QHhuPtfENskMsDyI5X z6d/uQYEdG30rVEd43s51eNDLIhjcFYxWrkU2UUO+COfHIAiQIPLfn7knHIGiDsrS+X9ehNJtNuo z6RuKPBIv7kEAy7r9irD4umWsFh0XWRDbzmwuBBdsEtJTE/CVj0EbUo5PtirV1o+r2nqfWrG4t/S b05fVidOLlS/FuQFDxBanhiqDxV2KuxV2KuxV2KuxV2KuxV2KuxVR9XFXerirvV98Vd6vvirfq++ Ku9X3xV3q4q71cVd6uKu9XFXerirvVxV6Fc+c7MC9CzUUJpUICylxItueUqovRVC/Cyrtt4nCqaX HnW0kuJYTeKVnW6aNxeMHPrTQy0Nxx/cKyxUCDpSnhiqTL5ktr3Xbi6muoUkjv3ug1eMbxtF6R4s 1KkUHz65rdThPGZAXxCI/wBLIn9Lm4JR4aJquI/MV+hXl802xi4pdxq1yrC3laYs0chtWiRqUHoK jNQ9z1zFx6WQFkfTXToJX/nd/wBzfPJEnYje+vl9iHt/MZi+qpPqiTmzuoZLt+SqJF9NEXjQ/vfS YUJ9uWWnTbyIhXFH9JNfHu5MBkGw4uUv0BCXOuPDocltJqKzXvCKN545PVYg3DyceVauAu57ffk8 OA+Nx1UfV90B+gsMsx4fDdy2/wB9+sMjn822kkt1dfpGOPWL+1urWK4iuZPRKyem6yMJGP1ZmZPs g0zZuCv0zzxYW1w9zPfo9pdLYx2Fvz5G2eC1aF2Mf+6qMeoxVCab5qhtrZLPVNTj1G5ekC3gkUG1 WS2miZg3xCbgstOTfzU7YVeczJLA4SVeLFVcDb7LqGXp4g5GMhLcMpRI5qfqYWLvUGKu9QYq71Bi rvUGKu9QYq36gxV3qDFXeoMVd6gxVLfrUf8AOPvxS761H/OMVd9aj/nGKt/Wo/5h9+Ku+tR/zD78 Vd9ai/mGKu+tRfzjFXfWov5xirvrUX84xV31qP8AnGKu+tRfzjFXfWov5hirf1uL+YYq761F/MMV d9ai/nGKu+txfzjFU1t/N97BCkStCUjT01DRodqUBO25GY8tNAm9/mW6OokBW3yDn8337qVMkIG/ 2Y4wdyDWoFdiMRpoA3v8yp1Ej3fIOPm+7ZUD+gzRuzq5QVqyFN6bGnKvzx/LRvr818eXl8lr+bL1 3Ry8QKMWoqKoJYMDyoN9nOEaeI/tQc8j/YqP5z1F61kiHKtSqIDQ0qKge2AaWA7/AJlkdTM/2BLJ 9SE8zTSOC7mppsPAADwGXxAAoNJJJtZ9bh/nH34UO+txfzj78Vd9bi/nH34q763F/OPvxV31uL+c ffirvrcX84+/FW/rcX84+/FXfW4f5x9+Ku+tw/zj78Vd9bh/nH34q9k/QGg/9W21/wCRMf8AzTnm /wCez/z5/wCmKXfoDQf+rba/8iY/+acfz2f+fP8A0xV36A0H/q22v/ImP/mnH89n/nz/ANMVd+gN B/6ttr/yJj/5px/PZ/58/wDTFXfoDQf+rba/8iY/+acfz2f+fP8A0xV36A0H/q22v/ImP/mnH89n /nz/ANMVd+gNB/6ttr/yJj/5px/PZ/58/wDTFXfoDQf+rba/8iY/+acfz2f+fP8A0xV36A0H/q22 v/ImP/mnH89n/nz/ANMVd+gNB/6ttr/yJj/5px/PZ/58/wDTFXfoDQf+rba/8iY/+acfz2f+fP8A 0xV36A0H/q22v/ImP/mnH89n/nz/ANMVd+gNB/6ttr/yJj/5px/PZ/58/wDTFXfoDQf+rba/8iY/ +acfz2f+fP8A0xV36A0H/q22v/ImP/mnH89n/nz/ANMVd+gNB/6ttr/yJj/5px/PZ/58/wDTFXfo DQf+rba/8iY/+acfz2f+fP8A0xV36A0H/q22v/ImP/mnH89n/nz/ANMVd+gNB/6ttr/yJj/5px/P Z/58/wDTFXfoDQv+rba/8iY/+acfz2f+fP8A0xV36A0L/q22v/ImP/mnH89n/nz/ANMVd+gNC/6t tr/yJj/5px/PZ/58/wDTFXfoDQv+rba/8iY/+acfz2f+fP8A0xV36A0L/q22v/ImP/mnH89n/nz/ ANMVd+gNC/6ttr/yJj/5px/PZ/58/wDTFXfoDQv+rba/8iY/+acfz2f+fP8A0xV36A0L/q22v/Im P/mnH89n/nz/ANMVd+gNC/6ttr/yJj/5px/PZ/58/wDTFXfoDQv+rba/8iY/+acfz2f+fP8A0xV3 6A0L/q22v/ImP/mnH89n/nz/ANMVd+gNC/6ttr/yJj/5px/PZ/58/wDTFXfoDQv+rba/8iY/+acf z2f+fP8A0xVGmSMSCMsBIQWCVHIgdTTMfhNX0VBS67pcUxieccgaNQMQD8wMtGnmRdMDkCNjkjkQ PGwZGFVYbg5SQQaLIFdgS7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq 7FXYq7FXYq7FXYq8D/5yPmmh1/RJIpGjkS3kZHQlSD6g3BHTOv8AZ0Xil/W/QhgOjfmV5q0105XA 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 nir/AP/Z + 256 + + + + + + + + uuid:B9693FE917BFDE11829FB7E4595AC072 + uuid:df9847c4-e834-4621-a9bd-d2027292e152 + + uuid:C0693FE917BFDE11829FB7E4595AC072 + uuid:16907a6e-6b01-4ac6-a546-78446b0ba868 + + + + False + True + + 595.275574 + Points + 841.889771 + + 1 + + + Cyan + Magenta + Yellow + Black + + + + + + + + + + + + 0.000000 + 0.000000 + 0.000000 + CMYK + White + PROCESS + 0.000000 + + + 100.000000 + 0.000000 + 0.000000 + CMYK + Black + PROCESS + 0.000000 + + + 0.000000 + 0.000000 + 100.000000 + CMYK + CMYK Red + PROCESS + 100.000000 + + + 0.000000 + 0.000000 + 0.000000 + CMYK + CMYK Yellow + PROCESS + 100.000000 + + + 0.000000 + 100.000000 + 0.000000 + CMYK + CMYK Green + PROCESS + 100.000000 + + + 0.000000 + 100.000000 + 0.000000 + CMYK + CMYK Cyan + PROCESS + 0.000000 + + + 0.000000 + 100.000000 + 100.000000 + CMYK + CMYK Blue + PROCESS + 0.000000 + + + 0.000000 + 0.000000 + 100.000000 + CMYK + CMYK Magenta + PROCESS + 0.000000 + + + 10.000002 + 14.999998 + 100.000000 + CMYK + C=15 M=100 Y=90 K=10 + PROCESS + 90.000004 + + + 0.000000 + 0.000000 + 90.000004 + CMYK + C=0 M=90 Y=85 K=0 + PROCESS + 84.999996 + + + 0.000000 + 0.000000 + 80.000001 + CMYK + C=0 M=80 Y=95 K=0 + PROCESS + 94.999999 + + + 0.000000 + 0.000000 + 50.000000 + CMYK + C=0 M=50 Y=100 K=0 + PROCESS + 100.000000 + + + 0.000000 + 0.000000 + 35.000002 + CMYK + C=0 M=35 Y=85 K=0 + PROCESS + 84.999996 + + + 0.000000 + 5.000001 + 0.000000 + CMYK + C=5 M=0 Y=90 K=0 + PROCESS + 90.000004 + + + 0.000000 + 19.999999 + 0.000000 + CMYK + C=20 M=0 Y=100 K=0 + PROCESS + 100.000000 + + + 0.000000 + 50.000000 + 0.000000 + CMYK + C=50 M=0 Y=100 K=0 + PROCESS + 100.000000 + + + 0.000000 + 75.000000 + 0.000000 + CMYK + C=75 M=0 Y=100 K=0 + PROCESS + 100.000000 + + + 10.000002 + 84.999996 + 10.000002 + CMYK + C=85 M=10 Y=100 K=10 + PROCESS + 100.000000 + + + 30.000001 + 90.000004 + 30.000001 + CMYK + C=90 M=30 Y=95 K=30 + PROCESS + 94.999999 + + + 0.000000 + 75.000000 + 0.000000 + CMYK + C=75 M=0 Y=75 K=0 + PROCESS + 75.000000 + + + 0.000000 + 80.000001 + 10.000002 + CMYK + C=80 M=10 Y=45 K=0 + PROCESS + 44.999999 + + + 0.000000 + 69.999999 + 14.999998 + CMYK + C=70 M=15 Y=0 K=0 + PROCESS + 0.000000 + + + 0.000000 + 84.999996 + 50.000000 + CMYK + C=85 M=50 Y=0 K=0 + PROCESS + 0.000000 + + + 0.000000 + 100.000000 + 94.999999 + CMYK + C=100 M=95 Y=5 K=0 + PROCESS + 5.000001 + + + 25.000000 + 100.000000 + 100.000000 + CMYK + C=100 M=100 Y=25 K=25 + PROCESS + 25.000000 + + + 0.000000 + 75.000000 + 100.000000 + CMYK + C=75 M=100 Y=0 K=0 + PROCESS + 0.000000 + + + 0.000000 + 50.000000 + 100.000000 + CMYK + C=50 M=100 Y=0 K=0 + PROCESS + 0.000000 + + + 10.000002 + 35.000002 + 100.000000 + CMYK + C=35 M=100 Y=35 K=10 + PROCESS + 35.000002 + + + 0.000000 + 10.000002 + 100.000000 + CMYK + C=10 M=100 Y=50 K=0 + PROCESS + 50.000000 + + + 0.000000 + 0.000000 + 94.999999 + CMYK + C=0 M=95 Y=20 K=0 + PROCESS + 19.999999 + + + 0.000000 + 25.000000 + 25.000000 + CMYK + C=25 M=25 Y=40 K=0 + PROCESS + 39.999998 + + + 5.000001 + 39.999998 + 44.999999 + CMYK + C=40 M=45 Y=50 K=5 + PROCESS + 50.000000 + + + 25.000000 + 50.000000 + 50.000000 + CMYK + C=50 M=50 Y=60 K=25 + PROCESS + 60.000002 + + + 39.999998 + 55.000001 + 60.000002 + CMYK + C=55 M=60 Y=65 K=40 + PROCESS + 64.999998 + + + 0.000000 + 25.000000 + 39.999998 + CMYK + C=25 M=40 Y=65 K=0 + PROCESS + 64.999998 + + + 10.000002 + 30.000001 + 50.000000 + CMYK + C=30 M=50 Y=75 K=10 + PROCESS + 75.000000 + + + 25.000000 + 35.000002 + 60.000002 + CMYK + C=35 M=60 Y=80 K=25 + PROCESS + 80.000001 + + + 35.000002 + 39.999998 + 64.999998 + CMYK + C=40 M=65 Y=90 K=35 + PROCESS + 90.000004 + + + 50.000000 + 39.999998 + 69.999999 + CMYK + C=40 M=70 Y=100 K=50 + PROCESS + 100.000000 + + + 69.999999 + 50.000000 + 69.999999 + CMYK + C=50 M=70 Y=80 K=70 + PROCESS + 80.000001 + + + + Default Swatch Group + 0 + + + + + + 0.000000 + 0.000000 + 30.000001 + CMYK + C=0 M=30 Y=70 K=0 + PROCESS + 69.999999 + + + 0.000000 + 5.000001 + 69.999999 + CMYK + C=5 M=70 Y=90 K=0 + PROCESS + 90.000004 + + + 0.000000 + 5.000001 + 90.000004 + CMYK + C=5 M=90 Y=75 K=0 + PROCESS + 75.000000 + + + 0.000000 + 30.000001 + 0.000000 + CMYK + C=30 M=0 Y=95 K=0 + PROCESS + 94.999999 + + + 0.000000 + 60.000002 + 5.000001 + CMYK + C=60 M=5 Y=95 K=0 + PROCESS + 94.999999 + + + 0.000000 + 30.000001 + 0.000000 + CMYK + C=30 M=0 Y=10 K=0 + PROCESS + 10.000002 + + + 0.000000 + 60.000002 + 10.000002 + CMYK + C=60 M=10 Y=5 K=0 + PROCESS + 5.000001 + + + 0.000000 + 80.000001 + 5.000001 + CMYK + C=80 M=5 Y=10 K=0 + PROCESS + 10.000002 + + + + Print Color Group + 1 + + + + + + 255 + GRAY + K=100 + PROCESS + + + 229 + GRAY + K=90 + PROCESS + + + 203 + GRAY + K=80 + PROCESS + + + 178 + GRAY + K=70 + PROCESS + + + 152 + GRAY + K=60 + PROCESS + + + 127 + GRAY + K=50 + PROCESS + + + 101 + GRAY + K=40 + PROCESS + + + 76 + GRAY + K=30 + PROCESS + + + 50 + GRAY + K=20 + PROCESS + + + 25 + GRAY + K=10 + PROCESS + + + 12 + GRAY + K=5 + PROCESS + + + + Grayscale + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +endstream +endobj +xref +0 1 +0000000000 65535 f +732 1 +0000455354 00000 n +trailer +<< +/Size 733 +/Root 1 0 R +/Info 728 0 R +/ID [ <853773F6453D6C4B975DCE320B064888> ] +/Prev 440512 +>> +%EndExifToolUpdate 455332 +startxref +503136 +%%EOF diff --git a/tests/testfiles/test013.fla b/tests/testfiles/test013.fla new file mode 100644 index 0000000000..433f4e48c2 Binary files /dev/null and b/tests/testfiles/test013.fla differ diff --git a/tests/testfiles/test014.swf b/tests/testfiles/test014.swf new file mode 100644 index 0000000000..20b323ca31 Binary files /dev/null and b/tests/testfiles/test014.swf differ diff --git a/tests/testfiles/test015.eps b/tests/testfiles/test015.eps new file mode 100644 index 0000000000..ce8ea57bb3 Binary files /dev/null and b/tests/testfiles/test015.eps differ diff --git a/tests/testfiles/test016.ai b/tests/testfiles/test016.ai new file mode 100644 index 0000000000..55c31a3b43 --- /dev/null +++ b/tests/testfiles/test016.ai @@ -0,0 +1,1772 @@ +%PDF-1.5 % +1 0 obj <>/OCGs[15 0 R]>>/Type/Catalog>> endobj 24 0 obj <>stream + + + + + application/pdf + + + Logo vp_fond noir + + + + + Adobe Illustrator CS3 + 2009-11-27T16:29:02+02:00 + 2009-11-27T16:29:02+01:00 + 2009-11-27T16:29:02+01:00 + + + + 256 + 60 + JPEG + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAPAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A825luA7FXYq7FXYq7FXY q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FUTpthPqF9DZwU9WZqAnoAN2 Y07KAScLdp8E82QY4C5SNBP/ADH5Im06D63ZO1zbIKzKw/eJQbttsV/V+OExId52v7O5dJHjB48f U93vHd5sf0/Tb7ULj6vZQtPNQtxXsB3JOw8PngdDixTyGogyPkh8DW7FXYq7FXYq7FXYq7FXYq7F XYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FWUakNOt9JutESNTc6csVxLdqQS9wXWOZK/wAq+qFH +rvi7DOYxgcVDijRJ68XKQ9wuq/o31Yvi692KuxV2Ks0/Ly2ihj1DVZ6rHCnpiSlQFUepKaeICrk 4971fsvCGM5dTPlih9p7vgK+LtC89XEuqvFqNPql3J+6JoPQJNFWtBVKUBr8/GqJd69me0mQZiNQ eLFkJu9+G+7+j3juT/jpXl+9gt4ICp1e5KtT9ghQFUU6D1GHEe5w/SXd8OLsvPCOMX48+Z/hht6Q feb+VjkXn3mS1+q69fQ8Qo9VnRV6BZPjUf8AAsMgXhNfhGLPOA5RnIfaluBxHYq7FXYq7FXYq7FX Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqmug2djOLuScxy3EMYNpZSyCFJnZuJq5ZPsA14hg Wxb8MYmyeg2F1+PdzUZW1G81VyFH1rUZieCEenI0ktaAklSvqDuabYsSZTn/AEpH707s9Ti0dfrF 1NaXN0UPpWVpDbMPiPWa4jSgAp9lGJ6dMXLw5I4iJS4ZEfw0P9ka5eQ93pYxcTPPPJO4AeV2dgoo KsamgHQYuDORkST1WYsXYqyzTLhofy91LiSpe5KKw/yxCGH/AANRkuj0OCfB2XkrnPKIn3AcTE8i 889Gs7htV8v6Pcl+U9pe2oloanmkwhHImpqyuGPzyR+l6/Lm8fs3FL+PHlEL+H6uH5MR83zpN5kv nXorrGf9aNFRvxXAeboe1pCWrykfz5fetbypr6+WF80fVeWhPdGx+uK8bBbgLz9N0VjInw7gsoBw W6+trX6d5O8y6lolxrllYtNpltcxWUk4ZAWubggRwxxlhJK5qNo1NOp2xtaZav8Azjz+b7FV/QSi RgCIWvrBZNxUAxtOHB9qVyPGGXAUl0H8qPzC1271G003RpXm0mT0dR9Z4rZIZa09N5Lh4k5bdK1w mQQIko27/J7zzosml3ev6W0Oi395Dam9t57e5j/euFoXtpJglQdi2IkCpiQmH5tfk3rvk7VdUvLe 04+VYLgQ2V1JdWsszK4HHlEj+rUmvWMYIytMoUgNJ/I780dV0y21O00SlldoJLaS4ubS1Z0IqHVL iaJ+JB2NKHDxBAgWOeafKXmPyrqraT5gsXsL8IJBE5VgyMSA6OhZGUlSKqSMINoIIRPlHyB5v83z XEXl3TmvjaKHupOccMUYavHnLM0caluJoC2+JICiJKZeZfye/Mfy1pD6xq+jmLTI2CS3cM9tdIjM QBzNvJLwqWAq3jgEgUmBCK038i/zU1HTrbUbfRONpdoJbZ7i6s7ZnjYAqwjnmjehB2PHHiC8BVLb 8qdek02905tImbzPDqttpsUi3liLRWuIGlEDEy/FI9AVZTx7V5bY8S8LDhoOsHXf0CLSQ6x9Z+pf UqfvPrHP0/T8K89sNopk+m/kt+ZepXmpWllo/qyaRObXUZDc2iQxTgAmL13lWJnXkOSqxI74OIJ4 CoeYPyi/MXy+tk2p6NIqajOtpZPbywXSyXD/AGYgbZ5QHbsp3OIkFMSE3H/OPP5vnYaEvqU5ej9d sPV6Vp6fr8+VO1K4OMJ8MvPry0urK7ns7uJoLq2kaG4gkBV0kjYq6MD0KsKHJMGX+X/yZ/MrzBpE OsaXo5fTbgkW9zNcWtssnEkEoLiWIsKjqBgMgyECXP8Ak3+ZK+YYPLy6MZtXuYDdR28E9tOohVuB keWKV4oxy2+Nh+OPEF4DdIu9/IX82LOxuL6TQvUt7VDLP9WurO5dUUVJ9OCaSQ/QuPEE8BSzyt+V Pn/zTp51LRNKNxYBzGLqWa3to2deqo1xJEGI78a4mQCBElN/+hffzfE0sT+X2i9HjyllubOOJuY5 L6czzLHJsN+DGnfBxhPAWLebPJnmfylqY0zzFYPp94yCVEco6vGSQGSSNnRhUEVVskDbEgjmo2Pm G5s47dVt7eWW05/VLiRCZI+dTsQQDxYll5A0JxcjFqeCNcMSehN3H3b18wfJK8XGTzT/ACXr96qv 6AtomFQ9weH/AAgBffseNMkAS7bSdiavUDihA8PedvlfP4JNPDLBNJBKvCWJikinsymhG3vkXVEU aKzFDKktdRtvI97DdWskCi5SVDIOJIfgp+E/EPsjcjvh6O4GLJHQy4okROSBFjn6Z3X2MVwOnej+ S9PuF8tRO0fqI9y90I9wWCKFQBvsq3qxKw5bUyQet7Jwz/LRoGR8XjA7+AVH5zIB8oyPRg2u2t1a 6vdQ3cqTXXPnNJGSVLyDmaVC/wA2+2ReZ1OKWPJKEvqB367sz/KPzPpFvc6l5R8yzCLyr5ph+rXk 7/ZtbpPitbsV2HpyfaPhuemCQa4noWQ69538taF5p8j+WdFu1ufKHk2+s77UNRtxyF7eesk11cgD 7XBaog+YBpTBV2niAI8noGv6rb3fmi61rR5vyxu4Zbk3VlqGoTvFf/a5I04adSJV7kfMU6ZEfFke fR555788yax5A8yQ319YDXr7zbHLeWmlyE281vb2LQmaEFnZ4GliRgxJBNDkgN2JOx96B/L/AM1a VpX5V+ZrO8vEW6Oq6ReWmnlwJJVguA8zRITvRUHIj2xI3QDsmf5reSNO8w+atc83aP5x8uT6ZfFr 6C3k1FIrygjBMRt3Xl6lVoq99vlgieiZRs3bPNc8zaH5nsdC1DSJfId7HFpdtbXEXml2h1C3miWj xUaWMGME7EDrXtQkAV3sib7mBedvzMvbLzXaNruj+U/MsdlpcdjZQacXu9OhhEpZQreq9JUpxpXZ aZIR2Yylv0Tjypa2nnb8qPPHpzaV5RS91aweKOWQ2mnIY4x+65nlwDcSwrX4sB2IUCwUr03TdH8h fl551t9Q80aPqt95htLe00/TNHuxesXSUsZJSihU4Kajfx70qbsrVA7sV/PHW7HWfzIv7vTryO90 5bexitZoWDxhY7KEOqEbfDLzr71wxGzGZ3b0bXbK0/JzVbGO9SDWTr1ld2sCvxn4QwSAyoB8VEYj cd8a3UHZ6IfPXkL6sfzdFzB/ysA6f9S/w+Qtf0vT0P0lwr9j0fipTr35ZGjy6M+Ic+ql+WXmzR7/ APKi50Ce88vSeYY9YfUJLTzczraTRzRAGWOTnGPVDV3J8fEHGQ3tYnakzT8wJ/K1z5ft9QbyVD5Z OtQXd7F5VleeeN0Uj6w8YlkAUKBU8a7AYKtPFVckw1TU1/xRca1os35XzVu2vLDU7q4aG+qZPUSS Ws4ZZgd2P82PTqvW9nh+sW8vmrzD5s1vU9W0yyvoHnvHjSRvRu5ebVjsSS5flSqVbcZMbNZ3Z35g 0fR/P/kvyXJpfmvRdNudC0tdN1DTNYvBYyJMjbyRh1IcP3I9vojdFkRYCzyFp2keU5/M/lfVfNOk RzeadEkt7HWbG7F1ZxS8/wC5uJox+79QVr7fQMJ3UbbIr8sPK+lfl55xt/N2tedtAksNLjnZ7TSr 8Xl1c84XjWNIo03BLA7/ANoEjYpMRRu2IfmBrOlXf5b/AJd6dZXcU1xp1rfi/tYmBaGSW5DL6ij7 LOu+/XJAbljI7BV/MbzHZal+XH5dadBqC3VzptneJfWqyc2gYzqIg61+E+mvw+2IG5WR2Cz8z9d0 3U/KX5fW9rex3dzp2kNb3saOHeFxL8MbivwkLSg8MQOaJHYPOsLFlWmvp/l7SbfVJYVudXvKvaRv 9mJFNA+3c7GvXsKb4Xd6eWLS4Y5ZRE8074QfpiAa4iOpsGh8VTy9qfm7VNYhuVllktI5FW5APCBU Pwt8IKqzBWr3bCLJb+zdRrNVq4Tuc+GQJ7gL38hsv84eU1tEvNXNwxaWbn6BSlDK2/x133J7YyDZ 232P4QnnE4mMspAEel2d+4juQmgS2+kaDLr3oLcXpufqlt6leMZEYk57ePTbfwO5wAuBocsNPiOb hEsnFwxvlHazKup5cPxKU6jr+sajUXd07xmlYQeMe3T4FotR4nfElw9Vr8+oN5JmX3fAcgivLXlm 61i5BIaOxQ/vpwOv+Qldi36vwKBbkdl9lz1c6HphH6pdIj9f45Mm8x+dLaytxpujlWkjX0xMm8cS gUCod+TUHXoPftIy7nou1O3seKHgaTYAcJn5D+b+v3kd7AGZmYsxLMxqSdyScg8U1irsVdirsVdi rsVdirsVTGDzFrUGhXOgxXTJpF5Mlxc2lF4vLEKIxJHLb2OKbS7FDsVdirsVdirsVR8Oga5NxMdh cFX2VzGwX/giAv44XIw6TNl+iEpe4EplD5D8ySOFkhjgUivN5UI+6Mu34YeEuzxezutnuMZHvIH3 lHQfltqBelxeQon80QeQ/cwjw8Bdhj9j9XIWTCPxP6A3c+TNC092/SGtKoUfFEEVJQT34cpWI+S4 KHe0ZexdLiH7zUxvujHi+4oC6j8hxckhl1C4cfZkQxLGf+DRW/DA62Y0cTsck/8ASw/4v7ktnu9H Nt6dvp7pNQj15ZzIanvxVIl27YHDySgT6AQPM3+gfcgMWl2KppYaNBLpkup3t0bWzST0IykfqySS 8efALyQD4d6k0xcnHgBgZyNRBrvJPkPLruni6FY6s+iR2t79bjUvaXMgHpMEj5TovBt1Jj5LXcVH XDbmwxRznFDiFD0nvA4jKzf9Y8u5Ca55wmlH1DR/9C02GqR+lVHdfGuxVT4dT+1XElnru15Tj4WH 93gHQcz5yPUn5JpbeX7ufyLFDbukEszm7uBMSqmMBqb0alVVCOgp3yVGnZQ0GafZY4R6eOWQm+kY 8P2pLotnfXOlXVrMgi02crJFezsIoop0PFWDOVDchVG41I69qZB5/AJSgYbcJ33IG4vqfIkUqRQ+ UtK+K6mbWLxQf3EAKW6sOxc0LjwYVHiuFyscdJh3mTml3R9MfjI+o/AD3obV/NuqahF9WTjZ2IHF bW3HBeHZWI6inbZfbG2vV9qZc0eDaGMcoR2j+342kuB1zsVdirsVdirsVdirsVRMemalKFMVpM4c AoVjY1B3FKDFkIk9FYeX9fIJGm3VB1PoSU/4ji2w0uWf0xkfcCr2/lPzFOoKWMgBr/ecYzt7OVw0 XKHZGrP+Syf6UolPIvmUyBXt0jBFebSxsP8AhGY4eEuVj9nNdIWMZ+JiPvKNX8uNXqvO6tgpPxcT IWA8aFFH44eAuZi9ktZLnwx95/UCjofyyBoWv2alPUVIabezcz+rHg73Lj7IGP8Ae5YQ/HmYohvJ flOzdGu7pxQ0ZLieONSTuOioR9+HhHe3Q7A7OgP3moB90oj9asYfy/06so+qN0BHP61/wpaXx8Mf S2wx9iYuZ4j/AJ5+7ZefOflSwj42bVQneO1hMY367MIlx4h3NsfaHs3D/d4tx3QiPtu0FcfmTYq9 LaylmSnWR1iNfkBL+vHxHHze2pr0Y/nL9AH6UpuvzD1qRWWCOG33qrhS7j/gyUP/AAOAzLqs/tXr J8jGHuH/ABVpPe+YdbveYuL2VkkFHiVuEZHuicV/DI26TUa7Pm/vJyl7yUvwOK7FXYq7FXYqyXyp qGmyRfofVOK2rXCXcEj04CVKBlcHbi6Djvt/Ah2/ZeTDI+Fm2gZRN9xHMHykNvI0eSYa8PPFxrUl rbidIVb/AEYwVii9Mj4eUvwitBvybrsMJc/Xy1s9TPFj4+DfhjCxHgPL6dqI6oNdA0LRj6mvXQnu F6adbVJrt9s/CR16Hj7E4HEjotPp99RLil/qcDf+mlyHwsoPXfN2oaqpt0UWljt/o0Z6gdObUFfk AB02xJtq7Q7ZzakCH0Yo8oDl5e/8VSRYHUuxV2KuxV2KuxV2KuFajj17U61xVNrf/Fvqj6v9f9Xf j6frcvelN8W7H4l+nivytF/8hC/7W3/Tzi5X+F/7Z/skb/yEii8vrHQU58OVKd+W/wB+EX0czF/K fD6PG4fLid/yEX/i7/klh3823/Xb/b/9m0f+Vi0P99/yTx380f67f7f/ALNLf+d2/wC1l/yXw7sf 9df+gj/pYqj/AJWBxFP0txptT6zSmQcLJ+cs8Xi31viQ8/8AjCg9f9IU/Z9T1/wri4cuPraTmtTy 6969a4tTsVdirsVdirsVdirsVdirsVdirsVT3RfqnwU/Rn1mh4/XfrPWn7Vf3H34t+Kv6N+d/wBn zR2q/wCPPq68/U+p8T6f1Hh6fpU2/wB59+HHpy7YXZ6r894fq4vB/o14f+w9LFMDpXYq7FXYq7FX Yq7FX//Z + + + + + + uuid:0C406B9569DBDE11A9C6AA1839EDD742 + uuid:8097c4fb-8578-4247-95de-589bdc34d786 + + uuid:6BDB89F344C1DD11BE22E0656B3E5FE1 + uuid:68DB89F344C1DD11BE22E0656B3E5FE1 + + + + 1 + False + False + + 570.000000 + 134.000000 + Pixels + + + + Cyan + Magenta + Yellow + Black + + + + + + Groupe de nuances par défaut + 0 + + + + Blanc + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 0.000000 + + + Noir + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 100.000000 + + + Fusain + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 75.000000 + + + Graphite + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 60.000002 + + + Cendre + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 44.999999 + + + Fumée + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 30.000001 + + + Latte + CMYK + PROCESS + 10.000002 + 23.000002 + 32.999998 + 0.000000 + + + Capuccino + CMYK + PROCESS + 15.999997 + 37.000000 + 57.999998 + 0.000000 + + + Mochaccino + CMYK + PROCESS + 31.999999 + 48.000002 + 75.999999 + 19.999999 + + + Moka + CMYK + PROCESS + 42.999995 + 51.999998 + 80.000001 + 38.000000 + + + Rouge Mars + CMYK + PROCESS + 25.000000 + 100.000000 + 100.000000 + 25.000000 + + + Rubis + CMYK + PROCESS + 25.000000 + 100.000000 + 100.000000 + 0.000000 + + + Rouge + CMYK + PROCESS + 0.000000 + 100.000000 + 100.000000 + 0.000000 + + + Citrouille + CMYK + PROCESS + 0.000000 + 75.000000 + 100.000000 + 0.000000 + + + Jus + CMYK + PROCESS + 0.000000 + 50.000000 + 100.000000 + 0.000000 + + + Soleil + CMYK + PROCESS + 0.000000 + 25.000000 + 100.000000 + 0.000000 + + + Jaune pur + CMYK + PROCESS + 0.000000 + 0.000000 + 100.000000 + 0.000000 + + + Olivine + CMYK + PROCESS + 25.000000 + 0.000000 + 100.000000 + 0.000000 + + + Vert choux + CMYK + PROCESS + 50.000000 + 0.000000 + 100.000000 + 0.000000 + + + Jade + CMYK + PROCESS + 50.000000 + 0.000000 + 100.000000 + 25.000000 + + + Menthe à l’eau + CMYK + PROCESS + 75.000000 + 0.000000 + 100.000000 + 25.000000 + + + Emeraude + CMYK + PROCESS + 100.000000 + 25.000000 + 100.000000 + 25.000000 + + + Bleu-vert + CMYK + PROCESS + 100.000000 + 25.000000 + 50.000000 + 25.000000 + + + Bleu des mers du Sud + CMYK + PROCESS + 100.000000 + 25.000000 + 25.000000 + 0.000000 + + + Cyan pur + CMYK + PROCESS + 100.000000 + 0.000000 + 0.000000 + 0.000000 + + + Bleu hawaiien + CMYK + PROCESS + 100.000000 + 25.000000 + 0.000000 + 0.000000 + + + Bleu crépuscule + CMYK + PROCESS + 100.000000 + 50.000000 + 0.000000 + 0.000000 + + + Bleu nuit étoilée + CMYK + PROCESS + 100.000000 + 75.000000 + 0.000000 + 0.000000 + + + Bleu fonds marins + CMYK + PROCESS + 100.000000 + 100.000000 + 0.000000 + 0.000000 + + + Lavande fraîche + CMYK + PROCESS + 75.000000 + 75.000000 + 0.000000 + 0.000000 + + + Violet + CMYK + PROCESS + 75.000000 + 100.000000 + 0.000000 + 0.000000 + + + Améthyste + CMYK + PROCESS + 50.000000 + 100.000000 + 0.000000 + 0.000000 + + + Framboise + CMYK + PROCESS + 25.000000 + 100.000000 + 0.000000 + 0.000000 + + + Magenta pur + CMYK + PROCESS + 0.000000 + 100.000000 + 0.000000 + 0.000000 + + + Rouge global + PROCESS + 100.000000 + CMYK + 0.000000 + 100.000000 + 100.000000 + 0.000000 + + + Jus global + PROCESS + 100.000000 + CMYK + 0.000000 + 50.000000 + 100.000000 + 0.000000 + + + Jaune pur global + PROCESS + 100.000000 + CMYK + 0.000000 + 0.000000 + 100.000000 + 0.000000 + + + Vert global + PROCESS + 100.000000 + CMYK + 80.000001 + 0.000000 + 100.000000 + 0.000000 + + + Cyan pur global + PROCESS + 100.000000 + CMYK + 100.000000 + 0.000000 + 0.000000 + 0.000000 + + + Bleu fonds marins global + PROCESS + 100.000000 + CMYK + 100.000000 + 100.000000 + 0.000000 + 0.000000 + + + + + + + + + Document + + + Adobe PDF library 8.00 + + + + + + + + + + + + + + + + + + + + + + + + + +endstream endobj 2 0 obj <> endobj 15 0 obj <> endobj 16 0 obj [/View/Design] endobj 17 0 obj <>>> endobj 14 0 obj [15 0 R] endobj 5 0 obj <>/ArtBox[0.554199 0.0 570.0 134.0]/MediaBox[0.0 0.0 570.0 134.0]/Thumb 23 0 R/TrimBox[0.0 0.0 570.0 134.0]/Resources<>/ExtGState<>>>/Type/Page/LastModified(D:20091127162902+02'00')>> endobj 19 0 obj <>stream +HͪUe2\ "oWKtTee8'?鯟?n[_~}?Y-MmIg-{oi:LxZs\sۃVy>Ψ`ۏTǶϣ%wz4|ׇ8>{W+9"ȐŕYƖڑ:ImGm#Gy2OĴ~pJطyNMVSIYimJ +QRT3m*1O%>Q"Y3Lq͔kp.IRE3܊: +qo.:[2C 8\7V.$EpPT7ke-S֎͟as6e bNʟ8Ie* ABbyF(S'NBXOct'Re# Tf&;&tH=TF#3o9T{NClPj$V*E^C~rD(LCȠg>r7rH9=)vtT8A_q2]_lKbM&$*Iݜz7wF!{o;xMAXݪYЏ|Mѿ;RN\Hrk}}u!.MтXԻ(U05ΩfInΣ^6\:g,F[-]&4X^$ +> { +WyՑ2$SWigܬQbWApx$Utmah;@GN9YjZA0Q4Kl56R*; +3CPI\ĢaAq64Y݆y鬶J!P]hyNNճ EzAC @E3+|YBoZs1?5⪲ފh"LwCMCܮLE_% (zqkۻd$VXɄ+. +%L4'vϰ%EB^]ɥa NPA"EbA/ + ku*9hKp\*v6jdT6}*_֤#%rIX)4g +YhE6ōT4M); $i(KܢʨHCq6B^0 :i8ʣIJhejLF +dIZPQ#3Z>kHmc3;k3%@ qB˦Ly!5ҲG87$DmͧVsۆeYղ#i"E0Xo@ꁈGcO!gg0gYJ\2"{Ќt ^E5GӎҼ03;epT ISlX nJӽil`t䵈\gϨMh +u!GqӸCEƕ\R2KEӣ}oa]R0?}MFx?8ɟ70p'x YMEO5 qN+K,bi6gb4   +Bxv( jT#d<ޟow /|, 2βg9U߬׋g]9aq +ʱ币Uߏ[q += $%;p4S"h&3ͻaSu kFb3+nh\$A!w8Nؘ8都m{eSb0jtuf WH }UqvqajnC3?RDfʬzMuaB֑R&^ o3hb?oAy7h}gs{A Z-Nyrҟ\6ZYhn<6Z'-NyN7^qN'+oy`+ʶl'+f協NmwgV+eg;KfDŽ,вA6.dٴa6̖el kd턬ḑ{3]nNs7dfزۗ9{Qt)9*qT&Hd*ًqP K`bϨϻt"T)TUR#;T&Q\c+q6>Upu ِLѨ;/ɞ!D潧 M⇔0YY#D$ƕ a >AT>`| +endstream endobj 23 0 obj <>stream +8;X-@5n:gU#h0OfpVp_**IOc@8ti6;NR^ijYYN3#.Xdb9q=8M\Qrs:nJia/RC*s;H +0)\FA4$eK_4Kb!@YYjb77s9X,fN9#8b@i/l.^I%B!]SpRHFK\I+1Vl]3LfpteJL6= +T`e`N:76N2)/=.lfY3@iq1OSk6o).()-Y8B9uS;<:/iHr*RdZd3u_sbl3'C)(3X$B +GP4D-@A\7%=nB&72M+)l8D:W%N0j'D>Eg_C>QXtM0aAp~> +endstream endobj 18 0 obj <> endobj 21 0 obj [/Indexed/DeviceRGB 255 22 0 R] endobj 22 0 obj <>stream +8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0 +b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup` +E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn +6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O( +l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~> +endstream endobj 6 0 obj <> endobj 7 0 obj <> endobj 8 0 obj <>stream +%!PS-Adobe-3.0 +%%Creator: Adobe Illustrator(R) 13.0 +%%AI8_CreatorVersion: 13.0.2 +%%For: (Patricia LE GARREC) () +%%Title: (Logo vp_fond noir.eps) +%%CreationDate: 11/27/2009 4:29 PM +%%BoundingBox: 0 1 571 135 +%%HiResBoundingBox: 0.554199 1 570.5547 134.9995 +%%DocumentProcessColors: Cyan Magenta Yellow Black +%AI5_FileFormat 9.0 +%AI12_BuildNumber: 434 +%AI3_ColorUsage: Color +%AI7_ImageSettings: 0 +%%CMYKProcessColor: 1 1 0 0 (Bleu fonds marins global) +%%+ 1 0 0 0 (Cyan pur global) +%%+ 0 0 1 0 (Jaune pur global) +%%+ 0 0.5 1 0 (Jus global) +%%+ 0 1 1 0 (Rouge global) +%%+ 0.8 0 1 0 (Vert global) +%%+ 1 1 1 1 ([Registration]) +%AI3_TemplateBox: 285.5 67.5 285.5 67.5 +%AI3_TileBox: -97.3799 -223.8398 666.7803 359.8398 +%AI3_DocumentPreview: None +%AI5_ArtSize: 570 134 +%AI5_RulerUnits: 6 +%AI9_ColorModel: 2 +%AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 +%AI5_TargetResolution: 800 +%AI5_NumLayers: 1 +%AI9_OpenToView: -125 316 1.5 1183 930 18 0 0 1731 34 0 0 1 1 1 0 1 +%AI5_OpenViewLayers: 7 +%%PageOrigin:0 0 +%AI7_GridSettings: 72 8 72 8 1 0 0.8 0.8 0.8 0.9 0.9 0.9 +%AI9_Flatten: 1 +%AI12_CMSettings: 00.MS +%%EndComments + +endstream endobj 9 0 obj <>stream +%%BoundingBox: 0 1 571 135 +%%HiResBoundingBox: 0.554199 1 570.5547 134.9995 +%AI7_Thumbnail: 128 32 8 +%%BeginData: 10217 Hex Bytes +%0000330000660000990000CC0033000033330033660033990033CC0033FF +%0066000066330066660066990066CC0066FF009900009933009966009999 +%0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 +%00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 +%3333663333993333CC3333FF3366003366333366663366993366CC3366FF +%3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 +%33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 +%6600666600996600CC6600FF6633006633336633666633996633CC6633FF +%6666006666336666666666996666CC6666FF669900669933669966669999 +%6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 +%66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF +%9933009933339933669933999933CC9933FF996600996633996666996699 +%9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 +%99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF +%CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 +%CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 +%CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF +%CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC +%FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 +%FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 +%FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 +%000011111111220000002200000022222222440000004400000044444444 +%550000005500000055555555770000007700000077777777880000008800 +%000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB +%DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF +%00FF0000FFFFFF0000FF00FFFFFF00FFFFFF +%050005000500050005000500050005000500050005000500050005000500 +%050005000500050005000500050005000500050005000500050005000500 +%050005000500050005000500050005000500050005000500050005000500 +%050005000500050005000500050005000500050005000500050005000500 +%0500050005000500F805F805F805F805F805F805F805F805F805F805F805 +%F805F805F805F805F805F805F805F805F805F805F805F805F805F805F805 +%F805F805F805F805F805F805F805F805F805F805F805F805F805F805F805 +%F805F805F805F805F805F805F805F805F805F805F805F805F805F805F805 +%F805F8050505F805F805F805F805F80505F8050005F8050005F8050005F8 +%050005F8050005F8050005F8050005F8050005F8050005F8050005F80500 +%05F8050005F8050005F8050005F8050005F8050005F8050005F8050005F8 +%050005F8050005F8050005F8050005F8050005F8050005F8050005F80500 +%05F8050005F8050005F80527B776050005F8050005F80500F805F805F805 +%F805F805F805F805F805F805F805F805F805F805F805F805F805F805F805 +%F805F805F805F805F805F805F805F805F805F805F805F805F805F805F805 +%F805F805F805F805F805F805F805F805F805F805F805F805F805F805F805 +%F805F805F805F805F805F805F805F805F805F8778E8E7005F8050052F805 +%F80505000500050005000500050005000500050005000500050005000500 +%050005000500050005000500050005000500050005000500050005000500 +%050005000500050005000500050005000500050005000500050005000500 +%0500050005275200050005000500050005000500050005000500528EB794 +%B27705009B8E27000500F805F805F805F805F805F805F805F805F805F805 +%F805F805F805F805F805F805F805F805F805F805F805F805F805F805F805 +%F805F805F805F805F805F805F805F805F805F805F805F805F805F805F805 +%F805F805F805F805F805F80500774C05F805F805F805F805F805F805F805 +%F805F8054CB2522794B24C528EB72805F805050005F8050005F8050005F8 +%050005F8050005F8050005F8050005F8050005F8050005F8050005F80500 +%05F8050005F8050005F8050005F8050005F8050005F8050005F8050005F8 +%050005F8050005F8050005F8050005F8050005F853525200050005F80500 +%05F8050005F8050005F805009B8E27002E8EB7955270770005F8F805F805 +%F805F805F805F805F805F805F805F805F805F805F805F805F805F805F805 +%F805F805F805F805F805F805F805F805F805F805F805F805F805F805F805 +%F805F805F805F805F805F805F805F805F805F805F805F805F805F8054C05 +%2727F805F805F805F805F805F805F805F805F8278E77F805F8528E95F877 +%7005F8050500050005000500050005000500050005000500050005000500 +%050005000500050005000500050005000500050005000500050005000500 +%050005000500050005000500050005000500050005000500050005000500 +%05000500050553002805270528272800050005000500050005000552B228 +%050005009B8F27529B000500F805F805F805F805F805F805F805F805F805 +%F805F805F805F805F805F805F805F805F805F805F805F805F805F805F805 +%F805F805F805F805F805F805F805F805F805F805F805F805F805F805F805 +%F805F805F805F805F805F805F8522705F805272E279B2805F805F805F805 +%F805F805F8528E27F805F80528B24C2E7005F80505F8050005F8050005F8 +%050005F8050005F8050005F8050005F8050005F8050005F8050005F80500 +%05F8050005F8050005F8050005F8050005F8050005F8050005F8050005F8 +%050005F8050005F8050005F8050005F8050005F8522805F8050005007752 +%05F8050005F8050005F8050005529B0005F80500278E77289BF80500F805 +%F805F805F805F805F805F805F805F805F805F805F805F805F805F805F805 +%F805F805F805F805F805F805F805F805F805F805F805F805F805F805F805 +%F805F805F805F805F805F805F805F805F805F805F805F805F805F8054C28 +%F805F82727522705F805F805F805F805F805F805F8777005F805F805F89B +%70527000F805050005000500050005000500050005000500050005000500 +%050005000500050005000500050005000500050005000500050005000500 +%050005000500050005000500050005000500050005000500050005000500 +%0500050005002E2828270552520505000500050005000500050005000552 +%9B000500050005769B5277000500F805F805F805F805F805F805F805F805 +%F805F805F805F805F805F805F805F805F805F805F805F805F805F805F805 +%F805F805F805F805F805F805F805F805F805F805F805F805F805F805F805 +%F805F805F805F805F805F805F805F80500522877F805F805F805F805F805 +%F805F805F805F8527005F805F805F85270772805F805050005F8050005F8 +%050005F8050005F8050005287D0005F8050005F8050005F8050005F80500 +%05F8050005F8052E59F8050005F8050005F8050005F8050005F8050005F8 +%050005F8050005F8050005F8050005F8050005F8050005F8052777270500 +%05F8050005F8050005F8050005F8054C77F8050005F805527752050005F8 +%F827F805F8050505F8280527F805F8270527F852A8270505F80505270005 +%F805F805F8270527F805F8270527002E2E05F805F805050500270527F805 +%00280505F805F805F80505270527F80505280505F8050527000505270005 +%F805F805F805F805F805F8052752F805F805F805F805F8275205F805F805 +%F87770522705F805057E7E000552A828AF84A8A85905A9A8A8FF592EFFA8 +%7E0053A8A884FF2E0500050084A8A9A8840059A9A9A8842E7D7E7D000553 +%A82EAF84A8A85228AF84A8A85300050028A8AFA8FF5352A8AFA8FF590559 +%AFA8FF84A9A8FF52050005000500050005005277B78E5200050005000500 +%52779B000500050027779B949B000500F82EA827F8AF2E59FF7D53A8A859 +%A805F859A8528427000584FF5359A8A827592852A827002EA85284590527 +%F8537D2EA805F8FF2784A85952FFA8A8A85953FF7E05F80552AF05270505 +%847EF8272EFF27AF270552FF270559A9F805F805F805F80500778E774C95 +%4C05F805F805059B8E9B2805F805F8054CB78E950005F80505F8A87D53A8 +%057DFF59847D59597D000527A852A8F80500A8A87E7D842E7DA87E52A800 +%05F8A853A805050005528400A8527DA8057DFF7D7E7D7DA8A859847D59F8 +%05007D7D050005F8AF2E05F805A853A805002EA805005284050005F80500 +%05059B9B28F8057777F8050005009B7728F8050005F80505958E9B0505F8 +%0500F80505FFFF2EF87DA82E272EF8595905F82EA82EA85227277DA8052E +%2705F805F852A8532753A82E8427F805F8537D0527FFA82EF87EA82E272E +%F8848428272E00052E282EFF2E2E2E277DA8272E53AF28A8F80527A9F805 +%2EA8F805F805F805F8777005F805F85294774C05F8527005F805F805F805 +%F89B8E770005F805F805050005598400050584A8A8A8532E590005277E00 +%7EA8AF532884A9A8A92E0500052EFFA8A8A87D007D05050005287D000559 +%7E000527A8A8A8A85205A8A8A9A85200842E057DFFA8AF53287EA9A8A92E +%285927002E59270052590500050005002E76050005005395B78E9B000552 +%53000500050005009B8E7700050005000500F805F805F805F805F805F805 +%F805F805F805F805F805F805F805F805F805F805F8528405F805F805F805 +%F805F805F805F805F805F805F805F805F805F805F805F805F805F805F805 +%F805F805F805F805F805F805F805F805F805F805F805F8052852F8050077 +%8E7727777005F8524C05F805F80527959452F805F805F805F805050005F8 +%050005F8050005F8050005F8050005F8050005F8050005F8050005F80527 +%2EF8050005F8050005F8050005F8050005F8050005F8050005F8050005F8 +%050005F8050005F8050005F80505282752282E05050005F8050005F80500 +%05F82E5205F89B9552F8057677F80505770005002E76B77627F8050005F8 +%050005F8F805F805F805F805F805F805F805F805F805F805F805F805F805 +%F805F805F805F805F805F805F805F805F805F805F805F805F805F805F805 +%F805F805F805F805F805F805F827272E28777077709B707628522705F805 +%F805F805F805F80527522777F8767005F805F89B4C00F805272727777077 +%0505F805F805F805F805F805050005000500050005000500050005000500 +%050005000500050005000500050005000500050005000500050005000500 +%05000500050005000500050005000527525277769B95B78EB28E9B765327 +%2700050005000500050005000500052877272E522E765200050027959B52 +%0505527677522700050005000500050005000500F805F805F805F805F805 +%F805F805F805F805F805F805F805F805F805F805F805F805F805F805F805 +%F805F805F805F805F805F805F805F805F82727524C7770958EB28EB18EB2 +%94774C28F805F805F805F805F805F805F805F805F805F877280527777677 +%F805F80528B28E52F82E2828F805F805F805F805F805F805F805F80505F8 +%050005F8050005F8050005F8050005F8050005F8050005F8050005F80500 +%05F8050005F8050005F8050005F8050005F805052E2877769B94B78EB28E +%B28EB28E9B765327270005F8050005F8050005F8050005F8050005F80500 +%054C7728B752522705F80528B794520005F8050005F8050005F8050005F8 +%050005F80500F805F805F805F805F805F805F805F805F805F805F805F805 +%F805F805F805F805F805F805F800F805F800F805002727524C7770958E8E +%8EB18E8E8EB7949B70772827F805F800F805F805F805F805F805F805F805 +%F805F805F805F805F80570955200F805F8054CB75205F805F805F805F805 +%F805F805F805F805F805F805F80505000500050005000500050005000500 +%05000500050005000500050005000500050027052827525277769B959B8E +%B78EB28EB28EB7949B9B7752532828000500050005000500050005000500 +%0500050005000500050005000500050005002E8E9B00052777949B280500 +%05000500050005000500050005000500050005000500F805F805F805F805 +%F805F805050500282728272E2728272E2852285352777077709B94B78EB7 +%8EB78EB28EB79495707752524C5227270005F805F805F805F805F805F805 +%F805F805F805F805F805F805F805F805F805F805F805F805F805F8287077 +%4C9B7052F805F805F805F805F805F805F805F805F805F805F805F805F805 +%050005F8050005F805000500282728285228522853527752777677527776 +%7776775277525352522852272805270005F8050005F8050005F8050005F8 +%050005F8050005F8050005F8050005F8050005F8050005F8050005F80500 +%05F80505282877777727050005F8050005F8050005F8050005F8050005F8 +%050005F8050005F8F805F805F805F805F805F805F805F805F805F800F805 +%F805F805F805F805F800F805F800F805F800F805F805F805F805F805F805 +%F805F805F805F805F805F805F805F805F805F805F805F805F805F805F805 +%F805F805F805F805F805F82827280505F805F805F805F805F805F805F805 +%F805F805F805F805F805F805F805F805 +%%EndData + +endstream endobj 10 0 obj <>stream +%AI12_CompressedDatax7r'"gplkvnlDIyzDawB(,Fb2q9)ʳ:y%D"oWooqg^/N >fhuuk?|_oW~ۗ?y^}~v'o}?|˜Z+jǴ~/xzUh-~~߽oWY0H|\i^f?}wog@Dnn\*O1ƫwr0=gy_~qw]7MġGȰR'WyH%ϙokw/?|# ?|e䫗y 13{Lύ 'g`>Lvy}$gNη꬘HB'oI~woBK_͇7tK0EJ;dٯ >~{7߂GRw#? _dd?o@?͇?}g?t?|Mk M݇7i }}}t|o/|Zvc݇h?~ ,W_|ke}xw-~OsPd|K*减d?J_>~J|XW>x_7Dxq?:fҩ?ꞿmyyߧOȾew/?~Įo- 7 s+|MZ~?=nލ-@^՛o ~+nϾyNg5} +}2}}"}}<}},} }|q>>ų7OOOOO珣xDs?7>)I q19xX^Q~o?)'Oh>},>s/Oz>ɖ>.r)\uþ,G1W| %T2^/ksmݵ&:^kBp]oh8ω_17pnMψ/nd h\c|FVs_nSω{Ga:78i0#gff3kyz9,eq͒y2DR邔A u>H!R $k%NscW_ߐgax0=p#(5 0X7DL6\4/a%#z,6Ѭ{moHt3: .v9Kx-B|_{~^ G0Q!r(>74kieKG4F41ٟ"W,iҚyUc +"0,αH@(i*`>U 5D0 }Y=g,R*Uaтp%"+)@Y<"W,x’X#ѲM$G^Xz ŠTJuUMJV:63)A+J5*ZV/.YxLO 97Mާ᪈ld񵬐 O򗬖_ [l#dVӉUud+.W/Uq-mlG4FPȫ;R`k8N\hw~= ]r|ri8گ>3=|#8U<|O=6oV?{ \'|LfBrWŚOBEg37{$@; D붱::"uxHҧOu-gԖOSʺ ZD`\2_siݯiM_<R!O$K0: _sLiFi՜OlNƿTy jûw/o_sNA~w`Ç>0I>zj1Ϛ5z̩b+#ɖ㨾œYPlQH9mdۢZe3|`0l!,9/(vGifyulec~O˜Yϵ~nڧϋ!iq-3 -CQ-Vi5ǿoZ-#5 W3"]܊vY9y>i߿:%Q']3ټs.!uZS?_ʇ0~9 a)Iՙ^mjQ!^fKEv]+Vs zWtoӾ}pE¢sˈ]>iY窕_ZS0I+ kܽ`mm*= 3`szk{iKkZj'~o,o&s3|zQd+ʜo7N83u1[(/Ow3vgxRk/== {)„qg/`lK}1؝ "A3"jvMa"&쿦y7"=곩NO?\vݙtFM0nnҝ{n|Ng+ݹu_ܳX:o4WPMIdsp^Kp5 Wp4Fcj*G9DB9<7Diy rFhJ;/k_L۝ϼsiM)jP?ټղfca[=|/y_\>oEnxNy^K z{38f8ijS[)ZK;~wQg}sj4גmwf#7ΗLq k]Z-{=ʸfq+a{p s6G>9~0>]O +ף(>7.'CD/!WodŞ lRV/3A,_?o>~ɂ짯vW; |W)MBdеG)lX==iN?6ȁ8 rvF8Jv̈́x {tW'Y!'O- > m Lri9O)ȥ+XҎ\O>0ݕͧqA@,"_YMB$"`,KE\ȂuB*Эp#-e9zބed0䮛P#'O jWL#Y,mirmhBUhNq1}"Ů,IW)9tT vNcOhuTǞ ^EOݾMB24,R!BHƜ"JLLξ?O Vgdqz[=g4a(mdځ= `cVOʛb21{n NH :#d" ']X,ľHSS v7ZC4E!Ja^vMR@8`up6a_sd{^ qUk<+j8>Lk!tṂً3ZHmۆ(6}B}k8 +_66*l!`!g!7>Mdvfh# ΁@Jh;$HIY57^Y RolX@ZZ!(!c՘l-RɴO1E]{g{+b}#6p\.#!%FOc8,;O6ݡm@ b&x],u+>h {lؿC6FCN'dU4f[ CAtmS54ċegċ1Y6)(igF&aNaVn@ќQHjLj6b(CL3"6_HnWI,Zbb-, GY G" 뀆Rlܶ)֡f~i(gQy"Ҩ؛0Σ0n4Yc$\hi]AӀPɪrw4"8b e2SC514gd[ np@&/FLU:\.8kW-k *4,P\dgc]66izN#D4NXU%xrDm):yw<ޢ*tMp8CMj坍lt I"Cm̴KQW<-VO4 AcD1#J$I( 7#)fPwՋ u I0VW, OqZ5mk͖ l-#+SnA,-:Ovp|jEȦ}q]u8IɕdF2>#3 QccPP.X(Pmҝ-vT[L@Mpdӡ`k%/L +ePp@dK%'b^79{π2^Vm޿c[o.=nlA k+nHq8 UKPn72}I$ `@9i0U4xH{9$C`#K`0w@O}<EjVOZ̅Rk jLrxvXt66FEaB1$X U`5P!5ԝ?ئ]A5)"huKScl6 #s B;IFd5Όuؘ!YMD3a!{++>R˧^Q1zdF`D?8%8J@ȰhSF؝nd- AO-HzĚHFS-jGUiD@TC7_nU`Pg unplᶵfk!Tr( jOGecMfx30n (Y"( N! ) @F Nl 8Q0.*(:>Vȹb+5pzGIuT=&| Y#H٠ +=4[JZ=kBNMp\?5h| hS!h9d6"֧(|DzLY-IVBMpqN-T*fԇ@k0SE$7$|+/,CF;$5csn NH,7f*bFp`SmJ#L O!5}NcͧtR$+Шp,3ݽA5B~=%x;bEGĄyY ; +-jeDJhPgBOc7 dkiu )eo/1[tbgKe,}Nn[sV/xƉ Nu(t%-tZ/پ I3D]r,1,87?/%Z!87! Fy/2/KYƽG$^߭g9Q"@t &rhZ^ -ڞu4x%* !oPpU*B XwT4ANH z{Q17F: +"!7W5Q+@6o ++!ydBRu#Ɂu@xBc2j~Brv%{ٳ(B*/PնNfǑ|8EEl3E9JVM)$bP;.5\fUnDM/'*`q`B蜦}?$ܢ'Z~kA&)Ys iCzk oDz&VY%9zYbQ{!-27 +؞"7pZl;Z*4ySXNUdP"2Z ru<8v$SNӹgŧk*>A>5(B;/?"v=bLlMjP|?jT® K!jJT\d|VaSj΋QMj]4b(vx}eSʃ)9/s#X; m=]ރ}.K|2ghAqzfM~@z,L@tkN ^5u:πz<b]e9jY%λET 1ɽ7;r<\=XL>.,tG'YwnnυqɪhqtIx >2(P}]\G6;-;gevj k YuɼuѪG*%bH$F&#Dbhԥpy˳]8d,@L< +q*S_px=ھ7HbiB誉Dex,#r,]>;hʜ:RqVI ;v vZtրO=2t1Hp2*ő>s5|Aۉ.NI)uU#Sr;` Pe?B%cf;\k:Qc!1r|J_as;b$~$J"HBq"RN+B+C=iAEאOlM8$TLK5J>+;Gv#wnOPtQ. O82GJd#H%딭`>_!?o3؁aCcFF=S,ȃ"cAi^R,mMlgĜßCjhL]+y +>ko-z7G ֈ +" ;GK5y!2Z.=RO(u5r.[ϪTSA]1OО=P<BݑmO4ci*TgM̻ > &Ҩh Dz2 .qV:2PV4bFbQvqYE'uaǒ~QruR4ԙAVׅ@DI,6vdL!mQR  EHj!-¿!Ac35l0MgT5.`)\wfCQW8^Lpb. P׳sxkʵ'XZKtpB9ؤjW+Gp3{So LIHF֡]*AaL8D:=SZ'QBH'+ݰ6VjP$?H5 +JI +dcY)xWIQ5)*dBFCoB]9aj'\$b.u1E4w$փjwC i軘* {kv +)&uNRuDWpHZ7Ӄ[ErX8+Y,bCo[7C- iQ݆q(Xq +Zȵ( b$zbbǁhG;ԉþ@&A\2|~$=.f&TiKD ˃ÿ! -pqbLTo3 +)MhZtT'N7x3Ha +f.C`X |#T{X04$TPbSZ*"lR +4՟wyu8* b A2 1VQr~p|Zr-2*h,,}+D[Y(;R*EEGHG"9Ӳ:DQ8#+0x. SߜG_"zɂ6_pKHg7Jy!b VHEȩ)wwY'8yyn:φs\x۪p[q.Ui8dQ 텨o B)#N}ⴡѰPI#q"L.  ܃)2fx48ju@ȅn|0<@EzMa&ۜP=0hpCe# +өƙ\@Uk 1AtU v\ur|E·欻z# 0v\,`8á<qkGO !#:<lijzc=%al/>]\Qˡe[?9~Pcl-]6=UZ!ќMg&7!Sy +kԍI +h|cg#zUV3fl߸ͅMn8`JJՇ:|BѵTˠ=`Yw\U;0ԔHIU"(pGiPtz5=GdCKIJ]b9SFV9Iڄmu}{.] )dV):PL_ѻ_Pâ9Pl K/7`/_Soء]LvzHFQ8khC`t7F*7s6|% &z?z^t#yb G?8/70XP;D6?|-U_Vo JCJˆX|X#Hx`:ZA!7p~`g80]. WzO1or ^gg˪f2s E^ waMR}l 3!IA晰!S#x.dZ:K\V^xZ9'R9K% Ra{S{}${V\a0Mp#=!\`J   ,1d E#˦waA2%#.h. ma."eG;$L5/xt.pFCI|ÆC=mK]T|\q)nr%p5ɲz0ô:* +D)@Dܛ6)K +K㳨r + +fP`^= vp,w$hӫeqr^R^MTw2˹ i)L$%|?̐y`ྂ;jK=R81y:951VuCJNsxš\һh"6lJw]CxgELTs'Sb +ؖC٩_hT)/|+^#i3pm{჏xc^! B_ǁ]"mHZ,1_=M8r_,Zv->B-Q=h*в+RT/BHOtHA<;d3ԎO(!~;`?!Գ IP  +|AvjP5#c$ajo г8h>'i$y\i\#zڲHҀb`R'.z\,Xnwp>EJ;\sGpgxz;/yuj-izWz|]Tv;cDC\.'SYzQn\7tV8=Nag8$HZ%`P +^]&Cg-q%~ȅh նǀ9ydwHznrass.P DfcKKv>֋D%QOOXk(b)zlXzkhM'RjR cN]InZt ha#zoGfJ¼ST m-j&nEÆ+^k1T2RY1dmk𲑒4a@=;J<諀Q˯p; ]ƪh AfRLLT/jsEG+n]ۗuHRc.<5-R_\^pDbveG`P͐LfEe`x#$cj sxp~KY8tܕJAk$rh;@]9&jy #_;xa%Ž@'p9iSl*lB\::W&c԰cӨtv|2a|Q5 z &,?5Z8W4\d+*^CU|;. w1`ktmvdt[ie7{kVXk8pP6;+qߔl.aюkB%5ƴж.8;1,Xl!72s,E^rt V`yG[yER7\JG`{=^^wG )u;7Ƽ쇠:Nҷ[ '}BO8Uz3,S_Y ߔ<վS iW,yW3TV0Hje[jT#.:Q΋6 ?$ U$a)= 9ь$lz-K" +ĹJc"4DJ# qsOM>EؓH1?V=Bq}n%!5҈ъdI Hғ{40F4|;QȎ3L])3_5hE2$ ${nz8͜:=O|[(]jUy%=CE)ƞhF6=}!anؖn3C;o۵WH!(*9/H\?i M-6m?mh +F{*M"IKO*9ьoz0Kv2(Q9.eۯ?F+'yۻ]zsO=H̦:l$Y +!Hy&:F{/Ezh4#sOYI^zcOB4 + 驽5Tu0#'O/4khn^|SC3DSCC+RDX>W6 +O_O(Q8 CbJvS)b܊sbAj&-:t^q'4(%`©o&(d ϑͽ +̞j^y0![:X0ۛB#ߛPӁbf$fIIZz#nz4o͊_OT Щi_'֛,(Ϥ ;u$v~$(y?ߊ?&v!q8[.XB`*6Ti5q+ +7S`يF49Cq֏oY%'SAi!qQ.;k(SoXuZ:"\;V"+Iъ&TβW9Of,()S?Ɔ^QWӑٳ^k#O3dXG>z|JFtգ{hviQT(G3Tq1=6¤Y<7=}/2kZNAe P1M= +DwTe=Naozlk5ᤤ Vqkl'E@[blbsB+]{cJ4sgܦzCWed2l&\}S +db20輌ybqz9tn ܮP4MNsx%˄l9rFG_J[Ot'Q꽔!:/Zi.o>㒹3%&$w7>$@O24@.#Y4L׵atKh&P[QbpPBYZ?}Bc&Q\SBlA7K([0qjܔPr-7Kp1m !QX K,i6rǦqm$7"i(eVo0G}Pm Jbmb=%llbʛ(nWh fp:̭ܟ>יTp}-?X@^VQ٭Q'C#/؛>Vj~+fko$FJY6lb؎?`CgW ]V,ֶ69C1#4X:2S~)YQ~m1ա-o `1Pڛ!Zc(F4+u|S6Z(Ȼ$94 Rz "')4^h_Sz:)lQ[j0-a!R[~GiliHliHE8f~r7QF.U "[c>f$iIT$ni *9(/H*ѭz CN|&38FgSjkwQ^乧P+_zsOhErޓT +(0=kӄzJ\}՞joi}է"NMX +S_ mt弯yLvj4բ<滸}71UhKWuDvoJB(YCW|`@pNMkW,~KR s_f0 晒4Rל;G̜j^+u.ZG:hyd< PZIH$H&_F;ق,s +۝;7t:Y̮V%~f&*Up{FyA!jsOe1!gh4#)Zdw`u}·#m|o$nI( I\zS4H'q>S3-@&Ɩ@@F37$ L dƞi$~S @&49H&HWA$aL")KOcCɑFqA rL)vC[!-(@8`m+X D#{wW$ijwf 8@FZ9Phrmd$vIHғN%7MOR>YԷ)xg7 EDG-sgal"9 Hғ{40Zvlzk{d{2~ɑs΃b.k-Xܗ<@`1K_fK j%nr3 rfK\ET6ԃ}p^J0^267yLtx]S]ۤ9%ggiSCى9!њVkZ%4_Kn^؇>@+Ů A ڭr4(.36[?}N혉 +k#di"ЛyܔЖkENj\3T'fg +D~/U_Mg׊<}VqE3OZQgA(Dܟ>Wr"*kSQ̊lVDIF R\+&|П!KNY9L*yC:!#?BCI{X,=M!GITPh!6^ZCv= V$SOI&A▞S%GIT;1{W cMcwBm$aI H)9ь$nzSY$=jDgЊ~SyrC)<~)YQ~x//}ۗ?n^]קp6@.O??ɴ?r_?o>~WjO_:6Zα1]7g/nʏ?և_>W|'RF%2 hcmę8 Hqf.}3p BVv? %7fF[}elyΌj'8ki&`hOD Zz$Mm)lG6M K}akϝwݢ/4r2qq }.O29y"PRg{fH@ }&QlHX%*Q4 d^'F5x""Q[LMNȨHKzEP +_z4 T(ڮ:ߨ7vze0{oF7a&M4mEl' 6ѴSZ,MUno~`H MM[aɛhhQ&x\ic;ݶD;-:4$X li(_E9mpNkinmN&EC7 Nxs +h7mf);&Yְ1l -w(wƔx5<%u/mLjnCy%V탻۽yo:ÕQ9[I&hN+_Ɔy@񳗿m0!K~RIIxg 8m^tQťݻ4k"p > +~oCya!;qhX;0Om q`B<< +G''Ee}8Dqֱַm,xkMF@p|/ma6~dgr3KOnY5VDdU}B[:U&۟m>EQ2^hD%*]iQ48K~o# cs)Ag|m̑wlc-a/ʯqf@*۟qE>T8rWV8Fm0jLmv[lW#_5Ϝ^x"2~Qu؟1';r=XIj%&&c2d9ʳmd5%doFO8隣?E"|蓝)¡l%5iCUh7ٮGN[5yqC8:?)/UGi4h)=6mh6PAYjkkxk0m$WFOx؟" D/?7cg44[IpqƝ<ەhܩj xW~37(?/ G>T8rWxOS$"[Hk<d%C9ݸg{dh44n'yW~37(?/4QC7F<x1_`\dɰmrd=ɮk0ڛHɐwGHD1_39lq +b5qg tsNh5铕;\{05곝䍵9JO8>rw;? &ƍ*>?GbdS Ϲ90gرCc9vQ{Y pPvs7΃a%~LC!WIwix1B6c~&;Yaڤg.0oRmTNcUv[51w1?N=/}꼗WPY K`HSWVR;FOS8p1p0 V=7$7΃nVI"9юXJg1$4ND5ijI% +t~wJjJv̻JgU<4$y̾~J_AWt4F9Fb4Y4QciVImT!NcyWIxiURW֛1?ڜjݤӟ=gͥ^<m=ߵj.}pVFT0ؔ +SԺ@n(Ps` IkIi{vfFU_T'Վk5Ь{XxG[k,yVQavx?pe}+1"z|Ry؈ѯV\LܤՙX%˫\=[}<0ɛQ"iȤ9_r lp tD:Σ* + QGOqmw!y=0=};@$Jј|Q䕕cJA)`p|` 3Slbecߌq%on|ag{. +j%NB88s&f. lG!v3$] }Z}gc$Φ _A nyW<q?bO3wތdCÎԊ'nǘڎGyÆ+LP8jE:Q7Yʤm9!||;e:7VIǜ +~Q2iO5F4Dęx۞GS;5*TϮc +ߪq'/`7uiAii[oɒGwnr;u"yz"|-k <O?i9-w[=vj&ϣ]윭`2Sswj顅)>N^ݧקq`:W(&^``xo 쑚iB$i.YૻFaCV.vFNTȣ/wTwzTī>j^QuӘ[Y!fG.nٽ%jŹ#~Մ{ٖv v;V74=ۉ\f&1߫6d;kփ,JnLTUa`7f\ՀyE,k-fGvPS}Pl.'6uqB#2SBKQSpAf{LFimÝw{`(l3۹gl9s%y{d_/'^`  W;;ۉ*,VXu@ $Y_Ih_ }!sLjq^lP#gjqNW#=d(L Qe&fdyɌ݌݌݌݌80#q3#q7#a7#a3#IIYq^2#e7#e7#y7#y7#+'HHHHHkF̱UZVkmVoX/a$F!`a$'(ɰSqS;z.Tn%N!nn&ݝz2dܩ-0S&%&%&%&%&e z-4ݤݤݤݤ8*NUƝVNU~¤ݤݤݤݤ872le)-0l9D坶tΚq;m?A[杶zV;m;mBmw2w2ީ-N[Nu9qIYq>E[杶I IYq~;mvq ;my>)i7)i7)q7)q7)+Χh˼ӖSy7)y7)+OЖq-N1nq-'EZjYUmVX{չϭ*a]q_vX{e_]+QHHi<,r +i v=K^vW~sp†\h7Dom nw~4P!{9H|:kl($Gl쇝mpd^/p?mclۈ$ޟN6uu#N$zkOeYmN3L2vw]Onӕ*⦫tWWyy3ܱ7foffs; vws݌v9n ۺ mn;o% ,&xdM\www-&.h 'vsm:8FXT?NY;JYqnt؛ݹ]+֍K⶿oźӖ;=? rtۻ+iJ_}>oy"+ؐ08v8CV "Ɩ<^-l% +tx(VPpej%$ԗHpGœgcGz#>M=jcbgD%"݊#u Ak̐L"Sns y\JOY&v!> ki(sSJpӉt4!y4qgȖAk#%ɝ|󬕻rG|`!VzsqgɭPI)]?aTI(굑}XWFsX,ùOԅ&ՑmgH(kؾR ~#w=ZwspGW [*q#PA]ɀ3d?vm5E3um}7k3w"֊y-v` ?[>WGu$'G'&3H͢9OVR[>lLv,1ij/A 5\ڨ418#l':B4rz> P2KS+s YZs8Ψ\tcg#ӄ(ͻX1 sji4Y{9kf)s-dDS#κuǀhd +P|U<6Rڪm쎵E3\[ku8Oo6YK2 6BѺyåc@ &t;{ci")sߟd-q[NM)FJ`*ksF3}$OVdžta%L`ﵑP4V4hwNh"Tt ec}4iDM[[)UcwGk1ztju('Fnh KC_>g;v"zʉ>T2AzUX.D.^K]/M?F`բ10T_BPqLcOMxzy=xWu+GJ_lfo?pR% aPSRGۜrO + Nߜ&8[ 9"M8xTqGJ>%pr<|8pqYElʽ#/ˣn}Bn"' +.O=BQtOf|g><~DS(Osw~O +M;yJ&Ũ{˔x8 #ZxݓŽ8\ӓ +ţ{IJxXٓkwӓrcON[DҀOO\„3}AqX_o}|h[|V\zL8,ώ??`` S9U~mHW)p:nZB_-Xw4@o]F)Oܫ5W@X#4" sT }n-< +%ԃ<[gl՞JqyD7W|$lc ގ0߼r`?E9nn_w?D/N-wO]ٌ`2w%S u 6F;*gᘔkě:# rgmMq#X!LW9ȸb +{[*ʙv%<Ѹ? t}lIF$+B$ՄT!cʍ)wg~,+y~6Ɣ'ۊAFVki.E_)^e>lXL+p EERgS~dGLڸ&b"G4bm„Y~li5(lF0갹)q)*yfXے=+0帬> +mzZBpoBh;@Zӹ!sR5LuW ؜:4"M Ԁ\+a$_0 /c3Hqtrr_a^Y2+?@4N_%fQ g4n{CV>Ҁ'pWDsʲ,6N2d'-~t}O:NOfU6 IU}zX"wP{Gn}0F]ÀA6ꎾE1TxYڇeKp1x%iShT%%6:a LUl"I aVŅAheiXTA%|Vhp6]4$c`(mhQj T%pMP*I5n}\w zf5!6 64`YK1,hcUBÐjMrՐDc+bF&pqeh%-y0*%Kwb[ Fd]$6ґNL%^I9#8lHMMztC94R 7"m^ICliqJP՝O0CyڒchA>bIFJ0F}A2oK4Yl5{=*Z_t!FU>5`b1yAT; N/37;r:\ݹڤoemƩy nx]fMlƟ5Gy&kln.IT ɣ҇mv )~FpZs3ĩK5 q Y0/94VzuDbs[Y()bss2IP"TԉE['H؀9+"zAbohkpݘ 9<>(!lCj!3ynHH`T @@NU #ԂDWwd C[mHudkp&267*[Msy[J7`SL뮟QVP ?8XAzK.7U`4]4l m~1OMS xuhHlid $( u 1V+: +5ڡxTn5u`+'c=0ٴ +%辰;;b*[~#}\9;KneYαA>,Ҕ jґMh?ȑ@pj= +$a}K!Q%uGD\VmOK{ ?*we~L>_fN?>Q2o6KҐvb9* 7tx۪mI%P/!wzBBPE !DUZ]4{-3c,2#-ӧ_2lr缎'U)l)OF 3/ka +ϸI^² +W | Q5_B”ģ4xZ. AE)^x4=9?t4}&D5Ϫ|홅\eqw'H d0FFZ!~HkE(PT|@<@|pm9[df>IY~z>& ݌jM5xHyB,,05J%Ӿ65נ=061 Zyޙ a*0/_8M8|u86+ZHPN{6";;z(=0.\I,ש̸?}cdEVl%q] Y"Sey0;kl/\ica#^YyC 5g̊/?k|O{>ו +->7=AW] )"s~N}f%S:^gGx7?CMƕl}"g>cV'(1X :?ñuhd/ CM+_Ye©ΆEsY@3ɃF \A@e콰>eo]8mW%S[sKfr`r-9h<6wZud eE59Se&%R:^s~KJ?mCݔfhCN|VďjF#a +q9:/ |,&}v|R>Y{f²' Hy3bfcdlpFBue{%Ou=gIVNw/hwnBx*G'sZ-PR!ԲzK1X4&*f:!gcη5oi=߂gΗ`!iy71q,oyb04f[ ůp{+p (y8 OEjdBl! %tl]g sy(ڷGһM`: y$ "SfZ՜͠ !)9Rb&D遚w j3߄Gwe.R7B(/o0]#!2#V +@-j[B.0MCI ln:cc :t5%i8hjH 7a]Msj%0_)TF^]~J+p2ȴ?4`ԝy*Ѐ~\`9rCÃ`׈r1hTU5X<|!93hV`{|q4^$Vm}לoZ践i>'刍(2!PS-X^LC;l;ږ!9Д=9 ?߾ 2|.3p[)P0zbƃ5Lf>*tkT52%;PZÀׅ,fNu"z h0pl2"iPrfuAgc}q}kEQ˴]v1#qQ%Xޡ[D*2yNđJ59M@SK ~6JF٪:+kd}eώ1HNis}I"zWTX~$hSc;$YXRߗԆcK_2YT^|o6=֘Mvh3?4u0b8s:9 +p5/䙓E}J~CrJF!dl0n3Ifg- 'WiO?Sp8- +YHA,vgSxk2]ş)ߍVn.<'{؜{=-FܰL4Xc:yV-Oj}󷿿8VU٧an*f'a'9/ϊ2⷟ˑLBŬHp{$΄m"1}0`^ aΡCK8=qdz6hTV{8B&G^Ҵn/z—e/z +X.2GQQB8-x#Yvw, +R=g!]W]5;c.WHv ,$'z)x.*NtO#80n>y["8j28EK:{XT2*b ƒH[|L#8j*F+`""{M{桐(9r{jU>ܧEOvNïG[JGzt]1ir-ʱ P*z,-NƓlKǵ~Z+ZmMx؁TO@Q- Oʱ1X2ܖ.W~&V)_TUPDUj'a 2W(mf?ozNstq6˕n\csN¯szx:Tou)9bnGYBEd%dW?B.& *,IuΘ +" 8|\JNx"INs1=r9J‡ cqM?͜'bB~&UrqF>r^B:V%#4;`zA ?SS[M(c^De?8Z66¹zPFKɉ=ܰ\MFe +דiAlCȺu*d_I`BQ<;nkzx[iu+jٻ 1A(,MkŇYwODly,hW1?8Osl)X qn`Oݣ K31ixLJ:\f$2{U$ ϨŨ^baׄ1UtxLt+B9QXzy*X 0<(ֲx|¼kCqo5&و}Kjxd1Cm^UK׽iKQ0rbv$ J@(ezڨVp9Cg&9sX| !4,.(="ԥO{r9dG0Ӌu<*f? ,̯Q_'m(ۍc=3 4Vc2,_3ΝuG㶢xL.:we' ƸO4D^QzGv\>X#}[ut-őʽM0%ٞKc ?.]YRSܻ]y3{X ErtP+zm9yG{\fBKM<}# $ΑbJyL^r6ɚB R<麵: 0 ɟ ESD%300 g)k7kVPz.#׎sxrOogո Ml;F19/rٍgoZ&19]]sd_ 7 n;y!I`Ce7\Giz QM1T+ǤH#"̡ )m{WeE\;77vV'Lšsq,/#[.Re 39j`-TN49MPB"2ZtvjɰTW1.@A-S}PL,xmMWmң5QT/E\/LU.yTxql {Wm G\3Xa5a0Cn^rmcmUF{eajQ.Аh*5/9Gݪ"*إaÑZ*y^63]N2;lƪ[9V9"w\BryP^r} &a:Un r9Py8p(KX+ha-V"Y˸Y؇,?ܝF $؛Aל~> vϪS.SUb"m]R6C߬JW{QV.4X8sc?J@έd#U'l4gDAi_/[s$ha-^Gt2E@!%QQ644R0(%!nE SF!tܶ_0 +:Չ8FOr૗[9,m;OK֒ƛkX={7l k0ludیH9}PA`@=^<]ៗ]Fw,D5Y_l;W_%Vx|Ғ7]԰AP]J.Y5&t<VrDlEy bAZ x* 04LD1{_ +i`aJRKCEy֢oɠNA7i 5e ,:X|P0.ӆ(,b:E240,r9 ;i1R0FFX¶Dv}!4tY-j"! +Op漌6#$ɬak'Fk@j]XܴIĦUG~- +,Y|ԛj6nler  K37%g IMv>[-P +Z{Ɣݔ^M7!׈jѺo`L1kp2QSu{eUlnv.F=[pNA>.YXg DM0NuFvgYӻMg4,btEї yW ↂ|ȯjsik\8Uu^֭ar87d/-[%JliUQ ~Ť: +U+Z#hi,((y3 үw8LW^j_'RcCΨ; jD~"j +:/w5G3&/8`vpKb[3+ꪳ>TtWtU[(oBBjƀcũc^Kww",R#$JXPAs:ZiE+M kWf,'O +YhbC6BQ*n 4q}ml_S6XKh3oUkd,BNbѢsY>(jb˄_I},X{iQ: *g0{mݾ]_%m6$-K{=kߊL1&3{_[$1IgYY-N/]%Av`ttVX!XfI@PV[H2HLYOJ2g:@+,җa &ٳ DE&eu6 tj2-,7qH-䱇`%M,ob/;Q~հ,98ޅ'kvʝPVF]+NҰΦKw2/K i'WUՇԡL*F/ 2zϛe7W4R횂-qΑ# 6,DMxduTeLY~:-[1TG5&H.#ƾd/n~}D?6ShK6;ʜclw\D_Ufty%ʜ&tB߶CG~d/59y0W]TMC BSLݡhŰ}>+xVD!$OxjWC%ߢİ>!Q4Tm"bR|< ;,d#ȹb}U5KF7.P6ɢtfi&C-[~-yE=bvy"^rv,d@ r{D:;A{Lցr׼ .[ TC +2[OEI_-VTXdIjlOCLS10JYhؒ`(Ȅ|ȟk>Qh0r=Ɏ]2E#=`jAtfIl_Y? 9擾dllgZ3htG#{VBhJ4LjE_ B HG*Sx2hrO bPfi~=4l>"}Q*k+646hN%0 k_ml[1sF$eBr0HN׈] J CT6njtAw xg'2M%lbʾ%K*AwVm—1d!׵G_0 +Fb*jg?JS'\'{$}5 3HPAк<F9A&$e *;Xr6Ui +Q;&br\Mm2Jq;*B%j8.,Kz2ycze[5 FRu 7'/ +0:g͸z+ a~T~Qgp~"o0rax{Rj}ac/=Op5W#]LPV +̋Un2ұ?Xf7WbgՍLG^GC+h]Ԉi)dd\n*h 8o;J?iv-ЭP= JkWP +V3s`hyus0yjg2@1o z8`ZEdە7bbhetI Es40`=$E. 9Y#%=K|],Dİ呼W\豟͏=XB =$n $l5!rS) +l |)k ^<}kPp"jJт% 5X Y aK_%V3:#o])AXk/w8a]/G_P{Ws9N1$[֗ x mm!$nzwV :&?tMʈ W4,j8:YOӫhTUEYKpˎ"߻r$M_^Іtw\r_*Zzhc+Hr 懾2l^h&Tӗ&d5L\P~jd=M;Elc#Y  +I5V^VٖR,4w tN! "WXU|+goTojȏ~tBK#hhJb=k=~q/=?9l+˛`Sn-k˩ f: {NK̏9aAB=ѣۓJ:N;XnA +kS9QjcnRv4ˎ 0祁9Dk"ƷZ}WTDS:݅~갾ٞ7y8-րۦI&a{gop˳(݈J]+ɶG/>4~.d"/ZDl|?" +."R&Y>k:hx UZ$vr ft +0f[2@'tM[B3@{WMuyc md ! _&?;Ks\QjN a;d;0U]wM$&sXN8gS=5z<Ǵ[WN<:1e7 c:Fa[@<ڔĜPjL`[bL3na{0xc,S"Uv1ED$(C\}JLyO~X.꨻akA +;O{sOfȸM_%h n?ۄۂ oPXwb_fnwC5_7x039;;Id"{eӡt+_.VULNQe _F˽*.Vf&ˣL*(MxelIU0EZSk^CRaÌ&&۪9(eP9Y/aݺ0+d&ly^J K[CZ0 k(?H2s^K|Zvwݭ־jB;l`K +U.1Ga_#ojpdRZNKT>5I7O+3_2k +p?BwB$yv!} dܷeG Þxɖr? +,M>)C.d3bV#PyWAX\݄ݛ**Ta7~S{Xڭ%"Ӏ jaILww!3*wJmmVNrG.MA;<Nր)&^d^XSVKht^prқ )jTL @h,U^ PzGYY(8@xrU-x_2f'w쫥d͊ +iUH|^AaUg"4$ˊәΜ=.!ӰyJ4 Imu _o$xJd*7/twKP{5n N.qr&\@×ˬO ~Urͷv +)ZOBTTi: =6 " K0M#pԗdJ_=P GuOSx) +t֨٧(l`grw@BH`&qՙw}U +!,Z@g +ʜG :(\yf옭tfHx&'{TZ:69}M%B&ŶRqev z? 6`}|:E9nwp4R^IUsv d5\Eܥh* db~_f"e9<#{L/C(Un5KBxr@6'zyPJ:Q5x}.4_pA(٢w\>w.D(<] \&gv.DkrK.8<}UJ\?k=b'nO 697;c~Sh&sNIjY[-~Cwܽv8D>`>V4vK~MzL2DvA:|>f ܤkcf|%ߏ|%on4h3W^'kehg`"lhS䘄 +PDRT(O&hQ|yW]ciw9vegCIu&4]LgaIǗ>BY^Z0hr@ +ٴ5p|BGc# )`SGvV2oq 7z=|;B4Fá.b@A" +^vz]OxߎMv7yq'C`j _.4WkD + *ǩ-Z'k=:ޯ5cOT fWO +4df'+[`ThP@hxd0B 'W_tF|)_DrOCH{#:)\[/fd)t5ZP3V¬]=Udd޸ J'IYHOVb>LVOP]G 6y =C(W\W/]hk'{@*%lzq(Չl&,pu&Dm)' +fЬr3 _4f\# &7'%unr&ịu`-(W/H_?7am#r'̧q>6.T MV5`9EwzbY +Y\uW Hi"BU4۲{WwHb;P~d`Um@򡫊.{niU%wI呻o1^=tb2K7!{?g0]fBFnrq]j!VL2PYP<[pZ ,J(su{Qv<-$eg'&#(m3[gN72Ŀ&y:ݎ/W'ru0jM=n~.JZf"݄Z +?c$a9tcD U[ RN(]ۯYfM~W^43pJ,6OFf`kS⟛Q]&d%;}6m ?СHT:EÓ;}}׷&8YM-m46 +KzeC['ϔp0[(+4d +@:'94a gQd(h:g:؋EwSp\`y ӎW'y#¤eLm"o=z/vrMzA9+f/dlg]oȢ &Rvs&MP2P':Ԗ, U=tu6;qd9j ajM/xx쩖$S +gAVy&*9&ӥnw|/#DSPC90}T{*Kpw9l!7gXuܼBHFQNudfd1f\k'6* (}PTW"&MiѵDB ++!@Mu#8VtclfftcEUhOg=@gҀ"ޙմ +zDmI'QSe$K]&}kM +%U4>ۿmmѠ#d\!,ą;cm,Nߏؐm:L"E3Dz>)纯b.UVN {`v#oJmXnt8 ABڎ|| w.~nQ?ÏkPjNE5E e?$ȷEMr-N_ש6I)"*MK<>YN-W=\wܭ*۱4M& 6ً]KH7f +n3ā#bv3b! \&dKl dˣԄaѭ|}lװ ˗-6XA0(Q뤎}"rqwj#^?~1\3":eȹ ǂ{ɉib}G7lXn~1ݿ=__s=\xutCքЯI(ãdj1J +fMiWU,5u{p>s?)ν6Dأ +!_ҟWH(>4,}ן̡߬GϨOY8B`UȚ/Ҁi#ǐ3c]JJ @¤MpL4W.}=-^,"5*oFY:`VWvb.l]mydhoTY|d[#xC" aקz1mqvgzjG\ujPJA9eQ74a%2\[;M"|POd"TH4w{""EWS薽Ժ̔/:ZJ]\*{|7ݟgl0ʪE~U#AwGg /$QKY&{УsϋhNpKYOFs٭٫ShU˷b~¨:hx~6Klƺ0Fh4vaϱOZ? !κmj )(n^3N6H?z[KaüHE>/x+q1GZhC\9MS1s"dFЖ唶$m 7y&nL\ۋ:D8c&<ڮn'% +6qY8i?3C.e%}cOʳuF6^M4pCvlFhPv#y T+SJW;[_mWU{mLCx1@#!. +P9w H됍]cx+4LWQ߃mx[` J +pIeFJ5QhLYΌpl40AK> -R+CjzkS؜yUn\5:cKhwzKBJ; <+Mv1sr Rr#yݷ IV#цCd?$L\5:'!t6/F5Š6j="^8-a҈L1䆬<[f]h(ux*Uuovuۏk,BK +cO>K<~W#˹xtM<՞' ZwGڈ"CiSA' 6j0h%  ʣdRQgMZy_]-VvζX 1D1yeu#j3JU`<@[Fq{פ<[ HAB; wzvEj1Y!#CsYNe 6^m~R5dn;ub*WøMu3\~uew"RW#ؽ󖍖Q'ʚJw| fݜO@zS݈Ov*Hކb6^)1-&ҟ|q6*zڏk8ל*CR N%M =9/mJ>}B ؙDBڨ*k3a H}K`){ |J>$]31aTVtO|(}qNeqSs$)|.Q7 }Sw%aշG3 85+s!h6ec?&fW+gpڭ 5l6JkйAk _.MԎBO/([=OJ?\Rne_{4!|q;r.>g;7S|Q ~Bo=ykU!ųh/O"lrNbM/OtXg  BɒKC~idc㲋J^)rDkykl/p3n(uᜢY:ac_Gv)u/]+(PM?&l.8]Kōh$|J. kVއ=1n\DA*j7 +N`fWQӕ~<~ҳ[͐CGEuo?O?O o?o.%7﬊(=_[ߞ?;k1Dt:?_C]Q,qg+=ėC~Oxz}7bZב!vZc&aB?~G&c5 C1W!= È8C>`' #qʟ5$/wӱf#\>dMXyDh(V+Xݎ~qN[ϙ|#ȄjՄjeYyNp<BpWzx8+=,Ͳ2IBbQĭ{'9ꚊPf? ުV޵>B"=Lb[0XX.@YDKس5;Ô6c?wo4>)].Ŕy18w՟ 5SP5_ӐBBYI*ɻRv*R" HY uJ 4͓J&|ÞgG_gUZ^ fy(?2UToݣ5ԴlzJ0Ij1.@p&Ch˹ۑl>?Ÿ\lWF3=Ưden-n z g;vtdi$GZ$#XdGq:NŔh-3ّ\YųT^!;j L8EGק5qQNh&Ez6%{Ж:PO'a,a/aٕ.-J+yX=L.qxDXO-f6V.eP-%ka.SKX=g ." t! Fq|25slezDy/9k8\za˕5 M\f:-2RIdd /+_"CZf|Ѿ<o_-eu3=wc/LПqEhsHT:ԅWA/=od[Qp6qif+"ʢ +ՠoV}Qd\O7`5acM!pwFpy-҇Oyo]0ڢwu{i3pho'f|\Tye`H-a_OL^Qr: K䞵5 x GX45u*мSrD:W@?(":!ե|f"W2e#jK<Ȧl,fA/Cp+n44~ +Aú.q[abއk_]PQ+x 8~5yg6V_Zѿ/.'WSJU1r4&&ýϗ6a8HCp޼? AGg ˟Ay!W~ZvU+iË7ϙx"hY7}h7[ϯ4Y 4>g2qʏ-)/`d9 BAݶ;LgڝT`֣-)qFpH>쾍#t |Fh8Vi E_Ie9W>(',hӪ]s=׀gV3-1fBzHrq^^g]5 {ްR^%QVy]ldv]_"\sN`$y rnPo، B_er0a :[En51U~|/ɲh#+` +:ײɵ0JCW5ӊ XntZ Г<-~.s֟#BzڮsYlr*ąHPC+Z[gitmkolK03ҕ)%%dcpd.Igs RN 2" uDinkZ!~[){"={FeMRN;JB3&W@v\ZOF+ĥP5 ɝN}˸溻iy9 .<~U:cYJ~JTxxfj@oj ʹnʏblFPCAFc1 +(@i&~eF2s3`XđXu7@`y➨kg +ӗ^j.Ou,V /}ݬ--ut!F.I 7ړ[#vZoo#W]øvPl˾==;T5=lop~ih}qlԭC u"-@I&Lmg:T_KJmy}6J#נb՚ד& }_BU M.3Vr!0,:fE7б_|}P e{fM98'8ak'>:Ux`,z_y9]dĊˋI{i,[u:_B]Ig^wUg*&_Yڢ;,c6ﲨvOAkwvqNjh@zH 4Zʯ?Ā9M륁U\3ޅp꾩BL\i"Tbcs!MtkUf,jCciz[&2fС"ZeL. ^w +,elZ斦AAiZH浃VgqvZ?Մt rl&l=|TT`[RfUBvuX#6 ԹR&|]LpQ4$n+,V)uЪl;oEEfoۿ]>M|?:( <|*n<Վ5/F;/W ;_QÕICE04qM0@=[B7-9=$9[fS< 1=eZOhw +(rt3h%hZiW/2ca)C֚x 01!h;ꓵ+(HF)jf]gsow &sj349o|Nuj75޻H>:QPrr9wN*b'C\QZ5"oi^p)24d4jzѐi\Cn1Vr/!q~F 5vW,rt!>aB"r:d+A Q{?F. C=Ee)Ӡ֯ISUH!ڏF}24J1( +Ks^`v S[Q fUEq[8FYU#GHaY`Z6Ku6cUz!mwDO밺 }p:ngss0vbFz/WuU8VJC+ڳ_A?(۠MмSNOK .병=*\Ý]U~#ZlվnB!?hh + +51ㅳV(~#* FTA U;h{jd-*JfUied7E=j"d|ul(7K{4[6xC~8kZ@Oh k%~#M!b!it>aZJϕ6H)ݭvZ1U ŭh͝+Ђ +emtrAt%NB0GRy|7 Lq}lӊ0&Gb{ǼYk}^ +K :aPLΛ@Ή5E|&iW;faRGNWMxψN@ חH&}zŨQ.%O~'G}cLwXӓ߮Gҽ$UonB8Nx ǡeitϭakmj }:(MxB 7;]jxlz|k lgyIhiL$s:} p3et!9ʘ|ڰ1M(^&cP0 EY)2}:avV)U {pL6֨!mC,Yd[FBt!qkKR&(J$nyOAC+ajnpÛnM،$y5j(|b<檙E jJ;5ߕ7hOI[8!dDUI/$G7#j'"(UZUCޗic]s +H~fm:ӣiҢDC1$_-բAAraBJכFCQLHU+UZ[UAjf?~ӚAp&B/aʷgk^4ȱ]HmyMfhCwZzUܴP0cm}Fky64KBlS$4HBnzlBÏַGUhT*hpIlA7kX'O]* &+ P?4|a㛨2)YA--І0U TUF\z _" + XZ@CX;͉xFoL1X;A^F=FΝsi8^y1oWkH؜*>ܻSZByd{rANm&ךN/Vq2uq aUz.S:X51'iQvz#$8u&;l~f`MK,ڴN ugDG$uQ.|xWv\ _@2;G$]K5u.+V`#}6ll}xw]8E+B?^e"GC@e9v`@3 pc0':~ة|Y N7E +h䙜յj%_fj(EɆ OoP?gt`kaqKƶT6W4@$|1/uLќQ "ILF ʕdPBhj4ɪgW6nKoP'Lauʆ*HA"]fЈ* O +ZMy[Ӟt"a1#aELUsI`|V$f G1@OAP Fff`#USNXL3 ] ުd˶'6,@`n 0/ء LH+`)ZEW[Atр1QsBD QJ:;6J:!Z$Cb IN0*J(ۦ2Lhhq0 ` eH-Mk] NalȼTK"[gvN6C͸`K6 >9!O7Jk(5+.} ~xŷnȖ_LE brBB윷 n@Gi ¬X5"u;l䖑m'4Hh -niُ]Ckv(0ٺ`!O2Ê/)(M#%Gy:lnlYr۬9ӓF%n01Ox`,2!hDD 6]x'j=Ȩ]$uTF7bI N<6$:QEe"J)=hWhiI͍U],(>)=G pLYb"4jբqDd2Bef4ӕ7H~4i0;~#^MZQTܧ)6-w9Gū1Agj2e~,ۓ.[FF]'!oq`ZMz89J_7[";< H1Mxi-ً8ß=gUW٠_dXe?Ő1XDh/ѫZ c3~綔޲9Fjod8"F%@ڲ^cQcx4xEqʰ hحYŔ5hzM)Ѣ(4Ikm|+C*cfRjћVȒkfTDhn}7>йG֛g#̃UYP8dm86YNAlك=[!6Bhiآ|azR$A:)|TelgDMfW<4(thh |XQ&rUk^PK{|W+4.[tuu/mU: 48,={P82vR8ݒu q1>ٯqA7F<3=ZNŢGA{t!lsɋ2on%!':䩡#w[U⇔X".Hc6D5WEB/6d2"AiK]9&^\d +ySb7Uy&( )/fϑZLa#%5$1 +rS pl+8_}U z||)EYr6d iC#Ε^ Jt<C-EFX.KodB@:(F۴^[ϴ>*W>:Oh[ YA3=1d<YыR%L1qVMM"dc$ݷf")lTcQla3M; fnͪ.Pmk":Q!ό_]b͖ݗh5` R +=t^,d);Dk0DXڲ8 K +A?Ysȩ]Dj:֬uTJB3bF; @ gUsӫ (#C28OT¤nj\hwxلn*3+:)v@2\_;l f@pA!7;fnƇrAb[px.26_RU(P_,~JLMBd9s{v]"[Vj +@:lJHb!St ̠[)~@SmRЩf.R0Ɇ*D4%:JfG-R[,'P8sko69 eЉ>@[Q@G]B \z73Sf_n +AӰҢ3zy2ÜDN` <q Z1(TyfU'80[ۣ_h> +C4괫QٖɰDk5}:eb %MU"|u,/ 7kVuhb\|ruFU^sDXSҩζ `hRE*@zNr6"LeTnW3C*ۄBH{Z) ZL7I |~*#jWs㿸ɥ* UD t*t% +E!WSt`>=60{atISjj!Zφ,)a +KѲLc)lD-W*amo +/#Yc2]j '*MQXt̢31OJW@`zD+Wဈ9hyMZ)*j .N{>V~pCU=PIj!QK8,I*jj*dp7zքVpb[gUL{뜎ȨNH2 Z`"rC;q;ʝY7zhج~MkC)7B4c/#mrtZF1RبfDuhCOTC_RMTsVu4fhbp `:Z]WMCuJM6U،m R`aPl,1(`@r*f"xH.Gjcfj%6j 66^m8: +g{AnU7ĺ'{R=BdSۤ#B-FNn/) `Dx"Zppf*3meW=&4zK`7L% +Yy*%6\ D#0t#*!2r-nU96ʐ›C &P lvTܚfo T6%ʝJAۯ15~0MQ1@*kp_rmqlNʔ2%&q)|X(Zt:#jn63r{l2`xq$Y7 z'FŴ"LM1bxWLTvL΅Ib[ᔯV1i7e(Q7XΥZΚ x00|' `KA1-Z GTGlN+R%uyxFVuό9@(\{a#[ M[ ֦Y4%,d͈NJ0GNJmF_ZV2LZ)G'Ur+1B)iSv?師*~3Dc z /4* 2]EsU ooRo72u#RmQH=l cb^A$T?ߒk;zˋ}H͙P38"*6nC,i/rrGBVU"6@q!S&:KR%CFX|kɪ*MΘB0M_ ]k[!ꤪyK)XVa]P4 +)l~3H]'T:L*@n6iZ4-FI3,h1Ua`F๷}}6t4C$$ǭtZEI MV-Z''DLU- ?Pq" kdta)џ)OC!}j9GsHpi=6<@DqV :])'5+1)ao5ٖC]|ug݋g_7G`OP&bXZ"7Qq)Վ'>MY'K]gn蔈E+ +~ܠJKŸr&f|;,lNDiMt7~2Tlf0,jQ?J1^ +#K >PrA*% +RIi1m<s:}Jv !5Չh~"b+a8Ѻ_){.! K{1Yt!);dTK 㥈bZS0ћ6[,pWMxJL/9ɨsX[CBr!ֶӀfu敝=4eh=>' u|N@%E\"|2ZU+4G"FX( SR%DndѓSf[, 6LHqns. i+ /ZnC 4!cryΌRţn/ѽ3N"Y!>lX,`Ed*I5jR@WKOiدZOwA]qJ ̫}VP"iayxSSCn Hd8 {]kJ˓}lvAlSed8mMDXi1y(TP'I1B[|i%\FUf8lm+ 69꼽6.HCn>+甐W둣PN6BT\Vfg[١aZ]zU:숚'jJ((s.͢eOIUvI= mB,ЮHS]:(&qXl2|(i cᑫy)\jQ|vJ5))M4J uզ@P&]50`%P@a@P9YKG+e՞($*J%O- byi-o )Z9VPI!KE*_(%mk(Q|Lh6'P20: K0"Y9#6Iq'FwY0I #bl@?r4tB:uhpUGv^Po. qYP p&k)<n +JRa"&X +rf> pcʅ]mLqG!z륀4m'@ #sWS)WPDN0pyJ^P7q^6ڑPFHͲnHD8 +DQqr򟐖C<_p :TeE/}Z;{A( *`HAs2|yg0J;昻nW9rSQ;Yi.F"I a:%\5ClRU +ƹEy.>mD%}Źpтa$vGah,4pMLmA! +ZT5ƢN(Kq"Nޤjs_6TmIfG[A gh}P8b +q "V􌂮E#T85΍Qӑ-F@E*ST&5n67֌>C١Zt.Y*h9%p]l⼘+  b P(qu]J +endstream endobj 11 0 obj <>stream +r !NMRߧbl~Bk4%ÿ"2cSc??O8gwAS־-sxS&zcQ&Yae6%bng"*UI;]9挈e`G䯖.+|#?1zA&R&:b.:{*qV2$FC N: l!!ly$ۜ«iv8cc N*29k4ᔋ(a16J+m?8727 f=+vbDT 1`"&06CjPT &-FT}>i@Ge.m{~PɴQ JoW&6*aTӧ&15æ&`#+@B6Rхf)( jL83_8,nNv,)u7Ai֥V٥t8Y]U%rWy&=tGHtbJ.؅~Z`1ܑ4N Ȑ!u;Lti5L(ߐMIo;S PjznRҸ0K #4 TfYE} +y)[>YB^+%5^NT̟,azXP"]N@6Lⴗc(Jg?$ j#||uJDRBҋ"&q^y4RTcl fQs)QY/M7SBDƔ*贕LJg OhͤyJ nF)6DbaIEEFΖ:7 ؊U7Vd!ܔԠbuU;yX?$vd 6 |آ ~ *$v0;3 `,=zYlaӨ9 EmrBRY(K)2j 02 e ~c6~UEya.7 ~Z X`S@T+gxR +ȇdC(ꈅ",Hg#U}5t}HQdy$H~_ghGIhNy|NK3 ?z$3z[ X\vh .vϢ :95m#?xXi#OR}T$3y+BoRݿ`)E5t?3|f*eoKbKS;䮫[ggp -Wi`Gyź"U^$1eB.S%9AZ@:F$4¹"X)%y%2> dJbKGV沕 m<vAo ҘT@WJ1|I4dV9I/e"GB"ixZu€T؉PJ./g<}҈:SX+FV"cN 1 ې$'UMgG2Qg@s(GGP>Er]"5W-΢ PӍ9@f荎 { %XL%Tr(drh)P$O<ԻmNLtSXi#I|[tU /AB7-=Lҹۃ6iPrM -/:'y bY0%)ދg;$2Z vS>XlxJsSܮ9>fnIiߠnՋ75GV-1}Ւxjlf3}IGlv#v m,jƽ Td@pNڵUn[Ecr}UYxLئlUXSn*B[U61oBUu|LܪV1}{nULbl5#9(,mQ?&cqiXs *f6}u0 O1xS\?"طk[Umţ1f.Uqڮb=mU޶y[ȭ#z6vU-ު@oS˷)lV$"C*L Ş'<[TaGֺ֚S %CB^1ک^|]‘r . ĹUQ0:ZjVzmJ4 +1(#NL/#L$xrrFt)hFd67:&)L|446LBG`[4S uGppPVt5Q`%f|ThUŒM!ĕz*`ŘF"/a#"KB\g qZQr]UkDxv9.B:C^mAO%v*N*2_%(+2@hRֈ4{i$ b[b(Arc-960C] UdpW8Ug͂Xϑ'p!\2i]0҂KުےJ֢ef}=CiZM̦mU0bJ +AI=vt'/$Q,/UQ+J%k7 MKU + u4XWs((߭承; m7>u +ΦBDJų0r+$ʶ9X)77k]6cfTl +*5 ǧ+:}ZP% jCG0׊X +', u"4p!betLti5M3=.-EKhPQ jH] +`dɊB"azziKT}M*֩>k g g2rZc֌}ճQVd8TnjkEi$(i(6$ ADWRj9Ǟd!D)vXC0!*{j#-`iC Y89 +,X!I%S656Lɚ׸OQYZdFeP l m>OU(=%^ʏJm˽Z;NZx#0[qkJ'l(FDba8^c!(4 +{kADH}(aAKTH{9AdGUxhT6Q/xr*QR o9D p*(So2tl4~ /+T%M gԞ@)hߙe4MV ]A҉i Nx;Wj+gEXBD' ELFoIaIBһmw~imbtou'[70`9f vƘ˥8Zum]<8M -QG:{~}"Z۲Y~xjXqt%s_ )wzxs7Cp\y,+W ΅2C݆S?Aӹ9x=jDhfiP$uxM)61S Q)% FG tqijr0Vv9+my(0e"M0w6/\v+SE6J]Kp4c6Tz5 +v%yAC(Qf4C[~nb#jz0Mʊ,էij2~ fsy>Ph=H |15KU ;j0m +jƜz>Hob<=|8Oxx1jؑ05ǃ:jruU Ք4d &N$uJ\DK-轹ZHHc +j)RTY )xhY͇03PS5VXė/tu5qeɵj)d?)i Ahj#cnj/frV6KiA#'KEs1<+Dd(ҒgyVE\eƯGlĊ4P4z`Q ;l9u늤_ +VuyjVVb&m|ɪ&PHDm#K)X dvZYkSUTq[S\3Yn$7A2j5J6P[㞃şHt[∖@R7YE`j[}FS\5`~qYgAWO;^!椨'hrALN")j b<7A<Wg,lFDZZ 3bW!P %5)e꬛G>9SRuR4-5 : YvH5ʠ$E%UldLtfJ&ze24Z@}KY@AI(06H-mYͽO؛LxMu׏yEUcluTt9 t{}VhUG'@m#F+LZѝ@N5:ǖf`Ԓ H4:QMD0&x@м F㚨| G,a3YJ~sʋxaj1BQC#fmaEjVo{LWkɴPxJxL'pu J,9d2x nj4Hl5S*m0kXAdA@lt&huM0M'4d~=+{H']+a"C_@eq^ְ|JVj2mml.>z%٤<#8FLf3*Ѧ+ 94It$9 Qw6?®'FPn@^%AęO9{( -%> YrYûƭea̝d@SoIK*Z_}ZWjtfP-ۂ⬈ڣaMMzJUҊ\sb=[.be)Z*%IM."%6#|wTwI+s9.L&:-Y +$Mf@\/g?Wn^}}D!g]O?ׯG?1EP\Q_6;|C/#IbfYmbѦta75B&4k'6„.U 꿥j lU{ `C- ]*DFk$T<@S +2*y3S0=-6iEbGڰbO.8JSy-eڪb⊌I|`b]"rbVQgĹ=)(Q]OZ9TŽ (ge +.n) p\-\H~ixQ)'8in}9T~N#(~- +׾9T2\R*B]DćyNuǭm gMMBla#"Y^"hϦ+w+&h8Ep;T4,{'|wZ-=`qJEEh{2 HBq6`\g'qe6 qt&zAh0N:e҃pM?@he*8uZT۪ }"H=jޔ]*ӂ&Hm/ N8ՉpLMq@T]kd&WxfH@re'nMQ"g}>}Tl7K7?~>ԏ33Mgo\~O/ex/]ʷG[W;k6?WWoxt:OO_mx>_~_\|vī7ӭx衧=wA}^7}~`}?{n/]v`ח;xEW=aw_dS}޾\q<_]=3ΟNjwC<َ,q=Eq/?Lw+%~sחzwW}>C>˻ۻ?lo֫ۛ/o]vYr 痟|tCvqfƣzCv賵C?o-уDwGK,ݝGCY=GCbkƗm~ru?w}0 tH=zq_f/zB.mv]z=Omo/w /noAژ/a2 |# <'g;ayB;gp=Ƈw?%c矿?HÖ\C+#ЏL{ -@hyz -?x {5}_MTn٫/.޾^ތ_>~{[/Xݗ/o.˃j7}drrGΟC_F +pRpO:P֫ۻ_=ݗp0hO:4mrwk_J#xn^('e>t̮"iHK؛{ }!noUS}W_~Oj^^|L:i/xO]Mjs5ˮ_gOwvk߼>jxo|{v? Z}'.^\Єkg%~H$C$#2E +;J^^w +ܧwt +瓎|iRPQT=fߤ<>7G_=$ț'!/S^<@-Ꜽ,.E~{4 \޾9}MIDH_^}{OuN_A޷9P݉nցw_\ՋO#S?t^F ͗v,ܸT\}= +av+{|[j5Ej;s﬜O+iS}}~rϏ\jG /]ݼ'Xk?x#9ͻ,>U_P803DXJ+>jE|wy7дfw\ _} +~y|Q%{*Q#;jdD>~*^j,fGp ?6\c/@!/rGЬ(>:)WxB=(??a0CtW#YGY|Ň,7O͇]LgwYx]]0UؗR{:o~t>t>,pr_:~OF{G<@P_bs]mK~ j6tN_^9Cg=Xp߰Ym_Y>!޲i횹=Ύ_-/CA +[v'BxxVN|^GИ'O +aw维yۦ[酇ޣ3ٺG uh{}Wxt^AOϾwzaI@)n`Ɂ2˛ۿΜ@=X9#l/ǟ_CW`OXw_MlNG~9qrQsݑvhw_^_ݝ~+Ѧv\5ȮgOw_^_쁖xCRfƷ{ڋׯ߼P'Ny:/ÛϯS3*OVx{usq?n=4d9"`wxNjVcPq_ϾױRSyvcE+quxnh#%˾P>ĠϷ;10]l=E10e:]΍(4LPc0?.9㟧?41HEnv |c:4ֱWWb?|o?a]!%r6O%ϻG{nG#"b>ۏwQt_n/?|?~`G38z^ƣeVJ@=u_W7?7iu:yg5V?bƓ~.J/ymZk?-DX?SO_®R,q<:NtF\LtRNt::Nt\šTG.5OL;h_G6N8 a?٣FǁT2naFOC__CNJ0#eڞ0>i{"Y&َ˳پ,r/4ۧW67f;D_^ҐL)=)@PtO:4Mp7/<'&ﯮՏ̄>YNjw"vc_$v9۽20.w7{@~ɜ /%!#B~:>w|ya;_6|q@| GDn(Ov>9_]'!A2G>凓oOq߾?ջˋls=6pI̟׃;}caguv߭7{7=f Ët.?;q§/=H_}{WǏ;վ|Hͯݿ%ڿW7yk%'^u &~盾p~{{u,q| +Qz191rQ_f0A c.Ƈh NF}n?^gqt}Р:f Lhp#\s}ދnYk_iK]>V +}ƙ4{1CO<7=? Stwz4EMjo/84F};ӳΣ-zEwd_ylR%"}۷pSrG?~o"NNO:O:x9?ZNՏ??'oм'˙.P;3=N촯NɯNYNw?i.I9ɍә YDZN +1^}Rٔ;y/g[B'zV;-.g)V~Пe:@ g5-ᬤVg c򒣾WY2r;ui:B L?*1XR5]?"Y.Vz%ճxj߷\uC[[+j',K/ +PKX&Yrs(/$Z{|Ofz[2' O#%ڬNF[TYuF(^YXPڙ֖,8Yh!y&֡xod%8 hDg>W6xp؍QCڜsg:RS?>>\#oּd--n nHvB%mǭR۟ptm!ƷR:- 7c;g]^Gtt1΅6]B qu[vgt^Z-7zI`wtdZs7j-͜nm\!bjizrxOPB\84jfїKt>eޏ"KBt5؇'/B?}Y-M~xs҄7FGHF35^۾}GU{lNq[4fvwťFC|Чd5 [M"B'V`!2蕼zҦ%.Rq@V&]W%X?>=\5yF"/>PcyI>q$K]סRW[LzѴ ۱̋HIN- {TDb|y+1Je'BN IFص%a/oU&TV^dv}sCq|kz!jzKZ!"k:Nt;\o PoJw -)F|t;?hBf. +}jAzId*]ŋ}Q/B$ +N wkD0RIVtp&w}m(4T fU,Y5.Cm+jj˩&)鵦 _jPpQ{@o0FPzW$q:.*gVOsy8b]ng|ɉ,Mطճ]ԉ1Z7V[nqM~OJgr eNC9^XK ~O :Nwes/ +Mp+//ɤ7-*զly3lV/Oa6qceSy. 9}͡19kju9n29'ĥx0Ru--Y')VyPG:qd"~'UDRAX蔙7fЏ~m_.NW@n5>֎M̡S %~"m!Uk+}9^* > yLWjvY.8iDMt"vsga^4Įh\xJ^,hV{^:[]P^Eͨ}W׿BUxA]y(H$lrdvpv4@{%[/J%/y5MoBFw *Wb9):+!T.ՄcZvhHD8NkȾ :ĤoK@Dun *"b +.Lwv3ANgp u$i05Xbuo=VLjߕ;$jq;#Z03ZۧmŒi}Cyƒ3щ'gASY" +IъȮWXC֝τ[ F&F*BXy8C6։][+)ND7D>mCmG²ԇJݒ~JCi>NBu:v)-zx^jv7Q~UK #CnM\)_345"C|ѻ\ +Hd)"WKCk[jU:Ur"4n 6`w~tәv$j>m۝Ns%h[4x3a:$goX$4㝘u:97 2@F 9,_Z[ėI>T)`0o-+7MUVxɍiMnZY2_׃#('caκ𹤪]C^\[OW8&]z<*I"TbO8h(¦H&ɮΧE;)qz[Sc%GQgWG̭A͒IRibNb6,B{Z8j_ʼCb +l] +MEosgy^uOpg_F]fG{.v<X}ݮ1iE[6Sm> #:,BM f7Qq$^n"f })xDa̳޿of4's@)!`6i'Oc|&~B$Böz@O&Ⲩa!9V$ eHԥ Sb=)0竢.jE7s׮Yv-3]uV-#zoId$EF#r͛6i |h=b8Ɨ[} u%(Cd#ЦPm7 +[o19mG|Vym}K`eNT +r`|I +uVw&QYdw3.l'QnYY~cq'x1+.Ř*,d($(thŻ8VbX1:"ּ /,>w@>y~8z5&#+ͶLg9z#5Q<ʄ[ ,PGյ9zuC*{$:_u̾eN9No־Mtv#PE +˸+(˘C"snj $A%;F2+]zne=,l5<ة6&0R_K&R[2zVjH1ǦUG|mY`qpfŧ +#(=7k= ]-:ȃ\FCZX`łC)n'xKbtn +9.Y1PXh$Gư) P\.o\j H\åi5ϡEEET\֢<5=e8o  um|DpR1T!$#>Ɓ%/~#nhHGlv 8h!՝!z_Zkn3m3ݮMB! h%AVť!+6CKc+Da'A8E#bP`iG)z$linG[iHh[}[|p&E\cB`\5G. f\(Ɲ$n_*KZA"%|8IeW)cYb7- \j'  %Z#@OOT7E w(]-An:w̗#`bM3W=dn(i䵣fq,\9Bs䳟}|wNgُN~'4AMۖW})/R^_侽nE?Y /NKg?ٛvLK)A‹_j!n°t,H ͳg@VJDѫ; v ȼ@`GhhLƳe2\GB4T/(_Z_3,MΛNgK|ƪRxoڟU,-Kv AȫKȱζXȋ-++UW-9`&`2w( Ұqh(L AH"xJ]ALp#hll7~eЉya7Qj"G)#rX%UBu&$qpG[_sS9!,zZ҇J{!`8Ew :T'V\c0Rl)EuAy dRT"p"Q7 .Ī'8;zb"YX{dtZ]bo$pA tkv zb ⸿ [ `BX:&GYD^1-x +m6S ^ K[Sӭb%=D>C6)c{'S EQ|{ + kQ-эQ 3&Xg_I0$嫦&)dWZat2’ v^h<>1B4 FҥDE`1gਉ&„ ^Y/,o*Lzz@) ?X̜ФDx4h]wے8?VOO-QD EQG݋JլʬΪ=oۼh''%vy*"yseismh$ 8;)/^hF(fHevb1NB1ԊHJ#OCCzO?x8^l"jX\Xc$Y,(,Czň1:Ȧ؊'§*R2Lc*MJԜ4525(aԀeD_f>(1Pݾq=~N(ـXc<ݗeNjaH^BPݤ1$ɪP]bTp%!y,T;hDX(}ߊ$^;$˰ܜ\{(T'dnX,NA/d5dJl4e'WU X R:,T,CRMN` tiO1EIɎvEbx9 ȷtD Decyc1Xж +$5).u*9z$bMDkT.5례#"r>D](mQ89EA9jW?a"agk-ofr% ~\]jiqXoBtbP|=ՊFMH0ER;BL!٭'N4P"O-BlEF$^AvO;ͱP|\los\!ӓ31,Eyl>j5hD1?i !V@8Fal=y<&K);AhRc F ^Y}ˁvޞ-g{hA!}rX}`WW%Yc1BPW(t}h(* 7C_ qndin|uv^LC|y< ɇĔpD52]/,6H.B|ٙJĔ (} s54NaFr9S,E}yZKȎ!fdà Elj 4N5&VygB/Br# +?}mm%qcGljYSӏ1LN^VE;eH!]ZV [6 ɇ\%-fk&ch|K<V9 +jDH+i_-@j .kQ\OK^2TB +lN +BCkG$H3cЮH'|(z"4hdQN9 S,d_OdPo<.MP.QP&}uPOL2%b$BgB]'JPBWÄa9ɫP041 m< CKbPQ ݐi̢+_Xf +]q$8[qc`{ԽFD=?6&1xuf^lZ*=Ӕ!}^r|s!~LJDG<| 寤Bf 並x4TC^Nkrܒ'(GC𲉩*W*Cb> gR˾F +6^YNepb~P$2`00 &9H+YIEw[;YN;/zL( Pe;0F} K Li# Y Je! bKKCHq*!\ʯ0r2&9y}1KX8q`0["vsTC;äH\4E:|)Q9[X?cMDL817i׎y=ftPPR ~6}}:ۋ~ 5#OL!t{ Uh2iHla8FGCُP(=!Q/ʑ!/AJ2<%VFYkҰFg(:uv4HbLJ(PS@щcE53xt`I{N0+Q]I{ Yuk43!_w̅HnX!lE"N ga ?|7%/F47-ً7fQ!APtC |;]o](|'yQmz3%#XlߴPBcWVF؛ _,%Pn4zcXx! T!oNgh3 D:,n:q`#[ۼH|25 +T37AZ( 0_NR/DINBYXCha;<>:nGMJ7:TW蛄\ae?HVM2 tŭP0/(>I!(-ƍRAFBc$}@#= 'S2M2K[RNf̤JmEq |@xf:F pɈo핳CuJ_(Bz, br2UZl Ab]ԋRI_,Nl\$VmjЫq`ץ$T *ijC%ytKpr$;$t)˄%ee,RՠEH,.GeձJh|> +,NqƖc+%$A9/Ps%O0![MEi<*{ƶK/-hk+Cf u=bl\y iy5.*Kb=x2e^ن.0C)c,y(7Y~ +̧AM~azRӍ4F[  +3@>a(iP쁒 Ҭ-DKz9Uz8őhaMi/i꽙1-VݢN]G!>-\d-F߰O<&>HNHZSK CD4(I +OeAP~k"+3be{;PJptd5_,`Dtv$`5T1v7[]X"`1͝C^j0*>0J|w:%spSJK1n3#4'L2^ۙKgIL.ԙA8)VB| ȡ+"GVbi5={2OB)PěaX +R_;6tWm9: fO$@t)*v Gqv$awǛk W }[P)_ !VkcȢ%4V_g +p4er=g nȕsx8(.ڒKm6ln)Qa"PukvKAƺRH"9s$mqe=8OƕEA!dA+r0, m3 p' hd삭/$$, e7C z ]e{LnTW=7XH [}f(]Qm^To\8 `S;YpDZ'wT5"0a(M Rt/dqa1˗ -9իPA=Je$ y&NbPzየ"D22:ӽG`#P"둸X$ƖO[$Q!96wT5({/fbN&j{  3.Ca ++nOjQaqVy nUIr+.BvVO1cQ4ic{g9tX4-ef#zwvE0#Tp1 +(.2*|@W>CS-yC8W+heL<"Š`P[8.XClb 6<"d38NҠx.CeW<$P˜ }c5+Ui`o͑ ./NB͈WPqr al(g .BP7#cpل쑈B50n@,ulQLCB?4s& iBB%V2h-vX{藀^B "Lş5fP&D g1')=O|' M:[LU5Nܧ]ЬCՠT.2PsIH&0R)V":D %7*׽S0EξIN ĐrޤViþOZk i%oAeqzOQi)\6`"zr70HjV1bhhV$q^ē;o7VM4e} MJch[A,$lA0^_2U3+4cPSh}KW"mqyDs]N%RAggqNB֘ٲShGȳDYl<X:{}EW:d("# ™= @C?'L3GP+HZ9M)[j4V:F%*ҊU#WT&+aSZj + Lܱ\_ώŗvgFX&D`(j:Ľ\깦(޾diܭk3u9[!!,ڋ{?tlU)Fv(d\RTF,DCiS .V ]z#>-b"_3gj:قUґ{JY<8)+&QayhBc" =Ф RU76%ើi`Ы޷GboLctapA: EbA>VDb|N-gX(' %b>(q8$p.Z+BE_ $6~bk$,1 +7V` a{[\]>4b/)6 2{]@lzn&?~VR3ߞBLC/37u0Vv"wN,9~h7}8?q_W_7 Й=G4v֓:"j Tܷk'Ȓܐ_M)LVPv"Riho(jtO +2^$ͬw>+I* +PZHZ 0%8܈ݻo*Gn3K.!]A>C3:2i+9E{'ˤ Mt Όhr5;.k_ +ZMA9P+ɰNbo|%bD[^#4)<)Nta)d=gYW +P1;R2BSYRR_QA]#"4(Hk`DHVRliu$D +$0ӡ.d=UVf34.z;+FT.Z!s8*{p(pP4)/k +d6W,p`ӈzHw ZWEJ$v[>s)5&Y;䱑{_ E\+BŽZC[I@i;N +ÊP&\pXN$ +k9\aD m8zNusͲ\jc(+B!a9TG#-@&%M&GLHbQ>Rr*^ R7pEVhFuͺ*4 ꨧۉƅ?kQ{bBA*>Ի%7ptDNqI%\RthA=e +bᭃ5H2 jg;8!UB +Uw* X%isK8dPl@PYδUh(;?g5 _9nu\qK>\`ki>oL{1enB]Mf8>!XyW,ȈH3xqȈkXFdԀ( !?Ai|t|i5 ,a:V!r_u bi6[$\%K%D5'cGvR.pq #ZJ[m Qȑd4@ؒv{DPD;RdZ&xAC'ZbQЌ#rvYdR5 0շg :&]g%Ts5Yl ZnQ 4$luasr]U@ָʄ7TaˡHq=ity&Mq܍.w 3A0 qm49qh8:C"z)ygFpѻ.kEW/J-W,V8EzVS] +tÊUvO& (ĨUvL'֍Ƒf% +Q(tDL=hDY8DR;@:ai]΁W%dJR䨪`8Ι RFa}2CBiA7b b4(Q(#IjFN"^I\Sc-W +-"FZ d88~aڔ'<]v vbaWa'Fk'kKt8ے2RcKsb9 ]q,R5^D^Dbhu7]4D- Al4Ps;u;x2E٨Ρ4ϊњ$M}x\B*3MsQv6Xa1?-:oXz4㧵1d&:47;t|Zk^ nNxizs?ƾ @!0v CaDa1yct8&FTFw]b0҇}s,C(cB88Ccd?=BFtF"#:=x A..HwrVI eK,))8iNъ@ Ohz3X8WDXynv3҈8iDa4JQT<)|ek%kaS!m| {ߑL3h+GtbezZe:ll\{3;X%Ǹl &Lń>ǫxcNqNMyuo̬xcnqN~f_)+:+ ƅCJ㊎ÊB!qE!rcH +T&0 򳕱Ej!̩kb4eՋ2$ԛɍ}?0Cs~P|,B!{E!5{ыCŬ> ,{L6qx%>O|. 4(~gA_Ǣ +ȢȢ*Ȣ:ȢJȬZȢnՐwlGR#u@UdʛEF' F=:2tRK +qHdllTI- &D180$G+Uї/(]9:l)Z^(ADG8YGC >W!((sSj"i$ |BtB8*>% +@k x$ۤo%FiqPiT?dԙDp=nT z +$E9M¾1fTBxp5TRtqkcz Խ5sHkq׿X=<5tP,j`ヹ@] Er 4 gw[zx[`o!aF hfmLqUv9+/ p7tdGCV[/X$bCs' JQ@iJU( Blu+2#" e2 %ϡ8@:${fn<˖P4`r'q.Kѱs{.M8PxLGDpm$o[f\g4D)eRZ vZ7X0F>:A}fj)} sKIc>;8t^5}LfX2\SccW^߱XtP:)n +Cj&s8. a9(\& 4A kpbX9J> f"+QksDJ!(f@uR t#Tp +7@Iq6 C78Vf#z00[%8؝{PGZAXB +o ]^{*,4"tT<$g2{Ush.rn9jڰ~)2=RF.͸))7E)nODU3l"DtinK*4g+vĤ4^;<^'AZ`z@ztM|鍎ܣ3*B=,3x",6X1-ρ A"Q78hЧGR&"kIzBA ՋFZgWHUGD>{kh; "ty$B-D +ܖ@)9Jyу)eK#ب'Pj#`K墏簌®>t*CHEy@Wt5yb⦀*k9,ơ +/53,y}ilWU>Z1 $0j΋56`rqPI yqOHJ .)ThA^JufFEo%;rG*}I#;3I7Px BDą,]Ns{<%{h|TD ؜Ki8zLv~;(= ꂂs#98ˣEvu}| jjћwJQ1Oڔ!>صv UQPt,@Tv]Cخ\ڊv,Ÿh!QՑ$)㵚jD^ƍ|Z+LlⳡG:IL6|t3C(lيd?F#g HJiLl[Jnkk\8M!Z?EqtXqí2l}il(X Pt"JBdcCҺŕPrJB}-yq;t/*$-@>y`] ˫ٽGnv:X]/Of/dmjSg?r{j^~x}uv\Z?X_/?3>wy/N{|u֏#bB,}ye7P;ߔ;q@OaNj*G`Q8,OKwnܯZHD`l{7K3 G Aʟh؋W%ǵBўqtoׁ 2FAsȻ?C3>JXCY̷V`F2 dIɪu3uZtu6N|5L148N.RVFhiG>j() A,A_8OXPImK$Q +VheW{ _7dY(L :PǼGI4AqA6uy HQ3H%CؼJ8/}yuQ 0YLrr9JZJp"WQG|a,_a;x«W a oaq5T*䘆}zti< ++2W;?xF~frE᤹.nm> &s\bnO6A=T#=u Hot~0?\-wГ7x8BIUOVNIύOBi"wD6oăsF1vtȅ +]Sso?)?C!PzlU2#%m.ZP?= +çWٽǏ_S۩t l#CDz 4Y\FQN" '&$:dyFrl$)GYcYXhY8 d%x@|)Yma>Fi%%QqI +Xgp}@'*UQPu6S(:\QCE^:(&t<6*R1JFǻ& o:ȔK DCy&083ޠq}a>RA}:-!fC4Gqg]M1'f0|#' 7y`ճ4pp }J@S;>iL(jS@R9S +w 䱗%F(r䭽\#])Fw}NnʽDaMESbWG &&I+5rJZc +Nө8]礖 +Sg_vG4>HȇS +gn6e akA$rP{эk4JjL]YHuS`UԵgV4`5qukw3 .sL<+68ZOp1cKM3CnEAqp縊퐯Cl~y#c;*20 琏{unN]eP[ﵡoJɒB\Zs"ֺ$NA'24f1!+]I_K?tH P v)4 !Iz:yeϱ~^ w HhP,)K]ȉgfMΆL}ఁ"CJ3A3#ƚ :\Z2bPuzxx: Pph 1TG׹uIW'!11TwXOD FZ8@NR7ITRQ:EPj7x6JI0QYִZv}.,/`).'a5e{uInSxN:I4K,#DrAD^lQpF : =hN DQ)=*Is&fqaz1XJۣXY |ɠ}_/IyR< q;6NM{HPQw!V9x}$LT搎"_weV :Џa'WaֺQp[ W%ED, kss;h1iZaj1ڟ\+7xroaϙ<><"E.yLusѼ~0r0h+ȉOq r7 +A6$Fٞjպ:"3nR*ʏl/đŎP}uڈokm 9?;vEW4 SK|Iuj (MC#w$y ,@yGֽe@JhwJM@½(s>HQ1@VDNwU=t +[5/ȷ Ol̍&Ytv_진~F{Yޙ##+8c$tBF >A(VM{5/2ΠټO'jOMK:و[,ҽF 3bT muOSDBV Gwa\+[ͫxݮ{5D%.Ҵ>iވ'}jOP0SqUL-{ܰ:f{=OL+3m|VO~[=ͪ+]a|E{=u+O>7]lN:ƄAV[sn\lMi?ߜsD3niϭt*o>乛mۈ['om'uV<_>ymfm\'oWwaJ],߃|oȟl BuO67z F0ǎNO6Z!ޓ =CCnz2 + G<&NC7ȡM}/L}B#)uMŭaa|{Pdv+Oڷn[Qڡ}k/W[s ^ `MޢOߺ'zOnoti|k|S>'b[}td5?L*߷&6Y,]Do,=ɣw\6mdWnD#) *׍Sbn`\<>m˷?8YW*Z2 +@!RPz6\ISWM?TQbV Rdz Jg[G[i./W'u yN(˓i$Έ"FGE/[Y,m!)~L$EgE8ĊbAqX1"GK ѕ妆`c Q﷠l}Q{w_`zh:f{4lN fA?uƆb 7 +{AFPv݂l +eXA]t>U- >dBƞ^a3kg,Dw¶(@;0(- mٗr̥O4ZBWTkǘJS/pkMnJ1e&^RȲ8Qє&szxbS!·e0h d +qOTqc;>}N BQqUJj:AHBut 1z?iD粦idb\(2Z>+x#tT%4'PKT'yUL84k*׍[_'DT֋*,cm3\8;ЁH% `&&Pb j%.lZ.K+Ƒ7{̦ NRo$Nb$U)PMh@dlVty5Blw1U7a!@ȌGP;NSE'!\:%[HbK5P$+,}c6K<'N'EZ${Gb:X6sVB:F֐#?ɮ;,1vx'l")pLuӤlՌUaP)S쎡5T9Mw}n)3h[rFQMدXR{mu!;,9H'"UEM}y^'I4 !I`=d G^"Fat-B/9#%0}Y s) ]iiACgjN}Wp01՟ \[nS)~Za; @$T2[*By'ahߢp +JդԬ\_44B" ˑfV}3 Z}kcmxF2 E¬l BQl AN){<яp%di[ Nt ]ܚPTV]4QVQ6{ ƚwXS[5'PsoH=6=ɣ-{\\kNMcv +Z#쨮Vy}ɾoze״&4ntin{* ur-;aʷKθŜe4v@j[!MJ6ia6뫙ڭq89D䚾}rEt$}MzR:P]Z~4hXgU +EB̋SA2q)+<}[G\5C#pn!ַkոqS-fhL"Za3 +SX{VgI-6>KN&Ӊ媡 jA85BK PተB3UEY/lp}t;rFGi^N. Y/wMBw#ٛi*uƔ NF}\-q))q]%A AS覍5O$MQ0 U>sjJ $uvMF-\Ēw][tRYo8kn/]Ma^l/hL ۋD hro*m MϠf;ߖMc#>,/R|[ǏSVac&N\rS- MCpӸZvcEu@;ևj4a*G :˥F,: +<.BZdWVCp"W bXo{lD7T oqݚNѮ~4&GtrR!t B+<ڪgԸqF}yBvp +A4{ +΢mMcV%f"|TPFVXNMfɄ1 q čV}]:jALBC.ڸZvc=&;4L< WLr3`8j9uwꎣ魀3KxꆚȑBY +9yC4+*EB/CFMA{2m^ʝ::򯡂+tQUgɅrԿFB.y+4p(4ꌛQ@vdMVg1`J E\E+tȓTSk$@3m6A*-L$5uBjJyFujV @HJDTS aLPU*Iu1}:%BIBI!&ۋθzY1?a03n*/8ZͮŽ)T> |[ޫ׬⟥ȥ~jACu|Q1]SP +suMα>о :Hu7Pݍ)5g*dchrN֧@@5U"oiqGW1قu!Q 26i5jGoڇ-ojVn{F\ϒbK$r}H|-!Lz\!GI#%'tWxZ؋Y)?(@ґrŠȝG]ACJЧ٧Ԋ}Eq4k + 19lͦH(2Պ\%FhkbH`;p,k\4TyI:E. +92|v a0 [T!Djz=!TUɎ TG3re; +)cohD-a02\_x9R4 )t'^y[[AfyF>Po4VZa;ڄ/vx>q"{ dYXMg sjև!"i3HZ߽0{[YcBt~TqRO Ij7iv*QDwBQF(K!36,4 J4ֺ|&tuv2ŚudTʯVN6ӐV^HImQU ;ir@INPW~x-o{0tsQ';Ejr[+dS9UMN MOf앮|[$ǔW1^fR&aCm+D<3Pleq}F8zQh*{Cm 7r +4m{~tS'u`3rS*ZIRXh;߸zKw(>} ϙ4F.D-fpRL#Y+kG+,z8(}[ Ո /rCWvؙ4&O$jTX!r`>^TC<[> I+PoB|*g^z}e{!Pj*4 ZavrU-[Z[4tvpuG+ܕ2:֓Ի8|==yosr͚uلt[PnJgOTW6 X[TXsMP4Qr"Vwvemoo|աr=$Vbg+>жit**Ā@,Xָ}_S5_# %Pre _IBe[e\3پ&e}BέK3]#֫FX7SNԎ#DY -CpA`SAhִihVbVz$,p(5#y &FB7@,zipӸ<[E]_/AUfʦr2)1yVaG^dKsLw}(0K*viKS): DO#^:R eġho4]%VIcu;5660cچ#I|s (– ,>3yXӻ>miɷP [T*> 02;z๦b0pP?U&CV㮳#/ (' Z,ǭ ZV\{R'yBHǘ4}ݦ9]e\J4򡘂LbeC$^Aj5.'^eq1KpSd O fnԶt{#hh19z-OȦ6Y"9,/2iSTDdp7"R[>9Mg'kOtc  J EM,yIy>0 K#Ӑ`TE :|CCcGgӴ6l)j1NpkX+[hI] ɨVt6I|7k0r.ZMC ?-6%j ~J8XcZٸZ5EUȔTQգZ# -2 ФÀL=1ۥg'&kiك)jE>Qy1n 9OtiܚPʇ#8h o2BHf+ir`"0w+}TyqR'9rtփ jɅ[.l5vg%S6YMC jco{pT3Y̯aYxKtR,M !m8k[lS[1 r@j 6]-qnmھCِ!!D#| . fi5Af)3O} \\Ѡf*qn6JO2GP\Ft!BXMj49x2>䖘pӸ}eS52W,]K{o>?]<}}luŋ%D~}=,fW/[?8\Gg]71Œ/EcPٯ6/ag~dյ;Ev;vO# \NԋsٖQREG%rբ}$xq+(ٸnpc* D('\o7p{|F Ѹr9?_^'lo #?K^JvO"n=^#ۻ;v^gn4Hl<.T|~$s0O(~4W#n櫾V!r8?=~ +HeZOLτ`Laq +=iG#Dj$4TaH1V'Mpv +G(7m&~УQQ ha\q"3bx}}r|9Kه8CoLz4?;~X~Z=}Zᮖokt6lTNt{B_nۿNE?"?ˏW/'߻.` ME?y5mIp_ѱGu=_/7^O//}򻟯O?qm4{nv6秷hzIۿڱXߚ=Љ=.ȞO+]?$7w̓ǏvӮY<{ck|ۣ_w}{ܚ;q#M,<eu~N,mݷ3Tu[\jÒN/g_y=v,Fš<~ -x{[l\p㬐i86Y`~^-nqIj=x1%3DW7拍G8poCiX?2{Ё׍N*a!yw=}7Oz^~E}|'o:zv^X/./Oӎ7~|'ח_ot^?z-;?7ME\⊶rӄ_aJL#m׺0%S"e|#JSA˓CYYC@~;U3U/n9ytC'(5o7X}Gnmv"{I=Y.fO_OV?[/fVF7Y.(7Jty9_ QOh%ggf뫽B+G;âS0a\OMoly,;W߼5F*v\Wg^_s@}+y&+ۅ V7"ɣ 47]|[}d7~3"̈Ä˄H;-N3"̈-LoQA3| /._yr-G?3?x'j}}?1{Ũ7wNvs] W΋Gӥ\|j?|kkx~8Lnݳ/gO?h>^.7/>o~|ZOk¼/>gbX\٧?^̗'?-vvw;A.ruONϦ}{7ouEr;Gywоc@rYmtY-U1*T_}<ܩxkt1UL_gί>ʅ:ˈAy }o#ws;wWٹ~7'_9O7po./L<\f[g:9r?@ͦr'ٖ.l.q_;wM쟿4o7ͧgu4W ;T[դsw:=}j!<]_L>Z_,8ow}_qL;6~z~ cvyM*^C|V=oREmN?0s ~;ZtտͿLWRְq=j$O||ٍ9ntGdbn!ʔT/duM\J_ >r,\_VF2MD o/33!#81YO~:YOgx~M+0zҴ;~yz6nDLz}?ߖ 6ĘmWkkLӐ@МMC\-w; xgq\;7oOWcX?^|q5?3ui5޹B⽘js棙.ˋf)l:8}S ߕFXoޟ=Z>_^-@j>z]/Y[WVlT{ؗ3 i^/ſ7}fx-{g#[h zk!vr𯮯f/֫닛۞_-f((;voSB,._ڨHv $t|x}u1vޑ_6|ӎ.4>~MU)ݹks>o;{k۶6}pSbj1dǫٻ'WggWl{b=/_{ufI=7ΦbvY)SfOg;.W `1itՂRg$: 7~s|vz1"J8q] b}A}[atq8OV3Z`iw]gҶ't]GovG4_;k\g1dB.[Ȃ4{W;ݤo_nZIƭ8PSj_&G~F?wuF=Ln_Oī[6ٜ=ݏޢE8vj}=ճ>7]P}qu/6zӋg*⭛rOa`~zFٮIJ{rUt ruyjrMT8o7n+Oewy<]P5=o<WLWgofͬa4^EI&?ɪX? K,ϯ0 fw&.{xw-ij}:7ѻ[u1߶ʳ7vn}`~7Fp{7f3N7m6|uSxyy؎ 흳. yC0XxϗWNΦ_ggƻ]|p6v5yvJ+6ŋ9onuL[c/hvĝN\6Ń-tW#2NobuqM#-Ԅ?:YnXݣ[d=n7/vgmp[v_8~8tt2{ß盻4ju޴h4_t[4=ryuÆ%nU\w 8z'.wϩxzN gfEi*lsě[/ ۋw׷v'ֹ%ge;ol8|vzu>iwhi k䮻Ԯ 5ٔ|>Qz2Ѷy6Ѣ o23^,&;/G绢ltûV=6ݧxݺI`Հ!/30XܡuCx=5ok>Av||rwh]{~m|sc9^7.h96ͰՆ&.fsWwڣ$-j۝Co3kzi)F//ox8+<>}izw[ͭNMZ?zϏhR@.'r wty_ٴ*N?>?5-hNowg_{x{wY{}2b=[U*3͝3߸$۟o(7[zS|?X-W/׫E}r3N~hx@?Y.;{N7ر"|_vD[ٽ?}\8{NwW c#ZzwbyR}{+ُ^|ͷoGQQBbwfՌ=}_Ojdv򏋥Z ۞rxz_,k<_'>?͎E=eܯ9z?w?QGe-'ɫ멲ūQ97J `4CO{H/@gW\;| ~lM39T:Oo/˨xp7sa|Cqr=\LϤߟ\Pɍ^ۦx鉙pGoo/^|@7֯qrĂwh/\b9E@gax/do2hodNKLoNGyrn\<; +7c.scn6F[?u~qS:Q1Ƿݱo8G[㷋g.psk{\ Pݴ}mX<6BU$]B#3hEti~K΂ގ5?O=ɿ +o>oo;χgwwg>N/׏~?m{ ??e_ޕsg~|O>xw~|e?>ǼNj/_?xxխƦ<?[?]N?^]ZM_wu'+ZspN΃mpbgr*uP7EJ*UVvBUw_0$W +)'f۷eԥ<߄b{S*iu _/̤0_8CMS +˯0wݛ~Ӆ`xgaPsf5,?+OOF\Յ/^RmhH3?ţ Bv(T3+)jt֓Pw[K[nJf3s`]s ԣ\ƻrBPŹwi7[gG0HPʍ/7烠 U; >vo "Aun['7Q *}PB<[.̭MWՕYw1jFlT4?Ag^+gP륟PaTMwi}M#$V D7Z|U;mT#V_k^ urNkP\gzUN:`{{OR?i{uZFM,waCmL>?<>5P$7[=K^ Z8=[0=_} nC^+*a]l-B闏Cg/ ք Z{ Z7@5'z?ٯTÔjJA9Z,rndڂjy/ [o{TD{ޝB󃂢Qju7e?Mu5_-TBz0ǠvrY\^EA-?ZC]Ef^8.{ܘ]"juꪧ>\u9;y?Rav>y}uȵ_'b?WRw4C411MMLn|i9MfGW?VC~ qRugvbz&w{;SFV~rA6hA?8 ի4(ig5 6ﯪ+^Xʟm.1w>vv!mo ??8ykW!^{{)WBkusd]9D>k$c(/ɒ^ sqPOv#ܼL!m,?qy>׎y="X)Gr4 .widSٚs<)a8W[ƿeC˞` Iך>kG+ք "Qx| kl+6C<΢s34&.fÆՋVBa(e_abyNx;`^#nQ[(ޏ[ҾE4̐I.E+O?|T:CYb8A//{~ӹQz{mbJF A.}Pz?R +Ya;VM!㰾7kk^}90"%犏^cBVŽ(cxt2^ +йo7CEaS+ҼE}Ec)9¦zUPÏr:GY!/J;1HЩpg!qw1?s߃Iv'OkלWdKB.hZbXŶ8iAL-`D`[峸<8P[A6.`-mey5hM\77#bj{*ޚjF$"-f_*[ +&3ĎɜҞze*lLk.kܨe?k`ljt,Խ=޼ +fiLF z B+yǁ4tK9M+ lּ׻Fl9W+L04y; ` m>4{>cJKN΄X[{&@Prp;k~ԗ[^xp&j0H{X;LP GPDl +L>Ks7 pR y?5*H/zD=^Q =V Pp<ՆLW3r)ya- +j%G=TjQ`k>" @A \fuEoR4F Cl`~>z֔2Lbo$D؛0W YZ:зl;F(x2n;jw!g PoލTuE'4ɏ!7ʫ~|{48ވ]0wBlW0 ݚ#K7nm> z&n] 6-MYp:9)L Iҍ7Ȇ2]\&t͊EHWu[Bȁj$6W|9cCRhS=?,iK`]-揖> P @~ҳ[T!Ҡ /pа%1LlGo{"7q޼qBɧ-v>6;H9u + hA BŌOv(rLAH`tj83c +V,2jrJ8 zn\8_\Of&۸xWj/jx}%CsvgWLȭ[ݱYDddQM԰|Czlh/˟Lxv)5&6EEaXk|frZtqrZtqrZtqrZ>3rZtqrZ>ˀ/uG,+FSREQ/>i-0-\h+OP<䯧䯇._n26*)e>ެ%OVVL*A{+iN+ΰ`L ˻zLGt9U{,sNFR73$*ǕqM(fҨ4ɥ/ MoRLҖIZaդ>2X($ܭQR*1ۛ7 zkYԢ]4~"4d Ւֽ\QHcȍqQ J5#*O5LI7^e|5Wh#yM'.y-|iӓy1nL^Ȃ0{DLt0]LTMalMNb&$#3lLrJU*zV *ofU 2"O_1n_ͪ?e4ћ1Eo`r1$'&X$1p#Go2Xh~pEq#Do=&UPxu`GKF/~Jc''1R!S!{n'0f* KQp˅(K5䳤elǞG\HS7dN@_HK[vBu~k詜oO^/ȿ2/d`ܕy]ڕ+k_q޸+R&[7;x:e^p~+,0IcuqWAqm܅Tq@e6<[ +DtU7譅 ae $s54h,Ŧ3I@ŐR9z?ʣ :<~#f1vsZ9Pi:=_μOnFb,1`CR,ȇnN9&NΏiXnMLȿ[_9:Jip{Z|G *=V޼gͦ8Y,a[FKvcXdY$ H{ E⤿g R>>:h Bz7VF(-M~uD̉@!D6 EeFB>f1c`Q' )s۰xx% + W29lQ^9O s!PipVvY%|ewSPsXEXWSJTo)Z/ϺW +зBtd +U-@զfbvTݴ~{/hLSa_PUπGa_Ve3¾ ;q¾la3¾/o$YqH/h&<~>' -ucJz}!. , *c#/fqCx/- 2V]s\|D% ]6z iID~x =[V ٌx_Z.n8b@'sgm$~腽nUDa +ܘLuTS𨊽/l"Չ9$H~%l6w" )I(ow,V /X'D#_ L. _Po3T*AH)k!/K@r/˄U%~Wũ>ko|5 ǹ$ oW}%R6%s(3YM_L !3 +r|6Im/iIS; .򾱣/no ʣ_!7TӱZWA!U\-v,( Qtۋ{Ft4S ẘQ$1c[j_O 7fh[V)763L?OWRқ8ehlk9QY`%s[ՀLzwLax /wfA%SNT=WtK!PmϽ~vح!C A=Md{lh["kAQٙf?SE뫭`Jx c R؄|y(\m%ĝr19n ykW["コz)uat/IJI/݅AGhllrᡃȏi /WU{٩|;9z5}99/LN*[ma|jk㴑|kx^{;x0{S<>;}k+?gz!no Z⟯Z;sug2#L,.gE_~oO3[Mg3!m5&5>y ^ݿݟ\/W:xOX+qp2AxBLow{ϻSB-'@l [Fmy}ѽ ݰ> כ4k^ +`sW866fN뻹ӝeŜ׊N*,uu^o(zߛ +V9z5-u7ȢaEi7ɹڈĪ[l|.׷^֊~V/UzO53_oݫ68 +gZh鄺yjŭO-#Vo6WDG*R{$Tty竼ڜ,Ώ-r7 U)#o}M`Եyr/yzi.\ nמ]?Ԓq09P[S|r~+=/q~w^ +4Wޘwڊhoˏo\G*P,zv^yqi@F|[̶7tw̫mo޽?Q6Q;fCk,m_J}HˈKp힍l;;X%(N*Imo{~lh@\l[oh.ͳ0]-͖*}:4`bfczBCف>6*\b.h)H+MdJŰξ)5Ee8soZ)\s `[6tc9e=o}DiT 5ٵ'殺̅ 0Klܰ+~9S$={Nx5|IwCӲO:gwV:OXh5r(uwTk1ZQNIaBXnhu`'g>hA}D =p&l}PL0Dn4/eGT{2r[{@A#˝?3$vسn1XxFe9,6OD a "~ ;X8r\( OHލ^@Iu\;ݱ欑XG9,0HuYݞ:Y#Va˂aKLjoib#M^cX)E/ +ksS[O8qC{벦0݁tﭭq9wG/t'|9#?;Q21q|==l.k[!NdYLD׭. HE&Ku\Hf:ѧM_W?Td>/r_:( .Z y,yX}Էj`iS^p簲\~\0ol\6Wv*_iVq\7Ko꨾SF>}MԢڜǣ56%R@[8} Ά"+Q |OcSUd' Vs-[ 1wGY[*~.#c &c)_;CvcӨr[;OLB 3W=srҁkih6ZKu +䰟0a$@bHV8(3)QÚ҂1~o߰3ֱQw;C `Ynܤn向Tk10.q[BIkJ.v4RD@-7:yG }p&YЖ.z H9d!ݣn}upҺt_j{KGW?'BL0Mbϣ띃zaj]c_+}My|L<>t=/}l^eJZˤ$\e߯Fdnem+n\I26˻v64;'%qLG۵̜ճ [+y/58 X49pJext,kEΑ@k~ 8lvѨXvZP`Ŵ +7%xk_Jv?{u#d2I旪ÇÏԉ&L&dl'kC?I)YSR04+QYj%=RY1xI,KfRfV MnWZY #+eUTxI-dU!)$%&嬢eQ3A.* CѠS4*(G3 n5Y7Ej %e5/LIֲh2| fY >qɦf5U,JX60!keCCHC#|$Б +̲. jV6ʚȄ?C$BY"eAw$HI/"Je]QDhKHPCUͬY6%@H24H,`2@<O4(0UVC9rqݔ$`҈a> +LX RfqT ,!QH࣭G"GG% *-`ZL[#%ʊzِCxVYQc  lz@:P4*(INˊdxDQUtfvA""e"vEC  M`]>t +ѠgU-k4#[̢۸hd)HW:M xGJ5 qxZ#hI8X`U7DE&(Ldc?*HKY谊 @M)S͗Іd/*, *[@,(pH# d?7A>M$H$i"0LsT ?"*^ FTJ!Np#,X2H O:SbYG4I7[Ij,li0Ƒ!d> 5Bg%uOA!N!&e$ +P + T@0QbH <* +u0}@D LX V ;>D`1]5Il@ + Š +JBZ'zu K@4"- +Bه +h(E$W y4a@<#xѦA3x@ B1`ye1,) !PA &&H.Bgx:meYP H78 x2x\C t4ok E +\ !q +hȚ: +RJJOD(XP%REJDAK +ī +rA-$r) 2ՠJJGT2*QaqIdP(0J Ľ OH"L1@@YP @LF"8e]R XrVp\gHмjH>nT50d(*Hv +J5a6(rYWh6֚ZZ4dkU6'(D`[0I9h4M+Z_iSGՒnpMUx\!iWQ"5Ғ= 2`2T˪hq)"TAr#p4^-" ԁF Ta  ̲ 3hV +C$e +&ioU5X\/t$(h#C@ H@@W LX3}>D"*$P@P5 )faYTED d HU&)FT* e2aNB <*S/$[M(Эd ME`a=kBSjB +r,q"$Q -?,*Gh 5,W؀x)LC$~$в4TP3,f uKmF/͍ +pj",Z`AGP c4傓gM!61rK 0lJ;>M";ʄ3"24h0 +0%zJEm$Y@|$!>Ȑ6( P@F=&Ѱԟe lG"5H Z M e0\e`6|ϫ̳p($o4e(j +,8(`8@A7D`H8. !iNLY gr*"tu\ѓ2f6O#.a9m1i`7˸`ʶ 1GFa +H& г~zdTc[Vj)3[ _,|]J7I& *߄F'Zuc7 -:5@j*rB +nѐt$ՂtXZ0P1atP|94M餪UtM3@s3j|vSmRiU@ $9M0<&zD05b@aLH0$ADK4=[ݤ\] ¬2QpЧ@؋at[4aPDq4pvӄhAKrZfEUĭ +hDT4ju1iհlxFFWBM?%ε8?HEexlb +<ҜLFK8P@K36ZogȌӄ@بOt< *!)_1 `(Ě5ǁzyl`~A{w L4I, ςŠ`GGD6_]Ka/N PE,3NU&xS}hB^QD %4!:[454Ld!yFO%ԅ D6Uj⥛z8rT8?T8 8b} $~ 44;tU@ǁ:r&Gu +EQCjLzVe?@O +Yf! dOmN;LEr%,em0Ahhd\bCs?8AŰa-o[MF@h5nC)z0 1Ma!O&(IvFLEsSR{XdPkLg;,B9V[2V \p6WmE;ѺJx'vPMQv.$Eu*4Y<ӐДz6nؙnM&DV#%}ErWi:mpi`RԵ:z`.9:g@aÒ5"l\v[GkNA!hٴu*ccZ[>vZ,}ςjBؠ-mUrtZS+Kʈ=EN-cB~·sG5#1˲n(^S]SP7 K B{ḰvbiL +nJZ`[@>GeXijXT6ki]$0V6F l#Pئ }QmDMϪUshYe6i/C#ؠZ!;A`nCYm`@Y R vlHVPO517 P.芋L"jZ6tC 𵤙YmFD" h!,E)uz!S\ h Np W sϏ UtBgV$@Umbe>ddAsAoyfU\hw15n0"8mgQa5 ndD)gâ +NcVAmhʾ&w yՐ?(LLTNۜ[Mnmp7C>7)ᴩ,I],Y߾9͜y)VAm=ПoVezLTӵ2o0|n3ȷᶦ`oE܉BVnkfm kdJ +_2 $f( ,BHrMc ?+$F)BIA٣fm8J͒dfJ +riwѱ3% +cgJ~ ͱ3t8;mLZtNJCSJʄ,M! 7<GyD6p(6c[CᇈLBkh:m9@J#t,SMmSSBm0g8ECA45%Ua |A(SR>stream +?q} ̵f,U`8HoýܳB4Tp<ѧh%q"n8< +>3lRrNϸIz.i*1eBY8 T !h46kA1MՆEAڑ{k3Y6ڜm:=K^DgNXyd̮49k +" m0 D .؎#nu,%n0۶6%T,)th~Ba'bp&|: :Nke4B0% [i=YyԌ@DL52bb` 6>+ѳ' tDLռ_̀vBP$YuґQV?05*{+]>x!ihƝ&[PbB2lERdEµ4w6h1C9qkFi·5+8AQC5@~U.E.$`<3:Bn"(zMdհ<_#edhZ uDV^詰 /i~wpPjv8v@R.2l8Y{rTbejIFJdcNvdJӨIX&lS#=U"2 88pBE(Zn.N f w +yK"1TƃԑDkro [ `61 +jVP!n)Hר$DkG|T cҙA![9w~[#烙!E +endstream endobj 20 0 obj <> endobj xref +0 25 +0000000003 65535 f +0000000016 00000 n +0000035460 00000 n +0000000004 00001 f +0000000013 00000 f +0000035724 00000 n +0000041209 00000 n +0000041281 00000 n +0000041496 00000 n +0000042681 00000 n +0000053095 00000 n +0000118684 00000 n +0000184273 00000 n +0000000000 00001 f +0000035699 00000 n +0000035511 00000 n +0000035583 00000 n +0000035614 00000 n +0000040534 00000 n +0000036053 00000 n +0000185200 00000 n +0000040647 00000 n +0000040695 00000 n +0000040148 00000 n +0000000148 00000 n +trailer +<]>> +startxref +185381 +%%EOF +%BeginExifToolUpdate +24 0 obj +<< +/Type /Metadata +/Subtype /XML +/Length 28468 +>> +stream + + + + + + application/pdf + + + Logo vp_fond noir + + + + + + c36c4eae-79bc-474c-b49e-a9a1840c50c7 + + + + Document + + + + Adobe PDF library 8.00 + + + + 2009-11-27T16:29:02+02:00 + Adobe Illustrator CS3 + 2009-11-27T16:29:02+01:00 + 2009-11-27T16:29:02+01:00 + + + + + + + JPEG + 60 + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAPAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A825luA7FXYq7FXYq7FXY q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FUTpthPqF9DZwU9WZqAnoAN2 Y07KAScLdp8E82QY4C5SNBP/ADH5Im06D63ZO1zbIKzKw/eJQbttsV/V+OExId52v7O5dJHjB48f U93vHd5sf0/Tb7ULj6vZQtPNQtxXsB3JOw8PngdDixTyGogyPkh8DW7FXYq7FXYq7FXYq7FXYq7F XYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FWUakNOt9JutESNTc6csVxLdqQS9wXWOZK/wAq+qFH +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 Yq7FX//Z + 256 + + + + + + + + uuid:68DB89F344C1DD11BE22E0656B3E5FE1 + uuid:6BDB89F344C1DD11BE22E0656B3E5FE1 + + uuid:0C406B9569DBDE11A9C6AA1839EDD742 + uuid:8097c4fb-8578-4247-95de-589bdc34d786 + + + + False + False + + 134.000000 + Pixels + 570.000000 + + 1 + + + Cyan + Magenta + Yellow + Black + + + + + + + + + + + + 0.000000 + 0.000000 + 0.000000 + CMYK + Blanc + PROCESS + 0.000000 + + + 100.000000 + 0.000000 + 0.000000 + CMYK + Noir + PROCESS + 0.000000 + + + 75.000000 + 0.000000 + 0.000000 + CMYK + Fusain + PROCESS + 0.000000 + + + 60.000002 + 0.000000 + 0.000000 + CMYK + Graphite + PROCESS + 0.000000 + + + 44.999999 + 0.000000 + 0.000000 + CMYK + Cendre + PROCESS + 0.000000 + + + 30.000001 + 0.000000 + 0.000000 + CMYK + Fumée + PROCESS + 0.000000 + + + 0.000000 + 10.000002 + 23.000002 + CMYK + Latte + PROCESS + 32.999998 + + + 0.000000 + 15.999997 + 37.000000 + CMYK + Capuccino + PROCESS + 57.999998 + + + 19.999999 + 31.999999 + 48.000002 + CMYK + Mochaccino + PROCESS + 75.999999 + + + 38.000000 + 42.999995 + 51.999998 + CMYK + Moka + PROCESS + 80.000001 + + + 25.000000 + 25.000000 + 100.000000 + CMYK + Rouge Mars + PROCESS + 100.000000 + + + 0.000000 + 25.000000 + 100.000000 + CMYK + Rubis + PROCESS + 100.000000 + + + 0.000000 + 0.000000 + 100.000000 + CMYK + Rouge + PROCESS + 100.000000 + + + 0.000000 + 0.000000 + 75.000000 + CMYK + Citrouille + PROCESS + 100.000000 + + + 0.000000 + 0.000000 + 50.000000 + CMYK + Jus + PROCESS + 100.000000 + + + 0.000000 + 0.000000 + 25.000000 + CMYK + Soleil + PROCESS + 100.000000 + + + 0.000000 + 0.000000 + 0.000000 + CMYK + Jaune pur + PROCESS + 100.000000 + + + 0.000000 + 25.000000 + 0.000000 + CMYK + Olivine + PROCESS + 100.000000 + + + 0.000000 + 50.000000 + 0.000000 + CMYK + Vert choux + PROCESS + 100.000000 + + + 25.000000 + 50.000000 + 0.000000 + CMYK + Jade + PROCESS + 100.000000 + + + 25.000000 + 75.000000 + 0.000000 + CMYK + Menthe à l’eau + PROCESS + 100.000000 + + + 25.000000 + 100.000000 + 25.000000 + CMYK + Emeraude + PROCESS + 100.000000 + + + 25.000000 + 100.000000 + 25.000000 + CMYK + Bleu-vert + PROCESS + 50.000000 + + + 0.000000 + 100.000000 + 25.000000 + CMYK + Bleu des mers du Sud + PROCESS + 25.000000 + + + 0.000000 + 100.000000 + 0.000000 + CMYK + Cyan pur + PROCESS + 0.000000 + + + 0.000000 + 100.000000 + 25.000000 + CMYK + Bleu hawaiien + PROCESS + 0.000000 + + + 0.000000 + 100.000000 + 50.000000 + CMYK + Bleu crépuscule + PROCESS + 0.000000 + + + 0.000000 + 100.000000 + 75.000000 + CMYK + Bleu nuit étoilée + PROCESS + 0.000000 + + + 0.000000 + 100.000000 + 100.000000 + CMYK + Bleu fonds marins + PROCESS + 0.000000 + + + 0.000000 + 75.000000 + 75.000000 + CMYK + Lavande fraîche + PROCESS + 0.000000 + + + 0.000000 + 75.000000 + 100.000000 + CMYK + Violet + PROCESS + 0.000000 + + + 0.000000 + 50.000000 + 100.000000 + CMYK + Améthyste + PROCESS + 0.000000 + + + 0.000000 + 25.000000 + 100.000000 + CMYK + Framboise + PROCESS + 0.000000 + + + 0.000000 + 0.000000 + 100.000000 + CMYK + Magenta pur + PROCESS + 0.000000 + + + 0.000000 + 0.000000 + 100.000000 + CMYK + Rouge global + 100.000000 + PROCESS + 100.000000 + + + 0.000000 + 0.000000 + 50.000000 + CMYK + Jus global + 100.000000 + PROCESS + 100.000000 + + + 0.000000 + 0.000000 + 0.000000 + CMYK + Jaune pur global + 100.000000 + PROCESS + 100.000000 + + + 0.000000 + 80.000001 + 0.000000 + CMYK + Vert global + 100.000000 + PROCESS + 100.000000 + + + 0.000000 + 100.000000 + 0.000000 + CMYK + Cyan pur global + 100.000000 + PROCESS + 0.000000 + + + 0.000000 + 100.000000 + 100.000000 + CMYK + Bleu fonds marins global + 100.000000 + PROCESS + 0.000000 + + + + Groupe de nuances par défaut + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +endstream +endobj +xref +0 1 +0000000000 65535 f +24 1 +0000186060 00000 n +trailer +<< +/Size 25 +/Root 1 0 R +/Info 20 0 R +/ID [ <4AF546F3A32ACD429D393369D868A59F> <12A931A6BC578D40B9A53FD03C1A52C5> ] +/Prev 185381 +>> +%EndExifToolUpdate 186038 +startxref +214622 +%%EOF diff --git a/tests/testfiles/test017.wav b/tests/testfiles/test017.wav new file mode 100644 index 0000000000..1f7ddfbd90 Binary files /dev/null and b/tests/testfiles/test017.wav differ diff --git a/tests/testfiles/test018.TIFF b/tests/testfiles/test018.TIFF new file mode 100644 index 0000000000..18633c5abc Binary files /dev/null and b/tests/testfiles/test018.TIFF differ diff --git a/tests/testfiles/test019.mp3 b/tests/testfiles/test019.mp3 new file mode 100644 index 0000000000..c6f016fc40 Binary files /dev/null and b/tests/testfiles/test019.mp3 differ diff --git a/tests/testfiles/test020.mp3 b/tests/testfiles/test020.mp3 new file mode 100644 index 0000000000..777301ce5e Binary files /dev/null and b/tests/testfiles/test020.mp3 differ diff --git a/tests/testfiles/test021.fla b/tests/testfiles/test021.fla new file mode 100644 index 0000000000..f927569816 Binary files /dev/null and b/tests/testfiles/test021.fla differ diff --git a/tests/testfiles/test022.swf b/tests/testfiles/test022.swf new file mode 100644 index 0000000000..13fccc1f8b Binary files /dev/null and b/tests/testfiles/test022.swf differ diff --git a/tests/testfiles/test023.mp4 b/tests/testfiles/test023.mp4 new file mode 100644 index 0000000000..6d39b2a84b Binary files /dev/null and b/tests/testfiles/test023.mp4 differ diff --git a/tests/tests.sqlite b/tests/tests.sqlite new file mode 100644 index 0000000000..f2c3bcd886 Binary files /dev/null and b/tests/tests.sqlite differ diff --git a/tests/thesaurusTest.php b/tests/thesaurusTest.php new file mode 100644 index 0000000000..5c31743ce8 --- /dev/null +++ b/tests/thesaurusTest.php @@ -0,0 +1,21 @@ +assertEquals($string, thesaurus::xquery_escape($string)); + $string = '&é"\'(-è_ çà)=ù*!:;,?./§%µ+°0987654321'; + $this->assertEquals('&é"'(-è_ çà)=ù*!:;,?./§%µ+°0987654321', thesaurus::xquery_escape($string)); + } + +} + diff --git a/tests/unicodeTest.php b/tests/unicodeTest.php new file mode 100644 index 0000000000..aa4756f95a --- /dev/null +++ b/tests/unicodeTest.php @@ -0,0 +1,50 @@ +object = new unicode(); + } + + public function testRemove_diacritics() + { + $this->assertEquals('Elephant', $this->object->remove_diacritics('Eléphant')); + $this->assertEquals('&e"\'(-e_ca)=$*u:;,?./§%µ£°0987654321œ3~#{[|^`@]}e³²÷׿', $this->object->remove_diacritics('&é"\'(-è_çà)=$*ù:;,?./§%µ£°0987654321Œ3~#{[|^`@]}ê³²÷׿')); + $this->assertEquals('PeTARDS', $this->object->remove_diacritics('PéTARDS')); + } + + public function testRemove_nonazAZ09() + { + $this->assertEquals('Elephant', $this->object->remove_nonazAZ09('Eléphant')); + $this->assertEquals('e-e_cau09876543213e', $this->object->remove_nonazAZ09('&é"\'(-è_çà)=$*ù:;,?./§%µ£°0987654321Œ3~#{[|^`@]}ê³²÷׿', true, true)); + $this->assertEquals('eecau09876543213e', $this->object->remove_nonazAZ09('&é"\'(-è_çà)=$*ù:;,?./§%µ£°0987654321Œ3~#{[|^`@]}ê³²÷׿', false, false)); + $this->assertEquals('ee_cau09876543213e', $this->object->remove_nonazAZ09('&é"\'(-è_çà)=$*ù:;,?./§%µ£°0987654321Œ3~#{[|^`@]}ê³²÷׿', true, false)); + $this->assertEquals('e-ecau09876543213e', $this->object->remove_nonazAZ09('&é"\'(-è_çà)=$*ù:;,?./§%µ£°0987654321Œ3~#{[|^`@]}ê³²÷׿', false, true)); + $this->assertEquals('PeTARDS', $this->object->remove_nonazAZ09('PéTARDS')); + } + + + public function testRemove_first_digits() + { + $this->assertEquals('', $this->object->remove_first_digits('123456789')); + $this->assertEquals('abcdeé', $this->object->remove_first_digits('12345abcdeé')); + $this->assertEquals('abcdeé0987', $this->object->remove_first_digits('abcdeé0987')); + $this->assertEquals('a2b5cdeé', $this->object->remove_first_digits('4a2b5cdeé')); + } + +} + diff --git a/tests/unitTestsTest.php b/tests/unitTestsTest.php new file mode 100644 index 0000000000..eb2620a0dc --- /dev/null +++ b/tests/unitTestsTest.php @@ -0,0 +1,32 @@ +getFilename(), 0, 1) === '.') + continue; + if (substr($file->getFilename(), -4) !== '.php') + continue; + if (substr($file->getFilename(), -9) === 'class.php') + continue; + if($file->getFilename() === "BoilerPlate.php") + continue; + + $this->assertRegExp('/[a-zA-Z0-9-_\.]+Test.php/', $file->getPathname(), 'Verify that all tests files names'); + } + } + +} + diff --git a/tests/userTest.php b/tests/userTest.php new file mode 100644 index 0000000000..f403c7d37a --- /dev/null +++ b/tests/userTest.php @@ -0,0 +1,59 @@ +assertFalse(User_Adapter::get_usr_id_from_email(null)); + try + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + self::$user->set_email(null); + + $this->assertFalse(User_Adapter::get_usr_id_from_email(null)); + self::$user->set_email(''); + $this->assertFalse(User_Adapter::get_usr_id_from_email(null)); + self::$user->set_email('noone@example.com'); + $this->assertEquals(self::$user->get_id(), User_Adapter::get_usr_id_from_email('noone@example.com')); + } + catch(Exception $e) + { + $this->fail($e->getMessage()); + } + try + { + + self::$user->set_email('noonealt1@example.com'); + $this->fail('A user already got this address'); + } + catch(Exception $e) + { + + } + $this->assertFalse(User_Adapter::get_usr_id_from_email(null)); + } +} diff --git a/tests/uuidTest.php b/tests/uuidTest.php new file mode 100644 index 0000000000..3b0f87e292 --- /dev/null +++ b/tests/uuidTest.php @@ -0,0 +1,130 @@ +object = new uuid(); + } + + public function testGenerate_v3() + { + $datas = array(); + for ($i = 0; $i < 1000; $i++) + { + $uuid = uuid::generate_v4(); + + if (!uuid::is_valid($uuid)) + $this->fail('Generation d\'un uuid v4 invalide'); + + $uuid = uuid::generate_v3($uuid, random::generatePassword(12)); + if (!uuid::is_valid($uuid)) + $this->fail('Generation d\'un uuid v5 invalide'); + + $datas[] = $uuid; + } + + $datas = array_unique($datas); + + if (count($datas) !== 1000) + $this->fail('Generation de deux uuid identiques en v3' . count($datas)); + + unset($datas); + } + + public function testGenerate_v4() + { + $datas = array(); + for ($i = 0; $i < 1000; $i++) + { + $uuid = uuid::generate_v4(); + + if (!uuid::is_valid($uuid)) + $this->fail('Generation d\'un uuid v4 invalide'); + + $datas[] = $uuid; + } + + $datas = array_unique($datas); + + if (count($datas) !== 1000) + $this->fail('Generation de deux uuid identiques en v4' . count($datas)); + + unset($datas); + } + + public function testGenerate_v5() + { + $datas = array(); + for ($i = 0; $i < 1000; $i++) + { + $uuid = uuid::generate_v4(); + + if (!uuid::is_valid($uuid)) + $this->fail('Generation d\'un uuid v4 invalide'); + + $uuid = uuid::generate_v5($uuid, random::generatePassword(12)); + if (!uuid::is_valid($uuid)) + $this->fail('Generation d\'un uuid v5 invalide'); + + $datas[] = $uuid; + } + + $datas = array_unique($datas); + + if (count($datas) !== 1000) + $this->fail('Generation de deux uuid identiques en v5' . count($datas)); + + unset($datas); + } + + public function testIs_valid() + { + for ($i = 0; $i < 1000; $i++) + { + $uuid = uuid::generate_v4(); + if (!uuid::is_valid($uuid)) + $this->fail('Generation d\'un uuid v4 invalide'); + + $uuid = uuid::generate_v5($uuid, random::generatePassword(12)); + if (!uuid::is_valid($uuid)) + $this->fail('Generation d\'un uuid v5 invalide'); + + $uuid = uuid::generate_v3($uuid, random::generatePassword(12)); + if (!uuid::is_valid($uuid)) + $this->fail('Generation d\'un uuid v3 invalide'); + + unset($uuid); + } + } + + public function testCompare() + { + for ($i = 0; $i < 1000; $i++) + { + $uuid1 = uuid::generate_v4(); + $uuid2 = uuid::generate_v4(); + $this->assertFalse(uuid::compare($uuid1, $uuid2)); + } + } + + public function testIs_null() + { + $this->assertTrue(uuid::is_null('00000000-0000-0000-0000-000000000000')); + } + +} +