From 22afa8c26ffc9cfbac5e2f58cefd944e06c6bb64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Lucas?= Date: Thu, 2 Jan 2014 22:23:24 +0100 Subject: [PATCH] Add the real OPDSValidator (from https://github.com/zetaben/opds-validator) in addition to the relax-ng validation. --- test/OPDSTest.php | 65 ++++++-- test/OPDSValidator.jar | Bin 0 -> 17700 bytes test/res/atom.rnc | 338 +++++++++++++++++++++++++++++++++++++++++ test/res/opds_v1.0.rnc | 131 ++++++++++++++++ test/res/opds_v1.1.rnc | 151 ++++++++++++++++++ 5 files changed, 675 insertions(+), 10 deletions(-) create mode 100644 test/OPDSValidator.jar create mode 100644 test/res/atom.rnc create mode 100644 test/res/opds_v1.0.rnc create mode 100644 test/res/opds_v1.1.rnc diff --git a/test/OPDSTest.php b/test/OPDSTest.php index 55b40b0..336ea7d 100644 --- a/test/OPDSTest.php +++ b/test/OPDSTest.php @@ -13,6 +13,7 @@ require_once (dirname(__FILE__) . "/../OPDS_renderer.php"); define ("OPDS_RELAX_NG", dirname(__FILE__) . "/opds-relax-ng/opds_catalog_1_1.rng"); define ("OPENSEARCHDESCRIPTION_RELAX_NG", dirname(__FILE__) . "/opds-relax-ng/opensearchdescription.rng"); define ("JING_JAR", dirname(__FILE__) . "/jing.jar"); +define ("OPDSVALIDATOR_JAR", dirname(__FILE__) . "/OPDSValidator.jar"); define ("TEST_FEED", dirname(__FILE__) . "/text.atom"); class OpdsTest extends PHPUnit_Framework_TestCase @@ -25,7 +26,7 @@ class OpdsTest extends PHPUnit_Framework_TestCase unlink (TEST_FEED); } - function opdsValidateSchema($feed, $relax = OPDS_RELAX_NG) { + function jingValidateSchema($feed, $relax = OPDS_RELAX_NG) { $path = ""; if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { // huge hack, not proud about it @@ -39,7 +40,26 @@ class OpdsTest extends PHPUnit_Framework_TestCase return true; } + function opdsValidator($feed) { + $oldcwd = getcwd(); // Save the old working directory + chdir("test"); + $path = ""; + if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { + // huge hack, not proud about it + $path = "c:\\Progra~1\\Java\\jre7\\bin\\"; + } + $res = system($path . 'java -jar ' . OPDSVALIDATOR_JAR . ' ' . $feed); + chdir($oldcwd); + if ($res != '') { + echo 'OPDS validation error: '.$res; + return false; + } else + return true; + } + function opdsCompleteValidation ($feed) { + return $this->jingValidateSchema($feed) && $this->opdsValidator($feed); + } public function testPageIndex () { @@ -58,13 +78,38 @@ class OpdsTest extends PHPUnit_Framework_TestCase $OPDSRender = new OPDSRenderer (); file_put_contents (TEST_FEED, $OPDSRender->render ($currentPage)); - $this->AssertTrue ($this->opdsValidateSchema (TEST_FEED)); - file_put_contents (TEST_FEED, str_replace ("id>", "ido>", $OPDSRender->render ($currentPage))); - $this->AssertFalse ($this->opdsValidateSchema (TEST_FEED)); + $this->AssertTrue ($this->opdsCompleteValidation (TEST_FEED)); + + $_SERVER ["HTTP_USER_AGENT"] = "XXX"; + $config['cops_generate_invalid_opds_stream'] = "1"; + + file_put_contents (TEST_FEED, $OPDSRender->render ($currentPage)); + $this->AssertFalse ($this->jingValidateSchema (TEST_FEED)); + $this->AssertFalse ($this->opdsValidator (TEST_FEED)); $_SERVER['QUERY_STRING'] = NULL; } - + + public function testPageIndexMultipleDatabase () + { + global $config; + $config['calibre_directory'] = array ("Some books" => dirname(__FILE__) . "/BaseWithSomeBooks/", + "One book" => dirname(__FILE__) . "/BaseWithOneBook/"); + $page = Base::PAGE_INDEX; + $query = NULL; + $qid = "1"; + $n = "1"; + $_SERVER['QUERY_STRING'] = ""; + + $currentPage = Page::getPage ($page, $qid, $query, $n); + $currentPage->InitializeContent (); + + $OPDSRender = new OPDSRenderer (); + + file_put_contents (TEST_FEED, $OPDSRender->render ($currentPage)); + $this->AssertTrue ($this->opdsCompleteValidation (TEST_FEED)); + } + public function testOpenSearchDescription () { $_SERVER['QUERY_STRING'] = ""; @@ -72,12 +117,12 @@ class OpdsTest extends PHPUnit_Framework_TestCase $OPDSRender = new OPDSRenderer (); file_put_contents (TEST_FEED, $OPDSRender->getOpenSearch ()); - $this->AssertTrue ($this->opdsValidateSchema (TEST_FEED, OPENSEARCHDESCRIPTION_RELAX_NG)); + $this->AssertTrue ($this->jingValidateSchema (TEST_FEED, OPENSEARCHDESCRIPTION_RELAX_NG)); $_SERVER['QUERY_STRING'] = NULL; } - public function testPageIndexMultipleDatabase () + public function testPageAuthorMultipleDatabase () { global $config; $config['calibre_directory'] = array ("Some books" => dirname(__FILE__) . "/BaseWithSomeBooks/", @@ -95,7 +140,7 @@ class OpdsTest extends PHPUnit_Framework_TestCase $OPDSRender = new OPDSRenderer (); file_put_contents (TEST_FEED, $OPDSRender->render ($currentPage)); - $this->AssertTrue ($this->opdsValidateSchema (TEST_FEED)); + $this->AssertTrue ($this->opdsCompleteValidation (TEST_FEED)); } public function testPageAuthorsDetail () @@ -118,7 +163,7 @@ class OpdsTest extends PHPUnit_Framework_TestCase $OPDSRender = new OPDSRenderer (); file_put_contents (TEST_FEED, $OPDSRender->render ($currentPage)); - $this->AssertTrue ($this->opdsValidateSchema (TEST_FEED)); + $this->AssertTrue ($this->opdsCompleteValidation (TEST_FEED)); // Second page @@ -129,7 +174,7 @@ class OpdsTest extends PHPUnit_Framework_TestCase $OPDSRender = new OPDSRenderer (); file_put_contents (TEST_FEED, $OPDSRender->render ($currentPage)); - $this->AssertTrue ($this->opdsValidateSchema (TEST_FEED)); + $this->AssertTrue ($this->opdsCompleteValidation (TEST_FEED)); // No pagination $config['cops_max_item_per_page'] = -1; diff --git a/test/OPDSValidator.jar b/test/OPDSValidator.jar new file mode 100644 index 0000000000000000000000000000000000000000..3fe7b33907601f364e42a97fa5f758b23b19856a GIT binary patch literal 17700 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