1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
|
/*GRB*
Gerbera - https://gerbera.io/
script_test_fixture.h - this file is part of Gerbera.
Copyright (C) 2020-2025 Gerbera Contributors
Gerbera is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.
Gerbera is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Gerbera. If not, see <http://www.gnu.org/licenses/>.
$Id$
*/
#ifdef HAVE_JS
#ifndef GERBERA_SCRIPTTESTFIXTURE_H
#define GERBERA_SCRIPTTESTFIXTURE_H
#include "util/string_converter.h"
#include "common_script_mock.h"
#include <duktape.h>
#include <gtest/gtest.h>
#include <memory>
#include <tuple>
using namespace ::testing;
struct addCdsObjectParams {
std::map<std::string, std::string> objectValues;
std::string containerChain;
std::string objectType;
};
struct boxConfig {
std::string key;
std::string title;
std::string upnpClass;
bool enabled;
int size;
};
struct abcBoxParams {
std::string inputValue;
int boxType;
std::string divChar;
};
struct getRootPathParams {
std::string objScriptPath;
std::string origObjLocation;
};
struct copyObjectParams {
bool isObject;
};
struct getCdsObjectParams {
std::string location;
};
// The class provides a way to test the Duktape scripts
// providing various c++ translations of script functions
// useful for mocking said functions with expectations.
class ScriptTestFixture : public ::testing::Test {
// Loads common.js if running test for another script
void loadCommon(duk_context* ctx) const;
public:
// Builds up the Duktape context
// Reads in the script under test
void SetUp() override;
// Destroys the Duktape context
void TearDown() override;
// Creates a mock item(orig) global object in Duktape context
static duk_ret_t dukMockItem(duk_context* ctx, const std::string& mimetype, const std::string& id, int theora, const std::string& title,
const std::vector<std::pair<std::string, std::string>>& meta, const std::map<std::string, std::string>& aux, const std::map<std::string, std::string>& res,
const std::string& location, int onlineService);
static void dukMockItem(duk_context* ctx, const std::map<std::string, std::string>& props,
const std::vector<std::pair<std::string, std::string>>& meta, const std::map<std::string, std::string>& aux, const std::map<std::string, std::string>& res);
// Load playlist file from fixtures
static void mockPlaylistFile(const std::string& mockFile);
// Creates a mock metafile global object in Duktape context
duk_ret_t dukMockMetafile(duk_context* ctx, const std::string& location, const std::string& fileName);
static void dukMockMetafile(duk_context* ctx, const std::map<std::string, std::string>& props);
// Creates a mock playlist global object in Duktape context
duk_ret_t dukMockPlaylist(duk_context* ctx, const std::string& title, const std::string& location, const std::string& mimetype);
static void dukMockPlaylist(duk_context* ctx, const std::map<std::string, std::string>& props);
// Add global Duktape methods to proxy into c++ layer
void addGlobalFunctions(duk_context* ctx, const duk_function_list_entry* funcs, const std::map<std::string_view, std::string_view>& config = {}, const std::vector<boxConfig>& boxDefaults = {});
// Add config entries to global context
static void addConfig(duk_context* ctx, const std::map<std::string_view, std::string_view>& config, const std::vector<boxConfig>& boxDefaults = {});
// Access the global object(script) by name, and execute
void executeScript(duk_context* ctx);
void callFunction(duk_context* ctx, void(dukMockFunction)(duk_context* ctx, const std::map<std::string, std::string>& props), const std::map<std::string, std::string>& props, const std::string& rootPath = "");
// Proxy the common.js script with `createContainerChain`
// Mimics the creation of a directory chain
// Returns the chain as a vector of strings (for easy expectations)
static std::vector<std::string> createContainerChain(duk_context* ctx);
// Proxy the common.js script with `getLastPath`
// Mimics finding the last path of the item
// Pushes the last path value to the Duktape stack
// Returns the inputPath parameter sent by the script
static std::pair<std::string, int> getLastPath2(duk_context* ctx);
static std::string getLastPath(duk_context* ctx);
// Proxy the common.js script with `getPlaylistType`
// Mimics determination of the playlist type
// Pushes the playlist type `m3u`, `pls` to the duktape stack
// Returns the input parameter matching the mimetype
// sent by the script
static std::string getPlaylistType(duk_context* ctx);
// Proxy the common.js script with `print`
// Mimics the print of text
// Returns the string sent by the script to print
static std::string print(duk_context* ctx);
static std::tuple<std::string, std::string> print2(duk_context* ctx);
// Proxy the common.js script with `getYear`
// Mimics the parsing of YYYY
// Pushes the year in YYYY format to the duktape context
// Returns the full date sent by the script
static std::string getYear(duk_context* ctx);
// Proxy the Duktape script with `addCdsObject` global function.
// Translates the Duktape value stack to c++
static addCdsObjectParams addCdsObject(duk_context* ctx, const std::vector<std::string>& objectKeys);
// Proxy the Duktape script with `addContainerTree` C function.
// Translates the Duktape value stack to c++
static std::vector<std::string> addContainerTree(duk_context* ctx, std::map<std::string, std::string> resMap);
// Proxy the Duktape script with `abcbox` common.js function
static abcBoxParams abcBox(duk_context* ctx);
// Proxy the Duktape script with `getRootPath` common.js function
static getRootPathParams getRootPath(duk_context* ctx);
// Proxy the Duktape script with `copyObject` global function
static copyObjectParams copyObject(duk_context* ctx, const std::map<std::string, std::string>& obj, const std::map<std::string, std::string>& meta);
// Proxy the Duktape script with `copyObject` global function
static getCdsObjectParams getCdsObject(duk_context* ctx, const std::map<std::string, std::string>& obj, const std::map<std::string, std::string>& meta);
// Script file name under test
// System defaults to known project path `/scripts/js/<scriptName>`
std::string scriptName;
std::string functionName;
std::string objectName = "obj";
// Select audio layout to test
std::string audioLayout;
// Used to iterate through `readln` content
static int readLineCnt;
static std::vector<std::string> lines;
// The Duktape Context
duk_context* ctx;
};
class CommonScriptTestFixture : public ScriptTestFixture {
public:
// As Duktape requires static methods, so must the mock expectations be
static std::unique_ptr<CommonScriptMock> commonScriptMock;
CommonScriptTestFixture()
{
commonScriptMock = std::make_unique<::testing::NiceMock<CommonScriptMock>>();
}
~CommonScriptTestFixture() override
{
commonScriptMock.reset();
}
static inline duk_ret_t js_print(duk_context* ctx)
{
std::string msg = ScriptTestFixture::print(ctx);
return CommonScriptTestFixture::commonScriptMock->print(msg);
}
static inline duk_ret_t js_print2(duk_context* ctx)
{
auto [mode, msg] = ScriptTestFixture::print2(ctx);
return CommonScriptTestFixture::commonScriptMock->print2(mode, msg);
}
static inline duk_ret_t js_getPlaylistType(duk_context* ctx)
{
std::string playlistMimeType = ScriptTestFixture::getPlaylistType(ctx);
return CommonScriptTestFixture::commonScriptMock->getPlaylistType(playlistMimeType);
}
static inline duk_ret_t js_createContainerChain(duk_context* ctx)
{
std::vector<std::string> array = ScriptTestFixture::createContainerChain(ctx);
return CommonScriptTestFixture::commonScriptMock->createContainerChain(array);
}
static inline duk_ret_t js_getLastPath(duk_context* ctx)
{
std::string inputPath = ScriptTestFixture::getLastPath(ctx);
return CommonScriptTestFixture::commonScriptMock->getLastPath(inputPath);
}
static inline duk_ret_t js_getLastPath2(duk_context* ctx)
{
auto [inputPath, length] = ScriptTestFixture::getLastPath2(ctx);
return CommonScriptTestFixture::commonScriptMock->getLastPath2(inputPath, length);
}
};
#endif //GERBERA_SCRIPTTESTFIXTURE_H
#endif //HAVE_JS
|