package unit; /** * CWE-407 unit test: OpenDaylight ShardManagerSnapshot.getShardList().contains() * Defect: ImmutableList.contains() O(n) called per CreateShard during recovery * Fix: ImmutableSet.contains() O(1) * * slow(): ImmutableList — O(S) linear scan per shard lookup; S*S total during recovery * fast(): ImmutableSet — O(1) hash lookup per shard; S total during recovery * Assert: slowOps > fastOps * 5 (S=200 shards → ~20000 vs ~200 comparisons) */ public class OdlShardManagerSnapshotTest { static long eqCount; static class ShardName { final String name; OdlShardManagerSnapshotTest.Counter counter; ShardName(String name, OdlShardManagerSnapshotTest.Counter c) { this.name = name; this.counter = c; } @Override public boolean equals(Object o) { if (counter != null) counter.increment(); if (!(o instanceof ShardName)) return false; return name.equals(((ShardName) o).name); } @Override public int hashCode() { return name.hashCode(); } @Override public String toString() { return name; } } static class Counter { long count = 0; void increment() { count++; } } /** Slow: ImmutableList — O(S) contains per lookup. */ static long slow(int shardCount) { Counter c = new Counter(); java.util.List shards = new java.util.ArrayList<>(); for (int i = 0; i < shardCount; i++) { shards.add(new ShardName("shard-" + i, c)); } com.google.common.collect.ImmutableList snapshot = com.google.common.collect.ImmutableList.copyOf(shards); // Simulate S shard creations, each checking if shard was in snapshot for (int i = 0; i < shardCount; i++) { ShardName query = new ShardName("shard-" + i, null); for (ShardName s : snapshot) { if (s.equals(query)) break; } } return c.count; } /** Fast: ImmutableSet — O(1) contains per lookup. */ static long fast(int shardCount) { Counter c = new Counter(); java.util.Set shards = new java.util.HashSet<>(); for (int i = 0; i < shardCount; i++) { shards.add(new ShardName("shard-" + i, c)); } com.google.common.collect.ImmutableSet snapshot = com.google.common.collect.ImmutableSet.copyOf(shards); for (int i = 0; i < shardCount; i++) { ShardName query = new ShardName("shard-" + i, null); snapshot.contains(query); } return c.count; } public static void main(String[] args) { int S = 200; int MULTIPLIER = 5; long sOps = slow(S); long fOps = fast(S); System.out.println("S=" + S + " shards"); System.out.println("slow (ImmutableList.contains): " + sOps + " equals() calls"); System.out.println("fast (ImmutableSet.contains): " + fOps + " equals() calls"); if (sOps > fOps * MULTIPLIER) { System.out.println("1/1 PASS (slow=" + sOps + " > fast*" + MULTIPLIER + "=" + (fOps * MULTIPLIER) + ")"); } else { System.out.println("1/1 FAIL (slow=" + sOps + " not > fast*" + MULTIPLIER + "=" + (fOps * MULTIPLIER) + ")"); System.exit(1); } } }