Page Menu
Home
Sealhub
Search
Configure Global Search
Log In
Files
F9583260
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
10 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/filesystem/__tests__/PhutilDeferredLogTestCase.php b/src/filesystem/__tests__/PhutilDeferredLogTestCase.php
index 3e6c6432..1d129871 100644
--- a/src/filesystem/__tests__/PhutilDeferredLogTestCase.php
+++ b/src/filesystem/__tests__/PhutilDeferredLogTestCase.php
@@ -1,169 +1,147 @@
<?php
final class PhutilDeferredLogTestCase extends PhutilTestCase {
public function testLogging() {
$this->checkLog(
"derp\n",
'derp',
array());
$this->checkLog(
"[20 Aug 1984] alincoln\n",
'[%T] %u',
array(
'T' => '20 Aug 1984',
'u' => 'alincoln',
));
$this->checkLog(
"%%%%%\n",
'%%%%%%%%%%',
array(
'%' => '%',
));
$this->checkLog(
"\\000\\001\\002\n",
'%a%b%c',
array(
'a' => chr(0),
'b' => chr(1),
'c' => chr(2),
));
$this->checkLog(
"Download: 100%\n",
'Download: %C',
array(
'C' => '100%',
));
$this->checkLog(
"- bee -\n",
'%a %b %c',
array(
'b' => 'bee',
));
$this->checkLog(
"\\\\\n",
'%b',
array(
'b' => '\\',
));
$this->checkLog(
"a\t\\t\n",
"%a\t%b",
array(
'a' => 'a',
'b' => "\t",
));
$this->checkLog(
"\1ab\n",
"\1a%a",
array(
'a' => 'b',
));
$this->checkLog(
"a % xb\n",
'%a %% x%b',
array(
'a' => 'a',
'b' => 'b',
));
}
public function testLogWriteFailure() {
$caught = null;
try {
if (phutil_is_hiphop_runtime()) {
// In HipHop exceptions thrown in destructors are not normally
// catchable, so call __destruct() explicitly.
$log = new PhutilDeferredLog('/derp/derp/derp/derp/derp', 'derp');
$log->__destruct();
} else {
new PhutilDeferredLog('/derp/derp/derp/derp/derp', 'derp');
}
} catch (Exception $ex) {
$caught = $ex;
}
$this->assertTrue($caught instanceof Exception);
}
- public function testManyWriters() {
- $root = phutil_get_library_root('arcanist').'/../';
- $bin = $root.'scripts/test/deferred_log.php';
-
- $n_writers = 3;
- $n_lines = 8;
-
- $tmp = new TempFile();
-
- $futures = array();
- for ($ii = 0; $ii < $n_writers; $ii++) {
- $futures[] = new ExecFuture('%s %d %s', $bin, $n_lines, (string)$tmp);
- }
-
- id(new FutureIterator($futures))
- ->resolveAll();
-
- $this->assertEqual(
- str_repeat("abcdefghijklmnopqrstuvwxyz\n", $n_writers * $n_lines),
- Filesystem::readFile($tmp));
- }
-
public function testNoWrite() {
$tmp = new TempFile();
$log = new PhutilDeferredLog($tmp, 'xyz');
$log->setFile(null);
unset($log);
$this->assertEqual('', Filesystem::readFile($tmp), pht('No Write'));
}
public function testDoubleWrite() {
$tmp = new TempFile();
$log = new PhutilDeferredLog($tmp, 'xyz');
$log->write();
$log->write();
unset($log);
$this->assertEqual(
"xyz\n",
Filesystem::readFile($tmp), pht('Double Write'));
}
public function testSetAfterWrite() {
$tmp1 = new TempFile();
$tmp2 = new TempFile();
$log = new PhutilDeferredLog($tmp1, 'xyz');
$log->write();
$caught = null;
try {
$log->setFile($tmp2);
} catch (Exception $ex) {
$caught = $ex;
}
$this->assertTrue($caught instanceof Exception, pht('Set After Write'));
}
private function checkLog($expect, $format, $data) {
$tmp = new TempFile();
$log = new PhutilDeferredLog($tmp, $format);
$log->setData($data);
unset($log);
$this->assertEqual($expect, Filesystem::readFile($tmp), $format);
}
}
diff --git a/src/filesystem/__tests__/PhutilFileLockTestCase.php b/src/filesystem/__tests__/PhutilFileLockTestCase.php
index 17f9632b..9dcc2c30 100644
--- a/src/filesystem/__tests__/PhutilFileLockTestCase.php
+++ b/src/filesystem/__tests__/PhutilFileLockTestCase.php
@@ -1,184 +1,186 @@
<?php
final class PhutilFileLockTestCase extends PhutilTestCase {
public function testLockTesting() {
// We should be able to acquire locks.
$file = new TempFile();
$this->assertTrue($this->lockTest($file));
$this->assertTrue($this->lockTest($file));
}
public function testLockHolding() {
// When a process is holding a lock, other processes should be unable
// to acquire it.
$file = new TempFile();
$hold = $this->holdLock($file);
$this->assertFalse($this->lockTest($file));
$hold->resolveKill();
$this->assertTrue($this->lockTest($file));
}
public function testInProcessLocking() {
// Other processes should be unable to lock a file if we hold the lock.
$file = new TempFile();
$lock = PhutilFileLock::newForPath($file);
$lock->lock();
$this->assertFalse($this->lockTest($file));
$lock->unlock();
$this->assertTrue($this->lockTest($file));
}
public function testInProcessHolding() {
// We should be unable to lock a file if another process is holding the
// lock.
$file = new TempFile();
$lock = PhutilFileLock::newForPath($file);
$hold = $this->holdLock($file);
$caught = null;
try {
$lock->lock();
} catch (PhutilLockException $ex) {
$caught = $ex;
}
$this->assertTrue($caught instanceof PhutilLockException);
$hold->resolveKill();
$this->assertTrue($this->lockTest($file));
$lock->lock();
$lock->unlock();
}
public function testRelock() {
// Trying to lock a file twice should throw an exception.
$file = new TempFile();
$lock = PhutilFileLock::newForPath($file);
$lock->lock();
$caught = null;
try {
$lock->lock();
} catch (Exception $ex) {
$caught = $ex;
}
$this->assertTrue($caught instanceof Exception);
}
public function testExcessiveUnlock() {
// Trying to unlock a file twice should throw an exception.
$file = new TempFile();
$lock = PhutilFileLock::newForPath($file);
$lock->lock();
$lock->unlock();
$caught = null;
try {
$lock->unlock();
} catch (Exception $ex) {
$caught = $ex;
}
$this->assertTrue($caught instanceof Exception);
}
public function testUnlockAll() {
// unlockAll() should release all locks.
$file = new TempFile();
$lock = PhutilFileLock::newForPath($file);
$lock->lock();
$this->assertFalse($this->lockTest($file));
PhutilFileLock::unlockAll();
$this->assertTrue($this->lockTest($file));
// Calling this again shouldn't do anything bad.
PhutilFileLock::unlockAll();
$this->assertTrue($this->lockTest($file));
$lock->lock();
$lock->unlock();
}
public function testIsLocked() {
// isLocked() should report lock status accurately.
$file = new TempFile();
$lock = PhutilFileLock::newForPath($file);
$this->assertFalse($lock->isLocked());
$lock->lock();
$this->assertTrue($lock->isLocked());
$lock->unlock();
$this->assertFalse($lock->isLocked());
}
private function lockTest($file) {
list($err) = $this->buildLockFuture('--test', $file)->resolve();
return ($err == 0);
}
private function holdLock($file) {
$future = $this->buildLockFuture('--hold', $file);
// We can't return until we're sure the subprocess has had time to acquire
// the lock. Since actually testing for the lock would be kind of silly
// and guarantee that we loop forever if the locking primitive broke,
// watch stdout for a *claim* that it has acquired the lock instead.
// Make sure we don't loop forever, no matter how bad things get.
$future->setTimeout(30);
$buf = '';
while (!$future->isReady()) {
list($stdout) = $future->read();
$buf .= $stdout;
if (strpos($buf, 'LOCK ACQUIRED') !== false) {
return $future;
}
}
throw new Exception(pht('Unable to hold lock in external process!'));
}
private function buildLockFuture($flags, $file) {
$root = dirname(phutil_get_library_root('arcanist'));
- $bin = $root.'/scripts/utils/lock.php';
+ $bin = $root.'/support/test/lock-file.php';
+
+ $flags = (array)$flags;
// NOTE: Use `exec` so this passes on Ubuntu, where the default `dash` shell
// will eat any kills we send during the tests.
- $future = new ExecFuture('exec php %s %C %s', $bin, $flags, $file);
+ $future = new ExecFuture('exec php -f %R -- %Ls %R', $bin, $flags, $file);
$future->start();
return $future;
}
}
diff --git a/support/test/lock-file.php b/support/test/lock-file.php
new file mode 100644
index 00000000..b7ec9df3
--- /dev/null
+++ b/support/test/lock-file.php
@@ -0,0 +1,82 @@
+#!/usr/bin/env php
+<?php
+
+require_once dirname(__FILE__).'/../../scripts/init/init-script.php';
+
+$args = new PhutilArgumentParser($argv);
+$args->setTagline(pht('acquire and hold a lockfile'));
+$args->setSynopsis(<<<EOHELP
+**lock.php** __file__ [__options__]
+ Acquire a lockfile and hold it until told to unlock it.
+
+EOHELP
+);
+
+$args->parseStandardArguments();
+$args->parse(array(
+ array(
+ 'name' => 'test',
+ 'help' => pht('Instead of holding the lock, release it and exit.'),
+ ),
+ array(
+ 'name' => 'hold',
+ 'help' => pht('Hold indefinitely without prompting.'),
+ ),
+ array(
+ 'name' => 'wait',
+ 'param' => 'n',
+ 'help' => pht('Block for up to __n__ seconds waiting for the lock.'),
+ 'default' => 0,
+ ),
+ array(
+ 'name' => 'file',
+ 'wildcard' => true,
+ ),
+));
+
+
+$file = $args->getArg('file');
+if (count($file) !== 1) {
+ $args->printHelpAndExit();
+}
+$file = head($file);
+
+$console = PhutilConsole::getConsole();
+$console->writeOut(
+ "%s\n",
+ pht('This process has PID %d. Acquiring lock...', getmypid()));
+
+$lock = PhutilFileLock::newForPath($file);
+
+try {
+ $lock->lock($args->getArg('wait'));
+} catch (PhutilFileLockException $ex) {
+ $console->writeOut(
+ "**%s** %s\n",
+ pht('UNABLE TO ACQUIRE LOCK:'),
+ pht('Lock is already held.'));
+ exit(1);
+}
+
+// NOTE: This string is magic, the unit tests look for it.
+$console->writeOut("%s\n", pht('LOCK ACQUIRED'));
+if ($args->getArg('test')) {
+ $lock->unlock();
+ exit(0);
+}
+
+if ($args->getArg('hold')) {
+ while (true) {
+ sleep(1);
+ }
+}
+
+while (!$console->confirm(pht('Release lock?'))) {
+ // Keep asking until they say yes.
+}
+
+$console->writeOut("%s\n", pht('Unlocking...'));
+$lock->unlock();
+
+$console->writeOut("%s\n", pht('Done.'));
+exit(0);
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Oct 11, 09:16 (2 h, 4 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
984096
Default Alt Text
(10 KB)
Attached To
Mode
R118 Arcanist - fork
Attached
Detach File
Event Timeline
Log In to Comment