import "core-js/stable/array/fill.js" ; import faker from "faker" ; import { VNode , h , init as curInit } from "../../src/index" ; import { init as refInit } from "latest-snabbdom-release/build/package/init" ; import { assert } from "chai" ; import pReduce from "p-reduce" ; import pMapSeries from "p-map-series" ; import { std , mean } from "mathjs" ; const RUNS = 5 ; const PATCHES_PER_RUN = 100 ; const WARM_UP_RUNS = 1 ; const REQUEST_ANIMATION_FRAME_EVERY_N_PATCHES = 1 ; const BENCHMARK_TIMEOUT_MINUTES = 10 ; const REQUIRED_PRECISION = 0.02 ; declare global { const __karma__ ; } const ALLOWED_REGRESSION = 0.03 ; describe ( "core benchmark" , ( ) => { it ( "does not regress" , async function Benchmark ( ) { this . timeout ( BENCHMARK_TIMEOUT_MINUTES * 1000 * 60 ) ; faker . seed ( 0 ) ; const inputs = Array ( PATCHES_PER_RUN ) . fill ( null ) . map ( ( ) => { return new Array ( faker . random . number ( 20 ) ) . fill ( null ) . map ( ( ) => ( { name : faker . company . companyName ( ) , catchPhrase : faker . company . catchPhrase ( ) , suffix : faker . company . companySuffix ( ) , products : Array ( faker . random . number ( 3 ) ) . fill ( null ) . map ( ( ) => ( { name : faker . commerce . productName ( ) , color : faker . commerce . color ( ) , price : faker . commerce . price ( ) + faker . finance . currencySymbol ( ) , } ) ) , founded : faker . date . past ( ) , } ) ) ; } ) ; type Input = typeof inputs [ 0 ] ; const view = ( companies ) : VNode => h ( "table" , [ h ( "caption" , [ "Companies" ] ) , h ( "thead" , [ h ( "tr" , [ "Details" , "Products" ] . map ( ( th ) => h ( "th" , [ th ] ) ) ) , ] ) , h ( "tbody" , companies . map ( function companyView ( company ) { return h ( "tr" , [ h ( "td" , [ h ( "div" , [ h ( "b" , [ company . name ] ) , company . suffix && `\xa0${ company . suffix }` , ] ) , h ( "div" , h ( "i" , [ company . catchPhrase ] ) ) , h ( "td" , [ h ( "dt" , [ "Founded" ] ) , h ( "dd" , [ company . founded . toLocaleDateString ( ) ] ) , ] ) , ] ) , h ( "td" , [ h ( "ul" , company . products . map ( function productView ( product ) { return h ( "li" , [ h ( "dl" , [ h ( "dt" , [ "Name" ] ) , h ( "dd" , [ product . name ] ) , h ( "dt" , [ "Color" ] ) , h ( "dd" , [ product . color ] ) , h ( "dt" , [ "Price" ] ) , h ( "dd" , [ product . price ] ) , ] ) , ] ) ; } ) ) , ] ) , ] ) ; } ) ) , ] ) ; type Patcher = ReturnType < typeof refInit | typeof curInit > ; interface SingleRunResult { i : number ; cur : number ; ref : number ; } const subjectToResult = async ( subject , subjectId ) : Promise < number > => { await new Promise ( ( resolve ) => { requestAnimationFrame ( resolve ) ; } ) ; const markName = `mark:${ subjectId }` ; const measureName = `measure:${ subjectId }` ; performance . mark ( markName ) ; const lastVnode = await pReduce ( inputs , async function subjectToResultReducer ( acc , input , i ) { const vnode = view ( input ) ; subject ( acc , vnode ) ; if ( i % REQUEST_ANIMATION_FRAME_EVERY_N_PATCHES === 0 ) { await new Promise ( ( resolve ) => { requestAnimationFrame ( resolve ) ; } ) ; } return vnode ; } , document . body . appendChild ( document . createElement ( "section" ) ) ) ; performance . measure ( measureName , markName ) ; if ( ! ( "elm" in lastVnode ) ) throw new Error ( ) ; if ( ! lastVnode . elm ) throw new Error ( ) ; document . body . removeChild ( lastVnode . elm ) ; const measure = performance . getEntriesByName ( measureName ) [ 0 ] ; performance . clearMarks ( markName ) ; performance . clearMeasures ( measureName ) ; return measure . duration ; } ; const singleRun = async ( _ , runI ) : Promise < SingleRunResult > => { const cur = await subjectToResult ( curInit ( [ ] ) , `cur:${ runI }` ) ; const ref = await subjectToResult ( refInit ( [ ] ) , `ref:${ runI }` ) ; return { i : runI , cur , ref } ; } ; const runResults = ( await pMapSeries ( Array ( RUNS + WARM_UP_RUNS ) . fill ( null ) , singleRun ) ) . slice ( WARM_UP_RUNS ) ; __karma__ . info ( { benchmark : runResults } ) ; const results = { ref : runResults . map ( ( result ) => result . ref ) , cur : runResults . map ( ( result ) => result . cur ) , } ; const means = { ref : mean ( results . ref ) , cur : mean ( results . cur ) , } ; const stds = { ref : std ( results . ref , "uncorrected" ) , cur : std ( results . cur , "uncorrected" ) , } ; ( [ "ref" , "cur" ] as const ) . forEach ( ( subject ) => { const stdRatio = stds [ subject ] / means [ subject ] ; assert . isAtMost ( stdRatio , REQUIRED_PRECISION , `${ subject } not precise enough` ) ; } ) ; assert . isAtMost ( means . cur , means . ref * ( 1 + ALLOWED_REGRESSION ) ) ; } ) ; } ) ;