1+ """
2+ AgentServer 端测试
3+
4+ 测试范围:
5+ 1. AgentServer: 注册自定义识别器/动作、事件监听器、生命周期管理
6+ 2. Context: run_task/run_recognition/run_action、override_*、clone、anchor、hit_count
7+ 3. Tasker: get_*_detail、running/stopping/post_stop、clear_cache
8+ 4. Resource: get_node_data、node_list、custom_*_list、hash、override_*
9+ 5. Controller: 各种输入操作、post_key_down/up、post_scroll
10+ """
11+
112import os
213from pathlib import Path
314import sys
415import io
16+ import numpy
517
618# Fix encoding issues on Windows (cp1252 cannot encode some Unicode characters)
7- if sys .stdout .encoding != ' utf-8' :
8- sys .stdout = io .TextIOWrapper (sys .stdout .buffer , encoding = ' utf-8' , errors = ' replace' )
9- if sys .stderr .encoding != ' utf-8' :
10- sys .stderr = io .TextIOWrapper (sys .stderr .buffer , encoding = ' utf-8' , errors = ' replace' )
19+ if sys .stdout .encoding != " utf-8" :
20+ sys .stdout = io .TextIOWrapper (sys .stdout .buffer , encoding = " utf-8" , errors = " replace" )
21+ if sys .stderr .encoding != " utf-8" :
22+ sys .stderr = io .TextIOWrapper (sys .stderr .buffer , encoding = " utf-8" , errors = " replace" )
1123
1224if len (sys .argv ) < 4 :
1325 print ("Call agent_main_test.py instead of this file." )
3648from maa .library import Library
3749
3850
51+ analyzed : bool = False
52+ runned : bool = False
53+
54+
3955def main ():
4056 if len (sys .argv ) < 2 :
4157 print ("Usage: python agent_main.py <socket_id>" )
@@ -56,8 +72,14 @@ def analyze(
5672 argv : CustomRecognition .AnalyzeArg ,
5773 ) -> CustomRecognition .AnalyzeResult :
5874 print (
59- f"on MyRecognition.analyze, context: { context } , image: { argv .image .shape } , task_detail: { argv .task_detail } , reco_name: { argv .custom_recognition_name } , reco_param: { argv .custom_recognition_param } "
75+ f"on MyRecognition.analyze, context: { context } , image: { argv .image .shape } , "
76+ f"task_detail: { argv .task_detail } , reco_name: { argv .custom_recognition_name } , "
77+ f"reco_param: { argv .custom_recognition_param } "
6078 )
79+
80+ # ============================================================
81+ # Context API 测试
82+ # ============================================================
6183 entry = "ColorMatch"
6284 ppover = {
6385 "ColorMatch" : {
@@ -67,22 +89,95 @@ def analyze(
6789 "action" : "Click" ,
6890 }
6991 }
92+
93+ # 测试 run_task
7094 context .run_task (entry , ppover )
71- action_detail = context .run_action (entry , [114 , 514 , 191 , 810 ], "RunAction Detail" , ppover )
72- print (f"action_detail: { action_detail } " )
95+
96+ # 测试 run_action
97+ action_detail = context .run_action (
98+ entry , [114 , 514 , 191 , 810 ], "RunAction Detail" , ppover
99+ )
100+ print (f" action_detail: { action_detail } " )
101+
102+ # 测试 run_recognition
73103 reco_detail = context .run_recognition (entry , argv .image , ppover )
74- print (f"reco_detail: { reco_detail } " )
104+ print (f" reco_detail: { reco_detail } " )
75105
106+ # 测试 clone 和 override
76107 new_ctx = context .clone ()
77108 new_ctx .override_pipeline ({"TaskA" : {}, "TaskB" : {}})
78109 new_ctx .override_next (argv .node_name , ["TaskA" , "TaskB" ])
79110
80- node_detail = new_ctx .tasker .get_latest_node ("ColorMatch" )
81- print (node_detail )
111+ # 测试 get_node_data (Context 级别)
112+ node_data = new_ctx .get_node_data (argv .node_name )
113+ print (f" ctx.get_node_data keys: { list (node_data .keys ()) if node_data else None } " )
114+
115+ # 测试 anchor API
116+ new_ctx .set_anchor ("test_anchor" , "TaskA" )
117+ anchor_result = new_ctx .get_anchor ("test_anchor" )
118+ print (f" anchor_result: { anchor_result } " )
119+ assert anchor_result == "TaskA" , f"anchor should be 'TaskA', got { anchor_result } "
120+
121+ # 测试 hit count API
122+ hit_count = new_ctx .get_hit_count (argv .node_name )
123+ print (f" hit_count: { hit_count } " )
124+ new_ctx .clear_hit_count (argv .node_name )
82125
126+ # 测试 override_image (Context 级别)
127+ test_image = numpy .zeros ((100 , 100 , 3 ), dtype = numpy .uint8 )
128+ new_ctx .override_image ("test_image" , test_image )
129+
130+ # 测试 get_task_job
83131 task_job = new_ctx .get_task_job ()
132+ print (f" task_job: { task_job } " )
84133 new_task_detail = task_job .get ()
85- print (new_task_detail )
134+ print (
135+ f" task_detail entry: { new_task_detail .entry if new_task_detail else None } "
136+ )
137+
138+ # ============================================================
139+ # Tasker API 测试 (通过 context.tasker)
140+ # ============================================================
141+ tasker = new_ctx .tasker
142+ print (f" tasker.inited: { tasker .inited } " )
143+
144+ # 测试 get_latest_node
145+ node_detail = tasker .get_latest_node ("ColorMatch" )
146+ print (f" latest_node ColorMatch: { node_detail } " )
147+
148+ # 测试 running 和 stopping
149+ print (f" tasker.running: { tasker .running } " )
150+ print (f" tasker.stopping: { tasker .stopping } " )
151+
152+ # ============================================================
153+ # Resource API 测试 (通过 context.tasker.resource)
154+ # ============================================================
155+ resource = tasker .resource
156+
157+ # 测试 loaded (valid) 属性
158+ print (f" resource.loaded: { resource .loaded } " )
159+
160+ # 测试 get_node_data (Resource 级别)
161+ res_node_data = resource .get_node_data (argv .node_name )
162+ print (
163+ f" res.get_node_data keys: { list (res_node_data .keys ()) if res_node_data else None } "
164+ )
165+
166+ # 测试 hash 属性
167+ res_hash = resource .hash
168+ print (f" resource.hash: { res_hash [:16 ] if res_hash else None } ..." )
169+
170+ # 测试 node_list
171+ node_list = resource .node_list
172+ print (f" resource.node_list count: { len (node_list )} " )
173+
174+ # 测试 custom_recognition_list 和 custom_action_list
175+ reco_list = resource .custom_recognition_list
176+ action_list = resource .custom_action_list
177+ print (f" custom_recognition_list: { reco_list } " )
178+ print (f" custom_action_list: { action_list } " )
179+ assert "MyRec" in reco_list , "MyRec should be in custom_recognition_list"
180+ assert "MyAct" in action_list , "MyAct should be in custom_action_list"
86181
87182 global analyzed
88183 analyzed = True
@@ -100,53 +195,121 @@ def run(
100195 argv : CustomAction .RunArg ,
101196 ) -> CustomAction .RunResult :
102197 print (
103- f"on MyAction.run, context: { context } , task_detail: { argv .task_detail } , action_name: { argv .custom_action_name } , action_param: { argv .custom_action_param } , box: { argv .box } , reco_detail: { argv .reco_detail } "
198+ f"on MyAction.run, context: { context } , task_detail: { argv .task_detail } , "
199+ f"action_name: { argv .custom_action_name } , action_param: { argv .custom_action_param } , "
200+ f"box: { argv .box } , reco_detail: { argv .reco_detail } "
104201 )
202+
203+ # ============================================================
204+ # Controller API 测试 (通过 context.tasker.controller)
205+ # ============================================================
105206 controller = context .tasker .controller
207+
208+ # 测试 connected 和 uuid
209+ connected = controller .connected
210+ uuid = controller .uuid
211+ print (f" connected: { connected } , uuid: { uuid } " )
212+
213+ # 测试 post_screencap
106214 new_image = controller .post_screencap ().wait ().get ()
107- print (f"new_image: { new_image .shape } " )
215+ print (f" new_image: { new_image .shape } " )
216+
217+ # 测试 cached_image
218+ cached_image = controller .cached_image
219+ print (f" cached_image shape: { cached_image .shape } " )
220+
221+ # 测试基本输入操作
108222 controller .post_click (191 , 98 ).wait ()
109223 controller .post_swipe (100 , 200 , 300 , 400 , 100 ).wait ()
110224 controller .post_input_text ("Hello World!" ).wait ()
111225 controller .post_click_key (32 ).wait ()
226+
227+ # 测试触摸操作
112228 controller .post_touch_down (1 , 100 , 100 , 0 ).wait ()
113229 controller .post_touch_move (1 , 200 , 200 , 0 ).wait ()
114230 controller .post_touch_up (1 ).wait ()
231+
232+ # 测试按键操作
233+ controller .post_key_down (65 ).wait ()
234+ controller .post_key_up (65 ).wait ()
235+
236+ # 测试滚动操作
237+ controller .post_scroll (0 , 120 ).wait ()
238+
239+ # 测试应用操作
115240 controller .post_start_app ("aaa" )
116241 controller .post_stop_app ("bbb" )
117242
118- cached_image = controller .cached_image
119- connected = controller .connected
120- uuid = controller .uuid
243+ # ============================================================
244+ # Tasker API 补充测试 (详情获取)
245+ # ============================================================
246+ tasker = context .tasker
247+
248+ # 获取当前任务详情用于后续测试
249+ task_job = context .get_task_job ()
250+ task_detail = task_job .get ()
251+
252+ if task_detail :
253+ print (f" task_detail: entry={ task_detail .entry } , status={ task_detail .status } " )
254+
255+ # 测试 get_task_detail
256+ fetched_task_detail = tasker .get_task_detail (task_detail .task_id )
257+ print (
258+ f" get_task_detail: { fetched_task_detail .entry if fetched_task_detail else None } "
259+ )
260+
261+ # 测试 get_node_detail
262+ if task_detail .nodes :
263+ node = task_detail .nodes [0 ]
264+ node_detail = tasker .get_node_detail (node .node_id )
265+ print (
266+ f" get_node_detail: { node_detail .name if node_detail else None } "
267+ )
268+
269+ # 测试 get_recognition_detail
270+ if node .recognition :
271+ reco_detail = tasker .get_recognition_detail (node .recognition .reco_id )
272+ print (
273+ f" get_recognition_detail: { reco_detail .name if reco_detail else None } "
274+ )
275+
276+ # 测试 clear_cache
277+ tasker .clear_cache ()
278+ print (" tasker.clear_cache() called" )
121279
122280 global runned
123281 runned = True
124282
125283 return CustomAction .RunResult (success = True )
126284
127285
286+ # ============================================================================
287+ # Event Sink 装饰器方式注册
288+ # ============================================================================
289+
290+
128291@AgentServer .resource_sink ()
129292class MyResSink (ResourceEventSink ):
130293 def on_raw_notification (self , resource , msg : str , details : dict ):
131- print (f"resource: { resource } , msg: { msg } , details: { details } " )
294+ print (f"[ResourceSink] msg: { msg } " )
132295
133296
134297@AgentServer .controller_sink ()
135298class MyCtrlSink (ControllerEventSink ):
136299 def on_raw_notification (self , controller , msg : str , details : dict ):
137- print (f"controller: { controller } , msg: { msg } , details: { details } " )
300+ print (f"[ControllerSink] msg: { msg } " )
138301
139302
140303@AgentServer .tasker_sink ()
141304class MyTaskerSink (TaskerEventSink ):
142305 def on_raw_notification (self , tasker , msg : str , details : dict ):
143- print (f"tasker: { tasker } , msg: { msg } , details: { details } " )
306+ print (f"[TaskerSink] msg: { msg } " )
144307
145308
146309@AgentServer .context_sink ()
147310class MyCtxSink (ContextEventSink ):
148311 def on_raw_notification (self , context , msg : str , details : dict ):
149- print (f"context: { context } , msg: { msg } , details: { details } " )
312+ print (f"[ContextSink] msg: { msg } " )
150313
151314
152315if __name__ == "__main__" :
0 commit comments