From 7387cd63acbe023740de74da8a6c398bfc7f35dc Mon Sep 17 00:00:00 2001 From: epriestley Date: Sat, 30 Apr 2011 10:11:41 -0700 Subject: [PATCH] Provide an "isolated" database connection for testing Summary: This provides a new connection which doesn't connect to anything, so effects can be isolated to the current process (for unit testing). Test Plan: Ran unit tests. Reviewed By: aran Reviewers: aran, tuomaspelkonen, jungejason CC: aran, epriestley Differential Revision: 193 --- src/__phutil_library_map__.php | 6 ++ .../testing/testcase/PhabricatorTestCase.php | 26 ++++++ .../testing/testcase/__init__.php | 14 +++ .../AphrontIsolatedDatabaseConnection.php | 90 +++++++++++++++++++ src/storage/connection/isolated/__init__.php | 14 +++ ...rontIsolatedDatabaseConnectionTestCase.php | 65 ++++++++++++++ .../isolated/__tests__/__init__.php | 17 ++++ 7 files changed, 232 insertions(+) create mode 100644 src/infrastructure/testing/testcase/PhabricatorTestCase.php create mode 100644 src/infrastructure/testing/testcase/__init__.php create mode 100644 src/storage/connection/isolated/AphrontIsolatedDatabaseConnection.php create mode 100644 src/storage/connection/isolated/__init__.php create mode 100644 src/storage/connection/isolated/__tests__/AphrontIsolatedDatabaseConnectionTestCase.php create mode 100644 src/storage/connection/isolated/__tests__/__init__.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 943958f229..188f052976 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -39,6 +39,8 @@ phutil_register_library_map(array( 'AphrontFormView' => 'view/form/base', 'AphrontHeadsupActionListView' => 'view/layout/headsup/actionlist', 'AphrontHeadsupActionView' => 'view/layout/headsup/action', + 'AphrontIsolatedDatabaseConnection' => 'storage/connection/isolated', + 'AphrontIsolatedDatabaseConnectionTestCase' => 'storage/connection/isolated/__tests__', 'AphrontListFilterView' => 'view/layout/listfilter', 'AphrontMySQLDatabaseConnection' => 'storage/connection/mysql', 'AphrontNullView' => 'view/null', @@ -416,6 +418,7 @@ phutil_register_library_map(array( 'PhabricatorStandardPageView' => 'view/page/standard', 'PhabricatorStatusController' => 'applications/status/base', 'PhabricatorTaskmasterDaemon' => 'infrastructure/daemon/workers/taskmaster', + 'PhabricatorTestCase' => 'infrastructure/testing/testcase', 'PhabricatorTimelineCursor' => 'infrastructure/daemon/timeline/storage/cursor', 'PhabricatorTimelineDAO' => 'infrastructure/daemon/timeline/storage/base', 'PhabricatorTimelineEvent' => 'infrastructure/daemon/timeline/storage/event', @@ -504,6 +507,8 @@ phutil_register_library_map(array( 'AphrontFormView' => 'AphrontView', 'AphrontHeadsupActionListView' => 'AphrontView', 'AphrontHeadsupActionView' => 'AphrontView', + 'AphrontIsolatedDatabaseConnection' => 'AphrontDatabaseConnection', + 'AphrontIsolatedDatabaseConnectionTestCase' => 'PhabricatorTestCase', 'AphrontListFilterView' => 'AphrontView', 'AphrontMySQLDatabaseConnection' => 'AphrontDatabaseConnection', 'AphrontNullView' => 'AphrontView', @@ -795,6 +800,7 @@ phutil_register_library_map(array( 'PhabricatorStandardPageView' => 'AphrontPageView', 'PhabricatorStatusController' => 'PhabricatorController', 'PhabricatorTaskmasterDaemon' => 'PhabricatorDaemon', + 'PhabricatorTestCase' => 'ArcanistPhutilTestCase', 'PhabricatorTimelineCursor' => 'PhabricatorTimelineDAO', 'PhabricatorTimelineDAO' => 'PhabricatorLiskDAO', 'PhabricatorTimelineEvent' => 'PhabricatorTimelineDAO', diff --git a/src/infrastructure/testing/testcase/PhabricatorTestCase.php b/src/infrastructure/testing/testcase/PhabricatorTestCase.php new file mode 100644 index 0000000000..9e0c86cec4 --- /dev/null +++ b/src/infrastructure/testing/testcase/PhabricatorTestCase.php @@ -0,0 +1,26 @@ +configuration = $configuration; + + if (self::$nextInsertID === null) { + // Generate test IDs into a distant ID space to reduce the risk of + // collisions and make them distinctive. + self::$nextInsertID = 55555000000 + mt_rand(0, 1000); + } + } + + public function escapeString($string) { + return ''; + } + + public function escapeColumnName($name) { + return ''; + } + + public function escapeMultilineComment($comment) { + return ''; + } + + public function escapeStringForLikeClause($value) { + return ''; + } + + private function getConfiguration($key, $default = null) { + return idx($this->configuration, $key, $default); + } + + public function getInsertID() { + return $this->insertID; + } + + public function getAffectedRows() { + return $this->affectedRows; + } + + public function getTransactionKey() { + return 'xaction'; // TODO, probably need to stub this better. + } + + public function selectAllResults() { + return $this->allResults; + } + + public function executeRawQuery($raw_query) { + + // NOTE: This method is intentionally simplified for now, since we're only + // using it to stub out inserts/updates. In the future it will probably need + // to grow more powerful. + + $this->allResults = array(); + + // NOTE: We jitter the insert IDs to keep tests honest; a test should cover + // the relationship between objects, not their exact insertion order. This + // guarantees that IDs are unique but makes it impossible to hard-code tests + // against this specific implementation detail. + $this->insertID = (self::$nextInsertID += mt_rand(1, 10)); + $this->affectedRows = 1; + } + +} diff --git a/src/storage/connection/isolated/__init__.php b/src/storage/connection/isolated/__init__.php new file mode 100644 index 0000000000..6135420a1e --- /dev/null +++ b/src/storage/connection/isolated/__init__.php @@ -0,0 +1,14 @@ +newIsolatedConnection(); + + $test_phid = 'PHID-TEST-'.sha1(mt_rand()); + + queryfx( + $conn, + 'INSERT INTO phabricator_phid.phid (phid) VALUES (%s)', + $test_phid); + + try { + $real_phid = id(new PhabricatorPHID())->loadOneWhere( + 'phid = %s', + $test_phid); + $this->assertEqual( + null, + $real_phid, + 'Expect fake PHID to exist only in isolation.'); + } catch (AphrontQueryConnectionException $ex) { + // If we can't connect to the database, conclude that the isolated + // connection actually is isolated. Philosophically, this perhaps allows + // us to claim this test does not depend on the database? + } + } + + public function testInsertGeneratesID() { + $conn = $this->newIsolatedConnection(); + + queryfx($conn, 'INSERT'); + $id1 = $conn->getInsertID(); + + queryfx($conn, 'INSERT'); + $id2 = $conn->getInsertID(); + + $this->assertEqual(true, (bool)$id1, 'ID1 exists.'); + $this->assertEqual(true, (bool)$id2, 'ID2 exists.'); + $this->assertEqual(true, $id1 != $id2, 'IDs are distinct.'); + } + + private function newIsolatedConnection() { + $config = array(); + return new AphrontIsolatedDatabaseConnection($config); + } +} diff --git a/src/storage/connection/isolated/__tests__/__init__.php b/src/storage/connection/isolated/__tests__/__init__.php new file mode 100644 index 0000000000..ce36833299 --- /dev/null +++ b/src/storage/connection/isolated/__tests__/__init__.php @@ -0,0 +1,17 @@ +