File indexing completed on 2025-04-27 04:30:59
0001 /* 0002 SPDX-FileCopyrightText: 2019 Nicolas Carion 0003 This file is part of Kdenlive. See www.kdenlive.org. 0004 0005 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0006 */ 0007 0008 #include "fuzzing.hpp" 0009 #include "bin/model/markerlistmodel.hpp" 0010 #include "doc/docundostack.hpp" 0011 #include "fakeit_standalone.hpp" 0012 #include "logger.hpp" 0013 #include <mlt++/MltFactory.h> 0014 #include <mlt++/MltProducer.h> 0015 #include <mlt++/MltProfile.h> 0016 #include <mlt++/MltRepository.h> 0017 #include <sstream> 0018 #define private public 0019 #define protected public 0020 #include "assets/keyframes/model/keyframemodel.hpp" 0021 #include "assets/model/assetparametermodel.hpp" 0022 #include "bin/clipcreator.hpp" 0023 #include "bin/projectclip.h" 0024 #include "bin/projectfolder.h" 0025 #include "bin/projectitemmodel.h" 0026 #include "core.h" 0027 #include "effects/effectsrepository.hpp" 0028 #include "effects/effectstack/model/effectitemmodel.hpp" 0029 #include "effects/effectstack/model/effectstackmodel.hpp" 0030 #include "mltconnection.h" 0031 #include "project/projectmanager.h" 0032 #include "timeline2/model/clipmodel.hpp" 0033 #include "timeline2/model/compositionmodel.hpp" 0034 #include "timeline2/model/groupsmodel.hpp" 0035 #include "timeline2/model/timelinefunctions.hpp" 0036 #include "timeline2/model/timelineitemmodel.hpp" 0037 #include "timeline2/model/timelinemodel.hpp" 0038 #include "timeline2/model/trackmodel.hpp" 0039 0040 #pragma GCC diagnostic push 0041 #pragma GCC diagnostic ignored "-Wunused-parameter" 0042 #pragma GCC diagnostic ignored "-Wsign-conversion" 0043 #pragma GCC diagnostic ignored "-Wfloat-equal" 0044 #pragma GCC diagnostic ignored "-Wshadow" 0045 #pragma GCC diagnostic ignored "-Wpedantic" 0046 #include <rttr/registration> 0047 #pragma GCC diagnostic pop 0048 0049 using namespace fakeit; 0050 0051 namespace { 0052 QString createProducer(Mlt::Profile &prof, std::string color, std::shared_ptr<ProjectItemModel> binModel, int length, bool limited) 0053 { 0054 Logger::log_create_producer("test_producer", {color, binModel, length, limited}); 0055 std::shared_ptr<Mlt::Producer> producer = std::make_shared<Mlt::Producer>(prof, "color", color.c_str()); 0056 producer->set("length", length); 0057 producer->set("out", length - 1); 0058 0059 Q_ASSERT(producer->is_valid()); 0060 0061 QString binId = QString::number(binModel->getFreeClipId()); 0062 auto binClip = ProjectClip::construct(binId, QIcon(), binModel, producer); 0063 if (limited) { 0064 binClip->forceLimitedDuration(); 0065 } 0066 Fun undo = []() { return true; }; 0067 Fun redo = []() { return true; }; 0068 Q_ASSERT(binModel->addItem(binClip, binModel->getRootFolder()->clipId(), undo, redo)); 0069 0070 return binId; 0071 } 0072 0073 QString createProducerWithSound(Mlt::Profile &prof, std::shared_ptr<ProjectItemModel> binModel) 0074 { 0075 Logger::log_create_producer("test_producer_sound", {binModel}); 0076 // std::shared_ptr<Mlt::Producer> producer = std::make_shared<Mlt::Producer>(prof, 0077 // QFileInfo("../tests/small.mkv").absoluteFilePath().toStdString().c_str()); 0078 0079 // In case the test system does not have avformat support, we can switch to the integrated blipflash producer 0080 std::shared_ptr<Mlt::Producer> producer = std::make_shared<Mlt::Producer>(prof, "blipflash"); 0081 producer->set_in_and_out(0, 1); 0082 producer->set("kdenlive:duration", 2); 0083 0084 Q_ASSERT(producer->is_valid()); 0085 0086 QString binId = QString::number(binModel->getFreeClipId()); 0087 auto binClip = ProjectClip::construct(binId, QIcon(), binModel, producer); 0088 Fun undo = []() { return true; }; 0089 Fun redo = []() { return true; }; 0090 Q_ASSERT(binModel->addItem(binClip, binModel->getRootFolder()->clipId(), undo, redo)); 0091 0092 return binId; 0093 } 0094 inline int modulo(int a, int b) 0095 { 0096 const int result = a % b; 0097 return result >= 0 ? result : result + b; 0098 } 0099 namespace { 0100 bool isIthParamARef(const rttr::method &method, size_t i) 0101 { 0102 QString sig = QString::fromStdString(method.get_signature().to_string()); 0103 int deb = sig.indexOf("("); 0104 int end = sig.lastIndexOf(")"); 0105 sig = sig.mid(deb + 1, deb - end - 1); 0106 QStringList args = sig.split(QStringLiteral(",")); 0107 return args[(int)i].contains("&") && !args[(int)i].contains("const &"); 0108 } 0109 } // namespace 0110 } // namespace 0111 0112 void fuzz(const std::string &input) 0113 { 0114 Logger::init(); 0115 Logger::clear(); 0116 std::stringstream ss; 0117 ss << input; 0118 0119 Mlt::Profile profile; 0120 auto binModel = pCore->projectItemModel(); 0121 binModel->clean(); 0122 std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr); 0123 std::shared_ptr<MarkerListModel> guideModel = std::make_shared<MarkerListModel>(undoStack); 0124 KdenliveDoc::next_id = 0; 0125 0126 Mock<ProjectManager> pmMock; 0127 When(Method(pmMock, undoStack)).AlwaysReturn(undoStack); 0128 0129 ProjectManager &mocked = pmMock.get(); 0130 pCore->m_projectManager = &mocked; 0131 0132 std::vector<std::shared_ptr<TimelineModel>> all_timelines; 0133 0134 std::unordered_map<std::shared_ptr<TimelineModel>, std::vector<int>> all_clips, all_tracks, all_compositions, all_groups; 0135 auto update_elems = [&]() { 0136 all_clips.clear(); 0137 all_tracks.clear(); 0138 all_compositions.clear(); 0139 for (const auto &timeline : all_timelines) { 0140 all_clips[timeline] = {}; 0141 all_tracks[timeline] = {}; 0142 all_compositions[timeline] = {}; 0143 all_groups[timeline] = {}; 0144 auto &clips = all_clips[timeline]; 0145 clips.clear(); 0146 for (const auto &c : timeline->m_allClips) { 0147 clips.push_back(c.first); 0148 } 0149 std::sort(clips.begin(), clips.end()); 0150 0151 auto &compositions = all_compositions[timeline]; 0152 compositions.clear(); 0153 for (const auto &c : timeline->m_allCompositions) { 0154 compositions.push_back(c.first); 0155 } 0156 std::sort(compositions.begin(), compositions.end()); 0157 0158 auto &tracks = all_tracks[timeline]; 0159 tracks.clear(); 0160 for (const auto &c : timeline->m_iteratorTable) { 0161 tracks.push_back(c.first); 0162 } 0163 std::sort(tracks.begin(), tracks.end()); 0164 0165 auto &groups = all_groups[timeline]; 0166 groups.clear(); 0167 for (int c : timeline->m_allGroups) { 0168 groups.push_back(c); 0169 } 0170 std::sort(groups.begin(), groups.end()); 0171 } 0172 }; 0173 auto get_timeline = [&]() -> std::shared_ptr<TimelineModel> { 0174 int id = 0; 0175 ss >> id; 0176 if (all_timelines.size() == 0) return nullptr; 0177 id = modulo(id, (int)all_timelines.size()); 0178 return all_timelines[size_t(id)]; 0179 }; 0180 auto get_group = [&](std::shared_ptr<TimelineModel> timeline) { 0181 int id = 0; 0182 ss >> id; 0183 if (!timeline) return -1; 0184 if (timeline->isGroup(id)) return id; 0185 if (all_timelines.size() == 0) return -1; 0186 if (all_groups.count(timeline) == 0) return -1; 0187 if (all_groups[timeline].size() == 0) return -1; 0188 id = modulo(id, (int)all_groups[timeline].size()); 0189 return all_groups[timeline][id]; 0190 }; 0191 auto get_clip = [&](std::shared_ptr<TimelineModel> timeline) { 0192 int id = 0; 0193 ss >> id; 0194 if (!timeline) return -1; 0195 if (timeline->isClip(id)) return id; 0196 if (all_timelines.size() == 0) return -1; 0197 if (all_clips.count(timeline) == 0) return -1; 0198 if (all_clips[timeline].size() == 0) return -1; 0199 id = modulo(id, (int)all_clips[timeline].size()); 0200 return all_clips[timeline][id]; 0201 }; 0202 auto get_compo = [&](std::shared_ptr<TimelineModel> timeline) { 0203 int id = 0; 0204 ss >> id; 0205 if (!timeline) return -1; 0206 if (timeline->isComposition(id)) return id; 0207 if (all_timelines.size() == 0) return -1; 0208 if (all_compositions.count(timeline) == 0) return -1; 0209 if (all_compositions[timeline].size() == 0) return -1; 0210 id = modulo(id, (int)all_compositions[timeline].size()); 0211 return all_compositions[timeline][id]; 0212 }; 0213 auto get_item = [&](std::shared_ptr<TimelineModel> timeline) { 0214 int id = 0; 0215 ss >> id; 0216 if (!timeline) return -1; 0217 if (timeline->isClip(id)) return id; 0218 if (timeline->isComposition(id)) return id; 0219 if (all_timelines.size() == 0) return -1; 0220 int clip_count = 0; 0221 if (all_clips.count(timeline) > 0) { 0222 clip_count = all_clips[timeline].size(); 0223 } 0224 int compo_count = 0; 0225 if (all_compositions.count(timeline) > 0) { 0226 compo_count = all_compositions[timeline].size(); 0227 } 0228 if (clip_count + compo_count == 0) return -1; 0229 id = modulo(id, clip_count + compo_count); 0230 if (id < clip_count) { 0231 return all_clips[timeline][id]; 0232 } 0233 return all_compositions[timeline][id - clip_count]; 0234 }; 0235 auto get_track = [&](std::shared_ptr<TimelineModel> timeline) { 0236 int id = 0; 0237 ss >> id; 0238 if (!timeline) return -1; 0239 if (timeline->isTrack(id)) return id; 0240 if (all_timelines.size() == 0) return -1; 0241 if (all_tracks.count(timeline) == 0) return -1; 0242 if (all_tracks[timeline].size() == 0) return -1; 0243 id = modulo(id, (int)all_tracks[timeline].size()); 0244 return all_tracks[timeline][id]; 0245 }; 0246 std::string c; 0247 0248 while (ss >> c) { 0249 if (c == "u") { 0250 std::cout << "UNDOING" << std::endl; 0251 undoStack->undo(); 0252 } else if (c == "r") { 0253 std::cout << "REDOING" << std::endl; 0254 undoStack->redo(); 0255 } else if (Logger::back_translation_table.count(c) > 0) { 0256 // std::cout << "found=" << c; 0257 c = Logger::back_translation_table[c]; 0258 // std::cout << " translated=" << c << std::endl; 0259 if (c == "constr_TimelineModel") { 0260 all_timelines.emplace_back(TimelineItemModel::construct(&profile, guideModel, undoStack)); 0261 } else if (c == "constr_ClipModel") { 0262 auto timeline = get_timeline(); 0263 int id = 0, state_id; 0264 double speed = 1; 0265 PlaylistState::ClipState state = PlaylistState::VideoOnly; 0266 std::string binId; 0267 ss >> binId >> id >> state_id >> speed; 0268 QString binClip = QString::fromStdString(binId); 0269 bool valid = true; 0270 if (!pCore->projectItemModel()->hasClip(binClip)) { 0271 if (pCore->projectItemModel()->getAllClipIds().size() == 0) { 0272 valid = false; 0273 } else { 0274 binClip = pCore->projectItemModel()->getAllClipIds()[0]; 0275 } 0276 } 0277 state = static_cast<PlaylistState::ClipState>(state_id); 0278 if (timeline && valid) { 0279 ClipModel::construct(timeline, binClip, -1, state, speed); 0280 } 0281 } else if (c == "constr_TrackModel") { 0282 auto timeline = get_timeline(); 0283 int id, pos = 0; 0284 std::string name; 0285 bool audio = false; 0286 ss >> id >> pos >> name >> audio; 0287 if (name == "$$") { 0288 name = ""; 0289 } 0290 if (pos < -1) pos = 0; 0291 pos = std::min((int)all_tracks[timeline].size(), pos); 0292 if (timeline) { 0293 TrackModel::construct(timeline, -1, pos, QString::fromStdString(name), audio); 0294 } 0295 } else if (c == "constr_test_producer") { 0296 std::string color; 0297 int length = 0; 0298 bool limited = false; 0299 ss >> color >> length >> limited; 0300 createProducer(profile, color, binModel, length, limited); 0301 } else if (c == "constr_test_producer_sound") { 0302 createProducerWithSound(profile, binModel); 0303 } else { 0304 // std::cout << "executing " << c << std::endl; 0305 rttr::type target_type = rttr::type::get<int>(); 0306 bool found = false; 0307 for (const std::string &t : {"TimelineModel", "TimelineFunctions"}) { 0308 rttr::type current_type = rttr::type::get_by_name(t); 0309 // std::cout << "type " << t << " has methods count=" << current_type.get_methods().size() << std::endl; 0310 if (current_type.get_method(c).is_valid()) { 0311 found = true; 0312 target_type = current_type; 0313 break; 0314 } 0315 } 0316 if (found) { 0317 // std::cout << "found!" << std::endl; 0318 bool valid = true; 0319 rttr::method target_method = target_type.get_method(c); 0320 std::vector<rttr::variant> arguments; 0321 rttr::variant ptr; 0322 if (target_type == rttr::type::get<TimelineModel>()) { 0323 if (all_timelines.size() == 0) { 0324 valid = false; 0325 } 0326 ptr = get_timeline(); 0327 } 0328 int i = -1; 0329 for (const auto &p : target_method.get_parameter_infos()) { 0330 ++i; 0331 std::string arg_name = p.get_name().to_string(); 0332 // std::cout << arg_name << std::endl; 0333 if (arg_name == "compoId") { 0334 std::shared_ptr<TimelineModel> tim = 0335 (ptr.can_convert<std::shared_ptr<TimelineModel>>() ? ptr.convert<std::shared_ptr<TimelineModel>>() : nullptr); 0336 int compoId = get_compo(tim); 0337 valid = valid && (compoId >= 0); 0338 // std::cout << "got compo" << compoId << std::endl; 0339 arguments.emplace_back(compoId); 0340 } else if (arg_name == "clipId") { 0341 std::shared_ptr<TimelineModel> tim = 0342 (ptr.can_convert<std::shared_ptr<TimelineModel>>() ? ptr.convert<std::shared_ptr<TimelineModel>>() : nullptr); 0343 int clipId = get_clip(tim); 0344 valid = valid && (clipId >= 0); 0345 arguments.emplace_back(clipId); 0346 // std::cout << "got clipId" << clipId << std::endl; 0347 } else if (arg_name == "trackId") { 0348 std::shared_ptr<TimelineModel> tim = 0349 (ptr.can_convert<std::shared_ptr<TimelineModel>>() ? ptr.convert<std::shared_ptr<TimelineModel>>() : nullptr); 0350 int trackId = get_track(tim); 0351 valid = valid && (trackId >= 0); 0352 arguments.emplace_back(trackId); 0353 // std::cout << "got trackId" << trackId << std::endl; 0354 } else if (arg_name == "itemId") { 0355 std::shared_ptr<TimelineModel> tim = 0356 (ptr.can_convert<std::shared_ptr<TimelineModel>>() ? ptr.convert<std::shared_ptr<TimelineModel>>() : nullptr); 0357 int itemId = get_item(tim); 0358 valid = valid && (itemId >= 0); 0359 arguments.emplace_back(itemId); 0360 // std::cout << "got itemId" << itemId << std::endl; 0361 } else if (arg_name == "groupId") { 0362 std::shared_ptr<TimelineModel> tim = 0363 (ptr.can_convert<std::shared_ptr<TimelineModel>>() ? ptr.convert<std::shared_ptr<TimelineModel>>() : nullptr); 0364 int groupId = get_group(tim); 0365 valid = valid && (groupId >= 0); 0366 arguments.emplace_back(groupId); 0367 // std::cout << "got clipId" << clipId << std::endl; 0368 } else if (arg_name == "logUndo") { 0369 bool a = false; 0370 ss >> a; 0371 // we enforce undo logging 0372 a = true; 0373 arguments.emplace_back(a); 0374 } else if (arg_name == "itemIds") { 0375 int count = 0; 0376 ss >> count; 0377 // std::cout << "got ids. going to read count=" << count << std::endl; 0378 if (count > 0) { 0379 std::shared_ptr<TimelineModel> tim = 0380 (ptr.can_convert<std::shared_ptr<TimelineModel>>() ? ptr.convert<std::shared_ptr<TimelineModel>>() : nullptr); 0381 std::unordered_set<int> ids; 0382 for (int i = 0; i < count; ++i) { 0383 int itemId = get_item(tim); 0384 // std::cout << "\t read" << itemId << std::endl; 0385 valid = valid && (itemId >= 0); 0386 ids.insert(itemId); 0387 } 0388 arguments.emplace_back(ids); 0389 } else { 0390 valid = false; 0391 } 0392 } else if (!isIthParamARef(target_method, i)) { 0393 rttr::type arg_type = p.get_type(); 0394 if (arg_type == rttr::type::get<int>()) { 0395 int a = 0; 0396 ss >> a; 0397 // std::cout << "read int " << a << std::endl; 0398 arguments.emplace_back(a); 0399 } else if (arg_type == rttr::type::get<size_t>()) { 0400 size_t a = 0; 0401 ss >> a; 0402 arguments.emplace_back(a); 0403 } else if (arg_type == rttr::type::get<double>()) { 0404 double a = 0; 0405 ss >> a; 0406 arguments.emplace_back(a); 0407 } else if (arg_type == rttr::type::get<float>()) { 0408 float a = 0; 0409 ss >> a; 0410 arguments.emplace_back(a); 0411 } else if (arg_type == rttr::type::get<bool>()) { 0412 bool a = false; 0413 ss >> a; 0414 // std::cout << "read bool " << a << std::endl; 0415 arguments.emplace_back(a); 0416 } else if (arg_type == rttr::type::get<QString>()) { 0417 std::string str = ""; 0418 ss >> str; 0419 // std::cout << "read str " << str << std::endl; 0420 if (str == "$$") { 0421 str = ""; 0422 } 0423 arguments.emplace_back(QString::fromStdString(str)); 0424 } else if (arg_type == rttr::type::get<std::shared_ptr<TimelineItemModel>>()) { 0425 auto timeline = get_timeline(); 0426 if (timeline) { 0427 // std::cout << "got timeline" << std::endl; 0428 auto timeline2 = std::dynamic_pointer_cast<TimelineItemModel>(timeline); 0429 arguments.emplace_back(timeline2); 0430 ptr = timeline; 0431 } else { 0432 // std::cout << "didn't get timeline" << std::endl; 0433 valid = false; 0434 } 0435 } else if (arg_type.is_enumeration()) { 0436 int a = 0; 0437 ss >> a; 0438 rttr::variant var_a = a; 0439 var_a.convert((const rttr::type &)arg_type); 0440 // std::cout << "read enum " << arg_type.get_enumeration().value_to_name(var_a).to_string() << std::endl; 0441 arguments.push_back(var_a); 0442 } else { 0443 std::cout << "ERROR: unsupported arg type " << arg_type.get_name().to_string() << std::endl; 0444 assert(false); 0445 } 0446 } else { 0447 if (p.get_type() == rttr::type::get<int>()) { 0448 arguments.emplace_back(-1); 0449 } else { 0450 assert(false); 0451 } 0452 } 0453 } 0454 if (valid) { 0455 std::cout << "VALID!!! " << target_method.get_name().to_string() << std::endl; 0456 std::vector<rttr::argument> args; 0457 args.reserve(arguments.size()); 0458 for (auto &a : arguments) { 0459 args.emplace_back(a); 0460 // std::cout << "argument=" << a.get_type().get_name().to_string() << std::endl; 0461 } 0462 for (const auto &p : target_method.get_parameter_infos()) { 0463 // std::cout << "expected=" << p.get_type().get_name().to_string() << std::endl; 0464 } 0465 rttr::variant res = target_method.invoke_variadic(ptr, args); 0466 if (res.is_valid()) { 0467 std::cout << "SUCCESS!!!" << std::endl; 0468 } else { 0469 std::cout << "!!!FAILLLLLL!!!" << std::endl; 0470 } 0471 } 0472 } 0473 } 0474 } 0475 update_elems(); 0476 for (const auto &t : all_timelines) { 0477 assert(t->checkConsistency()); 0478 } 0479 } 0480 undoStack->clear(); 0481 all_clips.clear(); 0482 all_tracks.clear(); 0483 all_compositions.clear(); 0484 all_groups.clear(); 0485 for (auto &all_timeline : all_timelines) { 0486 all_timeline.reset(); 0487 } 0488 0489 pCore->m_projectManager = nullptr; 0490 Core::m_self.reset(); 0491 MltConnection::m_self.reset(); 0492 std::cout << "---------------------------------------------------------------------------------------------------------------------------------------------" 0493 "---------------" 0494 << std::endl; 0495 }