license import 'jasmine' ; import * as ts from 'typescript' ; import { CachedFileLoader , FileCache , ProgramAndFileCache } from './cache' ; import { invalidateFileCache , writeTempFile } from './test_support' ; function fauxDebug ( ... args ) { console . error . apply ( console , args ) ; } describe ( 'Cache' , ( ) => { let fileCache ; let fileLoader ; function loadFile ( name , fn ) { return fileLoader . loadFile ( name , fn , ts . ScriptTarget . ES5 ) ; } function setCurrentFiles ( ... fileName ) { const digests = new Map ( fileName . map ( ( fn ) : [ string , string ] => [ fn , 'digest' ] ) ) ; fileCache . updateCache ( digests ) ; } function expectFileCacheKeys ( ) { return expect ( fileCache . getFileCacheKeysForTest ( ) . map ( fn => fn . replace ( /.*\./ , '' ) ) ) ; } describe ( 'FileCache' , ( ) => { beforeEach ( ( ) => { fileCache = new FileCache ( fauxDebug ) ; fileLoader = new CachedFileLoader ( fileCache ) ; } ) ; it ( 'caches files' , ( ) => { const fn = writeTempFile ( 'file_cache_test' , 'let x: number = 12;\n' ) ; invalidateFileCache ( fileCache , fn ) ; const sourceFile = fileLoader . loadFile ( 'fileName' , fn , ts . ScriptTarget . ES5 ) ; let sourceFile2 = fileLoader . loadFile ( 'fileName' , fn , ts . ScriptTarget . ES5 ) ; expect ( sourceFile ) . toBe ( sourceFile2 ) ; invalidateFileCache ( fileCache , fn ) ; sourceFile2 = fileLoader . loadFile ( 'fileName' , fn , ts . ScriptTarget . ES5 ) ; expect ( sourceFile ) . not . toBe ( sourceFile2 ) ; } ) ; it ( 'caches in LRU order' , ( ) => { let free = false ; fileCache . shouldFreeMemory = ( ) => free ; const fn1 = writeTempFile ( 'file_cache_test1' , 'let x: number = 1;\n' ) ; const fn2 = writeTempFile ( 'file_cache_test2' , 'let x: number = 2;\n' ) ; const fn3 = writeTempFile ( 'file_cache_test3' , 'let x: number = 3;\n' ) ; const fn4 = writeTempFile ( 'file_cache_test4' , 'let x: number = 4;\n' ) ; const fn5 = writeTempFile ( 'file_cache_test5' , 'let x: number = 5;\n' ) ; setCurrentFiles ( fn1 , fn2 , fn3 , fn4 , fn5 ) ; const f1 = loadFile ( 'f1' , fn1 ) ; const f2 = loadFile ( 'f2' , fn2 ) ; const f3 = loadFile ( 'f3' , fn3 ) ; const f4 = loadFile ( 'f4' , fn4 ) ; expectFileCacheKeys ( ) . toEqual ( [ 'file_cache_test1' , 'file_cache_test2' , 'file_cache_test3' , 'file_cache_test4' , ] ) ; expect ( loadFile ( 'f1' , fn1 ) ) . toBe ( f1 , 'f1 should be cached' ) ; expectFileCacheKeys ( ) . toEqual ( [ 'file_cache_test2' , 'file_cache_test3' , 'file_cache_test4' , 'file_cache_test1' , ] ) ; setCurrentFiles ( fn2 , fn5 ) ; free = true ; const f5 = loadFile ( 'f5' , fn5 ) ; expectFileCacheKeys ( ) . toEqual ( [ 'file_cache_test2' , 'file_cache_test1' , 'file_cache_test5' , ] ) ; setCurrentFiles ( fn1 , fn2 , fn3 , fn4 , fn5 ) ; expect ( loadFile ( 'f1' , fn1 ) ) . toBe ( f1 , 'f1 should not be dropped, it was recently used' ) ; expect ( loadFile ( 'f2' , fn2 ) ) . toBe ( f2 , 'f2 should be pinned' ) ; expect ( loadFile ( 'f3' , fn3 ) ) . not . toBe ( f3 , 'f3 should have been dropped' ) ; expect ( loadFile ( 'f4' , fn4 ) ) . not . toBe ( f4 , 'f4 should have been dropped' ) ; expect ( loadFile ( 'f5' , fn5 ) ) . toBe ( f5 , 'f5 should be pinned' ) ; } ) ; it ( 'degenerates to cannotEvict mode' , ( ) => { fileCache . shouldFreeMemory = ( ) => true ; const fn1 = writeTempFile ( 'file_cache_test1' , 'let x: number = 1;\n' ) ; const fn2 = writeTempFile ( 'file_cache_test2' , 'let x: number = 2;\n' ) ; const fn3 = writeTempFile ( 'file_cache_test3' , 'let x: number = 3;\n' ) ; const fn4 = writeTempFile ( 'file_cache_test4' , 'let x: number = 4;\n' ) ; setCurrentFiles ( fn1 , fn2 , fn3 ) ; loadFile ( 'fn1' , fn1 ) , loadFile ( 'fn2' , fn2 ) ; loadFile ( 'fn3' , fn3 ) ; expect ( fileCache [ 'cannotEvict' ] ) . toBe ( true , 'all files are pinned' ) ; expectFileCacheKeys ( ) . toEqual ( [ 'file_cache_test1' , 'file_cache_test2' , 'file_cache_test3' , ] ) ; setCurrentFiles ( fn1 , fn4 ) ; expect ( fileCache [ 'cannotEvict' ] ) . toBe ( false , 'pinned files reset' ) ; loadFile ( 'fn4' , fn4 ) ; expectFileCacheKeys ( ) . toEqual ( [ 'file_cache_test1' , 'file_cache_test4' , ] ) ; } ) ; } ) ; describe ( 'ProgramAndFileCache' , ( ) => { let cache ; function loadProgram ( name ) { const p = { } as ts . Program ; cache . putProgram ( name , p ) ; return p ; } function expectProgramCacheKeys ( ) { return expect ( cache . getProgramCacheKeysForTest ( ) ) ; } beforeEach ( ( ) => { fileCache = new ProgramAndFileCache ( fauxDebug ) ; fileLoader = new CachedFileLoader ( fileCache ) ; cache = fileCache as ProgramAndFileCache ; } ) ; it ( 'caches programs' , ( ) => { const name = 'fauxprogram' ; const program = loadProgram ( name ) ; expect ( cache . getProgram ( name ) ) . toBe ( program ) ; } ) ; it ( 'caches programs in LRU order' , ( ) => { let free = false ; cache . shouldFreeMemory = ( ) => free ; const p1 = loadProgram ( 'p1' ) ; loadProgram ( 'p2' ) ; loadProgram ( 'p3' ) ; loadProgram ( 'p4' ) ; expectProgramCacheKeys ( ) . toEqual ( [ 'p1' , 'p2' , 'p3' , 'p4' , ] ) ; expect ( cache . getProgram ( 'p1' ) ) . toBe ( p1 ) ; expectProgramCacheKeys ( ) . toEqual ( [ 'p2' , 'p3' , 'p4' , 'p1' , ] ) ; free = true ; loadProgram ( 'p5' ) ; expectProgramCacheKeys ( ) . toEqual ( [ 'p4' , 'p1' , 'p5' , ] ) ; } ) ; it ( 'evicts programs before files' , ( ) => { let free = false ; cache . shouldFreeMemory = ( ) => free ; loadProgram ( 'p1' ) ; const fn1 = writeTempFile ( 'file_cache_test1' , 'let x: number = 1;\n' ) ; const fn2 = writeTempFile ( 'file_cache_test2' , 'let x: number = 2;\n' ) ; setCurrentFiles ( fn1 ) ; loadFile ( 'fn1' , fn1 ) ; expectProgramCacheKeys ( ) . toEqual ( [ 'p1' ] ) ; expectFileCacheKeys ( ) . toEqual ( [ 'file_cache_test1' ] ) ; free = true ; setCurrentFiles ( fn2 ) ; loadFile ( 'fn2' , fn2 ) ; expectProgramCacheKeys ( ) . toEqual ( [ ] ) ; expectFileCacheKeys ( ) . toEqual ( [ 'file_cache_test1' , 'file_cache_test2' ] ) ; } ) ; } ) ; } ) ;