Skip to content

Commit 45711fd

Browse files
committed
feat: add static analysis module, config-based API key, and update README
1 parent 79cb724 commit 45711fd

19 files changed

+46095
-28
lines changed

.npmignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
static/

README.md

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,38 @@ npm run build
1919
npm pack
2020
```
2121

22+
## Static Analysis Setup
23+
If you want to use the static analysis module (```--policy static_guided```), you need to install its dependencies first:
24+
```
25+
cd static/test-demo
26+
npm install
27+
```
28+
2229
## Instructions
2330

2431
### 1. Usage
2532
```
2633
haptest [options]
2734
2835
Options:
29-
-V, --version output the version number
30-
-i --hap <file/bundleName/sourceRoot> HAP bundle name or HAP file path or HAP project source root
31-
-o --output <dir> output dir (default: "out")
32-
--policy <policyName> policy name (default: "manu")
33-
-t --target [connectkey] hdc connectkey
34-
-c --coverage enable coverage (default: false)
35-
-h, --help display help for command
36+
-V, --version output the version number
37+
-i, --hap <file/bundleName/sourceRoot> HAP bundle name or HAP file path or HAP project source root
38+
-o, --output <dir> output dir (default: "out")
39+
--policy <policyName> policy name (default: "manu")
40+
-t, --target [connectkey] hdc connectkey
41+
-c, --coverage enable coverage (default: false)
42+
--llm enable LLM-guided exploration (default: false)
43+
--simk <number> set similarity threshold K for tarpit detection (default: 3)
44+
--staticConfig <file> path to static analysis configuration file (required when policy=static_guided)
45+
-h, --help display help for command
46+
3647
```
3748

49+
#### Note:
50+
- ```--policy static_guided:``` Enable the static-analysis-guided exploration policy (requires ```--staticConfig``` to specify the static module configuration file).
51+
-``` --llm:``` Enable the LLM-based enhanced exploration module; can be combined with static_guided policy for a hybrid strategy.
52+
- ```--simk:``` Set the UI similarity threshold for tarpit detection.
53+
3854
### 2. Using DevEco simulator to run HAP
3955

4056
1. download DevEco: https://developer.huawei.com/consumer/cn/deveco-studio/
@@ -57,6 +73,11 @@ haptest -i {Hap project root } --policy greedy_dfs -o out
5773
haptest --policy perf_start_hap -i ALL --exclude com.huawei.* com.ohos.* -o out
5874
```
5975

76+
### 5. Run with static analysis and LLM enabled
77+
```
78+
haptest -i com.example.demo --policy static_guided --staticConfig config.json --llm --simk 3 -o out
79+
```
80+
6081
## Contribution
6182

6283
1. Fork the repository

config.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"GPT_CONFIG":{
3+
"baseURL": "",
4+
"apiKey": ""
5+
}
6+
7+
}

src/cli/cli.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ const logger = getLogger();
3838
.option('--exclude [excludes...]', 'exclude bundle name')
3939
.option('--llm', 'start llm policy', false)
4040
.option('--simK <number>', '', '8')
41+
.option('--staticConfig <file>', '静态引导策略配置文件路径')
4142
.parse();
4243
let options = program.opts();
4344
let logLevel = LOG_LEVEL.INFO;
@@ -56,7 +57,8 @@ const logger = getLogger();
5657
reportRoot: options.report,
5758
excludes: options.exclude,
5859
llm: options.llm,
59-
simK: options.simK
60+
simK: options.simK,
61+
staticConfig: options.staticConfig,
6062
};
6163
let envChecker = new EnvChecker(fuzzOption);
6264
envChecker.check();

src/event/event_builder.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,4 +151,29 @@ export class EventBuilder {
151151
this.randomText.push(RandomUtils.genRandomString(len));
152152
}
153153
}
154+
155+
static createEventFromNode(node: any): Event |undefined{
156+
let eventType = node.call_back_method;
157+
console.info(`解析事件类型: ${eventType}`);
158+
console.info(`解析节点: ${JSON.stringify(node)}`);
159+
let component = SerializeUtils.deserialize(Component, node);
160+
console.info(`解析组件: ${JSON.stringify(component)}`);
161+
if(eventType.includes("onClick")){
162+
eventType = "onClick";
163+
console.info(`标准化事件类型为: ${eventType}`);
164+
} else if(eventType.includes("onTouch")){
165+
eventType = "onTouch";
166+
}
167+
switch (eventType) {
168+
case 'onClick':
169+
case 'onTouch':
170+
console.info(`组件 bounds: ${JSON.stringify(component.bounds)}, type: ${typeof component.bounds}`);
171+
let point = component.getCenterPoint();
172+
console.info(`生成 TouchEvent,坐标: (${point.x}, ${point.y})`);
173+
return new TouchEvent(point);
174+
default:
175+
// throw new Error(`Unsupported event type: ${eventType}`);
176+
return undefined;
177+
}
178+
}
154179
}

