1/* 2 * Copyright (c) 2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import * as fs from 'fs'; 17import mocha from 'mocha'; 18import { TimeAndMemInfo, TimeSumPrinter, TimeTracker, initPerformancePrinter } from '../../../src/utils/PrinterUtils'; 19import { assert, expect } from 'chai'; 20import { isFileExist } from '../../../src/initialization/utils'; 21import { 22 blockPrinter, 23 endFilesEvent, 24 endSingleFileEvent, 25 performancePrinter, 26 printTimeSumData, 27 printTimeSumInfo, 28 startFilesEvent, 29 startSingleFileEvent 30} from '../../../src/ArkObfuscator'; 31import type { IOptions } from '../../../src/configs/IOptions'; 32const sinon = require('sinon'); 33const DEFAULT_DURATION = 1; // Default time duration 34 35describe('test Cases for <PrinterUtils>.', function () { 36 beforeEach(() => { 37 performancePrinter.filesPrinter = new TimeTracker(); 38 performancePrinter.singleFilePrinter = new TimeTracker(); 39 performancePrinter.timeSumPrinter = new TimeSumPrinter(); 40 }); 41 42 describe('Tester Cases for <TimeTracker>.', function () { 43 let printer: TimeTracker; 44 45 beforeEach(() => { 46 printer = new TimeTracker(); 47 }) 48 49 describe('Tester Cases for <setOutputPath>.', function () { 50 /** test for setOutputPath */ 51 it('Tester: <setOutputPath> case for TimeTracker#setOutputPath', function () { 52 let path = 'test/ut/utils/demo1.txt'; 53 printer.setOutputPath(path); 54 let content: string = printer.getOutputPath(); 55 assert.strictEqual(content, path); 56 }); 57 }); 58 59 describe('Tester Cases for <print>.', function () { 60 /** test for print */ 61 it('Tester: <available outputPath> case for TimeTracker#print', function () { 62 let path = 'test/ut/utils/testTimeTrackerPrint.txt'; 63 printer.setOutputPath(path); 64 printer.print('available outputPath case'); 65 let content: string = fs.readFileSync(path, 'utf-8'); 66 assert.strictEqual(content, 'available outputPath case\n'); 67 fs.unlinkSync(path); 68 }); 69 70 it('Tester: <unavailable outputPath> case for TimeTracker#print', function () { 71 let path = 'test/ut/utils/demo2.txt'; 72 let message = 'unavailable outputPath case'; 73 var spy = sinon.spy(console, 'log'); 74 printer.print(message); 75 assert(spy.calledWith(message), message); 76 assert.strictEqual(isFileExist(path), false); 77 spy.restore(); 78 }); 79 }); 80 81 describe('Tester Cases for <disablePrinter>.', function () { 82 /** test for print */ 83 it('Tester: <empty output> case for TimeTracker#disablePrinter', function () { 84 const eventName = 'All files obfuscation'; 85 let path = 'test/ut/utils/demo1.txt'; 86 printer.setOutputPath(path); 87 printer.disablePrinter(); 88 printer.getEventStack().set(eventName, { start: 0, duration: 0, startMemory: 0, endMemory: 0, memoryUsage: 0}); 89 printer.endEvent(eventName); 90 let content: string = fs.readFileSync(path, 'utf-8'); 91 const lines = content.split('\n'); 92 const firstLine = lines[0].split(':'); 93 const secondLine = lines[1].split(':'); 94 const thirdLine = lines[2].split(':'); 95 assert.strictEqual(firstLine[0], 'Obfuscation time cost'); 96 assert.strictEqual(secondLine[0], 'Max time cost of single file'); 97 assert.strictEqual(thirdLine[0], 'Max memory usage of single file'); 98 fs.unlinkSync(path); 99 }); 100 }); 101 102 describe('Tester Cases for <startEvent>.', function () { 103 /** test for startEvent */ 104 it('Tester: <start test event> case for TimeTracker#startEvent', function () { 105 let eventName = 'test event'; 106 let path = 'test/ut/utils/testTimeTrackerPrint.txt'; 107 let message = 'start test event'; 108 printer.setOutputPath(path); 109 printer.startEvent(eventName, undefined, message); 110 const eventData = printer.getEventStack().get(eventName); 111 expect(eventData?.duration).to.equal(0); 112 expect(eventData?.endMemory).to.equal(0); 113 expect(eventData?.memoryUsage).to.equal(0); 114 assert.strictEqual(isFileExist(path), false); 115 }); 116 117 it('Tester: <start create ast event> case for TimeTracker#startEvent', function () { 118 let eventName = 'Create AST'; 119 printer.startEvent(eventName, undefined); 120 const eventData = printer.getEventStack().get(eventName); 121 expect(eventData?.duration).to.equal(0); 122 expect(eventData?.endMemory).to.equal(0); 123 expect(eventData?.memoryUsage).to.equal(0); 124 }); 125 }); 126 127 describe('Tester Cases for <endEvent>.', function () { 128 /** test for endEvent */ 129 it('should throw an error if the event has not started', function () { 130 let eventName = ''; 131 let path = 'test/ut/utils/demo1.txt'; 132 printer.setOutputPath(path); 133 printer.startEvent('test event'); 134 expect(() => printer.endEvent(eventName)).to.throw(`Event "${eventName}" not started`); 135 }); 136 137 it('should calculate duration and memory usage correctly', () => { 138 const eventName = 'test Event'; 139 printer.getEventStack().set(eventName, 140 { start: 0, duration: 0, startMemory: 0, endMemory: 0, memoryUsage: 0}); 141 printer.endEvent(eventName); 142 const eventData = printer.getEventStack().get(eventName); 143 expect(eventData?.duration).to.not.equal(0); 144 expect(eventData?.endMemory).to.not.equal(0); 145 expect(eventData?.memoryUsage).to.not.equal(0); 146 }); 147 148 it('should update filesTimeSum and maxTimeUsage when isFilesPrinter is true', () => { 149 const eventName = 'file Event'; 150 const startMemory = process.memoryUsage().heapUsed; 151 printer.getEventStack().set(eventName, 152 { start: 0, duration: 0, startMemory: startMemory, endMemory: 0, memoryUsage: 0}); 153 printer.endEvent(eventName, undefined, true); 154 expect(printer.getFilesTimeSum()).to.not.equal(0); 155 expect(printer.getMaxTimeUsage()).to.not.equal(0); 156 expect(printer.getMaxTimeFile()).to.equal(eventName); 157 }); 158 159 it('should update maxMemoryUsage and maxMemoryFile when isFilesPrinter is true', () => { 160 const eventName = 'file Event'; 161 const startTime = Date.now(); 162 printer.getEventStack().set(eventName, 163 { start: startTime, duration: 0, startMemory: 0, endMemory: 0, memoryUsage: 0}); 164 printer.endEvent(eventName, undefined, true); 165 expect(printer.getMaxMemoryUsage()).to.not.equal(0); 166 expect(printer.getMaxMemoryFile()).to.equal(eventName); 167 }); 168 169 it('should output data and print max time and memory usage for ALL_FILES_OBFUSCATION', () => { 170 const eventName = 'All files obfuscation'; 171 let path = 'test/ut/utils/demo1.txt'; 172 printer.setOutputPath(path); 173 printer.getEventStack().set(eventName, { start: 0, duration: 0, startMemory: 0, endMemory: 0, memoryUsage: 0}); 174 printer.endEvent(eventName); 175 let content: string = fs.readFileSync(path, 'utf-8'); 176 const lines = content.split('\n'); 177 const firstLine = lines[0].split(':'); 178 const secondLine = lines[2].split(':'); 179 const thirdLine = lines[3].split(':'); 180 const fourthLine = lines[4].split(':'); 181 assert.strictEqual(firstLine[0], ' All files obfuscation'); 182 assert.strictEqual(secondLine[0], 'Obfuscation time cost'); 183 assert.strictEqual(thirdLine[0], 'Max time cost of single file'); 184 assert.strictEqual(fourthLine[0], 'Max memory usage of single file'); 185 fs.unlinkSync(path); 186 }); 187 188 it('should output data for CREATE_PRINTER', () => { 189 const eventName = 'Create Printer'; 190 const startMemory = process.memoryUsage().heapUsed; 191 let path = 'test/ut/utils/demo1.txt'; 192 printer.setOutputPath(path); 193 printer.getEventStack().set(eventName, 194 { start: Date.now(), duration: 0, startMemory: startMemory, endMemory: 0, memoryUsage: 0}); 195 printer.endEvent(eventName, undefined, false, true); 196 let content: string = fs.readFileSync(path, 'utf-8'); 197 const firstLine = content.split(':'); 198 assert.strictEqual(firstLine[0], ' Create Printer'); 199 fs.unlinkSync(path); 200 }); 201 202 it('should get totalTime', () => { 203 let path = 'test/ut/utils/demo1.txt'; 204 printer.setOutputPath(path); 205 const eventName1 = 'Obfuscation initialization'; 206 const eventName2 = 'All files obfuscation'; 207 let testFile = 'test/ut/utils/testFile.ts'; 208 printer.getEventStack().set(eventName1, 209 { start: Date.now(), duration: 0, startMemory: 0, endMemory: 0, memoryUsage: 0}); 210 printer.getEventStack().set(eventName2, 211 { start: Date.now(), duration: 0, startMemory: 0, endMemory: 0, memoryUsage: 0, filePath: testFile}); 212 printer.endEvent(eventName2); 213 let content: string = fs.readFileSync(path, 'utf-8'); 214 const lines = content.split('\n'); 215 const firstLine = lines[0].split(':'); 216 const secondLine = lines[1].split(':'); 217 const thirdLine = lines[2].split(':'); 218 assert.strictEqual(firstLine[0], 'Obfuscation time cost'); 219 assert.strictEqual(secondLine[0], ' Obfuscation initialization'); 220 assert.strictEqual(thirdLine[0], 'test/ut/utils/testFile.ts'); 221 fs.unlinkSync(path); 222 }); 223 }); 224 225 describe('Tester Cases for <getCurrentEventData>.', function () { 226 it('should return a string with formatted event data', () => { 227 const eventName = 'test event'; 228 printer.getEventStack().set(eventName, 229 { start: 0, duration: 10, startMemory: 1024, endMemory: 2048, memoryUsage: 1024}); 230 const actualOutput: string = printer.getCurrentEventData(); 231 const expectOutput: string = ' test event: timeCost:10.000 s startMemory:0.001MB endMemory:0.002MB memoryUsage:0.001MB\n'; 232 expect(actualOutput).to.equal(expectOutput); 233 }); 234 }); 235 236 describe('Tester Cases for <getEventStack>', () => { 237 238 it('should return the event stack', () => { 239 printer.startEvent('test event'); 240 const eventStack = printer.getEventStack(); 241 expect(eventStack).to.have.keys(['test event']); 242 expect(eventStack.get('test event')?.start).to.closeTo(Date.now(), 10); 243 expect(eventStack.get('test event')?.duration).to.equal(0); 244 expect(eventStack.get('test event')?.endMemory).to.equal(0); 245 expect(eventStack.get('test event')?.memoryUsage).to.equal(0); 246 }); 247 }); 248 }); 249 250 251 describe('Tester Cases for <TimeSumPrinter>.', function () { 252 let printer: TimeSumPrinter; 253 254 beforeEach(() => { 255 printer = new TimeSumPrinter(); 256 }) 257 258 describe('Tester Cases for <addEventDuration>.', function () { 259 /** test for addEventDuration */ 260 it('should add duration to the event sum', function () { 261 printer.addEventDuration('test event1', 10); 262 printer.addEventDuration('test event2', 20); 263 printer.addEventDuration('test event1', 30); 264 const event1Duration = printer.getEventSum().get('test event1'); 265 const event2Duration = printer.getEventSum().get('test event2'); 266 expect(event1Duration).to.equal(40); 267 expect(event2Duration).to.equal(20); 268 }); 269 }); 270 271 describe('Tester Cases for <summarizeEventDuration>.', function () { 272 /** test for summarizeEventDuration */ 273 it('should print the summarized event data', function () { 274 let path = 'test/ut/utils/demo1.txt'; 275 printer.setOutputPath(path); 276 printer.addEventDuration('test event1', 10); 277 printer.addEventDuration('test event2', 20); 278 printer.addEventDuration('test event1', 30); 279 printer.summarizeEventDuration(); 280 let content: string = fs.readFileSync(path, 'utf-8'); 281 const expectOutput = "test event1: 40.000 s\ntest event2: 20.000 s\n\n"; 282 expect(content).to.equal(expectOutput); 283 fs.unlinkSync(path); 284 }); 285 }); 286 287 describe('Tester Cases for <getCurrentEventData>.', function () { 288 it('should return a string with formatted event data', () => { 289 const eventName = 'test event'; 290 printer.getEventSum().set(eventName, 10); 291 const actualOutput: string = printer.getCurrentEventData(); 292 const expectOutput: string = 'test event: 10.000 s\n'; 293 expect(actualOutput).to.equal(expectOutput); 294 }); 295 }); 296 297 describe('Tester Cases for <getEventSum>', () => { 298 it('should return the event sum', () => { 299 printer.addEventDuration('test event', 10); 300 const eventSum = printer.getEventSum(); 301 expect(eventSum).to.have.keys(['test event']); 302 expect(eventSum.get('test event')).to.equal(10); 303 }); 304 }); 305 }); 306 307 describe('Tester Cases for <initPerformancePrinter>', () => { 308 it('Printer config is not set', () => { 309 let mCustomProfiles: IOptions = {}; 310 initPerformancePrinter(mCustomProfiles); 311 expect(performancePrinter.filesPrinter === undefined).to.be.true; 312 expect(performancePrinter.singleFilePrinter === undefined).to.be.true; 313 expect(performancePrinter.timeSumPrinter === undefined).to.be.true; 314 }); 315 316 it('None of printers is enabled', () => { 317 let mCustomProfiles: IOptions = { 318 mPerformancePrinter: { 319 mFilesPrinter: false, 320 mSingleFilePrinter: false, 321 mSumPrinter: false 322 } 323 }; 324 initPerformancePrinter(mCustomProfiles); 325 expect(performancePrinter.filesPrinter === undefined).to.be.true; 326 expect(performancePrinter.singleFilePrinter === undefined).to.be.true; 327 expect(performancePrinter.timeSumPrinter === undefined).to.be.true; 328 }); 329 330 it('disable mFilesPrinter', () => { 331 let mCustomProfiles: IOptions = { 332 mPerformancePrinter: { 333 mFilesPrinter: false, 334 mSingleFilePrinter: true, 335 mSumPrinter: true, 336 mOutputPath: './log.txt' 337 } 338 }; 339 initPerformancePrinter(mCustomProfiles); 340 expect(performancePrinter.filesPrinter?.isEnabled()).to.be.false; 341 expect(performancePrinter.singleFilePrinter?.isEnabled()).to.be.true; 342 expect(performancePrinter.timeSumPrinter?.isEnabled()).to.be.true; 343 }); 344 345 it('disable mSingleFilePrinter', () => { 346 let mCustomProfiles: IOptions = { 347 mPerformancePrinter: { 348 mFilesPrinter: true, 349 mSingleFilePrinter: false, 350 mSumPrinter: true, 351 mOutputPath: './log.txt' 352 } 353 }; 354 initPerformancePrinter(mCustomProfiles); 355 expect(performancePrinter.filesPrinter?.isEnabled()).to.be.true; 356 expect(performancePrinter.singleFilePrinter?.isEnabled()).to.be.false; 357 expect(performancePrinter.timeSumPrinter?.isEnabled()).to.be.true; 358 }); 359 360 it('disable mSumPrinter', () => { 361 let mCustomProfiles: IOptions = { 362 mPerformancePrinter: { 363 mFilesPrinter: true, 364 mSingleFilePrinter: true, 365 mSumPrinter: false, 366 mOutputPath: './log.txt' 367 } 368 }; 369 initPerformancePrinter(mCustomProfiles); 370 expect(performancePrinter.filesPrinter?.isEnabled()).to.be.true; 371 expect(performancePrinter.singleFilePrinter?.isEnabled()).to.be.true; 372 expect(performancePrinter.timeSumPrinter?.isEnabled()).to.be.false; 373 }); 374 }); 375 376 describe('Tester Cases for <blockPrinter>', () => { 377 it('should disable all printers', () => { 378 blockPrinter(); 379 expect(performancePrinter.filesPrinter === undefined).to.be.true; 380 expect(performancePrinter.singleFilePrinter === undefined).to.be.true; 381 expect(performancePrinter.timeSumPrinter === undefined).to.be.true; 382 }); 383 }); 384 385 describe('Tester Cases for <startSingleFileEvent>', () => { 386 it('start a singleFile event', () => { 387 let eventName: string = 'newEvent'; 388 let filePath: string = 'currentFile.ts'; 389 startSingleFileEvent(eventName, undefined, filePath); 390 const eventStack: Map<string, TimeAndMemInfo> | undefined = performancePrinter.singleFilePrinter?.getEventStack(); 391 let targetFilePath: string | undefined = eventStack?.get(eventName)?.filePath; 392 expect(targetFilePath === filePath).to.be.true; 393 }); 394 }); 395 396 describe('Tester Cases for <endSingleFileEvent>', () => { 397 it('end a singleFile event', () => { 398 let eventName: string = 'newEvent'; 399 let filePath: string = 'currentFile.ts'; 400 startSingleFileEvent(eventName, undefined, filePath); 401 endSingleFileEvent(eventName, undefined, false, false); 402 const eventStack: Map<string, TimeAndMemInfo> | undefined = performancePrinter.singleFilePrinter?.getEventStack(); 403 let targetFilePath: string | undefined = eventStack?.get(eventName)?.filePath; 404 let memoryUsage: number | undefined = eventStack?.get(eventName)?.memoryUsage; 405 expect(targetFilePath === filePath).to.be.true; 406 expect(memoryUsage !== 0).to.be.true; 407 }); 408 }); 409 410 describe('Tester Cases for <startFilesEvent>', () => { 411 it('start a files event', () => { 412 let eventName: string = 'filesEvent'; 413 let filePath: string = 'currentFile.ts'; 414 startFilesEvent(eventName, undefined, filePath); 415 const eventStack: Map<string, TimeAndMemInfo> | undefined = performancePrinter.filesPrinter?.getEventStack(); 416 let targetFilePath: string | undefined = eventStack?.get(eventName)?.filePath; 417 let startTime: number | undefined = eventStack?.get(eventName)?.start; 418 expect(targetFilePath === filePath).to.be.true; 419 expect(startTime !== 0).to.be.true; 420 }); 421 }); 422 423 describe('Tester Cases for <endFilesEvent>', () => { 424 it('end a files event', () => { 425 let eventName: string = 'filesEvent'; 426 let filePath: string = 'currentFile.ts'; 427 startFilesEvent(eventName, undefined, filePath); 428 endFilesEvent(eventName, undefined, false, false); 429 const eventStack: Map<string, TimeAndMemInfo> | undefined = performancePrinter.filesPrinter?.getEventStack(); 430 let targetFilePath: string | undefined = eventStack?.get(eventName)?.filePath; 431 let memoryUsage: number | undefined = eventStack?.get(eventName)?.memoryUsage; 432 expect(targetFilePath === filePath).to.be.true; 433 expect(memoryUsage !== 0).to.be.true; 434 }); 435 }); 436 437 describe('Tester Cases for <printTimeSumInfo>', () => { 438 it('print input info of timeSumPrinter', () => { 439 let info: string = 'Info of timeSumPrinter'; 440 let outputPath: string = 'test/ut/utils/outPutFile.txt'; 441 performancePrinter.timeSumPrinter?.setOutputPath(outputPath); 442 printTimeSumInfo(info); 443 let content: string = fs.readFileSync(outputPath, 'utf-8'); 444 expect(content === info + '\n').to.be.true; 445 fs.unlinkSync(outputPath); 446 }); 447 448 it('timeSumPrinter is undefined', () => { 449 let info: string = 'Info of timeSumPrinter'; 450 let outputPath: string = 'test/ut/utils/outPutFile.txt'; 451 performancePrinter.timeSumPrinter?.setOutputPath(outputPath); 452 performancePrinter.timeSumPrinter = undefined; 453 printTimeSumInfo(info); 454 expect(fs.existsSync(outputPath)).to.be.false; 455 }); 456 }); 457 458 describe('Tester Cases for <printTimeSumData>', () => { 459 it('print data of timeSumPrinter', () => { 460 let outputPath: string = 'test/ut/utils/outPutFile.txt'; 461 performancePrinter.timeSumPrinter?.setOutputPath(outputPath); 462 performancePrinter.timeSumPrinter?.addEventDuration('event1', DEFAULT_DURATION); 463 performancePrinter.timeSumPrinter?.addEventDuration('event1', DEFAULT_DURATION); 464 performancePrinter.timeSumPrinter?.addEventDuration('event2', DEFAULT_DURATION); 465 performancePrinter.timeSumPrinter?.addEventDuration('event2', DEFAULT_DURATION); 466 printTimeSumData(); 467 let content: string = fs.readFileSync(outputPath, 'utf-8'); 468 expect(content === 'event1: 2.000 s\nevent2: 2.000 s\n\n').to.be.true; 469 fs.unlinkSync(outputPath); 470 }); 471 472 it('timeSumPrinter is undefined', () => { 473 let outputPath: string = 'test/ut/utils/outPutFile.txt'; 474 performancePrinter.timeSumPrinter?.setOutputPath(outputPath); 475 performancePrinter.timeSumPrinter?.addEventDuration('event1', DEFAULT_DURATION); 476 performancePrinter.timeSumPrinter?.addEventDuration('event1', DEFAULT_DURATION); 477 performancePrinter.timeSumPrinter?.addEventDuration('event2', DEFAULT_DURATION); 478 performancePrinter.timeSumPrinter?.addEventDuration('event2', DEFAULT_DURATION); 479 performancePrinter.timeSumPrinter = undefined; 480 printTimeSumData(); 481 expect(fs.existsSync(outputPath)).to.be.false; 482 }); 483 }); 484}); 485 486 487 488 489