add tdd for image file cache

Signed-off-by: zhujingru <zhujingru2@huawei.com>
This commit is contained in:
zhujingru 2024-03-29 12:53:19 +08:00 committed by liuyahui
parent a0304e3ee0
commit 2004351986
18 changed files with 539 additions and 67 deletions

View File

@ -49,7 +49,6 @@ RefPtr<ImageSource> ImageSource::Create(const uint8_t* data, uint32_t size)
RefPtr<ImageSource> ImageSource::Create(const std::string& filePath)
{
Media::SourceOptions opts;
opts.formatHint = "image/svg+xml";
uint32_t errorCode = 0;
auto src = Media::ImageSource::CreateImageSource(filePath, opts, errorCode);
if (errorCode != Media::SUCCESS) {
@ -138,4 +137,15 @@ uint32_t ImageSourceOhos::GetFrameCount()
}
return frameCount;
}
std::string ImageSourceOhos::GetEncodedFormat()
{
uint32_t errorCode;
auto sourceInfo = imageSource_->GetSourceInfo(errorCode);
if (errorCode != Media::SUCCESS) {
TAG_LOGW(AceLogTag::ACE_IMAGE, "Get image source info failed, errorCode = %{public}u", errorCode);
return "";
}
return sourceInfo.encodedFormat;
}
} // namespace OHOS::Ace

View File

@ -34,6 +34,7 @@ public:
RefPtr<PixelMap> CreatePixelMap() override;
Size GetImageSize() override;
uint32_t GetFrameCount() override;
std::string GetEncodedFormat() override;
private:
std::unique_ptr<Media::ImageSource> imageSource_;

View File

@ -62,6 +62,7 @@ std::shared_mutex mutex_;
constexpr char DISABLE_ROSEN_FILE_PATH[] = "/etc/disablerosen";
constexpr char DISABLE_WINDOW_ANIMATION_PATH[] = "/etc/disable_window_size_animation";
#endif
constexpr int32_t CONVERT_ASTC_THRESHOLD = 2;
using RsOrientation = Rosen::DisplayOrientation;
@ -257,6 +258,11 @@ bool GetImageFileCacheConvertToAstcEnabled()
return system::GetParameter("persist.image.filecache.astc.enable", "false") == "true";
}
int32_t GetImageFileCacheConvertAstcThresholdProp()
{
return system::GetIntParameter<int>("persist.image.filecache.astc.threshold", CONVERT_ASTC_THRESHOLD);
}
bool IsUseMemoryMonitor()
{
return (system::GetParameter("persist.ace.memorymonitor.enabled", "0") == "1");
@ -336,6 +342,7 @@ bool SystemProperties::astcEnabled_ = GetAstcEnabled();
int32_t SystemProperties::astcMax_ = GetAstcMaxErrorProp();
int32_t SystemProperties::astcPsnr_ = GetAstcPsnrProp();
bool SystemProperties::imageFileCacheConvertAstc_ = GetImageFileCacheConvertToAstcEnabled();
int32_t SystemProperties::imageFileCacheConvertAstcThreshold_ = GetImageFileCacheConvertAstcThresholdProp();
ACE_WEAK_SYM bool SystemProperties::extSurfaceEnabled_ = IsExtSurfaceEnabled();
ACE_WEAK_SYM uint32_t SystemProperties::dumpFrameCount_ = GetSysDumpFrameCount();
bool SystemProperties::enableScrollableItemPool_ = IsEnableScrollableItemPool();

View File

@ -71,4 +71,9 @@ uint32_t ImageSourcePreview::GetFrameCount()
{
return 0;
}
std::string ImageSourcePreview::GetEncodedFormat()
{
return "";
}
} // namespace OHOS::Ace

View File

@ -29,6 +29,7 @@ public:
RefPtr<PixelMap> CreatePixelMap() override;
Size GetImageSize() override;
uint32_t GetFrameCount() override;
std::string GetEncodedFormat() override;
};
} // namespace OHOS::Ace
#endif // FOUNDATION_ACE_ADAPTER_PREVIEW_OSAL_IMAGE_SOURCE_PREVIEW_H

View File

@ -79,6 +79,7 @@ bool SystemProperties::astcEnabled_ = false;
int SystemProperties::astcMax_ = 0;
int SystemProperties::astcPsnr_ = 0;
bool SystemProperties::imageFileCacheConvertAstc_ = false;
int32_t SystemProperties::imageFileCacheConvertAstcThreshold_ = 2;
bool SystemProperties::extSurfaceEnabled_ = false;
uint32_t SystemProperties::dumpFrameCount_ = 0;
bool SystemProperties::resourceDecoupling_ = true;

View File

@ -40,6 +40,7 @@ public:
virtual RefPtr<PixelMap> CreatePixelMap() = 0;
virtual Size GetImageSize() = 0;
virtual uint32_t GetFrameCount() = 0;
virtual std::string GetEncodedFormat() = 0;
};
} // namespace OHOS::Ace

View File

