Skip to content

Commit 5d730b6

Browse files
committed
add harmony unittest
1 parent aac9d97 commit 5d730b6

14 files changed

Lines changed: 560 additions & 61 deletions

File tree

.claude/settings.local.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"Bash(./scripts/test-patch-core.sh:*)",
5+
"Bash(bun test:*)"
6+
]
7+
}
8+
}
Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,36 @@
1+
import { describe, beforeAll, it, expect } from '@ohos/hypium';
12
import { hilog } from '@kit.PerformanceAnalysisKit';
2-
import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
3+
import { abilityDelegatorRegistry } from '@kit.TestKit';
4+
5+
const TAG = 'AbilityTest';
6+
const delegator = abilityDelegatorRegistry.getAbilityDelegator();
37

48
export default function abilityTest() {
5-
describe('ActsAbilityTest', () => {
6-
// Defines a test suite. Two parameters are supported: test suite name and test suite function.
7-
beforeAll(() => {
8-
// Presets an action, which is performed only once before all test cases of the test suite start.
9-
// This API supports only one parameter: preset action function.
10-
})
11-
beforeEach(() => {
12-
// Presets an action, which is performed before each unit test case starts.
13-
// The number of execution times is the same as the number of test cases defined by **it**.
14-
// This API supports only one parameter: preset action function.
15-
})
16-
afterEach(() => {
17-
// Presets a clear action, which is performed after each unit test case ends.
18-
// The number of execution times is the same as the number of test cases defined by **it**.
19-
// This API supports only one parameter: clear action function.
20-
})
21-
afterAll(() => {
22-
// Presets a clear action, which is performed after all test cases of the test suite end.
23-
// This API supports only one parameter: clear action function.
24-
})
25-
it('assertContain', 0, () => {
26-
// Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
27-
hilog.info(0x0000, 'testTag', '%{public}s', 'it begin');
28-
let a = 'abc';
29-
let b = 'b';
30-
// Defines a variety of assertion methods, which are used to declare expected boolean conditions.
31-
expect(a).assertContain(b);
32-
expect(a).assertEqual(a);
33-
})
34-
})
35-
}
9+
describe('AbilityLaunchTest', () => {
10+
beforeAll(async () => {
11+
hilog.info(0x0000, TAG, 'AbilityLaunchTest beforeAll');
12+
const want = {
13+
bundleName: 'com.example.harmony_use_pushy',
14+
abilityName: 'EntryAbility',
15+
};
16+
await delegator.startAbility(want);
17+
// Allow the RN surface time to initialize
18+
await new Promise<void>(resolve => setTimeout(resolve, 3000));
19+
});
20+
21+
it('ability should launch successfully', 0, async () => {
22+
hilog.info(0x0000, TAG, 'checking ability launch');
23+
const currentAbility = delegator.getCurrentTopAbility();
24+
expect(currentAbility).assertNotNull();
25+
hilog.info(0x0000, TAG, 'ability launched: %{public}s', JSON.stringify(currentAbility.context?.abilityInfo?.name));
26+
});
27+
28+
it('ability context should have valid filesDir', 0, async () => {
29+
const currentAbility = delegator.getCurrentTopAbility();
30+
const context = currentAbility.context;
31+
expect(context).assertNotNull();
32+
expect(context.filesDir).assertContain('/files');
33+
hilog.info(0x0000, TAG, 'filesDir: %{public}s', context.filesDir);
34+
});
35+
});
36+
}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import abilityTest from './Ability.test';
2+
import pushyIntegrationTest from './PushyIntegration.test';
23

