import { generate } from "randomstring" ; import { NON_EXECUTABLE_FILE_MODE , EXECUTABLE_FILE_MODE , } from "../src/patch/parse" ; import { assertNever } from "../src/assertNever" ; export interface File { contents : string ; mode : number ; } export interface Files { [ filePath ] : File ; } export interface TestCase { cleanFiles : Files ; modifiedFiles : Files ; } const fileCharSet = `
                                                     \n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
0123456789!@£$%^&*()-=_+[]{};'i\\:"|<>?,./\`~§±
0123456789!@£$%^&*()-=_+[]{};'i\\:"|<>?,./\`~§±
\r\r\r\r\t\t\t\t
` ; function makeFileContents ( ) { return { contents : generate ( { length : Math . floor ( Math . random ( ) * 1000 ) , charset : fileCharSet , } ) || "" , mode : Math . random ( ) > 0.5 ? 0o644 : 0o755 , } ; } function makeFileName ( ext ? ) { const name = generate ( { length : Math . ceil ( Math . random ( ) * 10 ) , charset : "abcdefghijklmnopqrstuvwxyz-_0987654321" , } ) ; return ext ? name + "." + generate ( Math . ceil ( Math . random ( ) * 4 ) ) : name ; } function makeFilePath ( ) { const numParts = Math . floor ( Math . random ( ) * 3 ) ; const parts = [ ] ; for ( let i = 0 ; i < numParts ; i ++ ) { parts . push ( makeFileName ( ) ) ; } parts . push ( makeFileName ( true ) ) ; return parts . join ( "/" ) ; } function makeFiles ( ) { const fileSystem = { } ; const numFiles = Math . random ( ) * 3 + 1 ; for ( let i = 0 ; i < numFiles ; i ++ ) { fileSystem [ makeFilePath ( ) ] = makeFileContents ( ) ; } return fileSystem ; } type MutationKind = "deleteFile" | "createFile" | "deleteLine" | "insertLine" | "renameFile" | "changeFileMode" ; const mutationKindLikelihoods = [ [ "deleteFile" , 1 ] , [ "createFile" , 1 ] , [ "renameFile" , 1 ] , [ "changeFileMode" , 1 ] , [ "deleteLine" , 10 ] , [ "insertLine" , 10 ] , ] ; const liklihoodSum = mutationKindLikelihoods . reduce ( ( acc , [ _ , n ] ) => acc + n , 0 ) ; function getNextMutationKind ( ) { const n = Math . random ( ) * liklihoodSum ; let sum = 0 ; for ( const [ kind , likelihood ] of mutationKindLikelihoods ) { sum += likelihood ; if ( n < sum ) { return kind ; } } return "insertLine" ; } function selectRandomElement < T > ( ts ) { return ts [ Math . floor ( Math . random ( ) * ts . length ) ] ; } function deleteLinesFromFile ( file ) { const numLinesToDelete = Math . ceil ( Math . random ( ) * 1 ) ; const lines = file . contents . split ( "\n" ) ; const index = Math . max ( Math . random ( ) * ( lines . length - numLinesToDelete ) , 0 ) ; lines . splice ( index , numLinesToDelete ) ; return { ... file , contents : lines . join ( "\n" ) } ; } function insertLinesIntoFile ( file ) { const lines = file . contents . split ( "\n" ) ; const index = Math . floor ( Math . random ( ) * lines . length ) ; const length = Math . ceil ( Math . random ( ) * 5 ) ; lines . splice ( index , 0 , generate ( { length , charset : fileCharSet } ) ) ; return { ... file , contents : lines . join ( "\n" ) } ; } function getUniqueFilename ( files ) { let filename = makeFileName ( ) ; const ks = Object . keys ( files ) ; while ( ks . some ( k => k . startsWith ( filename ) ) ) { filename = makeFileName ( ) ; } return filename ; } function mutateFiles ( files ) { const mutatedFiles = { ... files } ; const numMutations = Math . ceil ( Math . random ( ) * 1000 ) ; for ( let i = 0 ; i < numMutations ; i ++ ) { const mutationKind = getNextMutationKind ( ) ; switch ( mutationKind ) { case "deleteFile" : { if ( Object . keys ( mutatedFiles ) . length === 1 ) { break ; } const pathToDelete = selectRandomElement ( Object . keys ( mutatedFiles ) ) ; delete mutatedFiles [ pathToDelete ] ; break ; } case "createFile" : { mutatedFiles [ getUniqueFilename ( mutatedFiles ) ] = makeFileContents ( ) ; break ; } case "deleteLine" : { const pathToDeleteFrom = selectRandomElement ( Object . keys ( mutatedFiles ) ) ; mutatedFiles [ pathToDeleteFrom ] = deleteLinesFromFile ( mutatedFiles [ pathToDeleteFrom ] ) ; break ; } case "insertLine" : const pathToInsertTo = selectRandomElement ( Object . keys ( mutatedFiles ) ) ; mutatedFiles [ pathToInsertTo ] = insertLinesIntoFile ( mutatedFiles [ pathToInsertTo ] ) ; break ; case "renameFile" : const pathToRename = selectRandomElement ( Object . keys ( mutatedFiles ) ) ; mutatedFiles [ getUniqueFilename ( mutatedFiles ) ] = mutatedFiles [ pathToRename ] ; delete mutatedFiles [ pathToRename ] ; break ; case "changeFileMode" : const pathToChange = selectRandomElement ( Object . keys ( mutatedFiles ) ) ; const { mode , contents } = mutatedFiles [ pathToChange ] ; mutatedFiles [ pathToChange ] = { contents , mode : mode === NON_EXECUTABLE_FILE_MODE ? EXECUTABLE_FILE_MODE : NON_EXECUTABLE_FILE_MODE , } ; break ; default : assertNever ( mutationKind ) ; } } return { ... mutatedFiles } ; } export function generateTestCase ( ) { const cleanFiles = makeFiles ( ) ; return { cleanFiles , modifiedFiles : mutateFiles ( cleanFiles ) , } ; }