java-topology/defects/simplex-chat/unit/SimplexChatTest.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.");
}
}