package unit; import java.util.*; /** * CWE-407 unit test for simplex-chat defects: * simplex-chat-0001: APIMembersRole elem list scan — O(M*K) → O(M+K) * simplex-chat-0002: APIBlockMembersForAll elem list scan — O(M*K) → O(M+K) * simplex-chat-0003: introduceToRemaining notElem list scan — O(M*K) → O(M+K) * * Compile: javac -d . SimplexChatTest.java && java -ea unit.SimplexChatTest */ public class SimplexChatTest { static void bench(String label, Runnable slow, Runnable fast, long sOps, long fOps) { slow.run(); fast.run(); // warm up long t0 = System.nanoTime(); slow.run(); long sMs = (System.nanoTime() - t0) / 1_000_000; long t1 = System.nanoTime(); fast.run(); long fMs = (System.nanoTime() - t1) / 1_000_000; double r = fOps > 0 ? (double) sOps / fOps : 0; System.out.printf(" %-52s slow:%4dms (%,d ops) fast:%4dms (%,d ops) speedup:%.0fx%n", label, sMs, sOps, fMs, fOps, r); } // ----------------------------------------------------------------------- // Simulate APIMembersRole / APIBlockMembersForAll: // foldr over members list, elem check against memberIds list (SLOW) // vs. pre-built HashSet membership check (FAST) // M = group members, K = selected member IDs // ----------------------------------------------------------------------- static long slowMembersRole(List members, List memberIds) { long ops = 0; List selected = new ArrayList<>(); for (int memberId : members) { for (int id : memberIds) { // O(K) linear scan per member ops++; if (memberId == id) { selected.add(memberId); break; } } } return ops; } static long fastMembersRole(List members, List memberIds) { Set idSet = new HashSet<>(memberIds); // O(K) once long ops = 0; List selected = new ArrayList<>(); for (int memberId : members) { ops++; // O(1) HashSet lookup if (idSet.contains(memberId)) { selected.add(memberId); } } return ops; } // ----------------------------------------------------------------------- // Simulate introduceToRemaining: // filter over members, notElem check against introducedGMIds list (SLOW) // vs. pre-built HashSet (FAST) // M = members, K = already-introduced IDs // ----------------------------------------------------------------------- static long slowIntroduceToRemaining(List members, List introducedIds) { long ops = 0; List recipients = new ArrayList<>(); for (int memberId : members) { boolean notIntroduced = true; for (int iid : introducedIds) { // O(K) linear scan per member ops++; if (memberId == iid) { notIntroduced = false; break; } } if (notIntroduced) { recipients.add(memberId); } } return ops; } static long fastIntroduceToRemaining(List members, List introducedIds) { Set introducedSet = new HashSet<>(introducedIds); // O(K) once long ops = 0; List recipients = new ArrayList<>(); for (int memberId : members) { ops++; // O(1) HashSet lookup if (!introducedSet.contains(memberId)) { recipients.add(memberId); } } return ops; } public static void main(String[] args) { System.out.println("simplex-chat CWE-407 benchmarks"); System.out.println("=".repeat(80)); // --- simplex-chat-0001 / -0002: APIMembersRole / APIBlockMembersForAll --- // M=1000 group members, K=10 selected (typical admin bulk-role-change) { int M = 1000, K = 10; List members = new ArrayList<>(M); for (int i = 0; i < M; i++) members.add(i); List memberIds = members.subList(0, K); long sOps = slowMembersRole(members, memberIds); long fOps = fastMembersRole(members, memberIds); bench(String.format("0001/0002 APIMembersRole/Block M=%d K=%d", M, K), () -> slowMembersRole(members, memberIds), () -> fastMembersRole(members, memberIds), sOps, fOps); assert sOps >= fOps * 9 : "expected >= 9x speedup, got sOps=" + sOps + " fOps=" + fOps; } // M=1000, K=100 (larger multi-select) { int M = 1000, K = 100; List members = new ArrayList<>(M); for (int i = 0; i < M; i++) members.add(i); List memberIds = members.subList(0, K); long sOps = slowMembersRole(members, memberIds); long fOps = fastMembersRole(members, memberIds); bench(String.format("0001/0002 APIMembersRole/Block M=%d K=%d", M, K), () -> slowMembersRole(members, memberIds), () -> fastMembersRole(members, memberIds), sOps, fOps); assert sOps >= fOps * 50 : "expected >= 50x speedup, got sOps=" + sOps + " fOps=" + fOps; } // --- simplex-chat-0003: introduceToRemaining --- // M=1000 members, K=900 already introduced (near-full group join) { int M = 1000, K = 900; List members = new ArrayList<>(M); for (int i = 0; i < M; i++) members.add(i); List introducedIds = new ArrayList<>(members.subList(0, K)); long sOps = slowIntroduceToRemaining(members, introducedIds); long fOps = fastIntroduceToRemaining(members, introducedIds); bench(String.format("0003 introduceToRemaining M=%d K=%d", M, K), () -> slowIntroduceToRemaining(members, introducedIds), () -> fastIntroduceToRemaining(members, introducedIds), sOps, fOps); assert sOps >= fOps * 400 : "expected >= 400x speedup, got sOps=" + sOps + " fOps=" + fOps; } // M=500, K=250 (half-full group) { int M = 500, K = 250; List members = new ArrayList<>(M); for (int i = 0; i < M; i++) members.add(i); List introducedIds = new ArrayList<>(members.subList(0, K)); long sOps = slowIntroduceToRemaining(members, introducedIds); long fOps = fastIntroduceToRemaining(members, introducedIds); bench(String.format("0003 introduceToRemaining M=%d K=%d", M, K), () -> slowIntroduceToRemaining(members, introducedIds), () -> fastIntroduceToRemaining(members, introducedIds), sOps, fOps); assert sOps >= fOps * 100 : "expected >= 100x speedup, got sOps=" + sOps + " fOps=" + fOps; } System.out.println("=".repeat(80)); System.out.println("All assertions passed."); } }