* Copyright (c) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
#include <gtest/gtest.h>
#include <atomic>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <iostream>
#include <memory>
#include <optional>
#include <thread>
#include <tuple>
#include <unordered_map>
#include <vector>
#include "common.h"
#include "gmock/gmock.h"
#include "log.h"
#include "request_preload.h"
#include "set_permission.h"
#include "utf8_utils.h"
using namespace testing::ext;
using namespace OHOS::Request;
constexpr size_t SLEEP_INTERVAL = 1000;
static std::string TEST_URL_0 = "https://www.gitee.com/tiga-ultraman/downloadTests/releases/download/v1.01/test.txt";
constexpr uint64_t TEST_SIZE_0 = 1042003;
constexpr uint64_t INFO_SIZE_0 = 2;
class PreloadGetInfo : public testing::Test {
public:
void SetUp();
void TearDown();
};
void PreloadGetInfo::SetUp(void)
{
testing::UnitTest *test = testing::UnitTest::GetInstance();
ASSERT_NE(test, nullptr);
const testing::TestInfo *testInfo = test->current_test_info();
ASSERT_NE(testInfo, nullptr);
string testCaseName = string(testInfo->name());
REQUEST_HILOGI("[SetUp] %{public}s start", testCaseName.c_str());
GTEST_LOG_(INFO) << testCaseName.append(" start");
std::vector<std::string> permissions = { "ohos.permission.INTERNET", "ohos.permission.GET_NETWORK_INFO" };
SetPermission::SetAccessTokenPermission(permissions, "preload_test");
}
void PreloadGetInfo::TearDown(void)
{
SetPermission::SetAccesslNoPermission("preload_test");
}
void PreDownloadInfo(std::string url, uint64_t size)
{
Preload::GetInstance()->Remove(url);
EXPECT_FALSE(Preload::GetInstance()->Contains(url));
TestCallback test(size);
auto &[flagS, flagF, flagC, flagP, callback] = test;
Preload::GetInstance()->SetDownloadInfoListSize(INFO_SIZE_0);
auto handle = Preload::GetInstance()->load(url, std::make_unique<PreloadCallback>(callback));
EXPECT_FALSE(handle->IsFinish());
EXPECT_EQ(handle->GetState(), PreloadState::RUNNING);
while (!handle->IsFinish()) {
std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_INTERVAL));
}
return;
}
std::optional<CppDownloadInfo> TestGetInfo(std::string url)
{
return Preload::GetInstance()->GetDownloadInfo(url);
}
* @tc.name: GetInfoTest
* @tc.desc: Test GetInfoTest interface base function - GetDownloadInfo
* @tc.precon: NA
* @tc.step: 1. Remove test URL from preload manager
* 2. Create test callback and load valid URL
* 3. Verify handle is running
* 4. Wait for download completion
* 5. Verify download info retrieved successfully
* @tc.expect: Download info contains valid URL, size, and SUCCESS state
* @tc.type: FUNC
* @tc.require: issueNumber
* @tc.level: Level 1
*/
HWTEST_F(PreloadGetInfo, GetInfoTest, TestSize.Level1)
{
PreDownloadInfo(TEST_URL_0, TEST_SIZE_0);
std::optional<CppDownloadInfo> info = TestGetInfo(TEST_URL_0);
EXPECT_TRUE(info.has_value());
const auto &value = info.value();
EXPECT_TRUE(value.dns_time() >= 0);
EXPECT_TRUE(value.connect_time() >= 0);
EXPECT_TRUE(value.total_time() >= 0);
EXPECT_TRUE(value.tls_time() >= 0);
EXPECT_TRUE(value.first_send_time() >= 0);
EXPECT_TRUE(value.first_recv_time() >= 0);
EXPECT_TRUE(value.redirect_time() >= 0);
EXPECT_TRUE(value.resource_size() >= 0);
EXPECT_FALSE(value.server_addr().empty());
Preload::GetInstance()->Remove(TEST_URL_0);
}
* @tc.name: CppInfoMove
* @tc.desc: Test CppInfoMove interface base function - GetDownloadInfo
* @tc.precon: NA
* @tc.step: 1. Remove test URL from preload manager
* 2. Create test callback and load valid URL
* 3. Verify handle is running
* 4. Wait for download completion
* 5. Verify download info retrieved and moved successfully
* @tc.expect: Download info contains valid URL, size, and SUCCESS state after move
* @tc.type: FUNC
* @tc.require: issueNumber
* @tc.level: Level 1
*/
HWTEST_F(PreloadGetInfo, CppInfoMove, TestSize.Level1)
{
PreDownloadInfo(TEST_URL_0, TEST_SIZE_0);
std::optional<CppDownloadInfo> result = TestGetInfo(TEST_URL_0);
CppDownloadInfo info1(std::move(result.value()));
double dnsTime = info1.dns_time();
std::optional<CppDownloadInfo> result2 = TestGetInfo(TEST_URL_0);
CppDownloadInfo info2(std::move(result2.value()));
info2 = std::move(info1);
EXPECT_EQ(info2.dns_time(), dnsTime);
}
* @tc.name: InfoIsInvalidUtf8
* @tc.desc: Test GetDownloadInfo interface behavior with invalid UTF-8 URL input
* @tc.precon: Preload module should validate UTF-8 encoding before processing URLs
* @tc.step: 1. Verify UTF-8 validity
* 2. Create invalid UTF-8 URL byte sequence
* 3. Verify UTF-8 validation correctly identifies invalid input
* 4. Call GetDownloadInfo with invalid UTF-8 URL
* 5. Check return value for expected behavior
* @tc.expect: 1. UTF-8 validation should return false for invalid input
* 2. GetDownloadInfo should return std::nullopt for invalid UTF-8 URLs
* 3. No crash or undefined behavior should occur
* @tc.type: FUNC
* @tc.require: issueNumber
* @tc.level: Level 1
*/
HWTEST_F(PreloadGetInfo, InfoIsInvalidUtf8, TestSize.Level1)
{
std::string invalidUtf8Url = "Test String Invalid \xFF\xFE";
EXPECT_FALSE(Utf8Utils::RunUtf8Validation(std::vector<uint8_t>(invalidUtf8Url.begin(), invalidUtf8Url.end())));
auto result = Preload::GetInstance()->GetDownloadInfo(invalidUtf8Url);
EXPECT_FALSE(result.has_value());
EXPECT_FALSE(Preload::GetInstance()->Contains(invalidUtf8Url));
}
* @tc.name: InvalidUtf8_1
* @tc.desc: Test RunUtf8Validation interface behavior with invalid/valid UTF-8 input
* @tc.precon: NA
* @tc.step: 1. Verify UTF-8 validity
* @tc.expect: 1. Verify the validity is correct.
* @tc.type: FUNC
* @tc.require: issueNumber
* @tc.level: Level 1
*/
HWTEST_F(PreloadGetInfo, InvalidUtf8_1, TestSize.Level1)
{
std::vector<uint8_t> test_ee_valid = { 0xEE, 0x80, 0x80 };
EXPECT_TRUE(Utf8Utils::RunUtf8Validation(test_ee_valid));
std::vector<uint8_t> test_ef_valid = { 0xEF, 0xBF, 0xBD };
EXPECT_TRUE(Utf8Utils::RunUtf8Validation(test_ef_valid));
std::vector<uint8_t> test_ee_max = { 0xEE, 0xBF, 0xBF };
EXPECT_TRUE(Utf8Utils::RunUtf8Validation(test_ee_max));
std::vector<uint8_t> test_ef_min = { 0xEF, 0x80, 0x80 };
EXPECT_TRUE(Utf8Utils::RunUtf8Validation(test_ef_min));
std::vector<uint8_t> test_f1_valid = { 0xF1, 0x80, 0x80, 0x80 };
EXPECT_TRUE(Utf8Utils::RunUtf8Validation(test_f1_valid));
std::vector<uint8_t> test_f2_valid = { 0xF2, 0x80, 0x80, 0x80 };
EXPECT_TRUE(Utf8Utils::RunUtf8Validation(test_f2_valid));
std::vector<uint8_t> test_f3_valid = { 0xF3, 0x80, 0x80, 0x80 };
EXPECT_TRUE(Utf8Utils::RunUtf8Validation(test_f3_valid));
std::vector<uint8_t> test_f1_max = { 0xF1, 0xBF, 0xBF, 0xBF };
EXPECT_TRUE(Utf8Utils::RunUtf8Validation(test_f1_max));
std::vector<uint8_t> test_f3_max = { 0xF3, 0xBF, 0xBF, 0xBF };
EXPECT_TRUE(Utf8Utils::RunUtf8Validation(test_f3_max));
std::vector<uint8_t> valid2 = { 0xC3, 0x87 };
EXPECT_TRUE(Utf8Utils::RunUtf8Validation(valid2));
std::vector<uint8_t> valid3 = { 0xE0, 0xA4, 0x85 };
EXPECT_TRUE(Utf8Utils::RunUtf8Validation(valid3));
std::vector<uint8_t> valid4 = { 0xF0, 0x90, 0x8C, 0x82 };
EXPECT_TRUE(Utf8Utils::RunUtf8Validation(valid4));
std::vector<uint8_t> valid5 = { 0xF4, 0x80, 0x80, 0x80 };
EXPECT_TRUE(Utf8Utils::RunUtf8Validation(valid5));
std::vector<uint8_t> valid6 = { 0xF4, 0x8F, 0xBF, 0xBF };
EXPECT_TRUE(Utf8Utils::RunUtf8Validation(valid6));
std::vector<uint8_t> mixed = { 'H', 'e', 'l', 'l', 'o', 0xC3, 0xA4, ' ', 0xE2, 0x82, 0xAC, '!' };
EXPECT_TRUE(Utf8Utils::RunUtf8Validation(mixed));
}
* @tc.name: InvalidUtf8_2
* @tc.desc: Test RunUtf8Validation interface behavior with invalid/valid UTF-8 input
* @tc.precon: NA
* @tc.step: 1. Verify UTF-8 validity
* @tc.expect: 1. Verify the validity is correct.
* @tc.type: FUNC
* @tc.require: issueNumber
* @tc.level: Level 1
*/
HWTEST_F(PreloadGetInfo, InvalidUtf8_2, TestSize.Level1)
{
std::vector<uint8_t> invalid = { 0xC2 };
EXPECT_FALSE(Utf8Utils::RunUtf8Validation(invalid));
std::vector<uint8_t> invalid_1 = { 0xE0, 0x9F, 0x80 };
EXPECT_FALSE(Utf8Utils::RunUtf8Validation(invalid_1));
std::vector<uint8_t> invalid_2 = { 0xED, 0xA0, 0x80 };
EXPECT_FALSE(Utf8Utils::RunUtf8Validation(invalid_2));
std::vector<uint8_t> invalid_3 = { 0xF0, 0x8F, 0x80, 0x80 };
EXPECT_FALSE(Utf8Utils::RunUtf8Validation(invalid_3));
std::vector<uint8_t> invalid_4 = { 0xF4, 0x90, 0x80, 0x80 };
EXPECT_FALSE(Utf8Utils::RunUtf8Validation(invalid_4));
std::vector<uint8_t> invalid_5 = { 0xE0, 0xA0 };
EXPECT_FALSE(Utf8Utils::RunUtf8Validation(invalid_5));
std::vector<uint8_t> invalid_6 = { 0x80 };
EXPECT_FALSE(Utf8Utils::RunUtf8Validation(invalid_6));
std::vector<uint8_t> invalid_7 = { 0xFF };
EXPECT_FALSE(Utf8Utils::RunUtf8Validation(invalid_7));
std::vector<uint8_t> invalid_8 = { 0xC0 };
EXPECT_FALSE(Utf8Utils::RunUtf8Validation(invalid_8));
std::vector<uint8_t> invalid_9 = { 0xF0, 0x90, 0x80 };
EXPECT_FALSE(Utf8Utils::RunUtf8Validation(invalid_9));
std::vector<uint8_t> v2_invalid = { 0xC2, 0x7F };
EXPECT_FALSE(Utf8Utils::RunUtf8Validation(v2_invalid));
std::vector<uint8_t> v3_invalid = { 0xE2, 0x82, 0x7F };
EXPECT_FALSE(Utf8Utils::RunUtf8Validation(v3_invalid));
std::vector<uint8_t> v3_invalid2 = { 0xE0, 0xA0, 0x7F };
EXPECT_FALSE(Utf8Utils::RunUtf8Validation(v3_invalid2));
std::vector<uint8_t> v4_invalid = { 0xF0, 0x9F, 0x98, 0x7F };
EXPECT_FALSE(Utf8Utils::RunUtf8Validation(v4_invalid));
std::vector<uint8_t> v4_invalid2 = { 0xF0, 0x90, 0x7F, 0x80 };
EXPECT_FALSE(Utf8Utils::RunUtf8Validation(v4_invalid2));
std::vector<uint8_t> v4_invalid3 = { 0xF4, 0x7F, 0x80, 0x80 };
EXPECT_FALSE(Utf8Utils::RunUtf8Validation(v4_invalid3));
std::vector<uint8_t> v4_invalid4 = { 0xF4, 0x90, 0x80, 0x80 };
EXPECT_FALSE(Utf8Utils::RunUtf8Validation(v4_invalid4));
}
* @tc.name: ServerAddrInfo
* @tc.desc: Test GetDownloadInfo interface behavior for server address retrieval
* @tc.precon: NA
* @tc.step: 1. Remove test URL from preload manager
* 2. Create test callback and load valid URL
* 3. Verify handle is running
* 4. Wait for download completion
* 5. Call GetDownloadInfo and verify server address info
* @tc.expect: Download info contains non-empty server address list
* @tc.type: FUNC
* @tc.require: issueNumber
* @tc.level: Level 1
*/
HWTEST_F(PreloadGetInfo, ServerAddrInfo, TestSize.Level1)
{
PreDownloadInfo(TEST_URL_0, TEST_SIZE_0);
std::optional<CppDownloadInfo> result = TestGetInfo(TEST_URL_0);
EXPECT_TRUE(result.has_value());
EXPECT_TRUE(result.value().server_addr().size() >= 0);
}
* @tc.name: HttpFailCallbackInfo
* @tc.desc: Test get the download information in the download failure callback.
* @tc.precon: NA
* @tc.step: 1. Remove test URL from preload manager
* 2. Create test callback and load invalid path URL
* 3. Verify handle is running
* 4. Wait for download completion
* 5. Verify download info contains non-empty server address list in failure callback
* @tc.expect: Download info contains non-empty server address list
* @tc.type: FUNC
* @tc.require: issueNumber
* @tc.level: Level 1
*/
HWTEST_F(PreloadGetInfo, HttpFailCallbackInfo, TestSize.Level1)
{
std::string invalidUtf8Url = "https://www.gitee.com/tiga-ultraman/downloadTests/releases/download/v1.01/"
"notExistResource.txt";
Preload::GetInstance()->Remove(invalidUtf8Url);
EXPECT_FALSE(Preload::GetInstance()->Contains(invalidUtf8Url));
TestCallback test(0);
auto &[flagS, flagFDeprecated, flagC, flagP, callback] = test;
auto flagF = std::make_shared<std::atomic_bool>(false);
callback.OnFail = [flagF](const PreloadError &error, const std::string &taskId) {
flagF->store(true);
auto info = error.GetDownloadInfo();
EXPECT_FALSE(info->server_addr().empty());
};
auto handle = Preload::GetInstance()->load(invalidUtf8Url, std::make_unique<PreloadCallback>(callback));
EXPECT_FALSE(handle->IsFinish());
EXPECT_EQ(handle->GetState(), PreloadState::RUNNING);
size_t counter = 10;
while ((!handle->IsFinish() || !(flagC->load() || flagF->load() || flagS->load())) && counter-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_INTERVAL));
}
EXPECT_TRUE(flagF->load());
EXPECT_FALSE(flagC->load());
EXPECT_FALSE(flagS->load());
}
* @tc.name: DnsFailCallbackInfo
* @tc.desc: Test get the download information in the download failure callback.
* @tc.precon: NA
* @tc.step: 1. Remove test URL from preload manager
* 2. Create test callback and load invalid authority URL
* 3. Verify handle is running
* 4. Wait for download completion
* 5. Verify download info contains non-empty server address list in failure callback
* @tc.expect: Download info contains non-empty server address list
* @tc.type: FUNC
* @tc.require: issueNumber
* @tc.level: Level 1
*/
HWTEST_F(PreloadGetInfo, DnsFailCallbackInfo, TestSize.Level1)
{
std::string invalidUtf8Url = "https://PreloadGetInfo.DnsFailCallbackInfo.InvalidAuthority/releases/download/v1.01/"
"notExistResource.txt";
Preload::GetInstance()->Remove(invalidUtf8Url);
EXPECT_FALSE(Preload::GetInstance()->Contains(invalidUtf8Url));
TestCallback test(0);
auto &[flagS, flagFDeprecated, flagC, flagP, callback] = test;
auto flagF = std::make_shared<std::atomic_bool>(false);
callback.OnFail = [flagF](const PreloadError &error, const std::string &taskId) {
flagF->store(true);
auto info = error.GetDownloadInfo();
EXPECT_TRUE(info->server_addr().empty());
};
auto handle = Preload::GetInstance()->load(invalidUtf8Url, std::make_unique<PreloadCallback>(callback));
EXPECT_FALSE(handle->IsFinish());
EXPECT_EQ(handle->GetState(), PreloadState::RUNNING);
size_t counter = 90;
size_t oneSecond = 1000;
while ((!handle->IsFinish() || !(flagC->load() || flagF->load() || flagS->load())) && counter-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(oneSecond));
}
EXPECT_TRUE(flagF->load());
EXPECT_FALSE(flagC->load());
EXPECT_FALSE(flagS->load());
}