chore(hmos): 导入生产环境鸿蒙工程

- 以 D:\SmartSenior 生产工程为源,纳入 AppScope、entry、cjagent、hvigor 及根配置
- 移除 build-profile.json5 的 signingConfigs(p12/p7b/cer 路径与加密口令)及 products.signingConfig 引用
- 不纳入本地与开发产物:local.properties、.idea、.hvigor、.codegenie、demo、backup_a2a_native、SESSION_LOG.md、build_err.txt、build/、oh_modules
- .gitignore 追加 .codegenie 与密钥文件通配(*.p12/*.p7b/*.cer/*.jks/*.keystore/*.pem)
This commit is contained in:
CAPTAIN1275
2026-09-29 18:44:37 +08:00
parent d7f0e2f8a8
commit baf307e9d5
184 changed files with 31334 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
/node_modules
/oh_modules
/.preview
/build
/.cxx
/.test
**/IDL_Dependencies_List~
**/build-script-cache
+17
View File
@@ -0,0 +1,17 @@
/**
* Use these variables when you tailor your ArkTS code. They must be of the const type.
*/
export const HAR_VERSION = '1.0.0';
export const BUILD_MODE_NAME = 'debug';
export const DEBUG = true;
export const TARGET_NAME = 'default';
/**
* BuildProfile Class is used only for compatibility purposes.
*/
export default class BuildProfile {
static readonly HAR_VERSION = HAR_VERSION;
static readonly BUILD_MODE_NAME = BUILD_MODE_NAME;
static readonly DEBUG = DEBUG;
static readonly TARGET_NAME = TARGET_NAME;
}
View File
+21
View File
@@ -0,0 +1,21 @@
{
"apiType": "stageMode",
"buildOption": {
"cangjieOptions": {
"path": "./cjpm.toml",
"abiFilters": ["arm64-v8a","x86_64"]
},
"nativeLib": {
"filter": {
"enableOverride": true
}
}
},
"buildOptionSet": [
],
"targets": [
{
"name": "default"
}
]
}
+3
View File
@@ -0,0 +1,3 @@
version = 0
[requires]
+37
View File
@@ -0,0 +1,37 @@
[package]
cjc-version = "1.2.0"
compile-option = "--dy-std --cfg=\"${COMPILE_CONDITION}\""
override-compile-option = "${OVERRIDE_COMPILE_OPTION}"
description = "CangjieUI Application"
name = "ohos_app_cangjie_cjagent"
output-type = "dynamic"
src-dir = "./src/main/cangjie"
target-dir = ""
version = "1.0.0"
[profile]
[profile.build]
incremental = true
lto = ""
[profile.build.combined]
ohos_app_cangjie_cjagent = "dynamic"
[profile.customized-option]
debug = "-g -Woff all -Won apilevel-check"
release = "--fast-math -O2 -s -Woff all -Won apilevel-check"
asan = "--sanitize=address -lclang_rt.asan"
[profile.test]
[target.aarch64-linux-ohos]
compile-option = "-B \"${DEVECO_CANGJIE_HOME}/build-tools/third_party/llvm/bin\" -B \"${DEVECO_OH_NATIVE_HOME}/sysroot/usr/lib/aarch64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/sysroot/usr/lib/aarch64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/llvm/lib/clang/15.0.4/lib/aarch64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/llvm/lib/aarch64-linux-ohos\" --sysroot \"${DEVECO_OH_NATIVE_HOME}/sysroot\""
[target.aarch64-linux-ohos.bin-dependencies]
path-option = ["${AARCH64_LIBS}", "${AARCH64_MACRO_LIBS}", "${AARCH64_KIT_LIBS}"]
package-option = { }
[target.x86_64-linux-ohos]
compile-option = "-B \"${DEVECO_CANGJIE_HOME}/build-tools/third_party/llvm/bin\" -B \"${DEVECO_OH_NATIVE_HOME}/sysroot/usr/lib/x86_64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/sysroot/usr/lib/x86_64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/llvm/lib/clang/15.0.4/lib/x86_64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/llvm/lib/x86_64-linux-ohos\" --sysroot \"${DEVECO_OH_NATIVE_HOME}/sysroot\""
[target.x86_64-linux-ohos.bin-dependencies]
path-option = ["${X86_64_OHOS_LIBS}", "${X86_64_OHOS_MACRO_LIBS}", "${X86_64_OHOS_KIT_LIBS}"]
[target.x86_64-w64-mingw32.bin-dependencies]
path-option = ["${X86_64_LIBS}", "${X86_64_MACRO_LIBS}", "${X86_64_KIT_LIBS}"]
package-option = { }
+8
View File
@@ -0,0 +1,8 @@
cjpm.exe : Warning: field 'profile.build.combined' in '.\cjpm.toml' is experimental, it is unstable and has some limita
tions, please refer to related documentation for more details
所在位置 行:4 字符: 582
+ ... t=default"; & "$sdk\build-tools\tools\bin\cjpm.exe" build --target=aa ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (Warning: field ...or more details:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
+6
View File
@@ -0,0 +1,6 @@
import { harTasks } from '@ohos/hvigor-ohos-plugin';
export default {
system: harTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
plugins:[] /* Custom plugin to extend the functionality of Hvigor. */
}
+11
View File
@@ -0,0 +1,11 @@
{
"name": "cjagent",
"version": "1.0.0",
"description": "Please describe the basic information.",
"main": "",
"author": "",
"types": "./Index.d.ets",
"license": "Apache-2.0",
"dependencies": {
}
}
@@ -0,0 +1,9 @@
export declare interface CustomLib {
parseIntent(text: string): string
cjAgentVersion(): string
sm4EncryptText(keyHex: string, plain: string): string
sm4DecryptText(keyHex: string, cipherHex: string): string
sm4SelfTest(): boolean
splitIntents(text: string): string
inferSlot(hour: number): string
}
@@ -0,0 +1,6 @@
{
"name": "libark_interop_api.so",
"types": "./ark_interop_api.d.ts",
"version": "1.0.0",
"description": ""
}
+7
View File
@@ -0,0 +1,7 @@
package ohos_app_cangjie_cjagent
import kit.PerformanceAnalysisKit.Hilog
public func sayHello(): Unit {
Hilog.info(0, "Cangjie", "Hello Cangjie!")
}
+294
View File
@@ -0,0 +1,294 @@
package ohos_app_cangjie_cjagent
// 数智护老 · 仓颉意图引擎
// 输入小艺转发的用户文本,输出结构化意图 JSON:{"action":"...","slot":"...","date":"...","jump":"..."}
// 纯逻辑零依赖:只做文本解析,后端调用仍由 ArkTS 侧执行(业务零风险)
import ohos.ark_interop.*
import ohos.ark_interop_macro.*
import kit.PerformanceAnalysisKit.Hilog
import std.collection.ArrayList
// 日志过滤约定:仓颉侧统一用 Domain=0xC1C1 Tag=CJCJAGENT
let CJ_DOMAIN: UInt32 = 0xC1C1
let CJ_TAG: String = "CJCJAGENT"
// 包含判断的小工具
func has(text: String, key: String): Bool {
return text.contains(key)
}
// ASR 转写容错:方言/口音经小艺转写后常见错别字归一
// 例:四川话"定个餐"→"定餐","叁"被听成数字三等
let ASR_FIXES: Array<Array<String>> = [
["定餐", "订餐"], ["定饭", "订饭"], ["定个", "订个"], ["定明", "订明"],
["订叁", "订餐"], ["订叄", "订餐"], ["订散", "订餐"],
["午叁", "午餐"], ["晚叁", "晚餐"], ["早叁", "早餐"],
["天汽", "天气"], ["查寻", "查询"], ["枢查", "查询"],
["抱告", "报告"], ["健慷", "健康"]
]
func normalizeAsr(text: String): String {
var out = text
for (pair in ASR_FIXES) {
if (pair.size >= 2 && out.contains(pair[0])) {
out = out.replace(pair[0], pair[1])
}
}
return out
}
func detectSlot(text: String): String {
if (has(text, "早")) {
return "breakfast"
}
if (has(text, "晚")) {
return "dinner"
}
return ""
}
func detectDate(text: String): String {
if (has(text, "明天") || has(text, "明日")) {
return "tomorrow"
}
return "today"
}
// 跳转页面判断
func detectJump(text: String): String {
if (has(text, "天气") || has(text, "气温") || has(text, "下雨")) {
return "pages/elderly/ElderlyWeather"
}
if (has(text, "活动") || has(text, "八段锦") || has(text, "健身")) {
return "pages/elderly/Activities"
}
if ((has(text, "订餐") && has(text, "看")) || has(text, "菜单")) {
return "pages/elderly/ElderlyMealOrder"
}
return ""
}
// 主意图分类:直接返回动作名(避免枚举相等性比较)
func classify(text: String): String {
// 时间/日期问答:纯本地播报
if (has(text, "几点") || has(text, "时间") || has(text, "几号") || has(text, "几月")
|| has(text, "星期几") || has(text, "周几") || has(text, "日期")) {
return "time"
}
// 菜单查询:今天有什么餐/吃什么("看菜单"仍走页面跳转)
if (has(text, "有什么餐") || has(text, "有什么菜") || has(text, "有什么吃的")
|| (has(text, "吃什么") && !has(text, "订")) || (has(text, "菜单") && !has(text, "看"))) {
return "menu"
}
// 活动查询:有什么活动/活动安排
if (has(text, "活动") && !has(text, "看") && !has(text, "打开") && !has(text, "去")) {
return "activities"
}
// 跳转类优先(看天气/看活动/看菜单)
if (!detectJump(text).isEmpty()) {
return "jump"
}
// 问候:仍走 ArkTS 侧(带时段个性化),此处归 fallback
if (has(text, "你好") || has(text, "您好") || has(text, "早上好") || has(text, "下午好")
|| has(text, "晚上好") || has(text, "嗨") || text == "在吗") {
return "fallback"
}
let wantCancel = has(text, "取消") || has(text, "退订") || has(text, "不订")
|| has(text, "不要") || has(text, "不吃了")
let mealWord = has(text, "餐") || has(text, "饭") || has(text, "订") || has(text, "吃")
// 状态查询:先于下单/取消判断,避免"我订了什么"被当成下单
let statusWord = has(text, "状态") || has(text, "订了") || has(text, "订没订")
|| has(text, "订的") || has(text, "订好") || has(text, "记录") || has(text, "查询")
|| has(text, "查查") || has(text, "查一下") || has(text, "哪些") || has(text, "吃什么")
|| (has(text, "看看") && has(text, "订"))
if (mealWord && statusWord && !wantCancel) {
return "status"
}
if (wantCancel) {
return "cancel"
}
// 跨业务编排 回家
if (has(text, "回家") || has(text, "回家去") || has(text, "回家里")) {
return "gohome"
}
// 下单
if (has(text, "回家") || has(text, "回家去") || has(text, "回家里")) {
return "gohome"
}
// 求助
if (has(text, "护士") || has(text, "护理员") || has(text, "救命") || has(text, "紧急")
|| has(text, "不舒服") || has(text, "摔倒") || has(text, "疼") || has(text, "头晕")) {
return "help"
}
// AI 健康报告
if (has(text, "报告") && (has(text, "健康") || has(text, "看") || has(text, "查"))) {
return "report"
}
return "fallback"
}
// JSON 字符串转义(按 Rune 处理,中文原样保留)
func jsonEscape(s: String): String {
var out = ""
for (r in s.runes()) {
if (r == r'"') {
out = out + "\\\""
} else if (r == r'\\') {
out = out + "\\\\"
} else if (r == r'\n') {
out = out + "\\n"
} else {
out = out + r.toString()
}
}
return out
}
//导出给ArkTS调用
@Interop[ArkTS]
public func parseIntent(text: String): String {
let normalized = normalizeAsr(text)
let action = classify(normalized)
var jump = ""
if (action == "jump") {
jump = detectJump(normalized)
}
var slot = ""
var date = ""
if (action == "order" || action == "cancel" || action == "status") {
slot = detectSlot(normalized)
date = detectDate(normalized)
}
let result = "{\"action\":\"" + action
+ "\",\"slot\":\"" + slot
+ "\",\"date\":\"" + date
+ "\",\"jump\":\"" + jsonEscape(jump)
+ "\"}"
Hilog.info(CJ_DOMAIN, CJ_TAG, "parseIntent input=<${text}> result=<${result}>")
return result
}
// 餐次推断:按当前小时推断该订/取消哪一餐(订餐决策链下沉仓颉)
// 9 点前早餐、9-15 点午餐、15 点后晚餐;返回 JSON 保持与 parseIntent 同构的互操作风格
@Interop[ArkTS]
public func inferSlot(hour: Int64): String {
var slot = "lunch"
if (hour < 9) {
slot = "breakfast"
} else if (hour >= 15) {
slot = "dinner"
}
let result = "{\"slot\":\"" + slot + "\"}"
Hilog.info(CJ_DOMAIN, CJ_TAG, "inferSlot hour=<" + hour.toString() + "> result=<" + result + ">")
return result
}
// 健康检查:ArkTS 侧启动时调用,确认仓颉 so 加载成功
@Interop[ArkTS]
public func cjAgentVersion(): String {
return "cjagent-1.1.0"
}
// ── 多意图拆分 ──
// "订明天的晚饭,然后看看活动" → ["订明天的晚饭", "看看活动"]
// 拆分依据:标点(,。!?;、)与连接词(然后/接着/再/还/另外/并且/以及/顺便/同时)
func toRunes(s: String): ArrayList<Rune> {
let out = ArrayList<Rune>()
for (r in s.runes()) {
out.add(r)
}
return out
}
func runeEqAt(hay: ArrayList<Rune>, needle: ArrayList<Rune>, off: Int64): Bool {
if (off + needle.size > hay.size) {
return false
}
var i: Int64 = 0
while (i < needle.size) {
if (hay[off + i] != needle[i]) {
return false
}
i = i + 1
}
return true
}
func isPunct(r: Rune): Bool {
return r == r',' || r == r'。' || r == r'!' || r == r'?' || r == r';'
|| r == r'、' || r == r',' || r == r'.' || r == r'!' || r == r'?'
}
let CONNECTORS: Array<ArrayList<Rune>> = [
toRunes("然后"), toRunes("接着"), toRunes("再帮我"), toRunes("帮我再"),
toRunes("另外"), toRunes("并且"), toRunes("以及"), toRunes("顺便"),
toRunes("同时"), toRunes("还要"), toRunes("再")
]
// 主入口:返回 {"segments":[...]};单意图时 segments 只有原文一段
@Interop[ArkTS]
public func splitIntents(text: String): String {
let rs = toRunes(text)
let parts = ArrayList<String>()
var buf = ""
var i: Int64 = 0
while (i < rs.size) {
let ch = rs[i]
if (isPunct(ch)) {
if (!buf.isEmpty()) {
parts.add(buf)
buf = ""
}
i = i + 1
continue
}
var matched = false
var connLen: Int64 = 0
for (c in CONNECTORS) {
if (runeEqAt(rs, c, i)) {
matched = true
connLen = c.size
break
}
}
if (matched) {
if (!buf.isEmpty()) {
parts.add(buf)
buf = ""
}
i = i + connLen
continue
}
buf = buf + ch.toString()
i = i + 1
}
if (!buf.isEmpty()) {
parts.add(buf)
}
// 过滤过短片段(多半是"再""还"残余),最多取 3 段
let segs = ArrayList<String>()
for (p in parts) {
if (segs.size < 3 && p.size >= 2) {
segs.add(p)
}
}
var out = "{\"segments\":["
var n: Int64 = 0
if (segs.size <= 1) {
out = out + "\"" + jsonEscape(text) + "\""
n = 1
} else {
for (s in segs) {
if (n > 0) {
out = out + ","
}
out = out + "\"" + jsonEscape(s) + "\""
n = n + 1
}
}
out = out + "]}"
Hilog.info(CJ_DOMAIN, CJ_TAG, "splitIntents input=<${text}> segments=<${out}>")
return out
}
@@ -0,0 +1,8 @@
export declare function parseIntent(text: string): string
export declare function cjAgentVersion(): string
export declare function splitIntents(text: string): string
export declare function inferSlot(hour: number): string
@@ -0,0 +1,6 @@
{
"name": "libohos_app_cangjie_cjagent.so",
"types": "./Index.d.ts",
"version": "1.0.0",
"description": ""
}
+10
View File
@@ -0,0 +1,10 @@
{
"module": {
"name": "cjagent",
"type": "har",
"deviceTypes": [
"default",
"tablet"
]
}
}
@@ -0,0 +1,8 @@
{
"string": [
{
"name": "page_show",
"value": "page from npm package"
}
]
}
@@ -0,0 +1,8 @@
{
"string": [
{
"name": "page_show",
"value": "page from npm package"
}
]
}
@@ -0,0 +1,8 @@
{
"string": [
{
"name": "page_show",
"value": "page from npm package"
}
]
}
@@ -0,0 +1,10 @@
package ohos_app_cangjie_cjagent_test
import kit.AbilityKit.AbilityStage
import kit.PerformanceAnalysisKit.Hilog
class MyAbilityStage <: AbilityStage {
public override func onCreate(): Unit {
Hilog.info(1, "Cangjie", "MyAbilityStage onCreated.")
}
}
+44
View File
@@ -0,0 +1,44 @@
[package]
cjc-version = "1.2.0"
compile-option = "--dy-std --cfg=\"${COMPILE_CONDITION}\""
override-compile-option = "${OVERRIDE_COMPILE_OPTION}"
description = "CangjieUI Application"
link-option = "--unresolved-symbols=ignore-all"
name = "ohos_app_cangjie_cjagent_test"
output-type = "dynamic"
src-dir = "."
target-dir = ""
version = "1.0.0"
[dependencies]
[dependencies.ohos_app_cangjie_cjagent]
path = "../../../"
[profile]
[profile.build]
incremental = true
lto = ""
[profile.build.combined]
ohos_app_cangjie_cjagent_test = "dynamic"
[profile.customized-option]
debug = "-g -Woff all -Won apilevel-check"
release = "--fast-math -O2 -s -Woff all -Won apilevel-check"
withoutCoverage = "--cfg=\"coverage=false\""
withCoverage = "--cfg=\"coverage=true\""
asan = "--sanitize=address -lclang_rt.asan"
[profile.test]
[target.aarch64-linux-ohos]
compile-option = "-B \"${DEVECO_CANGJIE_HOME}/build-tools/third_party/llvm/bin\" -B \"${DEVECO_OH_NATIVE_HOME}/sysroot/usr/lib/aarch64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/sysroot/usr/lib/aarch64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/llvm/lib/clang/15.0.4/lib/aarch64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/llvm/lib/aarch64-linux-ohos\" --sysroot \"${DEVECO_OH_NATIVE_HOME}/sysroot\""
[target.aarch64-linux-ohos.bin-dependencies]
path-option = ["${AARCH64_LIBS}", "${AARCH64_MACRO_LIBS}", "${AARCH64_KIT_LIBS}"]
package-option = { }
[target.x86_64-linux-ohos]
compile-option = "-B \"${DEVECO_CANGJIE_HOME}/build-tools/third_party/llvm/bin\" -B \"${DEVECO_OH_NATIVE_HOME}/sysroot/usr/lib/x86_64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/sysroot/usr/lib/x86_64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/llvm/lib/clang/15.0.4/lib/x86_64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/llvm/lib/x86_64-linux-ohos\" --sysroot \"${DEVECO_OH_NATIVE_HOME}/sysroot\""
[target.x86_64-linux-ohos.bin-dependencies]
path-option = ["${X86_64_OHOS_LIBS}", "${X86_64_OHOS_MACRO_LIBS}", "${X86_64_OHOS_KIT_LIBS}"]
[target.x86_64-w64-mingw32.bin-dependencies]
path-option = ["${X86_64_LIBS}", "${X86_64_MACRO_LIBS}", "${X86_64_KIT_LIBS}"]
package-option = { }
+14
View File
@@ -0,0 +1,14 @@
/**
* Created on 2025/2/26
*/
package ohos_app_cangjie_cjagent_test
import std.unittest.expectEqual
import std.unittest.testmacro.Expect
func unittest() {
let a = "abc"
let b = "b"
@Expect(a.contains(b))
@Expect(a, a)
}
@@ -0,0 +1,17 @@
package ohos_app_cangjie_cjagent_test
import std.unittest.TestClass
import std.unittest.TestPackage
import std.unittest.TestSuite
import std.unittest.UnitTestCase
import std.unittest.common.Configuration
import std.unittest.testmacro.Test
import std.unittest.testmacro.TestCase
@Test
class TestExample00 {
@TestCase
func test00(): Unit {
unittest()
}
}
+36
View File
@@ -0,0 +1,36 @@
package ohos_app_cangjie_cjagent_test
import kit.ArkUI.LengthProp
import kit.ArkUI.Column
import kit.ArkUI.Row
import kit.ArkUI.Text
import kit.ArkUI.CustomView
import kit.ArkUI.CJEntry
import kit.ArkUI.loadNativeView
import kit.ArkUI.FontWeight
import kit.ArkUI.SubscriberManager
import kit.ArkUI.ObservedProperty
import kit.ArkUI.LocalStorage
import ohos.arkui.state_macro_manage.Entry
import ohos.arkui.state_macro_manage.Component
import ohos.arkui.state_macro_manage.State
@Entry
@Component
class CjagentView {
@State
var message: String = "Hello World"
func build() {
Row {
Column {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick ({
evt => this.message = "Hello Cangjie"
})
}.width(100.percent)
}.height(100.percent)
}
}
@@ -0,0 +1,9 @@
package ohos_app_cangjie_cjagent_test
import ohos_app_cangjie_cjagent_test.unittest_support.registerTestSuite
private let register = testsuite()
func testsuite() {
registerTestSuite {TestExample00()}
}
@@ -0,0 +1,28 @@
package ohos_app_cangjie_cjagent_test
import kit.PerformanceAnalysisKit.Hilog
import kit.AbilityKit.Want
import kit.AbilityKit.UIAbility
import kit.AbilityKit.LaunchParam
import kit.AbilityKit.LaunchReason
import kit.ArkUI.WindowStage
class MainAbility <: UIAbility {
public init() {
super()
registerSelf()
}
public override func onCreate(want: Want, launchParam: LaunchParam): Unit {
Hilog.info(1, "Cangjie", "MainAbility OnCreated.${want.abilityName}")
match (launchParam.launchReason) {
case LaunchReason.StartAbility => Hilog.info(1, "Cangjie", "START_ABILITY")
case _ => ()
}
}
public override func onWindowStageCreate(windowStage: WindowStage): Unit {
Hilog.info(1, "Cangjie", "MainAbility onWindowStageCreate.")
windowStage.loadContent("CjagentView")
}
}
@@ -0,0 +1,269 @@
package ohos_app_cangjie_cjagent_test.unittest_support
import std.fs.Path
import std.fs.Directory
import std.collection.filter
import std.fs.FileInfo
import std.collection.first
import std.fs.File
import std.collection.forEach
import std.convert.Parsable
import std.collection.any
import std.collection.HashMap
import std.collection.ArrayList
import std.unittest.common.Configuration
import std.unittest.TestClass
import std.unittest.TestSuite
import std.unittest.XmlReporter
import kit.AbilityKit.BundleManager
import kit.AbilityKit.BundleFlag
import kit.AbilityKit.Want
import kit.PerformanceAnalysisKit.Hilog
import kit.TestKit.AbilityDelegator
import kit.TestKit.AbilityDelegatorRegistry
import kit.TestKit.TestRunner
import std.env.setVariable
@When[coverage == "true"]
foreign func __gcov_dump(): Unit
@When[coverage == "true"]
public func gcov_dump(coveragePath: String): Unit {
Hilog.info(0, "Cangjie-Test", "execute test with coverage")
unsafe { __gcov_dump() }
let abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
abilityDelegator.print("GENERATE_COVERAGE: ${coveragePath}")
}
@When[coverage == "false"]
public func gcov_dump(coveragePath: String): Unit {
Hilog.info(0, "Cangjie-Test", "execute test without coverage")
}
public func registerTestSuite(testClassInstanceGenerator: () -> TestClass): Unit {
OpenHarmonyTestRunner
.testClassInstanceGenerators
.add(testClassInstanceGenerator)
}
public class OpenHarmonyTestRunner <: TestRunner {
public static let testSuiteMap = HashMap<String, TestSuite>()
public static let testClassInstanceGenerators = ArrayList<() -> TestClass>()
private static func getDetailedInfoOf(e: Exception): String {
let sb = StringBuilder("exception: ${e.toString()}\n")
e.getStackTrace() |>
forEach {
s => sb.append(" at ${s.declaringClass}.${s.methodName}(${s.fileName}:${s.lineNumber})\n")
}
sb.toString()
}
private static func joinStrings(list: ArrayList<String>, separator: String): String {
if (list.isEmpty()) {
return ""
}
let sb = StringBuilder()
for (i in 0..list.size) {
if (i > 0) {
sb.append(separator)
}
sb.append(list[i])
}
sb.toString()
}
private func executeTestClass(testClassName: String): Unit {
let abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
Hilog.info(0, "Cangjie-Test", "EXECUTE_CLASS_START: \"${testClassName}\"")
abilityDelegator.print("EXECUTE_CLASS_START: \"${testClassName}\"")
let testSuite = match (OpenHarmonyTestRunner
.testSuiteMap
.get(testClassName)) {
case None => return
case Some(testClassInstanceGenerator) => testClassInstanceGenerator
}
testSuite
.runTests()
.reportTo(XmlReporter(Path("/data/storage/el2/base")))
let testReportXmlFileName = "test-default.${testClassName}.xml"
match (Directory.readFrom("/data/storage/el2/base/tests") |>
filter {fileInfo => fileInfo.name == testReportXmlFileName} |> first) {
case None => Hilog.error(1, "Cangjie-Test", "${testReportXmlFileName} not found.")
case Some(testReportXmlFileInfo) =>
let testReportXmlFilePath = testReportXmlFileInfo.path
let testReportXmlFileContent = File.readFrom(testReportXmlFilePath)
let xmlReport = String.fromUtf8(testReportXmlFileContent)
reportTestReport("test-default.${testClassName}.xml")
Hilog.info(0, "Cangjie-Test", "XML_REPORT_CONTENT_LENGTH: ${xmlReport.size}")
abilityDelegator.print("XML_REPORT_CONTENT_LENGTH: ${xmlReport.size}")
splitPrint(xmlReport, abilityDelegator)
Hilog.info(0, "Cangjie-Test", "EXECUTE_CLASS_END: \"${testClassName}\"")
abilityDelegator.print("EXECUTE_CLASS_END: \"${testClassName}\"")
}
}
private func executeAllTestClasses(): Unit {
for (testClassName in OpenHarmonyTestRunner
.testSuiteMap
.keys()) {
executeTestClass(testClassName)
}
}
private func executeTestCase(testClassName: String, testCaseName: String): Unit {
let abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
abilityDelegator.print("EXECUTE_CLASS_START: \"${testClassName}\"")
Hilog.info(0, "Cangjie-Test", "EXECUTE_METHOD_START: \"${testClassName}#${testCaseName}\"")
let testSuite = match (OpenHarmonyTestRunner
.testSuiteMap
.get(testClassName)) {
case None => return
case Some(testClassInstanceGenerator) => testClassInstanceGenerator
}
let configuration = Configuration()
configuration.setByName("filter", "${testClassName}.${testCaseName}")
testSuite
.runTests(configuration)
.reportTo(XmlReporter(Path("/data/storage/el2/base")))
let testReportXmlFileName = "test-default.${testClassName}.xml"
match (Directory.readFrom("/data/storage/el2/base/tests") |>
filter {fileInfo => fileInfo.name == testReportXmlFileName} |> first) {
case None => Hilog.error(1, "Cangjie-Test", "${testReportXmlFileName} not found.")
case Some(testReportXmlFileInfo) =>
let testReportXmlFilePath = testReportXmlFileInfo.path
let testReportXmlFileContent = File.readFrom(testReportXmlFilePath)
let xmlReport = String.fromUtf8(testReportXmlFileContent)
reportTestReport("test-default.${testClassName}.xml")
Hilog.info(0, "Cangjie-Test", "XML_REPORT_CONTENT_LENGTH: ${xmlReport.size}")
abilityDelegator.print("XML_REPORT_CONTENT_LENGTH: ${xmlReport.size}")
splitPrint(xmlReport, abilityDelegator)
Hilog.info(0, "Cangjie-Test", "EXECUTE_METHOD_END: \"${testClassName}#${testCaseName}\"")
abilityDelegator.print("EXECUTE_CLASS_END: \"${testClassName}\"")
}
}
private func splitPrint(xmlReport: String, abilityDelegator: AbilityDelegator): Unit {
let lines = xmlReport.split("\n", removeEmpty: true)
let tempArray = ArrayList<String>()
for (line in lines) {
let trimmedLine = line.trimAscii()
if (trimmedLine.isEmpty()) {
continue
}
tempArray.add(line)
if (tempArray.size == 10) {
let output = OpenHarmonyTestRunner.joinStrings(tempArray, "\n")
Hilog.error(0, "Cangjie-Test", output)
abilityDelegator.print(output)
tempArray.clear()
}
}
if (tempArray.size > 0) {
let output = OpenHarmonyTestRunner.joinStrings(tempArray, "\n")
Hilog.error(0, "Cangjie-Test", output)
abilityDelegator.print(output)
}
}
private func reportTestReport(testReportName: String): Unit {
let abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
let abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments()
let bundleName = abilityDelegatorArguments.bundleName
abilityDelegator.print("REPORT_TEST_RESULT: /data/app/el2/100/base/${bundleName}/tests/${testReportName}")
}
public override func onPrepare(): Unit {
Hilog.info(0, "Cangjie-Test", "OpenHarmonyTestRunner onPrepare")
for (testClassInstanceGenerator in OpenHarmonyTestRunner.testClassInstanceGenerators) {
let testClassInstance = try {
testClassInstanceGenerator()
} catch (e: Exception) {
Hilog.error(1, "Cangjie-Test", "exception occurred during testsuite generation:\n${getDetailedInfoOf(e)}")
continue
}
let testSuite = testClassInstance.asTestSuite()
let testSuiteName = testSuite.name
Hilog.info(0, "Cangjie-Test", "Registering TestSuite ${testSuiteName}")
OpenHarmonyTestRunner
.testSuiteMap
.add(testSuiteName, testSuite)
}
}
public override func onRun(): Unit {
Hilog.info(0, "Cangjie-Test", "OpenHarmonyTestRunner onRun")
let abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments()
let abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
Hilog.info(0, "Cangjie-Test", "bundleName = ${abilityDelegatorArguments.bundleName}")
Hilog.info(0, "Cangjie-Test", "abilityDelegatorArguments.testRunnerClassName = ${abilityDelegatorArguments.testRunnerClassName}")
Hilog.info(0, "Cangjie-Test", "abilityDelegatorArguments.testCaseNames = ${abilityDelegatorArguments.testCaseNames}")
Hilog.info(0, "Cangjie-Test", "abilityDelegatorArguments.parameters = ${abilityDelegatorArguments.parameters}")
Hilog.info(0, "Cangjie-Test", "Started running tests.")
abilityDelegator.print("OpenHarmonyTestRunner onRun")
let parameters = abilityDelegatorArguments.parameters
let bundleName = abilityDelegatorArguments.bundleName
let bundleInfo = BundleManager.getBundleInfoForSelf(
BundleFlag.GET_BUNDLE_INFO_WITH_HAP_MODULE | BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION)
let moduleName = match (parameters.get("-m")) {
case None =>
abilityDelegator.finishTest("Module name was not specified.", -1)
return
case Some(moduleName) =>
if (bundleInfo.hapModulesInfo |> any {hapModuleInfo => hapModuleInfo.name == moduleName}) {
moduleName
} else {
abilityDelegator.finishTest(
"No module with name \"${moduleName}\" was found in bundle ${bundleName}.", -1)
return
}
}
let abilityName = match (bundleInfo.hapModulesInfo |> filter {hapModuleInfo => hapModuleInfo.name == moduleName} |>
first) {
case None =>
abilityDelegator.finishTest("No module with name \"${moduleName}\" was found in bundle ${bundleName}.",
-1)
return
case Some(hapModuleInfo) =>
let mainElementName = hapModuleInfo.mainElementName
Hilog.info(0, "Cangjie-Test", "mainElementName = ${mainElementName}")
mainElementName
}
let want = Want(bundleName: bundleName, moduleName: moduleName, abilityName: abilityName)
abilityDelegator.startAbility(want)
let timeOut = match (parameters.get("-s timeout")) {
case None => None<Int64>
case Some(timeOutText) => match (Int64.tryParse(timeOutText)) {
case None =>
abilityDelegator.finishTest("The parameter \"timeout\": ${timeOutText} is invalid", -1)
return
case Some(timeOutValue) => timeOutValue
}
}
spawn {
try {
match (parameters.get("-s class")) {
case None => executeAllTestClasses()
case Some(text) => text.split(",", removeEmpty: true) |>
forEach {
t: String =>
let pair = t.split("#", removeEmpty: true)
let testClassName = match (pair.get(0)) {
case None => return
case Some(text) => text.trimAscii()
}
match (pair.get(1)) {
case None => executeTestClass(testClassName)
case Some(testCaseName) => executeTestCase(testClassName, testCaseName)
}
}
}
} catch (e: Exception) {
Hilog.error(1, "Cangjie-Test", "an exception occurred:\n${getDetailedInfoOf(e)}")
}
var coverage = false
setVariable("GCOV_PREFIX", "/data/storage/el2/base/cov")
gcov_dump("/data/app/el2/100/base/${bundleName}/cov/")
abilityDelegator.finishTest("Test finished.", 0)
}
}
}
let _ = TestRunner.registerCreator("OpenHarmonyTestRunner") {OpenHarmonyTestRunner()}
+27
View File
@@ -0,0 +1,27 @@
{
"module": {
"name": "cjagent_test",
"type": "feature",
"description": "$string:module_desc",
"mainElement": "EntryAbility",
"deviceTypes": [
"default",
"tablet"
],
"deliveryWithInstall": true,
"installationFree": false,
"srcEntry": "ohos_app_cangjie_cjagent_test.MyAbilityStage",
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "ohos_app_cangjie_cjagent_test.MainAbility",
"description": "$string:EntryAbility_desc",
"icon": "$media:layered_image",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:startIcon",
"startWindowBackground": "$color:start_window_background",
"exported": true
}
]
}
}
@@ -0,0 +1,8 @@
{
"color": [
{
"name": "start_window_background",
"value": "#FFFFFF"
}
]
}
@@ -0,0 +1,16 @@
{
"string": [
{
"name": "module_desc",
"value": "module description"
},
{
"name": "EntryAbility_desc",
"value": "description"
},
{
"name": "EntryAbility_label",
"value": "label"
}
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

@@ -0,0 +1,7 @@
{
"layered-image":
{
"background" : "$media:background",
"foreground" : "$media:foreground"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

@@ -0,0 +1,2 @@
{
}
+22
View File
@@ -0,0 +1,22 @@
[package]
cjc-version = "1.2.0"
compile-option = "--dy-std --cfg=\"${COMPILE_CONDITION}\""
override-compile-option = "${OVERRIDE_COMPILE_OPTION}"
description = "CangjieUI Application"
name = "ohos_app_cangjie_cjagent_local_test"
output-type = "dynamic"
src-dir = "."
target-dir = ""
version = "1.0.0"
[profile]
[profile.build]
incremental = true
lto = ""
[profile.build.combined]
ohos_app_cangjie_cjagent_local_test = "dynamic"
[profile.customized-option]
debug = "-g -Woff all -Won apilevel-check"
release = "--fast-math -O2 -s -Woff all -Won apilevel-check"
asan = "--sanitize=address -lclang_rt.asan"
[profile.test]
+14
View File
@@ -0,0 +1,14 @@
/**
* Created on 2025/2/26
*/
package ohos_app_cangjie_cjagent_local_test
import std.unittest.expectEqual
import std.unittest.testmacro.Expect
func unittest() {
let a = "abc"
let b = "b"
@Expect(a.contains(b))
@Expect(a, a)
}
+17
View File
@@ -0,0 +1,17 @@
package ohos_app_cangjie_cjagent_local_test
import std.unittest.TestClass
import std.unittest.TestPackage
import std.unittest.TestSuite
import std.unittest.UnitTestCase
import std.unittest.common.Configuration
import std.unittest.testmacro.Test
import std.unittest.testmacro.TestCase
@Test
class TestExample00 {
@TestCase
func test00(): Unit {
unittest()
}
}