1- import { mkdir , writeFile } from "node:fs/promises" ;
1+ import { mkdir , readFile , writeFile } from "node:fs/promises" ;
22import path from "node:path" ;
33import process from "node:process" ;
44import { spawnSync } from "node:child_process" ;
55import { fileURLToPath } from "node:url" ;
66
7+ import {
8+ createAndroidPerformanceMarkdown ,
9+ parseGfxinfo ,
10+ parseLaunchOutput ,
11+ parseMeminfo
12+ } from "./android-performance-evidence-format.mjs" ;
713import { listNativeQaRows } from "./record-native-qa-evidence.mjs" ;
814
915const defaultAndroidPackage = "io.chartkit.showcase" ;
@@ -120,6 +126,29 @@ const runCommand = ({ args, command, encoding = "utf8" }) => {
120126 return result . stdout ;
121127} ;
122128
129+ const getGitCommit = ( runner ) => {
130+ try {
131+ return runner ( {
132+ args : [ "rev-parse" , "--short" , "HEAD" ] ,
133+ command : "git"
134+ } ) . trim ( ) ;
135+ } catch {
136+ return "n/a" ;
137+ }
138+ } ;
139+
140+ const readPackageVersion = async ( repoRoot ) => {
141+ try {
142+ const packageJson = JSON . parse (
143+ await readFile ( path . join ( repoRoot , "package.json" ) , "utf8" )
144+ ) ;
145+
146+ return packageJson . version ?? "n/a" ;
147+ } catch {
148+ return "n/a" ;
149+ }
150+ } ;
151+
123152const adbArgs = ( device , args ) => [ ...( device ? [ "-s" , device ] : [ ] ) , ...args ] ;
124153
125154const isLaunchOnlyScenario = ( rowId ) =>
@@ -166,133 +195,6 @@ const findRow = async ({ repoRoot, rowId }) => {
166195 return row ;
167196} ;
168197
169- const parseFirstNumber = ( text , pattern ) => {
170- const match = text . match ( pattern ) ;
171- return match ? Number ( match [ 1 ] . replaceAll ( "," , "" ) ) : undefined ;
172- } ;
173-
174- const parseGfxinfo = ( text ) => ( {
175- frameDeadlineMissed : parseFirstNumber (
176- text ,
177- / F r a m e d e a d l i n e m i s s e d : \s + ( [ \d , ] + ) /
178- ) ,
179- jankyFrames : parseFirstNumber ( text , / J a n k y f r a m e s : \s + ( [ \d , ] + ) / ) ,
180- p50Ms : parseFirstNumber ( text , / 5 0 t h p e r c e n t i l e : \s + ( [ \d . ] + ) m s / ) ,
181- p90Ms : parseFirstNumber ( text , / 9 0 t h p e r c e n t i l e : \s + ( [ \d . ] + ) m s / ) ,
182- p95Ms : parseFirstNumber ( text , / 9 5 t h p e r c e n t i l e : \s + ( [ \d . ] + ) m s / ) ,
183- p99Ms : parseFirstNumber ( text , / 9 9 t h p e r c e n t i l e : \s + ( [ \d . ] + ) m s / ) ,
184- totalFrames : parseFirstNumber ( text , / T o t a l f r a m e s r e n d e r e d : \s + ( [ \d , ] + ) / )
185- } ) ;
186-
187- const parseMeminfo = ( text ) => ( {
188- nativeHeapPssKb : parseFirstNumber ( text , / N a t i v e H e a p \s + ( [ \d , ] + ) / ) ,
189- totalPssKb : parseFirstNumber ( text , / T O T A L P S S : \s + ( [ \d , ] + ) / ) ,
190- totalRssKb : parseFirstNumber ( text , / T O T A L R S S : \s + ( [ \d , ] + ) / )
191- } ) ;
192-
193- const parseLaunchOutput = ( text ) => ( {
194- totalTimeMs : parseFirstNumber ( text , / T o t a l T i m e : \s + ( [ \d , ] + ) / ) ,
195- waitTimeMs : parseFirstNumber ( text , / W a i t T i m e : \s + ( [ \d , ] + ) / )
196- } ) ;
197-
198- const formatMetric = ( value , suffix = "" ) =>
199- Number . isFinite ( value ) ? `${ value . toLocaleString ( "en-US" ) } ${ suffix } ` : "n/a" ;
200-
201- const createMarkdown = ( {
202- commands,
203- deviceInfo,
204- gfxinfoSummary,
205- launchSummary,
206- launchOutput,
207- meminfoBeforeSummary,
208- meminfoSummary,
209- row,
210- screenshotPath,
211- androidPackage
212- } ) =>
213- [
214- `# ${ row . id } Android Performance Sample` ,
215- "" ,
216- `Date: ${ new Date ( ) . toISOString ( ) . slice ( 0 , 10 ) } ` ,
217- `Platform: Android emulator` ,
218- `Build: release APK, \`${ androidPackage } \`` ,
219- `Renderer: ${ row . renderer ?? "svg" } through React Native SVG` ,
220- `Scenario: ${ row . target } ` ,
221- `Showcase story: \`${ row . showcaseStoryId } \`` ,
222- `Deep link: \`${ row . launchUrl } \`` ,
223- "" ,
224- "Expected fixture:" ,
225- "" ,
226- `- Chart type: ${ row . expectedStoryMetrics ?. chartType ?? "n/a" } ` ,
227- `- Total points: ${ formatMetric ( row . expectedStoryMetrics ?. totalPoints ) } ` ,
228- `- Visible points: ${ formatMetric ( row . expectedStoryMetrics ?. visiblePoints ) } ` ,
229- `- Series count: ${ formatMetric ( row . expectedStoryMetrics ?. seriesCount ) } ` ,
230- "" ,
231- "Device:" ,
232- "" ,
233- `- Model: ${ deviceInfo . model || "n/a" } ` ,
234- `- Android: ${ deviceInfo . androidVersion || "n/a" } ` ,
235- `- Screen: ${ deviceInfo . screenSize || "n/a" } ` ,
236- "" ,
237- "Commands used:" ,
238- "" ,
239- "```sh" ,
240- ...commands . map ( ( item ) => commandText ( item . command , item . args ) ) ,
241- "```" ,
242- "" ,
243- "Launch output:" ,
244- "" ,
245- "```text" ,
246- launchOutput . trim ( ) || "n/a" ,
247- "```" ,
248- "" ,
249- "Launch timing:" ,
250- "" ,
251- "| Metric | Result |" ,
252- "| --- | ---: |" ,
253- `| TotalTime | ${ formatMetric ( launchSummary . totalTimeMs , " ms" ) } |` ,
254- `| WaitTime | ${ formatMetric ( launchSummary . waitTimeMs , " ms" ) } |` ,
255- "" ,
256- "Frame timing:" ,
257- "" ,
258- "| Metric | Result |" ,
259- "| --- | ---: |" ,
260- `| Total frames rendered | ${ formatMetric ( gfxinfoSummary . totalFrames ) } |` ,
261- `| Janky frames | ${ formatMetric ( gfxinfoSummary . jankyFrames ) } |` ,
262- `| p50 frame time | ${ formatMetric ( gfxinfoSummary . p50Ms , " ms" ) } |` ,
263- `| p90 frame time | ${ formatMetric ( gfxinfoSummary . p90Ms , " ms" ) } |` ,
264- `| p95 frame time | ${ formatMetric ( gfxinfoSummary . p95Ms , " ms" ) } |` ,
265- `| p99 frame time | ${ formatMetric ( gfxinfoSummary . p99Ms , " ms" ) } |` ,
266- `| Frame deadline missed | ${ formatMetric ( gfxinfoSummary . frameDeadlineMissed ) } |` ,
267- "" ,
268- "Memory:" ,
269- "" ,
270- "| Metric | Before scenario | After scenario |" ,
271- "| --- | ---: | ---: |" ,
272- `| Total PSS | ${ formatMetric (
273- meminfoBeforeSummary . totalPssKb ,
274- " KB"
275- ) } | ${ formatMetric ( meminfoSummary . totalPssKb , " KB" ) } |`,
276- `| Total RSS | ${ formatMetric (
277- meminfoBeforeSummary . totalRssKb ,
278- " KB"
279- ) } | ${ formatMetric ( meminfoSummary . totalRssKb , " KB" ) } |`,
280- `| Native heap PSS | ${ formatMetric (
281- meminfoBeforeSummary . nativeHeapPssKb ,
282- " KB"
283- ) } | ${ formatMetric ( meminfoSummary . nativeHeapPssKb , " KB" ) } |`,
284- "" ,
285- "Artifact:" ,
286- "" ,
287- `- Screenshot: [${ path . basename ( screenshotPath ) } ](${ path . basename ( screenshotPath ) } )` ,
288- "" ,
289- "Notes:" ,
290- "" ,
291- "- This is Android release-emulator evidence for one native performance matrix row." ,
292- "- It does not replace physical-device performance, iOS performance, Skia parity, or manual visible-correctness review." ,
293- ""
294- ] . join ( "\n" ) ;
295-
296198export const createAndroidPerformancePlan = async ( {
297199 androidPackage = defaultAndroidPackage ,
298200 device,
@@ -416,6 +318,9 @@ export const captureAndroidPerformanceEvidence = async ({
416318 const gfxinfo = runner ( gfxCommand ) ;
417319 const meminfo = runner ( memCommand ) ;
418320 const screenshot = runner ( { ...screenshotCommand , encoding : "buffer" } ) ;
321+ const repoRoot = options . repoRoot ?? defaultRepoRoot ;
322+ const commit = getGitCommit ( runner ) ;
323+ const packageVersion = await readPackageVersion ( repoRoot ) ;
419324 const deviceInfo = {
420325 androidVersion : runner ( {
421326 args : adbArgs ( options . device , [
@@ -440,17 +345,20 @@ export const captureAndroidPerformanceEvidence = async ({
440345 await writeFile ( plan . absoluteScreenshotPath , screenshot ) ;
441346 await writeFile (
442347 plan . absoluteOutputPath ,
443- createMarkdown ( {
348+ createAndroidPerformanceMarkdown ( {
349+ androidPackage : plan . androidPackage ,
350+ commandText,
444351 commands : plan . commands ,
352+ commit,
445353 deviceInfo,
446354 gfxinfoSummary : parseGfxinfo ( gfxinfo ) ,
447355 launchSummary : parseLaunchOutput ( launchOutput ) ,
448356 launchOutput,
449357 meminfoBeforeSummary : parseMeminfo ( meminfoBefore ) ,
450358 meminfoSummary : parseMeminfo ( meminfo ) ,
359+ packageVersion,
451360 row : plan . row ,
452- screenshotPath : plan . screenshotPath ,
453- androidPackage : plan . androidPackage
361+ screenshotPath : plan . screenshotPath
454362 } ) ,
455363 "utf8"
456364 ) ;
0 commit comments