1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3# Copyright (C) 2025 Huawei Device Co., Ltd. 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15import os 16import pytest 17 18from utils import GP, check_hdc_cmd, check_hdc_targets, check_soft_local, check_soft_remote, \ 19 check_shell, rmdir, check_version, get_local_path, get_remote_path, make_multiprocess_file, load_gp 20 21 22def clear_env(): 23 check_hdc_cmd("shell rm -rf data/local/tmp/it_*") 24 check_hdc_cmd("shell mkdir data/local/tmp/it_send_dir") 25 26 27def list_targets(): 28 assert check_hdc_targets() 29 30 31class TestFileCompress: 32 compress_file_table = [ 33 ("medium", "it_medium_z"), 34 ("word_100M.txt", "word_100M_compress.txt") 35 ] 36 37 @pytest.mark.L1 38 @pytest.mark.repeat(1) 39 @check_version("Ver: 3.1.0e") 40 @pytest.mark.parametrize("local_path, remote_path", compress_file_table) 41 def test_file_compress(self, local_path, remote_path): 42 clear_env() 43 assert check_hdc_cmd(f"file send -z {get_local_path(local_path)} {get_remote_path(remote_path)}") 44 assert check_hdc_cmd(f"file recv -z {get_remote_path(remote_path)} {get_local_path(f'{local_path}_recv')}") 45 46 47class TestFileBase: 48 base_file_table = [ 49 ("empty", "it_empty"), 50 ("small", "it_small"), 51 ("medium", "it_medium"), 52 ("large", "it_large"), 53 ("word_100M.txt", "word_100M") 54 ] 55 56 @pytest.mark.L0 57 @pytest.mark.repeat(2) 58 @pytest.mark.parametrize("local_path, remote_path", base_file_table) 59 def test_file_normal(self, local_path, remote_path): 60 clear_env() 61 assert check_hdc_cmd(f"file send {get_local_path(local_path)} {get_remote_path(remote_path)}") 62 assert check_hdc_cmd(f"file recv {get_remote_path(remote_path)} {get_local_path(f'{local_path}_recv')}") 63 64 65class TestDirBase: 66 @pytest.mark.L0 67 @pytest.mark.repeat(2) 68 def test_empty_dir(self): 69 clear_env() 70 assert check_shell(f"file send {get_local_path('empty_dir')} {get_remote_path('it_empty_dir')}", 71 "the source folder is empty") 72 assert check_hdc_cmd("shell mkdir data/local/tmp/it_empty_dir_recv") 73 assert check_shell(f"file recv {get_remote_path('it_empty_dir_recv')} {get_local_path('empty_dir_recv')}", 74 "the source folder is empty") 75 76 @pytest.mark.L0 77 @pytest.mark.repeat(2) 78 def test_long_path(self): 79 clear_env() 80 assert check_hdc_cmd(f"file send {get_local_path('deep_test_dir')} {get_remote_path('it_send_dir')}", 81 is_single_dir=False) 82 assert check_hdc_cmd(f"file recv {get_remote_path('it_send_dir/deep_test_dir')} " 83 f"{get_local_path('recv_test_dir')}", 84 is_single_dir=False) 85 86 @pytest.mark.L0 87 @pytest.mark.repeat(2) 88 def test_recv_dir(self): 89 if os.path.exists(get_local_path('it_problem_dir')): 90 rmdir(get_local_path('it_problem_dir')) 91 assert check_hdc_cmd(f"shell rm -rf {get_remote_path('it_problem_dir')}") 92 assert check_hdc_cmd(f"shell rm -rf {get_remote_path('problem_dir')}") 93 assert make_multiprocess_file(get_local_path('problem_dir'), get_remote_path(''), 'send', 1, "dir") 94 assert check_hdc_cmd(f"shell mv {get_remote_path('problem_dir')} {get_remote_path('it_problem_dir')}") 95 assert make_multiprocess_file(get_local_path(''), get_remote_path('it_problem_dir'), 'recv', 1, "dir") 96 97 98class TestDirMix: 99 muti_num = 5 # the count of multiprocess file 100 file_table = ['empty', 'medium', 'small'] 101 102 @classmethod 103 def setup_class(self): 104 pass 105 106 @classmethod 107 def teardown_class(self): 108 for test_item in self.file_table: 109 for i in range(0, self.muti_num - 1): 110 rmdir(get_local_path(f"{test_item}_recv_{i}")) 111 112 @pytest.mark.L1 113 @pytest.mark.repeat(2) 114 @pytest.mark.parametrize("test_item", file_table) 115 def test_mix_file(self, test_item): 116 assert make_multiprocess_file(get_local_path(f'{test_item}'), get_remote_path(f'it_{test_item}'), 117 'send', self.muti_num, "file") 118 assert make_multiprocess_file(get_local_path(f'{test_item}_recv'), get_remote_path(f'it_{test_item}'), 119 'recv', self.muti_num, "file") 120 121 122class TestFileNoExist: 123 remote_unexist_path = f"{get_remote_path('it_no_exist/deep_test_dir/')}" 124 125 @pytest.mark.L0 126 @pytest.mark.repeat(2) 127 @pytest.mark.parametrize("test_item", ['empty', 'medium', 'small', 'problem_dir']) 128 def test_no_exist_path(self, test_item): 129 check_hdc_cmd(f"shell rm -rf {get_remote_path('it_no_exist*')}") 130 local_unexist_path = get_local_path(f'{(os.path.join("recv_no_exist", "deep_test_dir", f"recv_{test_item}"))}') 131 if os.path.exists(get_local_path('recv_no_exist')): 132 rmdir(get_local_path('recv_no_exist')) 133 if test_item in ['empty', 'medium', 'small']: 134 assert check_shell(f"file send " 135 f"{get_local_path(f'{test_item}')} {self.remote_unexist_path}/it_{test_item}", 136 "no such file or directory") 137 assert check_shell(f"file recv {self.remote_unexist_path}/it_{test_item} " 138 f"{local_unexist_path}", 139 "no such file or directory") 140 else: 141 assert check_hdc_cmd(f"file send " 142 f"{get_local_path(f'{test_item}')} {self.remote_unexist_path}/it_{test_item}") 143 assert check_hdc_cmd(f"file recv {self.remote_unexist_path}/it_{test_item} " 144 f"{local_unexist_path}") 145 146 @pytest.mark.L0 147 @pytest.mark.repeat(2) 148 @pytest.mark.parametrize("test_item", ['empty', 'medium', 'small']) 149 def test_no_exist_path_with_seperate(self, test_item): 150 check_hdc_cmd(f"shell rm -rf {get_remote_path('it_no_exist*')}") 151 local_unexist_path = get_local_path(f'{(os.path.join("recv_no_exist", "deep_test_dir"))}') 152 if os.path.exists(get_local_path('recv_no_exist')): 153 rmdir(get_local_path('recv_no_exist')) 154 assert check_shell(f"file send " 155 f"{get_local_path(f'{test_item}')} {self.remote_unexist_path}/", 156 "no such file or directory") 157 assert check_shell(f"file recv {self.remote_unexist_path}/{test_item} " 158 f"{local_unexist_path}{os.sep}", 159 "no such file or directory") 160 161 162class TestFileNoExistBundelPath: 163 data_storage_el2_path = "data/storage/el2/base" 164 remote_unexist_path = f"{data_storage_el2_path}/it_no_exist/deep_test_dir/" 165 166 @classmethod 167 def setup_class(self): 168 check_shell(f"shell mkdir -p mnt/debug/100/debug_hap/{GP.debug_app}/{self.data_storage_el2_path}") 169 170 @pytest.mark.L0 171 @pytest.mark.repeat(3) 172 @check_version("Ver: 3.1.0e") 173 @pytest.mark.parametrize("test_item", ['empty', 'medium', 'small', 'problem_dir']) 174 def test_no_exist_bundle_path(self, test_item): 175 check_hdc_cmd(f"shell -b {GP.debug_app} rm -rf {self.data_storage_el2_path}/it_no_exist/") 176 local_unexist_path = get_local_path( 177 f'{(os.path.join("recv_no_exist", "deep_test_dir", f"recv_{test_item}"))}') 178 if os.path.exists(get_local_path('recv_no_exist')): 179 rmdir(get_local_path('recv_no_exist')) 180 181 assert check_hdc_cmd(f"shell -b {GP.debug_app} ls {self.data_storage_el2_path}/it_no_exist", 182 "No such file or directory") 183 assert not os.path.exists(get_local_path('recv_no_exist')) 184 if test_item in ['empty', 'medium', 'small']: 185 assert check_shell(f"file send -b {GP.debug_app} " 186 f"{get_local_path(f'{test_item}')} {self.remote_unexist_path}/it_{test_item}", 187 "no such file or directory") 188 assert check_shell(f"file recv -b {GP.debug_app} {self.remote_unexist_path}/it_{test_item} " 189 f"{local_unexist_path}", 190 "no such file or directory") 191 else: 192 assert check_hdc_cmd(f"file send -b {GP.debug_app} " 193 f"{get_local_path(f'{test_item}')} {self.remote_unexist_path}/it_{test_item}") 194 assert check_hdc_cmd(f"file recv -b {GP.debug_app} {self.remote_unexist_path}/it_{test_item} " 195 f"{local_unexist_path}") 196 197 198class TestFileExtend: 199 @pytest.mark.L0 200 @pytest.mark.repeat(1) 201 def test_node_file(self): 202 assert check_hdc_cmd(f"file recv {get_remote_path('../../../sys/power/state')} {get_local_path('state')}") 203 assert check_hdc_cmd(f"file recv {get_remote_path('../../../sys/firmware/fdt')} {get_local_path('fdt')}") 204 205 @pytest.mark.L0 206 @pytest.mark.repeat(1) 207 def test_running_file(self): 208 assert check_hdc_cmd(f"file recv system/bin/hdcd {get_local_path('running_recv')}") 209 210 @pytest.mark.L2 211 @pytest.mark.repeat(2) 212 def test_soft_link(self): 213 assert check_soft_local(get_local_path('small'), get_local_path('soft_small'), 214 get_remote_path('it_small_soft')) 215 assert check_soft_remote('it_small_soft', get_remote_path('it_soft_small'), 216 get_local_path('recv_soft_small')) 217 218 219class TestFileError: 220 @pytest.mark.L0 221 @pytest.mark.repeat(1) 222 def test_file_error(self): 223 assert check_hdc_cmd("target mount") 224 assert check_shell( 225 f"file recv", 226 "[Fail]There is no local and remote path" 227 ) 228 assert check_shell( 229 f"file send", 230 "[Fail]There is no local and remote path" 231 ) 232 assert check_hdc_cmd(f"shell rm -rf {get_remote_path('../../../large')}") 233 assert check_hdc_cmd(f"shell param set persist.hdc.control.file false") 234 assert check_shell( 235 f"file send {get_local_path('small')} {get_remote_path('it_small_false')}", 236 "debugging is not allowed" 237 ) 238 assert check_hdc_cmd(f"shell param set persist.hdc.control.file true") 239 assert check_hdc_cmd(f"file send {get_local_path('small')} {get_remote_path('it_small_true')}") 240 241 242class TestFileBundleOptionNormal: 243 data_storage_el2_path = "data/storage/el2/base" 244 245 @classmethod 246 def setup_class(self): 247 check_shell(f"shell rm -rf mnt/debug/100/debug_hap/{GP.debug_app}/{self.data_storage_el2_path}/it*") 248 check_shell(f"shell mkdir -p mnt/debug/100/debug_hap/{GP.debug_app}/{self.data_storage_el2_path}") 249 250 @classmethod 251 def teardown_class(self): 252 pass 253 254 @pytest.mark.L0 255 @check_version("Ver: 3.1.0e") 256 @pytest.mark.parametrize("test_item", ['empty', 'medium', 'small', 'problem_dir']) 257 def test_file_option_bundle_normal(self, test_item): 258 if test_item == 'problem_dir' and os.path.exists(get_local_path(f'recv_bundle_{test_item}')): 259 rmdir(get_local_path(f'recv_bundle_{test_item}')) 260 assert check_hdc_cmd(f"file send -b {GP.debug_app} " 261 f"{get_local_path(f'{test_item}')} {self.data_storage_el2_path}/it_{test_item}") 262 assert check_hdc_cmd(f"file recv -b {GP.debug_app} {self.data_storage_el2_path}/it_{test_item} " 263 f"{get_local_path(f'recv_bundle_{test_item}')}") 264 265 266class TestFileBundleOptionError: 267 data_storage_el2_path = "data/storage/el2/base" 268 error_table = [ 269 # 测试空文件夹发送和接收 270 ("Sending and receiving an empty folder", 271 f"file send -b {GP.debug_app} {get_local_path('empty_dir')} {get_remote_path('it_empty_dir')}", 272 "the source folder is empty"), 273 274 # 测试缺少本地和远程路径 275 ("Missing local and remote paths for send", f"file send -b {GP.debug_app}", "There is no local and remote path"), 276 ("Missing local and remote paths for recv", f"file recv -b {GP.debug_app}", "There is no local and remote path"), 277 278 # 测试错误优先级 279 ("Error priority for send", f"file send -b ./{GP.debug_app}", "There is no local and remote path"), 280 ("Error priority for recv", f"file recv -b ./{GP.debug_app}", "There is no local and remote path"), 281 282 # 测试缺少 bundle 参数 283 ("Missing bundle parameter for recv", f"file recv -b", "[E005003]"), 284 ("Missing bundle parameter for send", f"file send -b", "[E005003]"), 285 286 # 测试本地和远程路径错误 287 ("Incorrect local and remote paths for send", 288 f"file send -b ./{GP.debug_app} {get_local_path('small')} {get_remote_path('it_small')}", "[E005101]"), 289 ("Incorrect local and remote paths for recv", 290 f"file recv -b ./{GP.debug_app} {get_local_path('small')} {get_remote_path('it_small')}", "[E005101]"), 291 292 # 测试无效的 bundle 参数 293 ("invalid bundle parameter for send", 294 f"file send -b ./{GP.debug_app} {get_local_path('small')} {get_remote_path('it_small')}", "[E005101]"), 295 ("invalid bundle parameter with valid name for send", 296 f"file send -b com.AAAA {get_local_path('small')} {get_remote_path('it_small')}", "[E005101]"), 297 ("invalid bundle parameter with number for send", 298 f"file send -b 1 {get_local_path('small')} {get_remote_path('it_small')}", "[E005101]"), 299 ("Missing remote path for send", 300 f"file send -b {get_local_path('small')} {get_remote_path('it_small')}", "There is no remote path"), 301 302 # 测试远程路径错误 303 ("Incorrect remote path for recv", 304 f"file recv -b ./{GP.debug_app} {get_remote_path('it_small')} {get_local_path('small_recv')}", "[E005101]"), 305 306 # 测试路径逃逸 307 ("Path escaping for send", 308 f"file send -b {GP.debug_app} {get_local_path('small')} ../../../it_small", "[E005102]"), 309 ("Path escaping for recv", 310 f"file recv -b {GP.debug_app} ../../../it_small {get_local_path('small_recv')}", "[E005102]"), 311 ] 312 outside_error = "[E005102]" 313 outside_table = [ 314 # bundle根目录 逃逸 315 ("Escape to parent directory", "..", True), 316 ("Escape two levels up", "../..", True), 317 ("Escape three levels up", "../../..", True), 318 ("Escape four levels up", "../../../..", True), 319 ("Escape to parent directory with current directory", "../.", True), 320 ("Escape to parent directory from current directory", "./..", True), 321 ("Escape to parent directory with slash", "../", True), 322 (f"Escape to extra app directory", f"../{GP.debug_app}_extra/{data_storage_el2_path}/", True), 323 (f"Escape to non-existent app directory", f"../{GP.debug_app}_notexsit/{data_storage_el2_path}/", True), 324 (f"Escape to non-existent app directory", f"../{GP.debug_app}_notexsit/", True), 325 (f"Escape far beyond root", "./../../../../../../../../../../aa", True), 326 327 # bundle根目录 未逃逸 328 ("Stays in the current directory", ".", False), 329 ("Stays in the current directory with slash", "./", False), 330 ("Stays in the current directory with current directory", "./.", False), 331 ("Stays in the current directory with ellipsis", "...", False), 332 (f"Stays at root and return bundle", 333 f"../../../../../../../../../../../mnt/debug/100/debug_hap/{GP.debug_app}/", False), 334 ] 335 336 @classmethod 337 def setup_class(self): 338 check_shell(f"shell rm -rf mnt/debug/100/debug_hap/{GP.debug_app}/{self.data_storage_el2_path}/it*") 339 check_shell(f"shell mkdir -p mnt/debug/100/debug_hap/{GP.debug_app}/{self.data_storage_el2_path}") 340 check_shell(f"shell rm -rf mnt/debug/100/debug_hap/{GP.debug_app}_extra/{self.data_storage_el2_path}/*") 341 check_shell(f"shell mkdir -p mnt/debug/100/debug_hap/{GP.debug_app}_extra/{self.data_storage_el2_path}/") 342 343 @classmethod 344 def teardown_class(self): 345 pass 346 347 @pytest.mark.L0 348 @check_version("Ver: 3.1.0e") 349 @pytest.mark.parametrize("test_name, command, expected", error_table, 350 ids=[name for name, _, _ in error_table]) 351 def test_file_option_bundle_error_(self, test_name, command, expected): 352 assert check_shell(command, expected) 353 354 @pytest.mark.parametrize("test_name, remote_path, is_outside", outside_table, 355 ids=[name for name, _, _ in outside_table]) 356 @pytest.mark.L0 357 @check_version("Ver: 3.1.0e") 358 def test_file_option_bundle_check_outside(self, test_name, remote_path, is_outside): 359 if is_outside: 360 assert check_shell(f"file send -b {GP.debug_app} {get_local_path('small')} {remote_path}", 361 self.outside_error) 362 else: 363 assert not check_shell(f"file send -b {GP.debug_app} {get_local_path('small')} {remote_path}", 364 self.outside_error)