Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
static/
35 changes: 28 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,38 @@ npm run build
npm pack
```

## Static Analysis Setup
If you want to use the static analysis module (```--policy static_guided```), you need to install its dependencies first:
```
cd static/test-demo
npm install
```

## Instructions

### 1. Usage
```
haptest [options]

Options:
-V, --version output the version number
-i --hap <file/bundleName/sourceRoot> HAP bundle name or HAP file path or HAP project source root
-o --output <dir> output dir (default: "out")
--policy <policyName> policy name (default: "manu")
-t --target [connectkey] hdc connectkey
-c --coverage enable coverage (default: false)
-h, --help display help for command
-V, --version output the version number
-i, --hap <file/bundleName/sourceRoot> HAP bundle name or HAP file path or HAP project source root
-o, --output <dir> output dir (default: "out")
--policy <policyName> policy name (default: "manu")
-t, --target [connectkey] hdc connectkey
-c, --coverage enable coverage (default: false)
--llm enable LLM-guided exploration (default: false)
--simk <number> set similarity threshold K for tarpit detection (default: 3)
--staticConfig <file> path to static analysis configuration file (required when policy=static_guided)
-h, --help display help for command

```

#### Note:
- ```--policy static_guided:``` Enable the static-analysis-guided exploration policy (requires ```--staticConfig``` to specify the static module configuration file).
-``` --llm:``` Enable the LLM-based enhanced exploration module; can be combined with static_guided policy for a hybrid strategy.
- ```--simk:``` Set the UI similarity threshold for tarpit detection.

### 2. Using DevEco simulator to run HAP

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

### 5. Run with static analysis and LLM enabled
```
haptest -i com.example.demo --policy static_guided --staticConfig config.json --llm --simk 3 -o out
```

## Contribution

1. Fork the repository
Expand Down
7 changes: 7 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"GPT_CONFIG":{
"baseURL": "",
"apiKey": ""
}

}
4 changes: 3 additions & 1 deletion src/cli/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const logger = getLogger();
.option('--exclude [excludes...]', 'exclude bundle name')
.option('--llm', 'start llm policy', false)
.option('--simK <number>', '', '8')
.option('--staticConfig <file>', '静态引导策略配置文件路径')
.parse();
let options = program.opts();
let logLevel = LOG_LEVEL.INFO;
Expand All @@ -54,7 +55,8 @@ const logger = getLogger();
reportRoot: options.report,
excludes: options.exclude,
llm: options.llm,
simK: options.simK
simK: options.simK,
staticConfig: options.staticConfig,
};

HapTestLogger.configure(path.join(options.output, 'haptest.log'), logLevel);
Expand Down
25 changes: 25 additions & 0 deletions src/event/event_builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,4 +151,29 @@ export class EventBuilder {
this.randomText.push(RandomUtils.genRandomString(len));
}
}

static createEventFromNode(node: any): Event |undefined{
let eventType = node.call_back_method;
console.info(`解析事件类型: ${eventType}`);
console.info(`解析节点: ${JSON.stringify(node)}`);
let component = SerializeUtils.deserialize(Component, node);
console.info(`解析组件: ${JSON.stringify(component)}`);
if(eventType.includes("onClick")){
eventType = "onClick";
console.info(`标准化事件类型为: ${eventType}`);
} else if(eventType.includes("onTouch")){
eventType = "onTouch";
}
switch (eventType) {
case 'onClick':
case 'onTouch':
console.info(`组件 bounds: ${JSON.stringify(component.bounds)}, type: ${typeof component.bounds}`);
let point = component.getCenterPoint();
console.info(`生成 TouchEvent,坐标: (${point.x}, ${point.y})`);
return new TouchEvent(point);
default:
// throw new Error(`Unsupported event type: ${eventType}`);
return undefined;
}
}
}
44 changes: 31 additions & 13 deletions src/policy/llm_guided_policy.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import * as fs from 'fs';
import * as path from 'path';
import { Device } from "../device/device";
import { Event } from "../event/event";
import { EventBuilder } from "../event/event_builder";
Expand All @@ -23,13 +25,10 @@ export class LLMGuidedPolicy extends PTGPolicy {
private text: string;
private actionList:string[];

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

private openai = new OpenAI(LLMGuidedPolicy.GPT_CONFIG);
private openai: OpenAI;

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

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

// 加载 GPT 配置
LLMGuidedPolicy.GPT_CONFIG = this.loadConfig();

// 初始化 OpenAI 客户端
this.openai = new OpenAI(LLMGuidedPolicy.GPT_CONFIG);
}


private loadConfig(): { baseURL: string; apiKey: string } {
const configPath = path.resolve(__dirname, '../../config.json'); // 配置文件路径
try {
const configData = fs.readFileSync(configPath, 'utf-8');
const config = JSON.parse(configData);
return config.GPT_CONFIG;
} catch (error) {
this.logger.error(`加载配置文件失败: ${error}`);
throw new Error("无法加载 GPT 配置,请检查配置文件是否存在且格式正确。");
}
}

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

generateEventBasedOnPtg(): Event {
this.updateState();

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

if(!this.eventFetching){
this.logger.info("Start asynchronous call to selectEventFromLLM");
// Start asynchronous logic (only once)
this.logger.info("开始异步调用 selectEventFromLLM");
// 启动异步逻辑(只启动一次)
this.eventFetching = true;
// Start asynchronous logic to fetch events and cache them in pendingEvent
// 启动异步逻辑获取事件,缓存到 pendingEvent
this.selectEventFromLLM().then(event => {
if (event === undefined) {
if (this.retryCount > MAX_NUM_RESTARTS) {
Expand All @@ -74,14 +93,13 @@ export class LLMGuidedPolicy extends PTGPolicy {
} else {
this.retryCount = 0;
this.pendingEvent = event;
this.logger.info("selectEventFromLLM successfully returned");
this.logger.info("selectEventFromLLM 成功返回");
}
}).catch(err => {
this.logger.error(`selectEventFromLLM failed: ${err}`);
this.pendingEvent = EventBuilder.createRandomTouchEvent(this.device);
}).finally(() => {
this.logger.info("selectEventFromLLM finally");
this.eventFetching = false;
});
}

Expand Down
2 changes: 1 addition & 1 deletion src/policy/policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ export enum PolicyName {
RANDOM = 'random',
PERF_START_HAP = 'perf_start_hap',
LLM_GUIDED = 'llm_guided',
STATIC_GUIDED = 'static_guided',
}

export abstract class Policy {

protected device: Device;
protected hap: Hap;
protected _enabled: boolean;
Expand Down
7 changes: 6 additions & 1 deletion src/policy/policy_builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { PtgRandomSearchPolicy } from './ptg_random_search_policy';
import { PerfPolicy } from './perf_policy';
import { LLMGuidedPolicy } from './llm_guided_policy';
import { PTG } from '../model/ptg';
import { StaticGuidedPolicy } from './static_guided_policy';

export class PolicyBuilder {
static buildLLMPolicy(device: Device, hap: Hap, options: FuzzOptions, ptg: PTG): LLMGuidedPolicy {
Expand All @@ -43,7 +44,11 @@ export class PolicyBuilder {
return new PtgRandomSearchPolicy(device, hap, PolicyName.RANDOM);
} else if (options.policyName === PolicyName.PERF_START_HAP) {
return new PerfPolicy(device, hap, PolicyName.PERF_START_HAP);
} else {
} else if (options.policyName === PolicyName.STATIC_GUIDED) {
// 静态引导策略实际上是基于PTG的,因此这里复用PTG的类
return new StaticGuidedPolicy(device, hap, PolicyName.STATIC_GUIDED, options.staticConfig!);
}
else {
return new PtgNaiveSearchPolicy(device, hap, PolicyName.NAIVE);
}
}
Expand Down
Loading
Loading