package unit; import java.util.*; /** * PitiviMediaLibraryTest — CWE-407 pitivi-0001 * * Models MediaLibraryWidget.update_asset_thumbs() in pitivi/medialibrary.py lines 918-921: * slow() = O(I * U): for item in store: if item.id in asset_uris (list) * fast() = O(I + U): asset_uris_set = set(asset_uris); for item in store: if id in set * * Parameters: I=items in media library store, U=changed URIs from ThumbnailCache.update_caches(). * Assert: slowOps > fastOps * 5x at I=300, U=30. */ public class PitiviMediaLibraryTest { static long slowOps; static long fastOps; /** * Slow: O(I * U) — models: if item.asset.props.id in asset_uris (list) * asset_uris is list[str] returned by ThumbnailCache.update_caches(). */ static Set updateThumbsSlow(List storeIds, List assetUris) { slowOps = 0; Set disregarded = new LinkedHashSet<>(); for (String id : storeIds) { for (String uri : assetUris) { // O(U) linear scan per store item slowOps++; if (id.equals(uri)) { disregarded.add(id); break; } } } return disregarded; } /** * Fast: O(I + U) — models: asset_uris_set = set(asset_uris); if id in asset_uris_set * Build set once: O(U). Lookup per item: O(1). */ static Set updateThumbsFast(List storeIds, List assetUris) { fastOps = 0; Set uriSet = new HashSet<>(assetUris); fastOps += assetUris.size(); // set construction Set disregarded = new LinkedHashSet<>(); for (String id : storeIds) { fastOps++; // O(1) hash lookup if (uriSet.contains(id)) { disregarded.add(id); } } return disregarded; } public static void main(String[] args) { int passed = 0; int failed = 0; // Test 1: correctness — same items disregarded { List store = Arrays.asList( "file:///home/user/video/clip1.mp4", "file:///home/user/video/clip2.mp4", "file:///home/user/video/clip3.mp4", "file:///home/user/video/clip4.mp4" ); List changed = Arrays.asList( "file:///home/user/video/clip1.mp4", "file:///home/user/video/clip3.mp4" ); Set slow = updateThumbsSlow(store, changed); Set fast = updateThumbsFast(store, changed); if (slow.equals(fast) && slow.size() == 2) { System.out.println("PASS test1: both methods disregard same 2 items"); passed++; } else { System.out.println("FAIL test1: slow=" + slow + " fast=" + fast); failed++; } } // Test 2: op-count speedup at I=300, U=30 { int I = 300; int U = 30; List storeIds = new ArrayList<>(); for (int i = 0; i < I; i++) storeIds.add("file:///video/clip-" + i + ".mp4"); // U changed URIs, every 5th clip changed List assetUris = new ArrayList<>(); for (int i = 0; i < U; i++) assetUris.add("file:///video/clip-" + (i * 5) + ".mp4"); Set slow = updateThumbsSlow(storeIds, assetUris); long sOps = slowOps; Set fast = updateThumbsFast(storeIds, assetUris); long fOps = fastOps; if (!slow.equals(fast)) { System.out.println("FAIL test2: results differ slow=" + slow.size() + " fast=" + fast.size()); failed++; } else { double ratio = (double) sOps / fOps; if (ratio >= 5.0) { System.out.printf("PASS test2: I=%d U=%d sOps=%d fOps=%d ratio=%.1fx%n", I, U, sOps, fOps, ratio); passed++; } else { System.out.printf("FAIL test2: ratio=%.1fx (want >=5x) sOps=%d fOps=%d%n", ratio, sOps, fOps); failed++; } } } // Test 3: no changed files (empty asset_uris) { List store = Arrays.asList("file:///a.mp4", "file:///b.mp4"); Set slow = updateThumbsSlow(store, Collections.emptyList()); Set fast = updateThumbsFast(store, Collections.emptyList()); if (slow.isEmpty() && fast.isEmpty()) { System.out.println("PASS test3: empty asset_uris handled correctly"); passed++; } else { System.out.println("FAIL test3: expected empty, got slow=" + slow + " fast=" + fast); failed++; } } // Test 4: all files changed { List store = new ArrayList<>(); for (int i = 0; i < 10; i++) store.add("file:///clip-" + i + ".mp4"); Set slow = updateThumbsSlow(store, new ArrayList<>(store)); Set fast = updateThumbsFast(store, new ArrayList<>(store)); if (slow.equals(fast) && slow.size() == 10) { System.out.println("PASS test4: all-changed case correct"); passed++; } else { System.out.println("FAIL test4: slow=" + slow.size() + " fast=" + fast.size()); failed++; } } System.out.printf("%nResult: %d/%d tests passed%n", passed, passed + failed); if (failed > 0) System.exit(1); } }