File indexing completed on 2024-04-21 04:50:51

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 }