kdenlive: 4 new CWE-407 defects (0005-0008); deeper scan beyond thumbnail/timeline

kdenlive-0005 PreviewManager m_renderedChunks/m_dirtyChunks QVariantList .contains() O(N^2) MEDIUM 219x
kdenlive-0006 TimelineController canceled guides std::find O(G*C) MEDIUM 160x
kdenlive-0007 AssetParameterModel m_rows.indexOf in param loops O(P*R) MEDIUM 25x
kdenlive-0008 UrlListParamWidget addItemsInSameFolder std::find on QMap values O(E*M) MEDIUM 200x

8/8 unit tests PASS
This commit is contained in:
russell@unturf.com 2026-03-30 14:39:58 -04:00
parent 6e6609dbdb
commit 70741a6157
6 changed files with 377 additions and 0 deletions

View file

@ -6,6 +6,10 @@ import java.util.*;
* kdenlive-0002: TimelineModel clipIds vector linear find in mix loop
* kdenlive-0003: TimelineController sorted_clips vector linear find in moveGroup
* kdenlive-0004: TimelineModel all_items list linear find in resize
* kdenlive-0005: PreviewManager m_renderedChunks/m_dirtyChunks QVariantList linear contains
* kdenlive-0006: TimelineController canceled guides vector linear find
* kdenlive-0007: AssetParameterModel m_rows indexOf in parameter loops
* kdenlive-0008: UrlListParamWidget addItemsInSameFolder linear find on map values
*/
public class KdenliveTest {
@ -152,6 +156,145 @@ public class KdenliveTest {
return ops;
}
// --- kdenlive-0005: PreviewManager chunk lists linear contains ---
static long previewChunksDefect(int numDirty, int numNew) {
// Simulates m_dirtyChunks as QVariantList with .contains() dedup
List<Integer> dirtyChunks = new ArrayList<>();
for (int i = 0; i < numDirty; i++) dirtyChunks.add(i * 25);
List<Integer> renderedChunks = new ArrayList<>();
for (int i = 0; i < numDirty / 2; i++) renderedChunks.add(i * 25);
long ops = 0;
// reloadChunks pattern: loop over new chunks, .contains() on existing
for (int i = 0; i < numNew; i++) {
int frame = i * 25;
// m_dirtyChunks.contains(frame) - linear scan
for (int existing : dirtyChunks) {
ops++;
if (existing == frame) break;
}
// m_renderedChunks.contains(frame) - linear scan
for (int existing : renderedChunks) {
ops++;
if (existing == frame) break;
}
}
return ops;
}
static long previewChunksFixed(int numDirty, int numNew) {
Set<Integer> dirtySet = new HashSet<>();
for (int i = 0; i < numDirty; i++) dirtySet.add(i * 25);
Set<Integer> renderedSet = new HashSet<>();
for (int i = 0; i < numDirty / 2; i++) renderedSet.add(i * 25);
long ops = 0;
for (int i = 0; i < numNew; i++) {
int frame = i * 25;
ops++; // O(1) HashSet.contains for dirty
dirtySet.contains(frame);
ops++; // O(1) HashSet.contains for rendered
renderedSet.contains(frame);
}
return ops;
}
// --- kdenlive-0006: canceled guide filter ---
static long canceledGuidesDefect(int numGuides, int numCanceled) {
List<Integer> guides = new ArrayList<>();
for (int i = 0; i < numGuides; i++) guides.add(i * 30);
List<Integer> canceled = new ArrayList<>();
for (int i = 0; i < numCanceled; i++) canceled.add(i * 60); // every other guide
long ops = 0;
for (int guidePos : guides) {
// std::find on canceled vector
for (int c : canceled) {
ops++;
if (c == guidePos) break;
}
}
return ops;
}
static long canceledGuidesFixed(int numGuides, int numCanceled) {
List<Integer> guides = new ArrayList<>();
for (int i = 0; i < numGuides; i++) guides.add(i * 30);
Set<Integer> canceledSet = new HashSet<>();
for (int i = 0; i < numCanceled; i++) canceledSet.add(i * 60);
long ops = 0;
for (int guidePos : guides) {
ops++; // O(1) HashSet lookup
canceledSet.contains(guidePos);
}
return ops;
}
// --- kdenlive-0007: AssetParameterModel m_rows indexOf in loops ---
static long rowsIndexOfDefect(int numParams) {
// m_rows is QVector<QString>, indexOf called per param
List<String> rows = new ArrayList<>();
for (int i = 0; i < numParams; i++) rows.add("param_" + i);
long ops = 0;
// Simulates getAllParameters/toJson loop
for (int i = 0; i < numParams; i++) {
String name = "param_" + i;
// indexOf: linear scan of m_rows
for (int j = 0; j < rows.size(); j++) {
ops++;
if (rows.get(j).equals(name)) break;
}
}
return ops;
}
static long rowsIndexOfFixed(int numParams) {
Map<String, Integer> rowIndex = new HashMap<>();
for (int i = 0; i < numParams; i++) rowIndex.put("param_" + i, i);
long ops = 0;
for (int i = 0; i < numParams; i++) {
ops++; // O(1) HashMap lookup
rowIndex.get("param_" + i);
}
return ops;
}
// --- kdenlive-0008: UrlListParamWidget addItemsInSameFolder ---
static long urlListFindDefect(int numEntries, int numExisting) {
// QMap values iterated with std::find for each directory entry
Map<String, String> listValues = new LinkedHashMap<>();
for (int i = 0; i < numExisting; i++) {
listValues.put("file_" + i, "/path/to/file_" + i + ".png");
}
long ops = 0;
for (int i = 0; i < numEntries; i++) {
String path = "/dir/entry_" + i + ".png";
// std::find iterates all map values
for (String val : listValues.values()) {
ops++;
if (val.equals(path)) break;
}
}
return ops;
}
static long urlListFindFixed(int numEntries, int numExisting) {
Map<String, String> listValues = new LinkedHashMap<>();
Set<String> existingValues = new HashSet<>();
for (int i = 0; i < numExisting; i++) {
String path = "/path/to/file_" + i + ".png";
listValues.put("file_" + i, path);
existingValues.add(path);
}
long ops = 0;
for (int i = 0; i < numEntries; i++) {
ops++; // O(1) HashSet lookup
existingValues.contains("/dir/entry_" + i + ".png");
}
return ops;
}
public static void main(String[] args) {
int pass = 0, fail = 0;
@ -199,6 +342,50 @@ public class KdenliveTest {
if (ok) pass++; else fail++;
}
// Test kdenlive-0005: PreviewManager chunks (500 dirty, 500 new)
{
long defectOps = previewChunksDefect(500, 500);
long fixedOps = previewChunksFixed(500, 500);
double ratio = (double) defectOps / fixedOps;
boolean ok = ratio > 50.0;
System.out.printf("kdenlive-0005 PreviewManager chunk contains: defect=%d fixed=%d ratio=%.1fx %s%n",
defectOps, fixedOps, ratio, ok ? "PASS" : "FAIL");
if (ok) pass++; else fail++;
}
// Test kdenlive-0006: canceled guides (500 guides, 200 canceled)
{
long defectOps = canceledGuidesDefect(500, 200);
long fixedOps = canceledGuidesFixed(500, 200);
double ratio = (double) defectOps / fixedOps;
boolean ok = ratio > 50.0;
System.out.printf("kdenlive-0006 canceled guides linear find: defect=%d fixed=%d ratio=%.1fx %s%n",
defectOps, fixedOps, ratio, ok ? "PASS" : "FAIL");
if (ok) pass++; else fail++;
}
// Test kdenlive-0007: m_rows indexOf (50 params)
{
long defectOps = rowsIndexOfDefect(50);
long fixedOps = rowsIndexOfFixed(50);
double ratio = (double) defectOps / fixedOps;
boolean ok = ratio > 10.0;
System.out.printf("kdenlive-0007 m_rows indexOf in loop: defect=%d fixed=%d ratio=%.1fx %s%n",
defectOps, fixedOps, ratio, ok ? "PASS" : "FAIL");
if (ok) pass++; else fail++;
}
// Test kdenlive-0008: urllist addItemsInSameFolder (300 entries, 200 existing)
{
long defectOps = urlListFindDefect(300, 200);
long fixedOps = urlListFindFixed(300, 200);
double ratio = (double) defectOps / fixedOps;
boolean ok = ratio > 50.0;
System.out.printf("kdenlive-0008 urllist value find: defect=%d fixed=%d ratio=%.1fx %s%n",
defectOps, fixedOps, ratio, ok ? "PASS" : "FAIL");
if (ok) pass++; else fail++;
}
System.out.printf("%nSummary: %d/%d PASS%n", pass, pass + fail);
if (fail > 0) System.exit(1);
}