1/*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include <climits>
17#include <cstring>
18#include <fcntl.h>
19#include <gtest/gtest.h>
20#include <iostream>
21#include <sys/mman.h>
22#include <sys/stat.h>
23#include <unistd.h>
24#include "hash_data_verifier.h"
25#include "log.h"
26#include "pkg_stream.h"
27#include "pkg_utils.h"
28#include "scope_guard.h"
29#include "script_instruction.h"
30#include "script_manager.h"
31#include "script/script_unittest.h"
32#include "script_utils.h"
33#include "unittest_comm.h"
34#include "utils.h"
35
36using namespace std;
37using namespace Hpackage;
38using namespace Uscript;
39using namespace Updater;
40using namespace testing::ext;
41
42namespace {
43constexpr int32_t SCRIPT_TEST_PRIORITY_NUM = 3;
44constexpr int32_t SCRIPT_TEST_LAST_PRIORITY = 2;
45
46class TestPkgManager : public TestScriptPkgManager {
47public:
48    const FileInfo *GetFileInfo(const std::string &fileId) override
49    {
50        static FileInfo fileInfo {};
51        static std::vector<std::string> testFileNames = {
52            "loadScript.us",
53            "registerCmd.us",
54            "test_function.us",
55            "test_if.us",
56            "test_logic.us",
57            "test_math.us",
58            "test_native.us",
59            "testscript.us",
60            "Verse-script.us",
61            "test_script.us"
62        };
63        if (fileId == "hash_signed_data") {
64            fileInfo.unpackedSize = GetFileSize(TEST_PATH_FROM + fileId);
65            return &fileInfo;
66        }
67        if (std::find(testFileNames.begin(), testFileNames.end(), fileId) != testFileNames.end()) {
68            return &fileInfo;
69        }
70        return nullptr;
71    }
72    int32_t CreatePkgStream(StreamPtr &stream, const std::string &fileName, const PkgBuffer &buffer) override
73    {
74        stream = new MemoryMapStream(this, fileName, buffer, PkgStream::PkgStreamType_Buffer);
75        return PKG_SUCCESS;
76    }
77    int32_t ExtractFile(const std::string &fileId, StreamPtr output) override
78    {
79        if (fileId != "hash_signed_data") {
80            return PKG_SUCCESS;
81        }
82        if (output == nullptr) {
83            return PKG_INVALID_STREAM;
84        }
85        auto stream = static_cast<MemoryMapStream *>(output);
86        auto fd = open((TEST_PATH_FROM + fileId).c_str(), O_RDWR);
87        if (fd == -1) {
88            PKG_LOGE("file %s not existed", (TEST_PATH_FROM + fileId).c_str());
89            return PKG_INVALID_FILE;
90        }
91        ON_SCOPE_EXIT(close) {
92            close(fd);
93        };
94        std::string content {};
95        if (!Utils::ReadFileToString(fd, content)) {
96            PKG_LOGE("read file to string failed");
97            return PKG_INVALID_FILE;
98        }
99        PkgBuffer buffer {};
100        stream->GetBuffer(buffer);
101        if (content.size() + 1 != buffer.length) {
102            PKG_LOGE("content size is not valid, %u != %u", content.size(), buffer.data.size());
103            return PKG_INVALID_FILE;
104        }
105        std::copy(content.begin(), content.end(), buffer.buffer);
106        return PKG_SUCCESS;
107    }
108    int32_t CreatePkgStream(StreamPtr &stream, const std::string &fileName,
109         size_t size, int32_t type) override
110    {
111        FILE *file = nullptr;
112        std::string fileNameReal = fileName;
113        auto pos = fileName.rfind('/');
114        if (pos != std::string::npos) {
115            fileNameReal = fileName.substr(pos + 1);
116        }
117        char realPath[PATH_MAX + 1] = {};
118        if (realpath((TEST_PATH_FROM + fileNameReal).c_str(), realPath) == nullptr) {
119            LOG(ERROR) << (TEST_PATH_FROM + fileNameReal) << " realpath failed";
120            return PKG_INVALID_FILE;
121        }
122        file = fopen(realPath, "rb");
123        if (file == nullptr) {
124            PKG_LOGE("Fail to open file %s ", fileNameReal.c_str());
125            return PKG_INVALID_FILE;
126        }
127        stream = new FileStream(this, fileNameReal, file, PkgStream::PkgStreamType_Read);
128        return USCRIPT_SUCCESS;
129    }
130    void ClosePkgStream(StreamPtr &stream) override
131    {
132        delete stream;
133    }
134};
135
136
137class TestScriptInstructionSparseImageWrite : public Uscript::UScriptInstruction {
138public:
139    TestScriptInstructionSparseImageWrite() {}
140    virtual ~TestScriptInstructionSparseImageWrite() {}
141    int32_t Execute(Uscript::UScriptEnv &env, Uscript::UScriptContext &context) override
142    {
143        // 从参数中获取分区信息
144        std::string partitionName;
145        int32_t ret = context.GetParam(0, partitionName);
146        if (ret != USCRIPT_SUCCESS) {
147            LOG(ERROR) << "Error to get param";
148            return ret;
149        }
150        LOG(INFO) << "UScriptInstructionSparseImageWrite::Execute " << partitionName;
151        if (env.GetPkgManager() == nullptr) {
152            LOG(ERROR) << "Error to get pkg manager";
153            return USCRIPT_ERROR_EXECUTE;
154        }
155        return ret;
156    }
157};
158
159class TestScriptInstructionFactory : public UScriptInstructionFactory {
160public:
161    virtual int32_t CreateInstructionInstance(UScriptInstructionPtr& instr, const std::string& name)
162    {
163        if (name == "sparse_image_write") {
164            instr = new TestScriptInstructionSparseImageWrite();
165        }
166        return USCRIPT_SUCCESS;
167    }
168    virtual void DestoryInstructionInstance(UScriptInstructionPtr& instr)
169    {
170        delete instr;
171        instr = nullptr;
172    }
173    TestScriptInstructionFactory() {}
174    virtual ~TestScriptInstructionFactory() {}
175};
176
177class UTestScriptEnv : public UScriptEnv {
178public:
179    explicit UTestScriptEnv(Hpackage::PkgManager::PkgManagerPtr pkgManager) : UScriptEnv(pkgManager) {}
180    ~UTestScriptEnv()
181    {
182        if (factory_ != nullptr) {
183            delete factory_;
184            factory_ = nullptr;
185        }
186    }
187
188    virtual void PostMessage(const std::string &cmd, std::string content) {}
189
190    virtual UScriptInstructionFactoryPtr GetInstructionFactory()
191    {
192        if (factory_ == nullptr) {
193            factory_ = new TestScriptInstructionFactory();
194        }
195        return factory_;
196    }
197
198    virtual const std::vector<std::string> GetInstructionNames() const
199    {
200        static std::vector<std::string> updaterCmds = {"sparse_image_write"};
201        return updaterCmds;
202    }
203
204    virtual bool IsRetry() const
205    {
206        return isRetry;
207    }
208
209    virtual PostMessageFunction GetPostmsgFunc()
210    {
211        return nullptr;
212    }
213    UScriptInstructionFactory *factory_ = nullptr;
214private:
215    bool isRetry = false;
216};
217
218class UScriptTest : public ::testing::Test {
219public:
220    UScriptTest() {}
221
222    ~UScriptTest()
223    {
224        ScriptManager::ReleaseScriptManager();
225    }
226
227    int TestUscriptExecute()
228    {
229        int32_t ret {};
230        TestPkgManager packageManager;
231        UTestScriptEnv *env = new UTestScriptEnv(&packageManager);
232        HashDataVerifier verifier {&packageManager};
233        verifier.LoadHashDataAndPkcs7(TEST_PATH_FROM + "updater_fake_pkg.zip");
234        ScriptManager *manager = ScriptManager::GetScriptManager(env, &verifier);
235        if (manager == nullptr) {
236            USCRIPT_LOGI("create manager fail ret:%d", ret);
237            delete env;
238            return USCRIPT_INVALID_SCRIPT;
239        }
240        int32_t priority = SCRIPT_TEST_PRIORITY_NUM;
241        ret = manager->ExecuteScript(priority);
242        EXPECT_EQ(ret, USCRIPT_ABOART);
243        USCRIPT_LOGI("ExecuteScript ret:%d", ret);
244        priority = 0;
245        ret = manager->ExecuteScript(priority);
246        priority = 1;
247        ret = manager->ExecuteScript(priority);
248        priority = SCRIPT_TEST_LAST_PRIORITY;
249        ret = manager->ExecuteScript(priority);
250        delete env;
251        ScriptManager::ReleaseScriptManager();
252        return ret;
253    }
254
255protected:
256    void SetUp() {}
257    void TearDown() {}
258    void TestBody() {}
259
260private:
261    std::vector<std::string> testFileNames_ = {
262        "loadScript.us",
263        "registerCmd.us",
264        "test_function.us",
265        "test_if.us",
266        "test_logic.us",
267        "test_math.us",
268        "test_native.us",
269        "testscript.us",
270        "Verse-script.us",
271        "test_script.us"
272    };
273};
274
275HWTEST_F(UScriptTest, TestUscriptExecute, TestSize.Level1)
276{
277    UScriptTest test;
278    EXPECT_EQ(0, test.TestUscriptExecute());
279}
280}
281