• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3#
4# Copyright (c) 2024-2025 Huawei Device Co., Ltd.
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18import logging
19import warnings
20from signal import SIGTERM
21
22import trio
23from cdp import runtime
24from pytest import fixture
25
26from arkdb.compiler import StringCodeCompiler
27from arkdb.debug_client import DebuggerClient, DebugLocator
28from arkdb.logs import RichLogger
29from arkdb.runnable_module import ScriptFile
30from arkdb.runtime import Runtime, RuntimeProcess
31from arkdb.source_meta import parse_source_meta
32
33
34class RuntimeExitStatusWarning(UserWarning):
35    pass
36
37
38@fixture
39def script_file(
40    code_compiler: StringCodeCompiler,
41) -> ScriptFile:
42    code = """\
43class A {
44    public i: int;
45    public d: double;
46    public s: string;
47    public ss: String;
48    constructor() {
49        this.i = 1;
50        this.d = 2;
51        this.s = 's';
52        this.ss = 'ss';
53    }
54}
55
56function foo(arg: int): int {
57    let c: int = 2;
58    return arg * c
59}
60
61function main(): int {
62    let a: int = 100;
63    let b: int = a * foo(a);
64    let obj = new A();
65    console.log(a / b)
66    return a / b;
67}
68"""
69    return code_compiler.compile(code)
70
71
72def check_exit_status(process: RuntimeProcess, log: RichLogger):
73    status = process.returncode
74    if status != 0:
75        msg = f"Runtime exit status is {status}"
76        log.warning(msg)
77        warnings.warn(msg, RuntimeExitStatusWarning)
78
79
80async def test_run(
81    nursery: trio.Nursery,
82    ark_runtime: Runtime,
83    script_file: ScriptFile,
84    log: RichLogger,
85):
86    script_file.log(log, logging.INFO)
87    async with ark_runtime.run(nursery, module=script_file, debug=False) as process:
88        await process.wait()
89    check_exit_status(process, log)
90
91
92async def test_debug(
93    nursery: trio.Nursery,
94    ark_runtime: Runtime,
95    script_file: ScriptFile,
96    log: RichLogger,
97    debug_locator: DebugLocator,
98):
99    script_file.log(log, logging.INFO)
100    async with ark_runtime.run(nursery, module=script_file) as process:
101        async with debug_locator.connect(nursery) as client:
102            await client.configure(nursery)
103            paused = await client.run_if_waiting_for_debugger()
104            log.info("%s", paused)
105            await client.resume()
106    check_exit_status(process, log)
107
108
109async def _pause_and_get_vars(client: DebuggerClient, log: RichLogger, script_file: ScriptFile, line_number: int):
110    paused = await client.continue_to_location(script_id=runtime.ScriptId("0"), line_number=line_number)
111    script_file.log(log, highlight_lines=[paused.call_frames[0].location.line_number + 1])
112    log.info("paused: %s", paused)
113    object_ids = [
114        scope.object_.object_id
115        for frame in paused.call_frames
116        for scope in frame.scope_chain
117        if scope.object_.object_id is not None
118    ]
119    # fmt: off
120    props = [
121        prop
122        for props in [(await client.get_properties(obj_id))[0]
123                      for obj_id in object_ids]
124        for prop in props
125    ]
126    # fmt: on
127    variables = {prop.name: prop.value.value if prop.value is not None else None for prop in props}
128    log.info("Properties: \n%s", repr(props))
129    log.info("All variables: %r", variables)
130    return variables
131
132
133async def test_code_compiler(
134    nursery: trio.Nursery,
135    ark_runtime: Runtime,
136    code_compiler: StringCodeCompiler,
137    debug_locator: DebugLocator,
138    log: RichLogger,
139):
140    code = """
141    function main(): int {
142        let a: int = 100;
143        let b: int = a + 10;
144        console.log(a + 1)
145        return a;
146    }
147    """
148
149    script_file = code_compiler.compile(code)
150    async with ark_runtime.run(nursery, module=script_file) as process:
151        async with debug_locator.connect(nursery) as client:
152
153            await client.configure(nursery)
154            await client.run_if_waiting_for_debugger()
155
156            variables = await _pause_and_get_vars(client, log, script_file, 3)
157            assert variables == {"a": 100}
158
159            variables = await _pause_and_get_vars(client, log, script_file, 4)
160            assert variables == {"a": 100, "b": 110}
161
162            await client.resume()
163    check_exit_status(process, log)
164
165
166async def test_inline_breakpoints(
167    nursery: trio.Nursery,
168    ark_runtime: Runtime,
169    code_compiler: StringCodeCompiler,
170    debug_locator: DebugLocator,
171    log: RichLogger,
172):
173    code = """\
174    function main(): int {
175        let a: int = 100;    // # BP {}
176        let b: int = a + 10; //  #BP{1}
177        console.log(a + 1)  // # BP
178        return a;
179    }"""
180
181    script_file = code_compiler.compile(code)
182    meta = parse_source_meta(code)  # noqa F841
183    log.info("Parsed breakpoints %s", meta.breakpoints)
184    script_file.log(log, highlight_lines=[b.line_number for b in meta.breakpoints])
185
186    async def _check_breakpoints():
187        await trio.lowlevel.checkpoint()
188        for br in meta.breakpoints:
189            with trio.fail_after(2):
190                paused = await client.resume_and_wait_for_paused()
191                paused_ln = paused.call_frames[0].location.line_number
192                script_file.log(log, highlight_lines=[paused_ln])
193                assert paused_ln == br.line_number
194
195    async with ark_runtime.run(nursery, module=script_file) as process:
196        async with debug_locator.connect(nursery) as client:
197            await client.configure(nursery)
198            _ = await client.run_if_waiting_for_debugger()
199            for br in meta.breakpoints:
200                await client.set_breakpoint_by_url(
201                    url=script_file.source_file.name,
202                    line_number=br.line_number,
203                )
204
205            await _check_breakpoints()
206            process.terminate()
207    assert process.returncode == -SIGTERM
208