src/policy/llm_guided_policy.ts

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import * as fs from 'fs';
2+
import * as path from 'path';
13
import { Device } from "../device/device";
24
import { Event } from "../event/event";
35
import { EventBuilder } from "../event/event_builder";
@@ -23,13 +25,10 @@ export class LLMGuidedPolicy extends PTGPolicy {
2325
private text: string;
2426
private actionList:string[];
2527

26-
// GPT configuration
27-
private static readonly GPT_CONFIG = {
28-
baseURL: 'https://api.chatanywhere.tech/v1',
29-
apiKey: 'sk-t6Dn1cwNdxVCkNE8jSPYqna47G0SY0yuDC5ajQeP9OkNo97f'
30-
};
28+
// GPT 配置
29+
private static GPT_CONFIG: { baseURL: string; apiKey: string };
3130

32-
private openai = new OpenAI(LLMGuidedPolicy.GPT_CONFIG);
31+
private openai: OpenAI;
3332

3433
constructor(device: Device, hap: Hap, name: PolicyName, ptg: PTG) {
3534
super(device, hap, name, true);
@@ -41,27 +40,47 @@ export class LLMGuidedPolicy extends PTGPolicy {
4140
this.ptg = ptg;
4241

4342
this.taskPrompt = new String("You are an expert in App GUI testing. Please guide the testing tool to enhance the coverage of functional scenarios in testing the App based on your extensive App testing experience. "); // initial task prompt
43+
44+
// 加载 GPT 配置
45+
LLMGuidedPolicy.GPT_CONFIG = this.loadConfig();
46+
47+
// 初始化 OpenAI 客户端
48+
this.openai = new OpenAI(LLMGuidedPolicy.GPT_CONFIG);
49+
}
50+
51+
52+
private loadConfig(): { baseURL: string; apiKey: string } {
53+
const configPath = path.resolve(__dirname, '../../config.json'); // 配置文件路径
54+
try {
55+
const configData = fs.readFileSync(configPath, 'utf-8');
56+
const config = JSON.parse(configData);
57+
return config.GPT_CONFIG;
58+
} catch (error) {
59+
this.logger.error(`加载配置文件失败: ${error}`);
60+
throw new Error("无法加载 GPT 配置,请检查配置文件是否存在且格式正确。");
61+
}
4462
}
4563

46-
// Add a buffer to the class to store asynchronously fetched events
64+
// 给类添加一个缓冲区来保存异步获取的事件
4765
private pendingEvent: Event | null = null;
4866
private eventFetching: boolean = false;
4967

5068
generateEventBasedOnPtg(): Event {
5169
this.updateState();
5270

53-
// If an event has already been fetched asynchronously, return it directly
71+
// 如果已经异步拿到一个事件了,就直接返回它
5472
if (this.pendingEvent) {
5573
const event = this.pendingEvent;
5674
this.pendingEvent = null;
75+
this.eventFetching = false;
5776
return event;
5877
}
5978

6079
if(!this.eventFetching){
61-
this.logger.info("Start asynchronous call to selectEventFromLLM");
62-
// Start asynchronous logic (only once)
80+
this.logger.info("开始异步调用 selectEventFromLLM");
81+
// 启动异步逻辑(只启动一次)
6382
this.eventFetching = true;
64-
// Start asynchronous logic to fetch events and cache them in pendingEvent
83+
// 启动异步逻辑获取事件,缓存到 pendingEvent
6584
this.selectEventFromLLM().then(event => {
6685
if (event === undefined) {
6786
if (this.retryCount > MAX_NUM_RESTARTS) {
@@ -74,14 +93,13 @@ export class LLMGuidedPolicy extends PTGPolicy {
7493
} else {
7594
this.retryCount = 0;
7695
this.pendingEvent = event;
77-
this.logger.info("selectEventFromLLM successfully returned");
96+
this.logger.info("selectEventFromLLM 成功返回");
7897
}
7998
}).catch(err => {
8099
this.logger.error(`selectEventFromLLM failed: ${err}`);
81100
this.pendingEvent = EventBuilder.createRandomTouchEvent(this.device);
82101
}).finally(() => {
83102
this.logger.info("selectEventFromLLM finally");
84-
this.eventFetching = false;
85103
});
86104
}
87105

src/policy/policy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ export enum PolicyName {
3535
RANDOM = 'random',
3636
PERF_START_HAP = 'perf_start_hap',
3737
LLM_GUIDED = 'llm_guided',
38+
STATIC_GUIDED = 'static_guided',
3839
}
3940

4041
export abstract class Policy {
41-
4242
protected device: Device;
4343
protected hap: Hap;
4444
protected _enabled: boolean;

src/policy/policy_builder.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { PtgRandomSearchPolicy } from './ptg_random_search_policy';
2525
import { PerfPolicy } from './perf_policy';
2626
import { LLMGuidedPolicy } from './llm_guided_policy';
2727
import { PTG } from '../model/ptg';
28+
import { StaticGuidedPolicy } from './static_guided_policy';
2829

2930
export class PolicyBuilder {
3031
static buildLLMPolicy(device: Device, hap: Hap, options: FuzzOptions, ptg: PTG): LLMGuidedPolicy {
@@ -43,7 +44,11 @@ export class PolicyBuilder {
4344
return new PtgRandomSearchPolicy(device, hap, PolicyName.RANDOM);
4445
} else if (options.policyName === PolicyName.PERF_START_HAP) {
4546
return new PerfPolicy(device, hap, PolicyName.PERF_START_HAP);
46-
} else {
47+
} else if (options.policyName === PolicyName.STATIC_GUIDED) {
48+
// 静态引导策略实际上是基于PTG的,因此这里复用PTG的类
49+
return new StaticGuidedPolicy(device, hap, PolicyName.STATIC_GUIDED, options.staticConfig!);
50+
}
51+
else {
4752
return new PtgNaiveSearchPolicy(device, hap, PolicyName.NAIVE);
4853
}
4954
}

0 commit comments

Comments
 (0)