34
export default function testsuite() {
45
abilityTest();
5-
}
6+
pushyIntegrationTest();
7+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import { describe, beforeAll, it, expect } from '@ohos/hypium';
2+
import { hilog } from '@kit.PerformanceAnalysisKit';
3+
import { abilityDelegatorRegistry } from '@kit.TestKit';
4+
import preferences from '@ohos.data.preferences';
5+
6+
const TAG = 'PushyIntegrationTest';
7+
const delegator = abilityDelegatorRegistry.getAbilityDelegator();
8+
9+
export default function pushyIntegrationTest() {
10+
describe('PushyIntegrationTest', () => {
11+
let context: Context;
12+
13+
beforeAll(async () => {
14+
hilog.info(0x0000, TAG, 'PushyIntegrationTest beforeAll');
15+
const want = {
16+
bundleName: 'com.example.harmony_use_pushy',
17+
abilityName: 'EntryAbility',
18+
};
19+
await delegator.startAbility(want);
20+
await new Promise<void>(resolve => setTimeout(resolve, 3000));
21+
const ability = delegator.getCurrentTopAbility();
22+
context = ability.context;
23+
});
24+
25+
it('update preferences should be writable and readable', 0, async () => {
26+
hilog.info(0x0000, TAG, 'testing preferences read/write');
27+
const prefs = preferences.getPreferencesSync(context, {
28+
name: 'pushy_integration_test',
29+
});
30+
31+
prefs.putSync('testKey', 'testValue');
32+
prefs.flush();
33+
34+
const value = prefs.getSync('testKey', '') as string;
35+
expect(value).assertEqual('testValue');
36+
37+
// Clean up
38+
prefs.deleteSync('testKey');
39+
prefs.flush();
40+
hilog.info(0x0000, TAG, 'preferences test passed');
41+
});
42+
43+
it('update root directory should be creatable', 0, async () => {
44+
hilog.info(0x0000, TAG, 'testing update root directory');
45+
const updateRoot = context.filesDir + '/_update_test';
46+
const fileIo = await import('@ohos.file.fs');
47+
48+
// Ensure clean state
49+
try {
50+
if (fileIo.default.accessSync(updateRoot)) {
51+
fileIo.default.rmdirSync(updateRoot);
52+
}
53+
} catch {}
54+
55+
fileIo.default.mkdirSync(updateRoot);
56+
const exists = fileIo.default.accessSync(updateRoot);
57+
expect(exists).assertTrue();
58+
59+
// Clean up
60+
fileIo.default.rmdirSync(updateRoot);
61+
hilog.info(0x0000, TAG, 'update root directory test passed');
62+
});
63+
64+
it('hash info round-trip through preferences', 0, async () => {
65+
hilog.info(0x0000, TAG, 'testing hash info round-trip');
66+
const prefs = preferences.getPreferencesSync(context, {
67+
name: 'pushy_integration_test',
68+
});
69+
70+
const hashInfo = JSON.stringify({ name: 'v1', description: 'test version' });
71+
const key = 'hash_test123';
72+
73+
prefs.putSync(key, hashInfo);
74+
prefs.flush();
75+
76+
const retrieved = prefs.getSync(key, '') as string;
77+
expect(retrieved).assertEqual(hashInfo);
78+
79+
const parsed = JSON.parse(retrieved);
80+
expect(parsed.name).assertEqual('v1');
81+
expect(parsed.description).assertEqual('test version');
82+
83+
// Clean up
84+
prefs.deleteSync(key);
85+
prefs.flush();
86+
hilog.info(0x0000, TAG, 'hash info round-trip test passed');
87+
});
88+
89+
it('initial state should have no current version', 0, async () => {
90+
hilog.info(0x0000, TAG, 'testing initial state');
91+
const prefs = preferences.getPreferencesSync(context, {
92+
name: 'pushy_initial_state_test',
93+
});
94+
95+
const currentVersion = prefs.getSync('currentVersion', '') as string;
96+
expect(currentVersion).assertEqual('');
97+
98+
const firstTime = prefs.getSync('firstTime', false) as boolean;
99+
expect(firstTime).assertEqual(false);
100+
101+
// Clean up
102+
preferences.deletePreferences(context, { name: 'pushy_initial_state_test' });
103+
hilog.info(0x0000, TAG, 'initial state test passed');
104+
});
105+
});
106+
}
Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,31 @@
1-
import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
1+
import { describe, it, expect } from '@ohos/hypium';
22