@ -416,6 +416,11 @@ public:
return imageFileCacheConvertAstc_;
}
static int32_t GetImageFileCacheConvertAstcThreshold()
{
return imageFileCacheConvertAstcThreshold_;
}
static void SetExtSurfaceEnabled(bool extSurfaceEnabled)
{
extSurfaceEnabled_ = extSurfaceEnabled;
@ -532,6 +537,7 @@ private:
static int32_t astcMax_;
static int32_t astcPsnr_;
static bool imageFileCacheConvertAstc_;
static int32_t imageFileCacheConvertAstcThreshold_;
static bool extSurfaceEnabled_;
static uint32_t dumpFrameCount_;
static bool resourceDecoupling_;

View File

@ -23,6 +23,7 @@
#include "base/log/ace_trace.h"
#include "base/log/dump_log.h"
#include "base/log/log_wrapper.h"
#include "base/thread/background_task_executor.h"
#include "base/utils/system_properties.h"
#include "core/image/image_loader.h"
#include "core/image/image_source_info.h"
@ -40,11 +41,17 @@ const std::string ASTC_SUFFIX = ".astc";
const std::string CONVERT_ASTC_FORMAT = "image/astc/4*4";
const std::string SLASH = "/";
const std::string BACKSLASH = "\\";
static const uint32_t ASTC_MAGIC_ID = 0x5CA1AB13;
const mode_t CHOWN_RW_UG = 0660;
const std::string SVG_FORMAT = "image/svg+xml";
bool EndsWith(const std::string& str, const std::string& substr)
{
return str.rfind(substr) == (str.length() - substr.length());
}
bool IsAstcFile(const char fileName[])
{
auto fileNameStr = std::string(fileName);
return (fileNameStr.length() >= ASTC_SUFFIX.length()) && EndsWith(fileNameStr, ASTC_SUFFIX);
}
}
void ImageFileCache::SetImageCacheFilePath(const std::string& cacheFilePath)
@ -83,6 +90,31 @@ std::string ImageFileCache::ConstructCacheFilePath(const std::string& fileName)
#endif
}
bool ImageFileCache::WriteFile(const std::string& url, const void* const data, size_t size,
const std::string& fileCacheKey, const std::string& suffix)
{
std::string writeFilePath = ConstructCacheFilePath(fileCacheKey + suffix);
#ifdef WINDOWS_PLATFORM
std::ofstream outFile(writeFilePath, std::ios::binary);
#else
std::ofstream outFile(writeFilePath, std::fstream::out);
#endif
if (!outFile.is_open()) {
TAG_LOGW(AceLogTag::ACE_IMAGE, "open cache file failed, cannot write.");
return false;
}
outFile.write(reinterpret_cast<const char*>(data), size);
TAG_LOGI(
AceLogTag::ACE_IMAGE, "write image cache: %{public}s %{private}s", url.c_str(), writeFilePath.c_str());
#ifndef WINDOWS_PLATFORM
if (chmod(writeFilePath.c_str(), CHOWN_RW_UG) != 0) {
TAG_LOGW(AceLogTag::ACE_IMAGE, "write image cache chmod failed: %{public}s %{private}s",
url.c_str(), writeFilePath.c_str());
}
#endif
return true;
}
std::string ImageFileCache::GetImageCacheKey(const std::string& fileName)
{
size_t suffixStartAt = fileName.find_last_of(".");
@ -126,22 +158,38 @@ void ImageFileCache::SaveCacheInner(const std::string& cacheKey, const std::stri
std::vector<std::string>& removeVector)
{
auto cacheFileName = cacheKey + suffix;
cacheFileInfo_.emplace_front(cacheFileName, cacheSize, time(nullptr));
fileNameToFileInfoPos_[cacheKey] = cacheFileInfo_.begin();
cacheFileSize_ += cacheSize;
auto iter = fileNameToFileInfoPos_.find(cacheKey);
auto cacheTime = time(nullptr);
auto convertAstcThreshold = SystemProperties::GetImageFileCacheConvertAstcThreshold();
if (iter != fileNameToFileInfoPos_.end()) {
auto infoIter = iter->second;
cacheFileInfo_.splice(cacheFileInfo_.begin(), cacheFileInfo_, infoIter);
cacheFileSize_ = cacheFileSize_ + cacheSize - infoIter->fileSize;
removeVector.push_back(ConstructCacheFilePath(infoIter->fileName));
infoIter->fileName = cacheFileName;
infoIter->fileSize = cacheSize;
infoIter->accessTime = cacheTime;
infoIter->accessCount = suffix == ASTC_SUFFIX ? convertAstcThreshold : 1;
} else {
cacheFileInfo_.emplace_front(cacheFileName, cacheSize, cacheTime,
suffix == ASTC_SUFFIX ? convertAstcThreshold : 1);
fileNameToFileInfoPos_[cacheKey] = cacheFileInfo_.begin();
cacheFileSize_ += cacheSize;
}
// check if cache files too big.
if (cacheFileSize_ > static_cast<int32_t>(fileLimit_)) {
auto removeSizeTarget = static_cast<int32_t>(fileLimit_ * clearCacheFileRatio_);
int32_t removeSize = 0;
if (cacheFileSize_ > fileLimit_) {
auto removeSizeTarget = fileLimit_ * clearCacheFileRatio_;
size_t removeSize = 0;
auto iter = cacheFileInfo_.rbegin();
while (removeSize < removeSizeTarget && iter != cacheFileInfo_.rend()) {
removeSize += static_cast<int32_t>(iter->fileSize);
removeSize += iter->fileSize;
removeVector.push_back(ConstructCacheFilePath(iter->fileName));
fileNameToFileInfoPos_.erase(GetImageCacheKey(iter->fileName));
iter++;
}
cacheFileInfo_.erase(iter.base(), cacheFileInfo_.end());
cacheFileSize_ -= static_cast<int32_t>(removeSize);
cacheFileSize_ -= removeSize;
}
}
@ -189,52 +237,38 @@ void ImageFileCache::WriteCacheFile(
}
}
bool convertToAstc = false;
size_t astcSize = 0;
unsigned int magicVal = static_cast<const uint8_t*>(data)[0] + (static_cast<const uint8_t*>(data)[1] << 8) +
(static_cast<const uint8_t*>(data)[2] << 16) + (static_cast<const uint8_t*>(data)[3] << 24);
if (SystemProperties::IsImageFileCacheConvertAstcEnabled() && suffix == "" && magicVal != ASTC_MAGIC_ID) {
if (ConvertToAstcAndWriteToFile(data, size, fileCacheKey, astcSize, url)) {
convertToAstc = true;
}
#ifndef ACE_UNITTEST
// 2. if not in dist, write file into disk.
if (!WriteFile(url, data, size, fileCacheKey, suffix)) {
return;
}
if (!convertToAstc) {
// 2. if not in dist, write file into disk.
std::string writeFilePath = ConstructCacheFilePath(fileCacheKey + suffix);
#ifdef WINDOWS_PLATFORM
std::ofstream outFile(writeFilePath, std::ios::binary);
#else
std::ofstream outFile(writeFilePath, std::fstream::out);
#endif
if (!outFile.is_open()) {
TAG_LOGW(AceLogTag::ACE_IMAGE, "open cache file failed, cannot write.");
return;
}
outFile.write(reinterpret_cast<const char*>(data), size);
TAG_LOGI(
AceLogTag::ACE_IMAGE, "write image cache: %{public}s %{private}s", url.c_str(), writeFilePath.c_str());
}
std::vector<std::string> removeVector;
{
std::scoped_lock<std::mutex> lock(cacheFileInfoMutex_);
SaveCacheInner(fileCacheKey, convertToAstc ? ASTC_SUFFIX : suffix, convertToAstc ? astcSize : size,
removeVector);
SaveCacheInner(fileCacheKey, suffix, size, removeVector);
}
// 3. clear files removed from cache list.
ClearCacheFile(removeVector);
}
bool ImageFileCache::ConvertToAstcAndWriteToFile(const void* const data, size_t size, const std::string& fileCacheKey,
size_t& astcSize, const std::string& url)
void ImageFileCache::ConvertToAstcAndWriteToFile(const std::string& fileCacheKey, const std::string& filePath,
const std::string& url)
{
ACE_FUNCTION_TRACE();
RefPtr<ImageSource> imageSource = ImageSource::Create(static_cast<const uint8_t*>(data), size);
RefPtr<ImageSource> imageSource = ImageSource::Create(filePath);
if (!imageSource || imageSource->GetFrameCount() != 1) {
TAG_LOGI(AceLogTag::ACE_IMAGE, "Image frame count is not 1, will not convert to astc. %{public}s",
fileCacheKey.c_str());
return false;
return;
}
if (imageSource->GetEncodedFormat() == SVG_FORMAT) {
TAG_LOGI(AceLogTag::ACE_IMAGE, "Image is svg, will not convert to astc. %{public}s",
fileCacheKey.c_str());
return;
}
RefPtr<ImagePacker> imagePacker = ImagePacker::Create();
PackOption option;
option.format = CONVERT_ASTC_FORMAT;
@ -242,31 +276,50 @@ bool ImageFileCache::ConvertToAstcAndWriteToFile(const void* const data, size_t
if (pixelMap == nullptr) {
TAG_LOGW(AceLogTag::ACE_IMAGE, "Get pixel map failed, will not convert to astc. %{public}s",
fileCacheKey.c_str());
return false;
return;
}
auto astcFilePath = ConstructCacheFilePath(fileCacheKey + ASTC_SUFFIX);
auto astcFileName = fileCacheKey + ASTC_SUFFIX;
auto astcFilePath = ConstructCacheFilePath(astcFileName);
imagePacker->StartPacking(astcFilePath, option);
imagePacker->AddImage(*pixelMap);
int64_t packedSize = 0;
if (imagePacker->FinalizePacking(packedSize)) {
TAG_LOGW(AceLogTag::ACE_IMAGE, "convert to astc failed. %{public}s", fileCacheKey.c_str());
return false;
return;
}
#if !defined(WINDOWS_PLATFORM) && !defined(ACE_UNITTEST)
if (chmod(astcFilePath.c_str(), CHOWN_RW_UG) != 0) {
TAG_LOGW(AceLogTag::ACE_IMAGE, "convert to astc chmod failed: %{public}s %{private}s",
url.c_str(), astcFilePath.c_str());
}
#endif
astcSize = packedSize;
std::vector<std::string> removeVector;
{
std::scoped_lock<std::mutex> lock(cacheFileInfoMutex_);
removeVector.push_back(filePath);
auto infoIter = fileNameToFileInfoPos_[fileCacheKey];
cacheFileSize_ = cacheFileSize_ + packedSize - infoIter->fileSize;
infoIter->fileName = astcFileName;
infoIter->fileSize = packedSize;
}
// remove the old file before convert
ClearCacheFile(removeVector);
TAG_LOGI(AceLogTag::ACE_IMAGE, "write image astc cache: %{public}s %{private}s", url.c_str(), astcFilePath.c_str());
return true;
}
void ImageFileCache::ClearCacheFile(const std::vector<std::string>& removeFiles)
{
#ifndef ACE_UNITTEST
for (auto&& iter : removeFiles) {
if (remove(iter.c_str()) != 0) {
TAG_LOGW(AceLogTag::ACE_IMAGE, "remove file %{private}s failed.", iter.c_str());
continue;
}
}
#endif
}
std::string ImageFileCache::GetCacheFilePath(const std::string& url)
@ -279,14 +332,22 @@ std::string ImageFileCache::GetCacheFilePathInner(const std::string& url, const
{
auto fileCacheKey = std::to_string(std::hash<std::string> {}(url));
auto iter = fileNameToFileInfoPos_.find(fileCacheKey);
if (iter != fileNameToFileInfoPos_.end()) {
// either suffix not specified, or fileName ends with the suffix
if (iter != fileNameToFileInfoPos_.end() && (suffix == "" || EndsWith(iter->second->fileName, suffix))) {
auto infoIter = iter->second;
// either suffix not specified, or fileName ends with the suffix
if (suffix == "" || EndsWith(infoIter->fileName, suffix)) {
cacheFileInfo_.splice(cacheFileInfo_.begin(), cacheFileInfo_, infoIter);
infoIter->accessTime = time(nullptr);
return ConstructCacheFilePath(infoIter->fileName);
cacheFileInfo_.splice(cacheFileInfo_.begin(), cacheFileInfo_, infoIter);
infoIter->accessTime = time(nullptr);
infoIter->accessCount++;
auto filePath = ConstructCacheFilePath(infoIter->fileName);
if (SystemProperties::IsImageFileCacheConvertAstcEnabled() &&
infoIter->accessCount == SystemProperties::GetImageFileCacheConvertAstcThreshold()) {
BackgroundTaskExecutor::GetInstance().PostTask(
[this, fileCacheKey, filePath, url] () {
ConvertToAstcAndWriteToFile(fileCacheKey, filePath, url);
},
BgTaskPriority::LOW);
}
return filePath;
}
return "";
}
@ -304,7 +365,7 @@ void ImageFileCache::SetCacheFileInfo()
TAG_LOGW(AceLogTag::ACE_IMAGE, "cache file path wrong! maybe it is not set.");
return;
}
int64_t cacheFileSize = 0;
size_t cacheFileSize = 0;
dirent* filePtr = readdir(dir.get());
while (filePtr != nullptr) {
// skip . or ..
@ -315,10 +376,11 @@ void ImageFileCache::SetCacheFileInfo()
filePtr = readdir(dir.get());
continue;
}
cacheFileInfo_.emplace_front(filePtr->d_name, fileStatus.st_size, fileStatus.st_atime);
cacheFileInfo_.emplace_front(filePtr->d_name, fileStatus.st_size, fileStatus.st_atime,
IsAstcFile(filePtr->d_name) ? SystemProperties::GetImageFileCacheConvertAstcThreshold() : 1);
std::string fileCacheKey = GetImageCacheKey(std::string(filePtr->d_name));
fileNameToFileInfoPos_[fileCacheKey] = cacheFileInfo_.begin();
cacheFileSize += static_cast<int64_t>(fileStatus.st_size);
cacheFileSize += fileStatus.st_size;
}
filePtr = readdir(dir.get());
}
@ -344,7 +406,8 @@ void ImageFileCache::DumpCacheInfo()
auto fileSize = item.fileSize;
totalCount += fileSize;
DumpLog::GetInstance().Print(
"fileCache Obj of filePath: " + filePath + ", fileSize" + std::to_string(fileSize) + "(B)");
"fileCache Obj of filePath: " + filePath + ", fileSize: " + std::to_string(fileSize) + "(B)" +
", accessCount: " + std::to_string(item.accessCount));
}
DumpLog::GetInstance().Print("FileCache total size: " + std::to_string(totalCount) + "(B)");
}

