const NamedPipeLockServer = require('../lock.namedpipe'); const NamedPipeRWLock = require('../lock-client.namedpipe'); const fs = require('fs'); const os = require('os'); const path = require('path'); jest.setTimeout(30000); // Helper function to create a unique pipe path for testing const createTestPipePath = () => { if (process.platform === 'win32') { return `\\\\.\\pipe\\rwlock-test-${Date.now()}`; } else { return path.join(os.tmpdir(), `rwlock-test-${Date.now()}.sock`); } }; describe('NamedPipeLock', () => { let server; let pipePath; // 在所有测试之前启动服务器 beforeAll(async () => { pipePath = createTestPipePath(); server = new NamedPipeLockServer(pipePath); server.start(); }); // 在所有测试完成后停止服务器 afterAll(() => { server.stop(); // 清理管道文件 (仅限Unix系统) if (process.platform !== 'win32' && fs.existsSync(pipePath)) { fs.unlinkSync(pipePath); } }); describe('Basic Lock Operations', () => { test('should acquire and release read lock without waiting when not locked', async () => { const lock = new NamedPipeRWLock('resource1', pipePath); const startTime = Date.now(); await lock.readLock(); const lockAcquiredTime = Date.now(); // 应该几乎立即获得锁(无需等待) expect(lockAcquiredTime - startTime).toBeLessThan(100); await lock.unlock(); lock.close(); }); test('should acquire and release write lock without waiting when not locked', async () => { const lock = new NamedPipeRWLock('resource2', pipePath); const startTime = Date.now(); await lock.writeLock(); const lockAcquiredTime = Date.now(); // 应该几乎立即获得锁(无需等待) expect(lockAcquiredTime - startTime).toBeLessThan(100); await lock.unlock(); lock.close(); }); test('should allow consecutive acquisitions and releases without queueing', async () => { const lock = new NamedPipeRWLock('resource3', pipePath); // 第一次获取 await lock.readLock(); expect(lock.isLocked).toBe(true); await lock.unlock(); // 第二次获取 await lock.writeLock(); expect(lock.isLocked).toBe(true); await lock.unlock(); // 第三次获取 await lock.readLock(); expect(lock.isLocked).toBe(true); await lock.unlock(); lock.close(); }); }); describe('Multiple Clients', () => { test('should handle multiple concurrent read locks', async () => { const lock1 = new NamedPipeRWLock('sharedResource', pipePath); const lock2 = new NamedPipeRWLock('sharedResource', pipePath); const lock3 = new NamedPipeRWLock('sharedResource', pipePath); // 所有读锁应该能够同时获取 await Promise.all([ lock1.readLock(), lock2.readLock(), lock3.readLock() ]); expect(lock1.isLocked).toBe(true); expect(lock2.isLocked).toBe(true); expect(lock3.isLocked).toBe(true); // 释放所有锁 await Promise.all([ lock1.unlock(), lock2.unlock(), lock3.unlock() ]); lock1.close(); lock2.close(); lock3.close(); }); test('should queue write lock when read locks exist', async () => { const readLock1 = new NamedPipeRWLock('queuedResource', pipePath); const readLock2 = new NamedPipeRWLock('queuedResource', pipePath); const writeLock = new NamedPipeRWLock('queuedResource', pipePath); // 先获取两个读锁 await readLock1.readLock(); await readLock2.readLock(); // 尝试获取写锁,应该会被阻塞 let writeLockAcquired = false; const writeLockPromise = writeLock.writeLock().then(() => { writeLockAcquired = true; }); // 等待一小段时间,写锁不应该被获取 await new Promise(resolve => setTimeout(resolve, 50)); expect(writeLockAcquired).toBe(false); // 释放一个读锁 await readLock1.unlock(); // 等待一小段时间,写锁仍然不应该被获取(还有一个读锁) await new Promise(resolve => setTimeout(resolve, 50)); expect(writeLockAcquired).toBe(false); // 释放最后一个读锁 await readLock2.unlock(); // 现在写锁应该能被获取 await new Promise(resolve => setTimeout(resolve, 100)); expect(writeLockAcquired).toBe(true); // 释放写锁 await writeLock.unlock(); readLock1.close(); readLock2.close(); writeLock.close(); }); test('should queue read locks when write lock exists', async () => { const writeLock = new NamedPipeRWLock('queuedResource2', pipePath); const readLock1 = new NamedPipeRWLock('queuedResource2', pipePath); const readLock2 = new NamedPipeRWLock('queuedResource2', pipePath); // 先获取写锁 await writeLock.writeLock(); // 尝试获取读锁,应该会被阻塞 let readLockAcquired = false; const readLockPromise = readLock1.readLock().then(() => { readLockAcquired = true; }); // 等待一小段时间,读锁不应该被获取 await new Promise(resolve => setTimeout(resolve, 50)); expect(readLockAcquired).toBe(false); // 再尝试获取另一个读锁,也应该被阻塞 let readLock2Acquired = false; const readLock2Promise = readLock2.readLock().then(() => { readLock2Acquired = true; }); // 等待一小段时间,第二个读锁也不应该被获取 await new Promise(resolve => setTimeout(resolve, 50)); expect(readLock2Acquired).toBe(false); // 释放写锁 await writeLock.unlock(); // 现在读锁应该能被获取 await new Promise(resolve => setTimeout(resolve, 100)); expect(readLockAcquired).toBe(true); expect(readLock2Acquired).toBe(true); // 释放读锁 await Promise.all([ readLock1.unlock(), readLock2.unlock() ]); writeLock.close(); readLock1.close(); readLock2.close(); }); }); describe('Error Handling', () => { test('should reject when trying to acquire lock while already holding one', async () => { const lock = new NamedPipeRWLock('errorResource', pipePath); await lock.readLock(); // 尝试在已经持有锁的情况下再获取锁 await expect(lock.writeLock()).rejects.toThrow('Lock already held'); await lock.unlock(); lock.close(); }); test('should handle lock acquisition timeout', async () => { const lock = new NamedPipeRWLock('timeoutResource', pipePath, { timeout: 100 // 设置很短的超时时间 }); // 模拟一个永远不会释放的锁场景 const blockingLock = new NamedPipeRWLock('timeoutResource', pipePath); await blockingLock.writeLock(); // 尝试获取已经被占用的锁,应该会超时 await expect(lock.writeLock()).rejects.toThrow(/timeout/); await blockingLock.unlock(); blockingLock.close(); lock.close(); }); }); });