178 lines
7.2 KiB
Java
178 lines
7.2 KiB
Java
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<Integer> members, List<Integer> memberIds) {
|
|
long ops = 0;
|
|
List<Integer> 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<Integer> members, List<Integer> memberIds) {
|
|
Set<Integer> idSet = new HashSet<>(memberIds); // O(K) once
|
|
long ops = 0;
|
|
List<Integer> 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<Integer> members, List<Integer> introducedIds) {
|
|
long ops = 0;
|
|
List<Integer> 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<Integer> members, List<Integer> introducedIds) {
|
|
Set<Integer> introducedSet = new HashSet<>(introducedIds); // O(K) once
|
|
long ops = 0;
|
|
List<Integer> 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<Integer> members = new ArrayList<>(M);
|
|
for (int i = 0; i < M; i++) members.add(i);
|
|
List<Integer> 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<Integer> members = new ArrayList<>(M);
|
|
for (int i = 0; i < M; i++) members.add(i);
|
|
List<Integer> 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<Integer> members = new ArrayList<>(M);
|
|
for (int i = 0; i < M; i++) members.add(i);
|
|
List<Integer> 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<Integer> members = new ArrayList<>(M);
|
|
for (int i = 0; i < M; i++) members.add(i);
|
|
List<Integer> 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.");
|
|
}
|
|
}
|