View File

@ -27,8 +27,8 @@
namespace OHOS::Ace {
struct FileInfo {
FileInfo(std::string name, size_t size, time_t time)
: fileName(std::move(name)), fileSize(size), accessTime(time) {}
FileInfo(std::string name, size_t size, time_t time, size_t cnt)
: fileName(std::move(name)), fileSize(size), accessTime(time), accessCount(cnt) {}
// file information will be sort by access time.
bool operator<(const FileInfo& otherFile) const
@ -38,6 +38,7 @@ struct FileInfo {
std::string fileName;
size_t fileSize;
time_t accessTime;
size_t accessCount;
};
class ImageFileCache : public Singleton<ImageFileCache> {
@ -67,8 +68,10 @@ private:
void SaveCacheInner(const std::string& cacheKey, const std::string& suffix, size_t cacheSize,
std::vector<std::string>& removeVector);
std::string GetCacheFilePathInner(const std::string& url, const std::string& suffix);
bool ConvertToAstcAndWriteToFile(const void* const data, size_t size, const std::string& fileCacheKey,
size_t& astcSize, const std::string& url);
void ConvertToAstcAndWriteToFile(const std::string& fileCacheKey, const std::string& filePath,
const std::string& url);
bool WriteFile(const std::string& url, const void* const data, size_t size,
const std::string& fileCacheKey, const std::string& suffix);
std::shared_mutex cacheFilePathMutex_;
std::string cacheFilePath_;
@ -78,7 +81,7 @@ private:
std::atomic<float> clearCacheFileRatio_ = 0.5f; // default clear ratio is 0.5
std::mutex cacheFileSizeMutex_;
int64_t cacheFileSize_ = 0;
size_t cacheFileSize_ = 0;
std::mutex cacheFileInfoMutex_;
// lru

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2024 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 "test/mock/base/mock_image_packer.h"
namespace OHOS::Ace {
RefPtr<MockImagePacker> MockImagePacker::mockImagePacker_ = nullptr;
RefPtr<ImagePacker> ImagePacker::Create()
{
return MockImagePacker::mockImagePacker_;
}
} // namespace OHOS::Ace

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2024 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.
*/
#ifndef FOUNDATION_ACE_TEST_MOCK_BASE_MOCK_IMAGE_PACKER_H
#define FOUNDATION_ACE_TEST_MOCK_BASE_MOCK_IMAGE_PACKER_H
#include "gmock/gmock.h"
#include "base/image/image_packer.h"
#include "base/image/pixel_map.h"
namespace OHOS::Ace {
class MockImagePacker : public ImagePacker {
DECLARE_ACE_TYPE(MockImagePacker, ImagePacker)
public:
MOCK_METHOD3(StartPacking, uint32_t(uint8_t*, uint32_t, const PackOption&));
MOCK_METHOD2(StartPacking, uint32_t(const std::string&, const PackOption&));
MOCK_METHOD1(AddImage, uint32_t(PixelMap&));
MOCK_METHOD1(FinalizePacking, uint32_t(int64_t&));
static RefPtr<MockImagePacker> mockImagePacker_;
};
} // namespace OHOS::Ace
#endif // FOUNDATION_ACE_TEST_MOCK_BASE_MOCK_IMAGE_PACKER_H

View File

@ -14,19 +14,21 @@
*/
#include "test/mock/base/mock_image_source.h"
namespace OHOS::Ace {
RefPtr<MockImageSource> MockImageSource::mockImageSource_ = nullptr;
RefPtr<ImageSource> ImageSource::Create(int32_t fd)
{
return nullptr;
return MockImageSource::mockImageSource_;
}
RefPtr<ImageSource> ImageSource::Create(const uint8_t* data, uint32_t size)
{
return nullptr;
return MockImageSource::mockImageSource_;
}
RefPtr<ImageSource> ImageSource::Create(const std::string& filePath)
{
return nullptr;
return MockImageSource::mockImageSource_;
}
bool ImageSource::IsAstc(const uint8_t* data, size_t size)
@ -34,7 +36,7 @@ bool ImageSource::IsAstc(const uint8_t* data, size_t size)
return true;
}
Size ImageSource::GetASTCInfo(const uint8_t* data, size_t size)
ImageSource::Size ImageSource::GetASTCInfo(const uint8_t* data, size_t size)
{
return {0, 0};
}

View File

@ -19,17 +19,20 @@
#include "base/image/image_source.h"
#include "base/image/pixel_map.h"
namespace OHOS::Ace {
using Size = std::pair<int32_t, int32_t>;
class MockImageSource : public ImageSource {
DECLARE_ACE_TYPE(MockImageSource, ImageSource)
public:
MOCK_METHOD1(GetProperty, std::string(const std::string& key));
MOCK_METHOD1(CreatePixelMap, RefPtr<PixelMap>(const Size& size));
MOCK_METHOD2(CreatePixelMap, RefPtr<PixelMap>(uint32_t index, const Size& size));
MOCK_METHOD1(CreatePixelMap, RefPtr<PixelMap>(const ImageSource::Size& size));
MOCK_METHOD2(CreatePixelMap, RefPtr<PixelMap>(uint32_t index, const ImageSource::Size& size));
MOCK_METHOD0(CreatePixelMap, RefPtr<PixelMap>());
MOCK_METHOD0(GetImageSize, Size());
MOCK_METHOD0(GetImageSize, ImageSource::Size());
MOCK_METHOD0(GetFrameCount, uint32_t());
MOCK_METHOD0(GetEncodedFormat, std::string());
static RefPtr<MockImageSource> mockImageSource_;
};
} // namespace OHOS::Ace

View File

@ -62,6 +62,8 @@ bool SystemProperties::acePerformanceMonitorEnable_ = false;
bool SystemProperties::debugBoundaryEnabled_ = false;
bool SystemProperties::developerModeOn_ = false;
bool SystemProperties::faultInjectEnabled_ = false;
bool SystemProperties::imageFileCacheConvertAstc_ = true;
int32_t SystemProperties::imageFileCacheConvertAstcThreshold_ = 3;
bool g_irregularGrid = true;
bool g_segmentedWaterflow = true;

View File

@ -20,6 +20,7 @@ group("core_unittest") {
"common:core_common_unittest",
"event:core_event_unittest",
"gestures:gestures_test_ng",
"image_file_cache:image_file_cache_test_ng",
"image_provider:image_provider_test_ng",
"layout:core_layout_unittest",
"manager:core_manager_unittest",

View File

@ -0,0 +1,39 @@
# Copyright (c) 2024 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.
import("//foundation/arkui/ace_engine/test/unittest/ace_unittest.gni")
ohos_unittest("image_file_cache_test_ng") {
module_out_path = image_test_output_path
sources = [
"$ace_root/frameworks/core/image/image_file_cache.cpp",
"$ace_root/test/mock/base/mock_frame_trace_adapter.cpp",
"$ace_root/test/mock/base/mock_image_packer.cpp",
"$ace_root/test/mock/base/mock_image_source.cpp",
"$ace_root/test/mock/base/mock_system_properties.cpp",
"$ace_root/test/mock/core/image_provider/mock_skia_image_data.cpp",
"image_file_cache_test_ng.cpp",
]
deps = [
"$ace_root/test/unittest:ace_base",
"$ace_root/test/unittest:ace_core_extra",
"$ace_root/test/unittest:ace_engine_unittest_flutter_deps",
"$ace_root/test/unittest:ace_unittest_log",
"$ace_root/test/unittest:ace_unittest_trace",
"//third_party/googletest:gmock_main",
]
configs = [ "$ace_root/test/unittest:ace_unittest_config" ]
}

View File

@ -0,0 +1,269 @@
/*
* Copyright (c) 2024 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 <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#define protected public
#define private public
#include "core/image/image_file_cache.h"
#include "base/utils/system_properties.h"
#include "test/mock/base/mock_image_packer.h"
#include "test/mock/base/mock_image_source.h"
#include "test/mock/base/mock_pixel_map.h"
using namespace testing;
using namespace testing::ext;
namespace OHOS::Ace::NG {
const std::string ASTC_SUFFIX = ".astc";
const std::string CACHE_FILE_PATH = "/data/test/resource/imagecache/images";
const std::string SVG_FORMAT = "image/svg+xml";
class ImageFileCacheTestNg : public testing::Test {
public:
static void SetUpTestSuite();
static void TearDownTestSuite();
static void WaitForAsyncTasks();
void SetUp() override;
void TearDown() override;
};
void ImageFileCacheTestNg::SetUpTestSuite()
{
ImageFileCache::GetInstance().SetImageCacheFilePath(CACHE_FILE_PATH);
}
void ImageFileCacheTestNg::SetUp()
{
SystemProperties::imageFileCacheConvertAstc_ = true;
ImageFileCache::GetInstance().fileNameToFileInfoPos_.clear();
ImageFileCache::GetInstance().cacheFileInfo_.clear();
ImageFileCache::GetInstance().cacheFileSize_ = 0;
}
void ImageFileCacheTestNg::TearDown()
{
}
void ImageFileCacheTestNg::TearDownTestSuite()
{
MockImagePacker::mockImagePacker_ = nullptr;
MockImageSource::mockImageSource_ = nullptr;
}
// wait for load task to finish
void ImageFileCacheTestNg::WaitForAsyncTasks()
{
}
/**
* @tc.name: WriteCacheFileFunc001
* @tc.desc: Test WriteCacheFileFun with convert astc enabled.
* @tc.type: FUNC
*/
HWTEST_F(ImageFileCacheTestNg, WriteCacheFileFunc001, TestSize.Level1)
{
RefPtr<MockImagePacker> mockImagePacker = AceType::MakeRefPtr<MockImagePacker>();
RefPtr<MockImageSource> mockImageSource = AceType::MakeRefPtr<MockImageSource>();
RefPtr<MockPixelMap> mockPixelMap = AceType::MakeRefPtr<MockPixelMap>();
MockImagePacker::mockImagePacker_ = mockImagePacker;
MockImageSource::mockImageSource_ = mockImageSource;
/**
* @tc.steps: step1. construct a data.
*/
std::vector<uint8_t> imageData = {1, 2, 3, 4, 5, 6};
std::string url = "http:/testfilecache002/image";
std::string fileCacheKey = std::to_string(std::hash<std::string> {}(url));
/**
* @tc.steps: step2. call WriteCacheFile().
*/
ImageFileCache::GetInstance().WriteCacheFile(url, imageData.data(), imageData.size());
ASSERT_EQ(ImageFileCache::GetInstance().cacheFileSize_, static_cast<int32_t>(imageData.size()));
ASSERT_EQ(ImageFileCache::GetInstance().cacheFileInfo_.size(), 1);
FileInfo fileInfo = ImageFileCache::GetInstance().cacheFileInfo_.front();
size_t accessCount = fileInfo.accessCount;
ASSERT_EQ(accessCount, 1);
/**
* @tc.steps: step3. call GetCacheFilePathInner(), each call will increase accessCount by one,
* when accessCount reaches convertAstcThreshold, ConvertToAstcAndWriteToFile() will be called.
* @tc.expected: after ConvertToAstcAndWriteToFile() is called, fileInfo get updated, fileName contains ASTC_SUFFIX.
*/
std::string filePath;
auto convertAstcThreshold = SystemProperties::GetImageFileCacheConvertAstcThreshold();
while (accessCount < convertAstcThreshold) {
accessCount++;
bool convertToAstc = accessCount == convertAstcThreshold;
if (convertToAstc) {
EXPECT_CALL(*mockImageSource, GetFrameCount()).WillOnce(Return(1));
EXPECT_CALL(*mockImageSource, CreatePixelMap(_)).WillOnce(Return(mockPixelMap));
EXPECT_CALL(*mockImagePacker,
FinalizePacking(_)).WillOnce(DoAll(SetArgReferee<0>(imageData.size()), Return(0)));
}
filePath = ImageFileCache::GetInstance().GetCacheFilePathInner(url, "");
ASSERT_EQ(filePath, ImageFileCache::GetInstance().ConstructCacheFilePath(fileCacheKey));
if (convertToAstc) {
sleep(1);
}
ASSERT_EQ(ImageFileCache::GetInstance().cacheFileSize_, static_cast<int32_t>(imageData.size()));
ASSERT_EQ(ImageFileCache::GetInstance().cacheFileInfo_.size(), 1);
fileInfo = ImageFileCache::GetInstance().cacheFileInfo_.front();
ASSERT_EQ(fileInfo.fileName, convertToAstc ? fileCacheKey + ASTC_SUFFIX : fileCacheKey);
ASSERT_EQ(fileInfo.accessCount, accessCount);
}
}
/**
* @tc.name: WriteCacheFileFunc002
* @tc.desc: Test WriteCacheFileFun with convert astc disabled.
* @tc.type: FUNC
*/
HWTEST_F(ImageFileCacheTestNg, WriteCacheFileFunc002, TestSize.Level1)
{
SystemProperties::imageFileCacheConvertAstc_ = false;
/**
* @tc.steps: step1. construct a data.
*/
std::vector<uint8_t> imageData = {1, 2, 3, 4, 5, 6};
std::string url = "http:/testfilecache002/image";
std::string fileCacheKey = std::to_string(std::hash<std::string> {}(url));
/**
* @tc.steps: step2. call WriteCacheFile().
*/
ImageFileCache::GetInstance().WriteCacheFile(url, imageData.data(), imageData.size());
ASSERT_EQ(ImageFileCache::GetInstance().cacheFileSize_, static_cast<int32_t>(imageData.size()));
ASSERT_EQ(ImageFileCache::GetInstance().cacheFileInfo_.size(), 1);
FileInfo fileInfo = ImageFileCache::GetInstance().cacheFileInfo_.front();
size_t accessCount = fileInfo.accessCount;
ASSERT_EQ(accessCount, 1);
/**
* @tc.steps: step3. call GetCacheFilePathInner(), each call will increase accessCount by one,
* when accessCount reaches convertAstcThreshold, ConvertToAstcAndWriteToFile() will not be called.
* @tc.expected: fileInfo will not update, fileName does not contain ASTC_SUFFIX.
*/
std::string filePath;
auto convertAstcThreshold = SystemProperties::GetImageFileCacheConvertAstcThreshold();
while (accessCount < convertAstcThreshold) {
accessCount++;
filePath = ImageFileCache::GetInstance().GetCacheFilePathInner(url, "");
ASSERT_EQ(filePath, ImageFileCache::GetInstance().ConstructCacheFilePath(fileCacheKey));
ASSERT_EQ(ImageFileCache::GetInstance().cacheFileSize_, static_cast<int32_t>(imageData.size()));
ASSERT_EQ(ImageFileCache::GetInstance().cacheFileInfo_.size(), 1);
fileInfo = ImageFileCache::GetInstance().cacheFileInfo_.front();
ASSERT_EQ(fileInfo.fileName, fileCacheKey);
ASSERT_EQ(fileInfo.accessCount, accessCount);
}
}
/**
* @tc.name: WriteCacheFileFunc003
* @tc.desc: Test WriteCacheFileFun with suffix.
* @tc.type: FUNC
*/
HWTEST_F(ImageFileCacheTestNg, WriteCacheFileFunc003, TestSize.Level1)
{
/**
* @tc.steps: step1. construct a data.
*/
std::vector<uint8_t> imageData = {1, 2, 3, 4, 5, 6};
std::vector<uint8_t> imageDataWithSuffix = {1, 2, 3, 4, 5, 6, 7};
std::string url = "http:/testfilecache002/image";
std::string fileCacheKey = std::to_string(std::hash<std::string> {}(url));
/**
* @tc.steps: step2. call WriteCacheFile().
*/
ImageFileCache::GetInstance().WriteCacheFile(url, imageData.data(), imageData.size());
ASSERT_EQ(ImageFileCache::GetInstance().cacheFileSize_, static_cast<int32_t>(imageData.size()));
ASSERT_EQ(ImageFileCache::GetInstance().cacheFileInfo_.size(), 1);
FileInfo fileInfo = ImageFileCache::GetInstance().cacheFileInfo_.front();
ASSERT_EQ(fileInfo.fileName, fileCacheKey);
ASSERT_EQ(fileInfo.accessCount, 1);
/**
* @tc.steps: step3. call WriteCacheFile() with suffix.
* @tc.expected: fileInfo will update to imageDataWithSuffix.
*/
std::string suffix = ".jpg";
ImageFileCache::GetInstance().WriteCacheFile(url, imageDataWithSuffix.data(), imageDataWithSuffix.size(), suffix);
ASSERT_EQ(ImageFileCache::GetInstance().cacheFileSize_, static_cast<int32_t>(imageDataWithSuffix.size()));
ASSERT_EQ(ImageFileCache::GetInstance().cacheFileInfo_.size(), 1);
fileInfo = ImageFileCache::GetInstance().cacheFileInfo_.front();
ASSERT_EQ(fileInfo.fileName, fileCacheKey + suffix);
ASSERT_EQ(fileInfo.accessCount, 1);
}
/**
* @tc.name: WriteCacheFileFunc004
* @tc.desc: Test WriteCacheFileFun with svg.
* @tc.type: FUNC
*/
HWTEST_F(ImageFileCacheTestNg, WriteCacheFileFunc004, TestSize.Level1)
{
RefPtr<MockImageSource> mockImageSource = AceType::MakeRefPtr<MockImageSource>();
MockImageSource::mockImageSource_ = mockImageSource;
/**
* @tc.steps: step1. construct a data.
*/
std::vector<uint8_t> imageData = {1, 2, 3, 4, 5, 6};
std::string url = "http:/testfilecache002/image";
std::string fileCacheKey = std::to_string(std::hash<std::string> {}(url));
/**
* @tc.steps: step2. call WriteCacheFile().
*/
ImageFileCache::GetInstance().WriteCacheFile(url, imageData.data(), imageData.size());
ASSERT_EQ(ImageFileCache::GetInstance().cacheFileSize_, static_cast<int32_t>(imageData.size()));
ASSERT_EQ(ImageFileCache::GetInstance().cacheFileInfo_.size(), 1);
FileInfo fileInfo = ImageFileCache::GetInstance().cacheFileInfo_.front();
size_t accessCount = fileInfo.accessCount;
ASSERT_EQ(accessCount, 1);
/**
* @tc.steps: step3. call GetCacheFilePathInner(), each call will increase accessCount by one,
* when accessCount reaches convertAstcThreshold, ConvertToAstcAndWriteToFile() will be called.
* @tc.expected: because image is svg format, will not do the convert, fileInfo will not update,
* fileName does not contain ASTC_SUFFIX.
*/
std::string filePath;
auto convertAstcThreshold = SystemProperties::GetImageFileCacheConvertAstcThreshold();
while (accessCount <= convertAstcThreshold) {
accessCount++;
bool convertToAstc = accessCount == convertAstcThreshold;
if (convertToAstc) {
EXPECT_CALL(*mockImageSource, GetFrameCount()).WillOnce(Return(1));
EXPECT_CALL(*mockImageSource, GetEncodedFormat()).WillOnce(Return(SVG_FORMAT));
}
filePath = ImageFileCache::GetInstance().GetCacheFilePathInner(url, "");
ASSERT_EQ(filePath, ImageFileCache::GetInstance().ConstructCacheFilePath(fileCacheKey));
if (convertToAstc) {
sleep(1);
}
ASSERT_EQ(ImageFileCache::GetInstance().cacheFileSize_, static_cast<int32_t>(imageData.size()));
ASSERT_EQ(ImageFileCache::GetInstance().cacheFileInfo_.size(), 1);
fileInfo = ImageFileCache::GetInstance().cacheFileInfo_.front();
ASSERT_EQ(fileInfo.fileName, fileCacheKey);
ASSERT_EQ(fileInfo.accessCount, accessCount);
}
}
} // namespace OHOS::Ace::NG