UNPKG

unpak.js

Version:

Modern TypeScript library for reading Unreal Engine pak files and assets, inspired by CUE4Parse

297 lines (241 loc) 11.5 kB
import { PluginMarketplace, PluginManifest, CommunityPlugin } from '../src/community/PluginMarketplace'; import { AssetJITCompiler, JITOptimize } from '../src/ue4/performance/JITCompiler'; import { IncrementalParser } from '../src/ue4/performance/IncrementalParser'; describe('New Roadmap Features', () => { describe('Plugin Marketplace (Phase 13)', () => { let marketplace: PluginMarketplace; beforeEach(() => { marketplace = new PluginMarketplace('https://test-marketplace.dev', 'test-api-key'); }); test('should initialize plugin marketplace', () => { expect(marketplace).toBeInstanceOf(PluginMarketplace); }); test('should handle plugin search with local fallback', async () => { const query = { name: 'test-plugin', tags: ['texture', 'converter'], minRating: 4.0 }; const results = await marketplace.searchPlugins(query); expect(Array.isArray(results)).toBe(true); }); test('should get installed plugins list', () => { const installed = marketplace.getInstalledPlugins(); expect(Array.isArray(installed)).toBe(true); }); test('should validate plugin manifest structure', () => { const manifest: PluginManifest = { name: 'test-plugin', version: '1.0.0', description: 'Test plugin for unpak.js', author: 'Test Author', license: 'MIT', tags: ['test', 'converter'], compatibility: { unpakVersion: '2.0.0', ueVersions: ['UE5.3', 'UE5.4'], gameSupport: ['Fortnite', 'Valorant'] }, dependencies: {}, repository: 'https://github.com/test/plugin', downloadUrl: 'https://cdn.test.dev/plugin.tar.gz', checksums: { sha256: 'abc123', md5: 'def456' }, permissions: [], installation: { type: 'npm' } }; expect(manifest.name).toBe('test-plugin'); expect(manifest.version).toBe('1.0.0'); expect(manifest.compatibility.unpakVersion).toBe('2.0.0'); }); }); describe('JIT Compiler (Phase 11)', () => { let compiler: AssetJITCompiler; beforeEach(() => { compiler = new AssetJITCompiler({ performanceThreshold: 5, frequencyThreshold: 2, maxCacheSize: 10 }); }); test('should initialize JIT compiler', () => { expect(compiler).toBeInstanceOf(AssetJITCompiler); const stats = compiler.getStatistics(); expect(stats.totalHotPaths).toBe(0); expect(stats.optimizedPaths).toBe(0); }); test('should track asset processing performance', () => { const assetPath = '/Game/Textures/T_TestTexture.uasset'; const operation = 'parse'; const executionTime = 10.5; compiler.trackAssetProcessing(assetPath, operation, executionTime); const hotPaths = compiler.getHotPaths(); expect(hotPaths.length).toBe(1); expect(hotPaths[0].path).toBe(`${assetPath}:${operation}`); expect(hotPaths[0].frequency).toBe(1); expect(hotPaths[0].averageTime).toBe(executionTime); }); test('should detect hot paths for optimization', () => { const assetPath = '/Game/Textures/T_TestTexture.uasset'; const operation = 'parse'; // Track multiple executions to trigger optimization for (let i = 0; i < 5; i++) { compiler.trackAssetProcessing(assetPath, operation, 10); } const hotPaths = compiler.getHotPaths(); expect(hotPaths.length).toBe(1); expect(hotPaths[0].frequency).toBe(5); }); test('should generate optimization hints', () => { // Track some performance data compiler.trackAssetProcessing('/Game/Textures/T_Test.uasset', 'parse', 8); compiler.trackAssetProcessing('/Game/Meshes/SM_Test.uasset', 'export', 12); const hints = compiler.generateOptimizationHints(); expect(Array.isArray(hints)).toBe(true); }); test('should get JIT statistics', () => { const stats = compiler.getStatistics(); expect(stats).toHaveProperty('totalHotPaths'); expect(stats).toHaveProperty('optimizedPaths'); expect(stats).toHaveProperty('compilationTime'); expect(stats).toHaveProperty('performanceGain'); expect(stats).toHaveProperty('cacheHitRate'); }); test('should clear optimization cache', () => { // Add some tracking data compiler.trackAssetProcessing('/Game/Test.uasset', 'parse', 10); let stats = compiler.getStatistics(); expect(stats.totalHotPaths).toBe(1); compiler.clearCache(); stats = compiler.getStatistics(); expect(stats.optimizedPaths).toBe(0); expect(stats.cacheHitRate).toBe(0); }); }); describe('JIT Optimization Decorator', () => { class TestAssetProcessor { public jitCompiler = new AssetJITCompiler(); @JITOptimize('texture', 'parse') parseTexture(data: Buffer): any { // Simulate texture parsing return { format: 'DDS', width: 512, height: 512 }; } @JITOptimize('mesh', 'export') exportMesh(mesh: any, format: string): any { // Simulate mesh export return { format, exported: true }; } } test('should track decorated method performance', () => { const processor = new TestAssetProcessor(); const testData = Buffer.alloc(1024); processor.parseTexture(testData); const hotPaths = processor.jitCompiler.getHotPaths(); expect(hotPaths.length).toBe(1); expect(hotPaths[0].path).toBe('texture:parse'); }); test('should track multiple decorated methods', () => { const processor = new TestAssetProcessor(); const testData = Buffer.alloc(1024); const testMesh = { vertices: [], indices: [] }; processor.parseTexture(testData); processor.exportMesh(testMesh, 'obj'); const hotPaths = processor.jitCompiler.getHotPaths(); expect(hotPaths.length).toBe(2); }); }); describe('Incremental Parser Integration', () => { let parser: IncrementalParser; beforeEach(() => { parser = new IncrementalParser({ chunkSize: 5, maxConcurrency: 2, maxMemoryUsage: 100 * 1024 // 100KB for testing }); }); test('should process items incrementally', async () => { const items = Array.from({ length: 15 }, (_, i) => i); const processor = async (item: number) => item * 2; const results = await parser.processIncrementally(items, processor); expect(results).toHaveLength(15); expect(results[0]).toBe(0); expect(results[14]).toBe(28); }); test('should handle processing errors gracefully', async () => { const items = [1, 2, 3, 4, 5]; const processor = async (item: number) => { if (item === 3) { throw new Error('Test error'); } return item * 2; }; const results = await parser.processIncrementally(items, processor, { failFast: false }); // Should have 4 results (excluding the failed item) expect(results).toHaveLength(4); }); test('should provide progress tracking', (done) => { const items = Array.from({ length: 10 }, (_, i) => i); const processor = async (item: number) => { await new Promise(resolve => setTimeout(resolve, 10)); return item; }; let progressEvents = 0; parser.on('progress', (progress) => { progressEvents++; expect(progress).toHaveProperty('processed'); expect(progress).toHaveProperty('total'); expect(progress).toHaveProperty('percentage'); }); parser.on('completed', () => { expect(progressEvents).toBeGreaterThan(0); done(); }); parser.processIncrementally(items, processor); }); test('should cancel processing when requested', async () => { const items = Array.from({ length: 100 }, (_, i) => i); const processor = async (item: number) => { await new Promise(resolve => setTimeout(resolve, 50)); return item; }; // Start processing const processingPromise = parser.processIncrementally(items, processor); // Cancel after a short delay setTimeout(() => { parser.cancel(); }, 100); const results = await processingPromise; // Should have processed fewer items due to cancellation expect(results.length).toBeLessThan(100); }); test('should get processing statistics', () => { const stats = parser.getStatistics(); expect(stats).toHaveProperty('isProcessing'); expect(stats).toHaveProperty('processedItems'); expect(stats).toHaveProperty('totalItems'); expect(stats).toHaveProperty('percentage'); expect(stats).toHaveProperty('memoryUsage'); expect(stats).toHaveProperty('maxMemoryUsage'); }); }); describe('Enhanced Asset Types (Phase 4)', () => { test('should have ULevelSequence asset type', () => { // Import would be tested in actual implementation // For now, just verify the module structure expect(true).toBe(true); // Placeholder }); test('should have UMediaPlayer asset type', () => { // Import would be tested in actual implementation // For now, just verify the module structure expect(true).toBe(true); // Placeholder }); test('should have enhanced UNiagaraSystem support', () => { // Niagara system was already implemented and tested expect(true).toBe(true); // Placeholder }); }); });