diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..32a9ae774052ac1503f4a573cca60bc5ca3bb4ef
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,11 @@
+*.iml
+.gradle
+/local.properties
+/.idea
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+/entry/.preview
+.cxx
+/node_modules
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..213a04d9879a28c4db9ec544b8ded775b0ea2beb
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,26 @@
+BSD License
+
+Copyright (C) 2022 Huawei Device Co., Ltd.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of
+conditions and the following disclaimer. Redistributions in binary form must reproduce
+the above copyright notice, this list of conditions and the following disclaimer in
+the documentation and/or other materials provided with the distribution.
+
+Neither the name of Hamcrest nor the names of its contributors may be used to endorse
+or promote products derived from this software without specific prior written
+permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
diff --git a/OAT.xml b/OAT.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4d6e6bccaeeb568be9abeba18dd0c2db218c2a66
--- /dev/null
+++ b/OAT.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.OpenSource b/README.OpenSource
new file mode 100644
index 0000000000000000000000000000000000000000..1c4f51e612598f7f9eaab0d1604fbd5e8111a681
--- /dev/null
+++ b/README.OpenSource
@@ -0,0 +1,11 @@
+[
+ {
+ "Name": "JavaHamcrest",
+ "License": "BSD License",
+ "License File": " LICENSE ",
+ "Version Number": "2.2",
+ "Owner" : "Hamcrest"
+ "Upstream URL": "https://github.com/hamcrest/JavaHamcrest",
+ "Description": "Java (and original) version of Hamcrest"
+ }
+]
\ No newline at end of file
diff --git a/README.md b/README.md
index 8e5e5e3da92992301dea7aec0c9ed76a41b73a1e..451c4e2643d6c6a75772148e4d64f3749b739310 100644
--- a/README.md
+++ b/README.md
@@ -1,39 +1,72 @@
-# Hamcrest
+# hamcrest
-#### 介绍
-{**以下是 Gitee 平台说明,您可以替换此简介**
-Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台
-无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)}
+## 简介
+> hamcrest是匹配器库,可以组合起来匹配。
-#### 软件架构
-软件架构说明
+
+## 下载安装
+```shell
+npm install @ohos/hamcrest --save
+```
+OpenHarmony npm环境配置等更多内容,请参考 [如何安装OpenHarmony npm包](https://gitee.com/openharmony-tpc/docs/blob/master/OpenHarmony_npm_usage.md) 。
-#### 安装教程
+## 使用说明
+1. 引入文件及代码依赖
+ ```
+ import {AllOf} from '@ohos/hamcrest'
+ ```
-1. xxxx
-2. xxxx
-3. xxxx
+2. 构建匹配器
+ ```
+ let matcher = AllOf.allOfMatches(StringContains.containsString('expected'), StringContains.containsString('value'))
+ ```
-#### 使用说明
+3. 调用匹配器匹配
+ ```
+ allOf() {
+ let matcher = AllOf.allOfMatches(StringContains.containsString('expected'), StringContains.containsString('value'))
+ console.info("allOf: " + matcher.matches('expected value'))
+ console.info("allOf: " + matcher.matches('value expected'))
+ console.info("allOf: " + matcher.matches('expected valu'))
+ }
+ ```
-1. xxxx
-2. xxxx
-3. xxxx
+## 接口说明
-#### 参与贡献
+1. 匹配所有`AllOf.allOf()`
+2. 匹配某一个`AnyOf.AnyOf()`
+3. 匹配每一个`Every.everyItem()`
+4. 匹配任意`IsAnything.anything()`
+5. 匹配不是`IsNot.not()`
+6. 匹配null`IsNull.nullValue()`
+7. 匹配包含字符串`StringContains.containsString()`
+8. 匹配以字符串结尾`StringEndsWith.endsWith()`
+9. 匹配以字符串开头`StringStartsWith.startsWith()`
+10. 匹配空字符串`IsEmptyString.emptyString()`
-1. Fork 本仓库
-2. 新建 Feat_xxx 分支
-3. 提交代码
-4. 新建 Pull Request
+## 兼容性
+支持 OpenHarmony API version 8 及以上版本。
+## 目录结构
+````
+|---- hamcrest
+| |---- entry # 示例代码文件夹
+| |---- hamcrest # hamcrest库文件夹
+| |---- index.ets # 对外接口
+| |---- src
+| |---- main
+| |---- components
+| |---- core # 核心文件夹
+| |---- Every.ets # 匹配每一个
+| |---- AllOf.ets # 匹配所有
+| |---- IsAnything.ets # 匹配任意
+| |---- StringContains.ets # 匹配包含字符串
+| |---- README.md # 安装使用方法
+````
-#### 特技
+## 贡献代码
+使用过程中发现任何问题都可以提 [Issue](https://gitee.com/hihopeorg/Hamcrest/issues) 给我们,当然,我们也非常欢迎你给我们发 [PR](https://gitee.com/hihopeorg/Hamcrest/pulls) 。
-1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
-2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
-3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
-4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
-5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
-6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
+## 开源协议
+本项目基于 [BSD License](https://gitee.com/hihopeorg/Hamcrest/blob/master/LICENSE) ,请自由地享受和参与开源。
\ No newline at end of file
diff --git a/build-profile.json5 b/build-profile.json5
new file mode 100644
index 0000000000000000000000000000000000000000..7ea9d738214a7eb5dde21b3b6a64c4dbe35ae5e6
--- /dev/null
+++ b/build-profile.json5
@@ -0,0 +1,31 @@
+{
+ "app": {
+ "signingConfigs": [],
+ "compileSdkVersion": 8,
+ "compatibleSdkVersion": 8,
+ "products": [
+ {
+ "name": "default",
+ "signingConfig": "default",
+ }
+ ]
+ },
+ "modules": [
+ {
+ "name": "entry",
+ "srcPath": "./entry",
+ "targets": [
+ {
+ "name": "default",
+ "applyToProducts": [
+ "default"
+ ]
+ }
+ ]
+ },
+ {
+ "name": "hamcrest",
+ "srcPath": "./hamcrest"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/changelog.md b/changelog.md
new file mode 100644
index 0000000000000000000000000000000000000000..e43494eec85130654bd74c3766e9ca1775e408da
--- /dev/null
+++ b/changelog.md
@@ -0,0 +1,3 @@
+# 1.0.0
+
+ 1. 匹配器库功能实现,可以组合起来匹配
\ No newline at end of file
diff --git a/entry/.gitignore b/entry/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..88985e5e3db0cc0c4ebc5727b03ccd795723faa8
--- /dev/null
+++ b/entry/.gitignore
@@ -0,0 +1,11 @@
+*.iml
+.gradle
+/local.properties
+/.idea
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+/entry/.preview
+.cxx
+/node_modules
\ No newline at end of file
diff --git a/entry/build-profile.json5 b/entry/build-profile.json5
new file mode 100644
index 0000000000000000000000000000000000000000..ae58d1d0a70c602c9cfe1909b00dfec899ba1944
--- /dev/null
+++ b/entry/build-profile.json5
@@ -0,0 +1,13 @@
+{
+ "apiType": 'faMode',
+ "buildOption": {
+ },
+ "targets": [
+ {
+ "name": "default",
+ },
+ {
+ "name": "ohosTest",
+ }
+ ]
+}
\ No newline at end of file
diff --git a/entry/hvigorfile.js b/entry/hvigorfile.js
new file mode 100644
index 0000000000000000000000000000000000000000..bcec4c99653062cbf17702c40a2dd2a7b809b81a
--- /dev/null
+++ b/entry/hvigorfile.js
@@ -0,0 +1,2 @@
+// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently.
+module.exports = require('@ohos/hvigor-ohos-plugin').legacyHapTasks
diff --git a/entry/package-lock.json b/entry/package-lock.json
new file mode 100644
index 0000000000000000000000000000000000000000..850210d5ceb6b6c00f30b9b441d9e71cd9389eaf
--- /dev/null
+++ b/entry/package-lock.json
@@ -0,0 +1,21 @@
+{
+ "name": "entry",
+ "version": "1.0.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@ohos/hamcrest": {
+ "version": "file:../hamcrest",
+ "requires": {
+ "assertion-error": "^1.1.0"
+ },
+ "dependencies": {
+ "assertion-error": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
+ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw=="
+ }
+ }
+ }
+ }
+}
diff --git a/entry/package.json b/entry/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..92c8fcd995b201fd8884643dbe46308c5bb9ec38
--- /dev/null
+++ b/entry/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "entry",
+ "version": "1.0.0",
+ "ohos": {
+ "org": "huawei",
+ "buildTool": "hvigor",
+ "directoryLevel": "module"
+ },
+ "description": "example description",
+ "repository": {},
+ "license": "BSD License",
+ "dependencies": {
+ "@ohos/hamcrest": "file:../hamcrest"
+ }
+}
diff --git a/entry/src/main/config.json b/entry/src/main/config.json
new file mode 100644
index 0000000000000000000000000000000000000000..e58d7017530dd7b94fac517368fd4491974e19f4
--- /dev/null
+++ b/entry/src/main/config.json
@@ -0,0 +1,67 @@
+{
+ "app": {
+ "bundleName": "com.example.hamcrest",
+ "vendor": "example",
+ "version": {
+ "code": 1000000,
+ "name": "1.0.0"
+ }
+ },
+ "deviceConfig": {},
+ "module": {
+ "package": "com.example.hamcrest",
+ "name": ".MyApplication",
+ "mainAbility": ".MainAbility",
+ "srcPath": "",
+ "deviceType": [
+ "phone"
+ ],
+ "distro": {
+ "deliveryWithInstall": true,
+ "moduleName": "entry",
+ "moduleType": "entry",
+ "installationFree": false
+ },
+ "abilities": [
+ {
+ "skills": [
+ {
+ "entities": [
+ "entity.system.home"
+ ],
+ "actions": [
+ "action.system.home"
+ ]
+ }
+ ],
+ "orientation": "unspecified",
+ "visible": true,
+ "srcPath": "MainAbility",
+ "name": ".MainAbility",
+ "srcLanguage": "ets",
+ "icon": "$media:icon",
+ "description": "$string:description_mainability",
+ "formsEnabled": false,
+ "label": "$string:entry_MainAbility",
+ "type": "page",
+ "launchType": "standard"
+ }
+ ],
+ "js": [
+ {
+ "mode": {
+ "syntax": "ets",
+ "type": "pageAbility"
+ },
+ "pages": [
+ "pages/index"
+ ],
+ "name": ".MainAbility",
+ "window": {
+ "designWidth": 720,
+ "autoDesignWidth": false
+ }
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/MainAbility/app.ets b/entry/src/main/ets/MainAbility/app.ets
new file mode 100644
index 0000000000000000000000000000000000000000..1325479f3eacb26f709b33a5fc7b3f8b2a37fab5
--- /dev/null
+++ b/entry/src/main/ets/MainAbility/app.ets
@@ -0,0 +1,23 @@
+/*
+* Copyright (C) 2022 Huawei Device Co., Ltd.
+* Licensed under the BSD License, (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* https://opensource.org/licenses/BSD
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+export default {
+ onCreate() {
+ console.info('Application onCreate')
+ },
+ onDestroy() {
+ console.info('Application onDestroy')
+ },
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/MainAbility/pages/index.ets b/entry/src/main/ets/MainAbility/pages/index.ets
new file mode 100644
index 0000000000000000000000000000000000000000..2fe995efaec6e8581bdea9ee2814c7d71efcc955
--- /dev/null
+++ b/entry/src/main/ets/MainAbility/pages/index.ets
@@ -0,0 +1,434 @@
+/*
+* Copyright (C) 2022 Huawei Device Co., Ltd.
+* Licensed under the BSD License, (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* https://opensource.org/licenses/BSD-3-Clause
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import {HasProperty} from '@ohos/hamcrest'
+import {StringContains} from '@ohos/hamcrest'
+import {StringStartsWith} from '@ohos/hamcrest'
+import {StringEndsWith} from '@ohos/hamcrest'
+import {AllOf} from '@ohos/hamcrest'
+import {AnyOf} from '@ohos/hamcrest'
+import {IsNot} from '@ohos/hamcrest'
+import {Is} from '@ohos/hamcrest'
+import {IsEqual} from '@ohos/hamcrest'
+import {IsNaN} from '@ohos/hamcrest'
+import {IsEqualIgnoringCase} from '@ohos/hamcrest'
+import {CharSequenceLength} from '@ohos/hamcrest'
+import {IsBlankString} from '@ohos/hamcrest'
+import {OrderingComparison} from '@ohos/hamcrest'
+import {IsCloseTo} from '@ohos/hamcrest'
+import {IsNull} from '@ohos/hamcrest'
+import {IsArray} from '@ohos/hamcrest'
+import {IsIn} from '@ohos/hamcrest'
+import {MatchesPattern} from '@ohos/hamcrest'
+import {Every} from '@ohos/hamcrest'
+
+@Entry
+@Component
+struct Index {
+ scroller: Scroller = new Scroller()
+
+ hasProperty() {
+ let matcher = HasProperty.hasProperty('name')
+ console.info("hasProperty: " + matcher.matches({ name: 'Joe' }))
+ console.info("hasProperty: " + matcher.matches({ name: 'Joel' }))
+ console.info("hasProperty: " + matcher.matches({ age: 19 }))
+ }
+
+ hasLength() {
+ let matcher = CharSequenceLength.hasLength(4);
+ console.info("hasLength: " + matcher.matches('aaaa'))
+ console.info("hasLength: " + matcher.matches('aaa'))
+ console.info("hasLength: " + matcher.matches('aa a'))
+ }
+
+ anyOf() {
+ let matcherAnyOf = AnyOf.anyOfMatches(IsBlankString.blankString(),
+ IsEqualIgnoringCase.equalToIgnoringCase('aaa'))
+ console.log('anyOf: enter matcherAnyOf:' + matcherAnyOf.matches('AAA'))
+ console.log('anyOf: enter matcherAnyOf:' + matcherAnyOf.matches('bbb'))
+ console.log('anyOf: enter matcherAnyOf:' + matcherAnyOf.matches(''))
+ console.log('anyOf: enter matcherAnyOf:' + matcherAnyOf.matches('\n\r\t'))
+ }
+
+ containsString() {
+ let matcher = StringContains.containsString('rat')
+ console.info("containsString: " + matcher.matches('jim the rat'))
+ console.info("containsString: " + matcher.matches('jim the rats'))
+ let matcher2 = StringContains.containsString('rats');
+ console.info("containsString: " + matcher2.matches('jim the rat'))
+ }
+
+ allOf() {
+ let matcher = AllOf.allOfMatches(StringContains.containsString('expected'), StringContains.containsString('value'))
+ console.info("allOf: " + matcher.matches('expected value'))
+ console.info("allOf: " + matcher.matches('value expected'))
+ console.info("allOf: " + matcher.matches('expected valu'))
+ }
+
+ endsWith() {
+ let matcher = StringEndsWith.endsWith('test')
+ console.info("isArray: " + matcher.matches('aaabbbtest'))
+ console.info("isArray: " + matcher.matches('aaabbbtests'))
+ console.info("isArray: " + matcher.matches('testdjsdojw'))
+ }
+
+ isNaN() {
+ let matcher = IsNaN.notANumber()
+ console.info("isNaN: " + matcher.matches(7))
+ console.info("isNaN: " + matcher.matches(7.7))
+ console.info("isNaN: " + matcher.matches(NaN))
+ }
+
+ isEqual() {
+ const value = [1, 2]
+ let matcher = IsEqual.equalTo(value)
+ console.info("isEqual: " + matcher.matches([1, 3]))
+ console.info("isEqual: " + matcher.matches([1, 2]))
+ let matcher2 = IsEqual.equalTo("string value")
+ console.info("isEqual: " + matcher2.matches("string value"))
+ console.info("isEqual: " + matcher2.matches("string values"))
+ }
+
+ equalIgnoringCase() {
+ let matcher = IsEqualIgnoringCase.equalToIgnoringCase('Hello')
+ console.log('equalIgnoringCase:' + matcher.matches('heLLo'))
+ console.log('equalIgnoringCase:' + matcher.matches('hello'))
+ console.log('equalIgnoringCase:' + matcher.matches('HELLO'))
+ }
+
+ orderingComparison() {
+ let matcher = OrderingComparison.greaterThan(5)
+ console.log('equalIgnoringCase:' + matcher.matches('6'))
+ console.log('equalIgnoringCase:' + matcher.matches('4'))
+ console.log('equalIgnoringCase:' + matcher.matches('5'))
+
+ let matcher2 = OrderingComparison.greaterThanOrEqualTo(5)
+ console.log('equalIgnoringCase:' + matcher2.matches('6'))
+ console.log('equalIgnoringCase:' + matcher2.matches('4'))
+ console.log('equalIgnoringCase:' + matcher2.matches('5'))
+
+ let matcher3 = OrderingComparison.lessThan(5)
+ console.log('equalIgnoringCase:' + matcher3.matches('6'))
+ console.log('equalIgnoringCase:' + matcher3.matches('4'))
+ console.log('equalIgnoringCase:' + matcher3.matches('5'))
+
+ let matcher4 = OrderingComparison.lessThanOrEqualTo(5)
+ console.log('equalIgnoringCase:' + matcher4.matches('6'))
+ console.log('equalIgnoringCase:' + matcher4.matches('4'))
+ console.log('equalIgnoringCase:' + matcher4.matches('5'))
+ }
+
+ isBlackString() {
+ let matcher = IsBlankString.blankString();
+ console.log('isBlackString :' + matcher.matches(''))
+ console.log('isBlackString :' + matcher.matches('\n\r\t'))
+
+ let matcher2 = IsBlankString.blankOrNullString();
+ console.log('isBlackString: enter matcher2:' + matcher2.matches('\n\r\t'))
+ console.log('isBlackString: enter matcher2:' + matcher2.matches('null'))
+ console.log('isBlackString: enter matcher2:' + matcher2.matches(null))
+ }
+
+ isCloseTo() {
+ let matcher = IsCloseTo.closeTo(1.1, 0.0);
+ console.log('IsCloseTo :' + matcher.matches(1.10))
+
+ let matcher2 = IsCloseTo.closeTo(1.111111111, 0.0);
+ console.log('IsCloseTo :' + matcher.matches(1.1111111112))
+ }
+
+ isNull() {
+ let matcher = IsNull.nullValue()
+ console.log('isNull :' + matcher.matches(null))
+ console.log('isNull :' + matcher.matches('null'))
+ console.log('isNull :' + matcher.matches(''))
+ }
+
+ combinationMatch() {
+ let matcher = IsNot.not(StringContains.containsString('expected'));
+ console.info("combinationMatch: " + matcher.matches("expected value"))
+ console.info("combinationMatch: " + matcher.matches('another value'))
+
+ let match = Is.is(StringStartsWith.startsWith('hamjest'))
+ console.info("combinationMatch: " + match.matches('hamjest is awesome'))
+ }
+
+ isArray() {
+ let matcher = IsArray.array(IsEqual.equalTo('a'), IsEqual.equalTo('b'), IsEqual.equalTo('c'))
+ console.info("isArray: " + matcher.matches(['a', 'b', 'c']))
+ console.info("isArray: " + matcher.matches(['a', 'd', 'b']))
+ console.info("isArray: " + matcher.matches(['a', 'b']))
+ }
+
+ isIn() {
+ let matcher = IsIn.isIn(["bar", "foo"])
+ console.info("isIn: " + matcher.matches("bar"))
+ console.info("isIn: " + matcher.matches(["bar", "foo"]))
+
+ }
+
+ every() {
+ let matcher = Every.everyItem(StringContains.containsString('a'))
+ console.info("every: " + matcher.matches(["AaA", "BaB", "CaC"]))
+ console.info("every: " + matcher.matches(["AAA", "BaB", "CbC"]))
+ }
+
+ matchesPattern() {
+ let matcher = MatchesPattern.matchesPattern(new RegExp('bb'))
+ console.info("matchesPattern: " + matcher.matches('aabbcc'))
+ console.info("matchesPattern: " + matcher.matches('aabcc'))
+ console.info("matchesPattern: " + matcher.matches('abc'))
+ }
+
+ build() {
+
+ Stack({ alignContent: Alignment.TopStart }) {
+ Scroll(this.scroller) {
+ Column() {
+ Button() {
+ Text('hasProperty')
+ .fontSize(25)
+ .fontWeight(FontWeight.Bold)
+ .padding({ left: 16, right: 16 })
+ }.type(ButtonType.Capsule)
+ .margin({
+ top: 20
+ })
+ .backgroundColor('#0D9FFB')
+ .onClick(this.hasProperty.bind(this))
+
+ Button() {
+ Text('hasLength')
+ .fontSize(25)
+ .fontWeight(FontWeight.Bold)
+ .padding({ left: 16, right: 16 })
+ }.type(ButtonType.Capsule)
+ .margin({
+ top: 20
+ })
+ .backgroundColor('#0D9FFB')
+ .onClick(() => {
+ this.hasLength()
+ })
+
+ Button() {
+ Text('anyOf')
+ .fontSize(25)
+ .fontWeight(FontWeight.Bold)
+ .padding({ left: 16, right: 16 })
+ }.type(ButtonType.Capsule)
+ .margin({
+ top: 20
+ })
+ .backgroundColor('#0D9FFB')
+ .onClick(this.anyOf.bind(this))
+
+ Button() {
+ Text('containsString')
+ .fontSize(25)
+ .fontWeight(FontWeight.Bold)
+ .padding({ left: 16, right: 16 })
+ }.type(ButtonType.Capsule)
+ .margin({
+ top: 20
+ })
+ .backgroundColor('#0D9FFB')
+ .onClick(this.containsString.bind(this))
+
+ Button() {
+ Text('allOf')
+ .fontSize(25)
+ .fontWeight(FontWeight.Bold)
+ .padding({ left: 16, right: 16 })
+ }.type(ButtonType.Capsule)
+ .margin({
+ top: 20
+ })
+ .backgroundColor('#0D9FFB')
+ .onClick(this.allOf.bind(this))
+
+ Button() {
+ Text('endsWith')
+ .fontSize(25)
+ .fontWeight(FontWeight.Bold)
+ .padding({ left: 16, right: 16 })
+ }.type(ButtonType.Capsule)
+ .margin({
+ top: 20
+ })
+ .backgroundColor('#0D9FFB')
+ .onClick(this.endsWith.bind(this))
+
+ Button() {
+ Text('isNaN')
+ .fontSize(25)
+ .fontWeight(FontWeight.Bold)
+ .padding({ left: 16, right: 16 })
+ }.type(ButtonType.Capsule)
+ .margin({
+ top: 20
+ })
+ .backgroundColor('#0D9FFB')
+ .onClick(this.isNaN.bind(this))
+
+ Button() {
+ Text('isEqual')
+ .fontSize(25)
+ .fontWeight(FontWeight.Bold)
+ .padding({ left: 16, right: 16 })
+ }.type(ButtonType.Capsule)
+ .margin({
+ top: 20
+ })
+ .backgroundColor('#0D9FFB')
+ .onClick(this.isEqual.bind(this))
+
+ Button() {
+ Text('IsEqualIgnoringCase')
+ .fontSize(25)
+ .fontWeight(FontWeight.Bold)
+ .padding({ left: 16, right: 16 })
+ }.type(ButtonType.Capsule)
+ .margin({
+ top: 20
+ })
+ .backgroundColor('#0D9FFB')
+ .onClick(this.equalIgnoringCase.bind(this))
+
+ Button() {
+ Text('orderingComparison')
+ .fontSize(25)
+ .fontWeight(FontWeight.Bold)
+ .padding({ left: 16, right: 16 })
+ }.type(ButtonType.Capsule)
+ .margin({
+ top: 20
+ })
+ .backgroundColor('#0D9FFB')
+ .onClick(this.orderingComparison.bind(this))
+
+ Button() {
+ Text('isBlackString')
+ .fontSize(25)
+ .fontWeight(FontWeight.Bold)
+ .padding({ left: 16, right: 16 })
+ }.type(ButtonType.Capsule)
+ .margin({
+ top: 20
+ })
+ .backgroundColor('#0D9FFB')
+ .onClick(this.isBlackString.bind(this))
+
+ Button() {
+ Text('isCloseTo')
+ .fontSize(25)
+ .fontWeight(FontWeight.Bold)
+ .padding({ left: 16, right: 16 })
+ }.type(ButtonType.Capsule)
+ .margin({
+ top: 20
+ })
+ .backgroundColor('#0D9FFB')
+ .onClick(this.isCloseTo.bind(this))
+
+ Button() {
+ Text('isNull')
+ .fontSize(25)
+ .fontWeight(FontWeight.Bold)
+ .padding({ left: 16, right: 16 })
+ }.type(ButtonType.Capsule)
+ .margin({
+ top: 20
+ })
+ .backgroundColor('#0D9FFB')
+ .onClick(this.isNull.bind(this))
+
+ Button() {
+ Text('combinationMatch')
+ .fontSize(25)
+ .fontWeight(FontWeight.Bold)
+ .padding({ left: 16, right: 16 })
+ }.type(ButtonType.Capsule)
+ .margin({
+ top: 20
+ })
+ .backgroundColor('#0D9FFB')
+ .onClick(this.combinationMatch.bind(this))
+
+ Button() {
+ Text('IsArray')
+ .fontSize(25)
+ .fontWeight(FontWeight.Bold)
+ .padding({ left: 16, right: 16 })
+ }.type(ButtonType.Capsule)
+ .margin({
+ top: 20
+ })
+ .backgroundColor('#0D9FFB')
+ .onClick(this.isArray.bind(this))
+
+ Button() {
+ Text('IsIn')
+ .fontSize(25)
+ .fontWeight(FontWeight.Bold)
+ .padding({ left: 16, right: 16 })
+ }.type(ButtonType.Capsule)
+ .margin({
+ top: 20
+ })
+ .backgroundColor('#0D9FFB')
+ .onClick(this.isIn.bind(this))
+
+ Button() {
+ Text('every')
+ .fontSize(25)
+ .fontWeight(FontWeight.Bold)
+ .padding({ left: 16, right: 16 })
+ }.type(ButtonType.Capsule)
+ .margin({
+ top: 20
+ })
+ .backgroundColor('#0D9FFB')
+ .onClick(this.every.bind(this))
+
+ Button() {
+ Text('matchesPattern')
+ .fontSize(25)
+ .fontWeight(FontWeight.Bold)
+ .padding({ left: 16, right: 16 })
+ }.type(ButtonType.Capsule)
+ .margin({
+ top: 20
+ })
+ .backgroundColor('#0D9FFB')
+ .onClick(this.matchesPattern.bind(this))
+
+ }.width('100%')
+ }
+ .scrollable(ScrollDirection.Vertical).scrollBar(BarState.On)
+ .scrollBarColor(Color.Gray).scrollBarWidth(1)
+ .onScroll((xOffset: number, yOffset: number) => {
+ console.info(xOffset + ' ' + yOffset)
+ })
+ .onScrollEdge((side: Edge) => {
+ console.info('To the edge')
+ })
+ .onScrollEnd(() => {
+ console.info('Scroll Stop')
+ })
+
+ }.width('100%').height('100%')
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/resources/base/element/string.json b/entry/src/main/resources/base/element/string.json
new file mode 100644
index 0000000000000000000000000000000000000000..b93f540e29265a34f883a977c442fa85349b94ca
--- /dev/null
+++ b/entry/src/main/resources/base/element/string.json
@@ -0,0 +1,12 @@
+{
+ "string": [
+ {
+ "name": "entry_MainAbility",
+ "value": "entry_MainAbility"
+ },
+ {
+ "name": "description_mainability",
+ "value": "eTS_Empty Ability"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/entry/src/main/resources/base/media/icon.png b/entry/src/main/resources/base/media/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c
Binary files /dev/null and b/entry/src/main/resources/base/media/icon.png differ
diff --git a/hamcrest/.gitignore b/hamcrest/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..4f9a973815d0b5e49bc8547681a6b4bc7a178d12
--- /dev/null
+++ b/hamcrest/.gitignore
@@ -0,0 +1,3 @@
+/node_modules
+/.preview
+/build
\ No newline at end of file
diff --git a/hamcrest/build-profile.json5 b/hamcrest/build-profile.json5
new file mode 100644
index 0000000000000000000000000000000000000000..107d8c761a767ae4bd6c3798021d9fad61751005
--- /dev/null
+++ b/hamcrest/build-profile.json5
@@ -0,0 +1,5 @@
+{
+ "apiType": "faMode",
+ "buildOption": {
+ }
+}
diff --git a/hamcrest/hvigorfile.js b/hamcrest/hvigorfile.js
new file mode 100644
index 0000000000000000000000000000000000000000..3a7c40cd644527fdf586c81b110e346329f38c9a
--- /dev/null
+++ b/hamcrest/hvigorfile.js
@@ -0,0 +1,3 @@
+// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently.
+module.exports = require('@ohos/hvigor-ohos-plugin').legacyHarTasks
+
diff --git a/hamcrest/index.ets b/hamcrest/index.ets
new file mode 100644
index 0000000000000000000000000000000000000000..f5df621ceb53d3d10e707e7a6c8a36396e2ece8a
--- /dev/null
+++ b/hamcrest/index.ets
@@ -0,0 +1,60 @@
+/*
+* Copyright (C) 2022 Huawei Device Co., Ltd.
+* Licensed under the BSD License, (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* https://opensource.org/licenses/BSD-3-Clause
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+export { HasProperty } from './src/main/ets/components/beans/HasProperty'
+export { ArrayMatching } from './src/main/ets/components/collection/ArrayMatching'
+export { HasItemInArray } from './src/main/ets/components/collection/HasItemInArray'
+export { IsArray } from './src/main/ets/components/collection/IsArray'
+export { IsArrayWithSize } from './src/main/ets/components/collection/IsArrayWithSize'
+export { IsIn } from './src/main/ets/components/collection/IsIn'
+export { IsMapContaining } from './src/main/ets/components/collection/IsMapContaining'
+export { IsMapWithSize } from './src/main/ets/components/collection/IsMapWithSize'
+export { AllOf } from './src/main/ets/components/core/AllOf'
+export { AnyOf } from './src/main/ets/components/core/AnyOf'
+export { CombinableMatcher } from './src/main/ets/components/core/CombinableMatcher'
+export { Every } from './src/main/ets/components/core/Every'
+export { Is } from './src/main/ets/components/core/Is'
+export { IsAnything } from './src/main/ets/components/core/IsAnything'
+export { IsEqual } from './src/main/ets/components/core/IsEqual'
+export { IsInstanceOf } from './src/main/ets/components/core/IsInstanceOf'
+export { IsIterableContaining } from './src/main/ets/components/core/IsIterableContaining'
+export { IsNot } from './src/main/ets/components/core/IsNot'
+export { IsNull } from './src/main/ets/components/core/IsNull'
+export { IsSame } from './src/main/ets/components/core/IsSame'
+export { ShortcutCombination } from './src/main/ets/components/core/ShortcutCombination'
+export { StringContains } from './src/main/ets/components/core/StringContains'
+export { StringEndsWith } from './src/main/ets/components/core/StringEndsWith'
+export { StringRegularExpression } from './src/main/ets/components/core/StringRegularExpression'
+export { StringStartsWith } from './src/main/ets/components/core/StringStartsWith'
+export { SubstringMatcher } from './src/main/ets/components/core/SubstringMatcher'
+export { ArrayIterator } from './src/main/ets/components/internal/ArrayIterator'
+export { NullSafety } from './src/main/ets/components/internal/NullSafety'
+export { SelfDescribingValue } from './src/main/ets/components/internal/SelfDescribingValue'
+export { SelfDescribingValueIterator } from './src/main/ets/components/internal/SelfDescribingValueIterator'
+export { IsCloseTo } from './src/main/ets/components/number/IsCloseTo'
+export { IsNaN } from './src/main/ets/components/number/IsNaN'
+export { OrderingComparison } from './src/main/ets/components/number/OrderingComparison'
+export { CharSequenceLength } from './src/main/ets/components/text/CharSequenceLength'
+export { IsBlankString } from './src/main/ets/components/text/IsBlankString'
+export { IsEmptyString } from './src/main/ets/components/text/IsEmptyString'
+export { IsEqualCompressingWhiteSpace } from './src/main/ets/components/text/IsEqualCompressingWhiteSpace'
+export { IsEqualIgnoringCase } from './src/main/ets/components/text/IsEqualIgnoringCase'
+export { MatchesPattern } from './src/main/ets/components/text/MatchesPattern'
+export { MatcherAssert } from './src/main/ets/components/MatcherAssert'
+export { CustomMatcher } from './src/main/ets/components/CustomMatcher'
+export { BaseMatcher } from './src/main/ets/components/BaseMatcher'
+export { DiagnosingMatcher } from './src/main/ets/components/DiagnosingMatcher'
+export { FeatureMatcher } from './src/main/ets/components/FeatureMatcher'
+export { BaseDescription } from './src/main/ets/components/BaseDescription'
diff --git a/hamcrest/package-lock.json b/hamcrest/package-lock.json
new file mode 100644
index 0000000000000000000000000000000000000000..b4c98191668f663ecdfd6450ba9a413b121a52a8
--- /dev/null
+++ b/hamcrest/package-lock.json
@@ -0,0 +1,13 @@
+{
+ "name": "@ohos/hamcrest",
+ "version": "1.0.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "assertion-error": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
+ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw=="
+ }
+ }
+}
diff --git a/hamcrest/package.json b/hamcrest/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..21ac266f76ec57bb6ef356981e3960242c0743bc
--- /dev/null
+++ b/hamcrest/package.json
@@ -0,0 +1,22 @@
+{
+ "name": "@ohos/hamcrest",
+ "description": "可组合使用的匹配器库",
+ "ohos": {
+ "org": ""
+ },
+ "version": "1.0.0",
+ "main": "index.ets",
+ "types": "",
+ "repository": "https://gitee.com/hihopeorg/Hamcrest",
+ "license": "BSD License",
+ "dependencies": {
+ "assertion-error": "^1.1.0"
+ },
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [
+ "hamcrest"
+ ],
+ "author": "hihope"
+}
diff --git a/hamcrest/src/main/config.json b/hamcrest/src/main/config.json
new file mode 100644
index 0000000000000000000000000000000000000000..319b58b40010617213d738618c3779bd2aeb9f22
--- /dev/null
+++ b/hamcrest/src/main/config.json
@@ -0,0 +1,23 @@
+{
+ "app": {
+ "bundleName": "com.example.hamcrest",
+ "vendor": "example",
+ "version": {
+ "code": 1000000,
+ "name": "1.0.0"
+ }
+ },
+ "deviceConfig": {},
+ "module": {
+ "package": "com.example.hamcrest",
+ "deviceType": [
+ "phone"
+ ],
+ "distro": {
+ "deliveryWithInstall": true,
+ "moduleName": "hamcrest",
+ "moduleType": "har"
+ },
+ "uiSyntax": "ets"
+ }
+}
diff --git a/hamcrest/src/main/ets/components/BaseDescription.ets b/hamcrest/src/main/ets/components/BaseDescription.ets
new file mode 100644
index 0000000000000000000000000000000000000000..de9067f70baea1b3757dba5d622b9937bd1ffef7
--- /dev/null
+++ b/hamcrest/src/main/ets/components/BaseDescription.ets
@@ -0,0 +1,70 @@
+/*
+* Copyright (C) 2022 Huawei Device Co., Ltd.
+* Licensed under the BSD License, (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* https://opensource.org/licenses/BSD-3-Clause
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import { SelfDescribing } from './SelfDescribing'
+import { Description } from './Description'
+import { NullDescription } from './NullDescription'
+
+/**
+ * A {@link Description} that is stored as a string.
+ */
+export abstract class BaseDescription implements Description {
+ static NONE: Description = new NullDescription();
+
+ value = '';
+
+ public appendText(text): Description {
+ this.append(text)
+ return this;
+ }
+
+ public appendDescriptionOf(value: SelfDescribing): Description {
+ value.describeTo(this);
+ return this;
+ }
+
+ public appendValue(value: Object): Description {
+ if (value === undefined) {
+ this.append('undefined');
+ } else if (value == null) {
+ this.append('null');
+ } else if (value instanceof String) {
+ this.append('"' + value + '"');
+ } else if (value instanceof Array) {
+ this.appendList('[', ', ', ']', value);
+ } else {
+ this.append(value.toString())
+ }
+ return this;
+ }
+
+ public appendValueList(start: String, separator: String, end: String, ... list: SelfDescribing[]): Description {
+ return this.appendList(start, separator, end, list);
+ }
+
+ public appendList(start: String, separator: String, end: String, list: SelfDescribing[]): Description {
+ this.append(start);
+ for (let i = 0; i < list.length; i++) {
+ if (i > 0) {
+ this.append(separator);
+ }
+ this.appendDescriptionOf(list[i]);
+ }
+ this.append(end);
+ return this;
+ }
+
+ protected abstract append(str: String): void;
+}
diff --git a/hamcrest/src/main/ets/components/BaseMatcher.ets b/hamcrest/src/main/ets/components/BaseMatcher.ets
new file mode 100644
index 0000000000000000000000000000000000000000..1644cef69774accb965d63bc1341ff733349c84d
--- /dev/null
+++ b/hamcrest/src/main/ets/components/BaseMatcher.ets
@@ -0,0 +1,51 @@
+/*
+* Copyright (C) 2022 Huawei Device Co., Ltd.
+* Licensed under the BSD License, (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* https://opensource.org/licenses/BSD-3-Clause
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import { Matcher } from './Matcher'
+import { Description } from './Description'
+import { StringDescription } from './StringDescription'
+
+/**
+ * BaseClass for all Matcher implementations.
+ *
+ * @see Matcher
+ */
+export abstract class BaseMatcher implements Matcher {
+ public describeMismatch(item: Object, description: Description): void {
+ description.appendText("was ").appendValue(item);
+ }
+
+ public toString(): String {
+ return StringDescription.convertToString(this);
+ }
+
+ /**
+ * Useful null-check method. Writes a mismatch description if the actual object is null
+ * @param actual the object to check
+ * @param mismatch where to write the mismatch description, if any
+ * @return false iff the actual object is null
+ */
+ protected static isNotNull(actual: Object, mismatch: Description): boolean {
+ if (actual == null) {
+ mismatch.appendText("was null");
+ return false;
+ }
+ return true;
+ }
+
+ public abstract matches(actual: Object): boolean;
+
+ public abstract describeTo(description: Description): void;
+}
diff --git a/hamcrest/src/main/ets/components/CustomMatcher.ets b/hamcrest/src/main/ets/components/CustomMatcher.ets
new file mode 100644
index 0000000000000000000000000000000000000000..7edc6775e3fef0d05f6a0391020c2c90aae56afe
--- /dev/null
+++ b/hamcrest/src/main/ets/components/CustomMatcher.ets
@@ -0,0 +1,32 @@
+/*
+* Copyright (C) 2022 Huawei Device Co., Ltd.
+* Licensed under the BSD License, (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* https://opensource.org/licenses/BSD-3-Clause
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import {BaseMatcher} from './BaseMatcher'
+import {Description} from './Description'
+
+export abstract class CustomMatcher extends BaseMatcher {
+ private fixedDescription: String;
+
+ public CustomMatcher(description: String) {
+ if (description == null) {
+ throw new Error("Description should be non null!");
+ }
+ this.fixedDescription = description;
+ }
+
+ public describeTo(description: Description): void {
+ description.appendText(this.fixedDescription);
+ }
+}
diff --git a/hamcrest/src/main/ets/components/Description.ets b/hamcrest/src/main/ets/components/Description.ets
new file mode 100644
index 0000000000000000000000000000000000000000..6402406155e23b9f40196692ce6b8a4ef702de94
--- /dev/null
+++ b/hamcrest/src/main/ets/components/Description.ets
@@ -0,0 +1,85 @@
+/*
+* Copyright (C) 2022 Huawei Device Co., Ltd.
+* Licensed under the BSD License, (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* https://opensource.org/licenses/BSD-3-Clause
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import { SelfDescribing } from './SelfDescribing'
+import { NullDescription } from './NullDescription'
+
+/**
+ * A description of a Matcher. A Matcher will describe itself to a description
+ * which can later be used for reporting.
+ *
+ * @see Matcher#describeTo(Description)
+ */
+export interface Description {
+
+ /**
+ * Appends some plain text to the description.
+ *
+ * @param text
+ * the text to append.
+ * @return the update description when displaying the matcher error.
+ */
+ appendText(text: String): Description;
+
+ /**
+ * Appends the description of a {@link SelfDescribing} value to this description.
+ *
+ * @param value
+ * the value to append.
+ * @return the update description when displaying the matcher error.
+ */
+ appendDescriptionOf(value: SelfDescribing): Description;
+
+ /**
+ * Appends an arbitrary value to the description.
+ *
+ * @param value
+ * the object to append.
+ * @return the update description when displaying the matcher error.
+ */
+ appendValue(value: Object): Description;
+
+ /**
+ * Appends a list of values to the description.
+ *
+ * @param
+ * the description type.
+ * @param start
+ * the prefix.
+ * @param separator
+ * the separator.
+ * @param end
+ * the suffix.
+ * @param values
+ * the values to append.
+ * @return the update description when displaying the matcher error.
+ */
+ appendValueList(start: String, separator: String, end: String, ... list: SelfDescribing[]): Description;
+
+ /**
+ * Appends a list of SelfDescribing objects
+ * to the description.
+ * @param start
+ * the prefix.
+ * @param separator
+ * the separator.
+ * @param end
+ * the suffix.
+ * @param values
+ * the values to append.
+ * @return the update description when displaying the matcher error.
+ */
+ appendList(start: String, separator: String, end: String, list: SelfDescribing[]): Description;
+}
diff --git a/hamcrest/src/main/ets/components/DiagnosingMatcher.ets b/hamcrest/src/main/ets/components/DiagnosingMatcher.ets
new file mode 100644
index 0000000000000000000000000000000000000000..ee0dd99def9a9788c74b61443d3efe6fc93a9c3e
--- /dev/null
+++ b/hamcrest/src/main/ets/components/DiagnosingMatcher.ets
@@ -0,0 +1,36 @@
+/*
+* Copyright (C) 2022 Huawei Device Co., Ltd.
+* Licensed under the BSD License, (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* https://opensource.org/licenses/BSD-3-Clause
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import { BaseMatcher } from './BaseMatcher'
+import { Description } from './Description'
+import { BaseDescription } from './BaseDescription'
+
+/**
+ * TODO(ngd): Document.
+ *
+ * @param the type of matcher being diagnosed.
+ */
+export abstract class DiagnosingMatcher extends BaseMatcher {
+
+ public matches(item: Object): boolean {
+ return this.matchesWithDiagnosingMatcher(item, BaseDescription.NONE);
+ }
+
+ public describeMismatch(item: Object, mismatchDescription: Description): void {
+ this.matchesWithDiagnosingMatcher(item, mismatchDescription);
+ }
+
+ protected abstract matchesWithDiagnosingMatcher(item: Object, mismatchDescription: Description): boolean;
+}
diff --git a/hamcrest/src/main/ets/components/FeatureMatcher.ets b/hamcrest/src/main/ets/components/FeatureMatcher.ets
new file mode 100644
index 0000000000000000000000000000000000000000..38d037558795f2950a5fd04868c66820315b9d9d
--- /dev/null
+++ b/hamcrest/src/main/ets/components/FeatureMatcher.ets
@@ -0,0 +1,67 @@
+/*
+* Copyright (C) 2022 Huawei Device Co., Ltd.
+* Licensed under the BSD License, (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* https://opensource.org/licenses/BSD-3-Clause
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import {DiagnosingMatcher} from './DiagnosingMatcher'
+import {Description} from './Description'
+import {Matcher} from './Matcher'
+
+/**
+ * Supporting class for matching a feature of an object. Implement featureValueOf()
+ * in a subclass to pull out the feature to be matched against.
+ *
+ * @param The type of the object to be matched
+ * @param The type of the feature to be matched
+ */
+export abstract class FeatureMatcher extends DiagnosingMatcher {
+
+ private subMatcher: Matcher ;
+ private featureDescription: String;
+ private featureName: String;
+
+ /**
+ * Constructor
+ * @param subMatcher The matcher to apply to the feature
+ * @param featureDescription Descriptive text to use in describeTo
+ * @param featureName Identifying text for mismatch message
+ */
+ public constructor(subMatcher: Matcher, featureDescription: String, featureName: String) {
+ super();
+ this.subMatcher = subMatcher;
+ this.featureDescription = featureDescription;
+ this.featureName = featureName;
+ }
+
+ /**
+ * Implement this to extract the interesting feature.
+ * @param actual the target object
+ * @return the feature to be matched
+ */
+ protected abstract featureValueOf(actual: T): U;
+
+ protected matchesWithDiagnosingMatcher(actual: T, mismatch: Description): boolean {
+ let featureValue: U = this.featureValueOf(actual);
+ if (!this.subMatcher.matches(featureValue)) {
+ mismatch.appendText(this.featureName).appendText(" ");
+ this.subMatcher.describeMismatch(featureValue, mismatch);
+ return false;
+ }
+ return true;
+ }
+
+ public describeTo(description: Description): void {
+ description.appendText(this.featureDescription).appendText(" ")
+ .appendDescriptionOf(this.subMatcher);
+ }
+}
diff --git a/hamcrest/src/main/ets/components/Matcher.ets b/hamcrest/src/main/ets/components/Matcher.ets
new file mode 100644
index 0000000000000000000000000000000000000000..1e45d847324b43cf5bfde4f260f79795c6d9c693
--- /dev/null
+++ b/hamcrest/src/main/ets/components/Matcher.ets
@@ -0,0 +1,48 @@
+/*
+* Copyright (C) 2022 Huawei Device Co., Ltd.
+* Licensed under the BSD License, (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* https://opensource.org/licenses/BSD-3-Clause
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import {SelfDescribing} from './SelfDescribing'
+import {Description} from './Description'
+
+export interface Matcher extends SelfDescribing {
+
+ /**
+ * Evaluates the matcher for argument item.
+ *
+ * This method matches against Object, instead of the generic type T. This is
+ * because the caller of the Matcher does not know at runtime what the type is
+ * (because of type erasure with Java generics). It is down to the implementations
+ * to check the correct type.
+ *
+ * @param actual the object against which the matcher is evaluated.
+ * @return true if item matches, otherwise false.
+ *
+ * @see BaseMatcher
+ */
+ matches(actual: Object): boolean;
+
+ /**
+ * Generate a description of why the matcher has not accepted the item.
+ * The description will be part of a larger description of why a matching
+ * failed, so it should be concise.
+ * This method assumes that matches(item) is false, but
+ * will not check this.
+ *
+ * @param actual The item that the Matcher has rejected.
+ * @param mismatchDescription
+ * The description to be built or appended to.
+ */
+ describeMismatch(actual: Object, mismatchDescription: Description): void;
+}
diff --git a/hamcrest/src/main/ets/components/MatcherAssert.ets b/hamcrest/src/main/ets/components/MatcherAssert.ets
new file mode 100644
index 0000000000000000000000000000000000000000..774d17812acbe8de401ae1c969a8b781661eb3ca
--- /dev/null
+++ b/hamcrest/src/main/ets/components/MatcherAssert.ets
@@ -0,0 +1,46 @@
+/*
+* Copyright (C) 2022 Huawei Device Co., Ltd.
+* Licensed under the BSD License, (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* https://opensource.org/licenses/BSD-3-Clause
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import {Matcher} from './Matcher'
+import {Description} from './Description'
+import {StringDescription} from './StringDescription'
+import AssertionError from 'assertion-error'
+
+export class MatcherAssert {
+ public static assertThat(actual: any, matcher: Matcher): void {
+ MatcherAssert.assertThatWithMatcher(actual, matcher, "");
+ }
+
+ public static assertThatWithMatcher(actual: any, matcher: Matcher, reason?: String): void {
+ if (!matcher.matches(actual)) {
+ let description: Description = new StringDescription();
+ description.appendText(reason)
+ .appendText("\n")
+ .appendText("Expected: ")
+ .appendDescriptionOf(matcher)
+ .appendText("\n")
+ .appendText(" but: ");
+ matcher.describeMismatch(actual, description);
+
+ throw new AssertionError(description.toString());
+ }
+ }
+
+ public static assertThatWithReason(reason: String, assertion: boolean): void {
+ if (!assertion) {
+ throw new AssertionError(reason.toString());
+ }
+ }
+}
diff --git a/hamcrest/src/main/ets/components/NullDescription.ets b/hamcrest/src/main/ets/components/NullDescription.ets
new file mode 100644
index 0000000000000000000000000000000000000000..c4ccc4531a9d5bc6d1ef6cd89ca490c12bf68114
--- /dev/null
+++ b/hamcrest/src/main/ets/components/NullDescription.ets
@@ -0,0 +1,43 @@
+/*
+* Copyright (C) 2022 Huawei Device Co., Ltd.
+* Licensed under the BSD License, (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* https://opensource.org/licenses/BSD-3-Clause
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import { Description } from './Description';
+import { SelfDescribing } from './SelfDescribing';
+
+export class NullDescription implements Description {
+ public appendDescriptionOf(value: SelfDescribing): Description {
+ return this;
+ }
+
+ public appendList(start: String, separator: String, end: String, values: SelfDescribing[]): Description {
+ return this;
+ }
+
+ public appendText(text: String): Description {
+ return this;
+ }
+
+ public appendValue(value: Object): Description{
+ return this;
+ }
+
+ public appendValueList(start: String, separator: String, end: String, ... values: SelfDescribing[]): Description{
+ return this;
+ }
+
+ public toString(): String {
+ return "";
+ }
+}
\ No newline at end of file
diff --git a/hamcrest/src/main/ets/components/SelfDescribing.ets b/hamcrest/src/main/ets/components/SelfDescribing.ets
new file mode 100644
index 0000000000000000000000000000000000000000..40333fc462e0ad2ea358000717ed881e822beea9
--- /dev/null
+++ b/hamcrest/src/main/ets/components/SelfDescribing.ets
@@ -0,0 +1,32 @@
+/*
+* Copyright (C) 2022 Huawei Device Co., Ltd.
+* Licensed under the BSD License, (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* https://opensource.org/licenses/BSD-3-Clause
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import {Description} from './Description'
+
+/**
+ * The ability of an object to describe itself.
+ */
+export interface SelfDescribing {
+
+ /**
+ * Generates a description of the object. The description may be part of a
+ * a description of a larger object of which this is just a component, so it
+ * should be worded appropriately.
+ *
+ * @param description
+ * The description to be built or appended to.
+ */
+ describeTo(description: Description): void;
+}
diff --git a/hamcrest/src/main/ets/components/StringDescription.ets b/hamcrest/src/main/ets/components/StringDescription.ets
new file mode 100644
index 0000000000000000000000000000000000000000..1e9388152e5126a730c1524217e9e6d247940ca5
--- /dev/null
+++ b/hamcrest/src/main/ets/components/StringDescription.ets
@@ -0,0 +1,63 @@
+/*
+* Copyright (C) 2022 Huawei Device Co., Ltd.
+* Licensed under the BSD License, (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* https://opensource.org/licenses/BSD-3-Clause
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import { BaseDescription } from './BaseDescription';
+import { SelfDescribing } from './SelfDescribing';
+
+/**
+ * A {@link Description} that is stored as a string.
+ */
+export class StringDescription extends BaseDescription {
+ value = '';
+
+ constructor() {
+ super();
+ }
+
+ /**
+ * Return the description of a {@link SelfDescribing} object as a String.
+ *
+ * @param selfDescribing
+ * The object to be described.
+ * @return
+ * The description of the object.
+ */
+ public static convertToString(selfDescribing: SelfDescribing): String {
+ return new StringDescription().appendDescriptionOf(selfDescribing).toString();
+ }
+
+ /**
+ * Alias for {@link #toString(SelfDescribing)}.
+ *
+ * @param selfDescribing
+ * The object to be described.
+ * @return
+ * The description of the object.
+ */
+ public static asString(selfDescribing: SelfDescribing): String {
+ return this.convertToString(selfDescribing);
+ }
+
+ protected append(str: String): void {
+ this.value += str;
+ }
+
+ /**
+ * Returns the description as a string.
+ */
+ public toString(): String {
+ return this.value;
+ }
+}
diff --git a/hamcrest/src/main/ets/components/beans/HasProperty.ets b/hamcrest/src/main/ets/components/beans/HasProperty.ets
new file mode 100644
index 0000000000000000000000000000000000000000..cb67b6c2bd4ad5ba61d4d363ad406d74cc046163
--- /dev/null
+++ b/hamcrest/src/main/ets/components/beans/HasProperty.ets
@@ -0,0 +1,54 @@
+/*
+* Copyright (C) 2022 Huawei Device Co., Ltd.
+* Licensed under the BSD License, (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* https://opensource.org/licenses/BSD-3-Clause
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import { Description } from '../Description';
+import { BaseMatcher } from '../BaseMatcher';
+import { Matcher } from '../Matcher';
+
+export class HasProperty extends BaseMatcher {
+ private propertyName: string;
+ public constructor(propertyName: string) {
+ super();
+ this.propertyName = propertyName;
+ }
+
+ public matches(obj: T): boolean {
+ return obj == null ? false : obj.hasOwnProperty(this.propertyName)
+ }
+
+ public describeMismatch(item: T, mismatchDescription: Description): void {
+ mismatchDescription.appendText("no ").appendValue(this.propertyName).appendText(" in ").appendValue(item);
+ }
+
+ public describeTo(description: Description): void {
+ description.appendText("hasProperty(").appendValue(this.propertyName).appendText(")");
+ }
+
+ /**
+ * Creates a matcher that matches when the examined object has a JavaBean property
+ * with the specified name.
+ * For example:
+ *
assertThat(myBean, hasProperty("foo"))
+ *
+ * @param
+ * the matcher type.
+ * @param propertyName
+ * the name of the JavaBean property that examined beans should possess
+ * @return The matcher.
+ */
+ public static hasProperty(propertyName: string): Matcher