33
export default function localUnitTest() {
44
describe('localUnitTest', () => {
5-
// Defines a test suite. Two parameters are supported: test suite name and test suite function.
6-
beforeAll(() => {
7-
// Presets an action, which is performed only once before all test cases of the test suite start.
8-
// This API supports only one parameter: preset action function.
5+
it('JSON parse round-trip for update manifest', 0, () => {
6+
const manifest = {
7+
copies: { 'assets/icon.png': 'assets/original.png' },
8+
deletes: ['assets/old.png'],
9+
};
10+
const serialized = JSON.stringify(manifest);
11+
const parsed = JSON.parse(serialized);
12+
expect(parsed.copies['assets/icon.png']).assertEqual('assets/original.png');
13+
expect(parsed.deletes[0]).assertEqual('assets/old.png');
914
});
10-
beforeEach(() => {
11-
// Presets an action, which is performed before each unit test case starts.
12-
// The number of execution times is the same as the number of test cases defined by **it**.
13-
// This API supports only one parameter: preset action function.
14-
});
15-
afterEach(() => {
16-
// Presets a clear action, which is performed after each unit test case ends.
17-
// The number of execution times is the same as the number of test cases defined by **it**.
18-
// This API supports only one parameter: clear action function.
19-
});
20-
afterAll(() => {
21-
// Presets a clear action, which is performed after all test cases of the test suite end.
22-
// This API supports only one parameter: clear action function.
15+
16+
it('version string comparison works correctly', 0, () => {
17+
const v1 = '1.0.0';
18+
const v2 = '1.0.0';
19+
const v3 = '2.0.0';
20+
expect(v1 === v2).assertEqual(true);
21+
expect(v1 === v3).assertEqual(false);
2322
});
24-
it('assertContain', 0, () => {
25-
// Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
26-
let a = 'abc';
27-
let b = 'b';
28-
// Defines a variety of assertion methods, which are used to declare expected boolean conditions.
29-
expect(a).assertContain(b);
30-
expect(a).assertEqual(a);
23+
24+
it('hash string concatenation for kv keys', 0, () => {
25+
const hash = 'abc123';
26+
const key = `hash_${hash}`;
27+
expect(key).assertEqual('hash_abc123');
28+
expect(key).assertContain('hash_');
3129
});
3230
});
33-
}
31+
}

cpp/patch_core/tests/patch_core_test.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,74 @@ void TestArchivePatchCoreSupportsCustomBundlePatchEntry() {
442442
ExpectEq(plan.merge_source_subdir, "", "custom bundle patch merge subdir mismatch");
443443
}
444444

445+
void TestArchivePatchCoreHarmonyBundlePatchFromPackage() {
446+
PatchManifest manifest;
447+
manifest.copies.push_back(CopyOperation{"assets/a.png", "assets/b.png"});
448+
449+
pushy::archive_patch::ArchivePatchPlan plan;
450+
Status status = pushy::archive_patch::BuildArchivePatchPlan(
451+
pushy::archive_patch::ArchivePatchType::kPatchFromPackage,
452+
manifest,
453+
{"__diff.json", "bundle.harmony.js.patch", "assets/new.png"},
454+
&plan,
455+
"bundle.harmony.js.patch");
456+
Expect(status.ok, status.message);
457+
Expect(plan.enable_merge, "harmony package plan should enable merge");
458+
ExpectEq(plan.merge_source_subdir, "assets", "harmony package merge subdir should be assets");
459+
460+
// ppk variant uses empty merge subdir
461+
pushy::archive_patch::ArchivePatchPlan ppk_plan;
462+
status = pushy::archive_patch::BuildArchivePatchPlan(
463+
pushy::archive_patch::ArchivePatchType::kPatchFromPpk,
464+
manifest,
465+
{"__diff.json", "bundle.harmony.js.patch", "assets/new.png"},
466+
&ppk_plan,
467+
"bundle.harmony.js.patch");
468+
Expect(status.ok, status.message);
469+
Expect(ppk_plan.enable_merge, "harmony ppk plan should enable merge");
470+
ExpectEq(ppk_plan.merge_source_subdir, "", "harmony ppk merge subdir should be empty");
471+
}
472+
473+
void TestStateCoreRollbackToEmptyVersion() {
474+
State state;
475+
state.current_version = "current";
476+
state.last_version = "";
477+
state.first_time = false;
478+
state.first_time_ok = true;
479+
480+
State rolled = pushy::state::Rollback(state);
481+
Expect(rolled.current_version.empty(), "rollback with empty last should clear current");
482+
Expect(rolled.last_version.empty(), "last_version should remain empty");
483+
ExpectEq(rolled.rolled_back_version, "current", "rolled_back_version should record original");
484+
Expect(!rolled.first_time, "first_time should be false after rollback");
485+
Expect(rolled.first_time_ok, "first_time_ok should be true after rollback");
486+
}
487+
488+
void TestStateCoreResolveLaunchNoCurrentVersion() {
489+
State state;
490+
state.current_version = "";
491+
state.first_time = false;
492+
state.first_time_ok = true;
493+
494+
LaunchDecision decision = pushy::state::ResolveLaunchState(state, false, true);
495+
Expect(decision.load_version.empty(), "empty current should yield empty load_version");
496+
Expect(!decision.did_rollback, "should not rollback when no current version");
497+
Expect(!decision.consumed_first_time, "should not consume first_time when no current version");
498+
}
499+
500+
void TestStateCoreSwitchToSameVersion() {
501+
State state;
502+
state.package_version = "1.0.0";
503+
state.current_version = "same_hash";
504+
state.last_version = "old_hash";
505+
506+
State switched = pushy::state::SwitchVersion(state, "same_hash");
507+
ExpectEq(switched.current_version, "same_hash", "current should remain same_hash");
508+
ExpectEq(switched.last_version, "old_hash", "last_version should not change when switching to same");
509+
Expect(switched.first_time, "first_time should be set even when switching to same");
510+
Expect(!switched.first_time_ok, "first_time_ok should be false");
511+
}
512+
445513
} // namespace
446514

447515
int main() {
@@ -457,6 +525,10 @@ int main() {
457525
{"ArchivePatchCoreBuildPlanAndCopyGroups", TestArchivePatchCoreBuildPlanAndCopyGroups},
458526
{"ArchivePatchCoreRejectsMissingEntries", TestArchivePatchCoreRejectsMissingEntries},
459527
{"ArchivePatchCoreSupportsCustomBundlePatchEntry", TestArchivePatchCoreSupportsCustomBundlePatchEntry},
528+
{"ArchivePatchCoreHarmonyBundlePatchFromPackage", TestArchivePatchCoreHarmonyBundlePatchFromPackage},
529+
{"StateCoreRollbackToEmptyVersion", TestStateCoreRollbackToEmptyVersion},
530+
{"StateCoreResolveLaunchNoCurrentVersion", TestStateCoreResolveLaunchNoCurrentVersion},
531+
{"StateCoreSwitchToSameVersion", TestStateCoreSwitchToSameVersion},
460532
};
461533

462534
for (const auto& test : tests) {

harmony/pushy/build-profile.json5

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
"targets": [
1111
{
1212
"name": "default",
13+
},
14+
{
15+
"name": "ohosTest",
1316
}
1417
]
1518
}

harmony/pushy/oh-package.json5

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
{
22
license: 'MIT',
33
types: '',
4-
devDependencies: {},
4+
devDependencies: {
5+
'@ohos/hypium': '1.0.19',
6+
},
57
name: 'pushy',
68
description: '',
79
main: 'index.ets',

harmony/pushy/src/main/ets/PushyTurboModule.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ export class PushyTurboModule extends UITurboModule {
185185
this.context.markSuccess();
186186
return true;
187187
} catch (error) {
188-
logger.error(TAG, `markSuccess failed: ${this.getErrorMessage(error)}`);
188+
logger.error(TAG, `markSuccess failed: ${getErrorMessage(error)}`);
189189
throw error;
190190
}
191191
}

0 commit comments

Comments
 (0)