unpak.js
Version:
Modern TypeScript library for reading Unreal Engine pak files and assets, inspired by CUE4Parse
297 lines (241 loc) • 11.5 kB
text/typescript
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();
('texture', 'parse')
parseTexture(data: Buffer): any {
// Simulate texture parsing
return { format: 'DDS', width: 512, height: 512 };
}
('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
});
});
});