작업 정의 : 사용자 지정 동시 수집 또는 동시 환경에서 수집을 조작하는 일부 컨테이너를 테스트해야합니다. 더 정확하게 - 읽기 API와 쓰기 API가 있습니다. 일관성없는 데이터를 얻을 수있는 시나리오가 있는지 테스트해야합니다.동시 테스트 : 테스트 사례 시나리오 자동화
문제 : 모든 동시 테스트 프레임 워크 (MultiThreadedTC
같은 , 내 질문의 MultiThreadedTc 섹션을 확인합니다) 당신에게 비동기 코드 실행 순서를 제어 할 수있는 기능을 제공합니다. 나는 당신이 스스로 중요한 시나리오를 가정해야한다는 뜻입니다.
광범위한 질문 : @SharedResource, @ readAPI, @writeAPI와 같은 특수 효과를 사용할 수 있고 데이터가 항상 일관성이 있는지 확인할 수있는 프레임 워크가 있습니까? 그게 불가능한가요? 아니면 시작 아이디어가 누설 되었습니까?
주석 : 이러한 프레임 워크가 없지만 아이디어가 매력적이라면 저에게 연락하거나 아이디어를 제안 할 수 있습니다.
좁은 질문 : 나는 동시성이 새로 도입되었습니다. 다음 시나리오에서 어떤 시나리오를 테스트해야하는지 제안 할 수 있습니까? (PeerContainer
클래스를 보면)
PeerContainer :
public class PeersContainer {
public class DaemonThreadFactory implements ThreadFactory {
private int counter = 1;
private final String prefix = "Daemon";
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r, prefix + "-" + counter);
thread.setDaemon(true);
counter++;
return thread;
}
}
private static class CacheCleaner implements Runnable {
private final Cache<Long, BlockingDeque<Peer>> cache;
public CacheCleaner(Cache<Long, BlockingDeque<Peer>> cache) {
this.cache = cache;
Thread.currentThread().setDaemon(true);
}
@Override
public void run() {
cache.cleanUp();
}
}
private final static int MAX_CACHE_SIZE = 100;
private final static int STRIPES_AMOUNT = 10;
private final static int PEER_ACCESS_TIMEOUT_MIN = 30;
private final static int CACHE_CLEAN_FREQUENCY_MIN = 1;
private final static PeersContainer INSTANCE;
private final Cache<Long, BlockingDeque<Peer>> peers = CacheBuilder.newBuilder()
.maximumSize(MAX_CACHE_SIZE)
.expireAfterWrite(PEER_ACCESS_TIMEOUT_MIN, TimeUnit.MINUTES)
.removalListener(new RemovalListener<Long, BlockingDeque<Peer>>() {
public void onRemoval(RemovalNotification<Long, BlockingDeque<Peer>> removal) {
if (removal.getCause() == RemovalCause.EXPIRED) {
for (Peer peer : removal.getValue()) {
peer.sendLogoutResponse(peer);
}
}
}
})
.build();
private final Striped<Lock> stripes = Striped.lock(STRIPES_AMOUNT);
private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1, new DaemonThreadFactory());
private PeersContainer() {
scheduledExecutorService.schedule(new CacheCleaner(peers), CACHE_CLEAN_FREQUENCY_MIN, TimeUnit.MINUTES);
}
static {
INSTANCE = new PeersContainer();
}
public static PeersContainer getInstance() {
return INSTANCE;
}
private final Cache<Long, UserAuthorities> authToRestore = CacheBuilder.newBuilder()
.maximumSize(MAX_CACHE_SIZE)
.expireAfterWrite(PEER_ACCESS_TIMEOUT_MIN, TimeUnit.MINUTES)
.build();
public Collection<Peer> getPeers(long sessionId) {
return Collections.unmodifiableCollection(peers.getIfPresent(sessionId));
}
public Collection<Peer> getAllPeers() {
BlockingDeque<Peer> result = new LinkedBlockingDeque<Peer>();
for (BlockingDeque<Peer> deque : peers.asMap().values()) {
result.addAll(deque);
}
return Collections.unmodifiableCollection(result);
}
public boolean addPeer(Peer peer) {
long key = peer.getSessionId();
Lock lock = stripes.get(key);
lock.lock();
try {
BlockingDeque<Peer> userPeers = peers.getIfPresent(key);
if (userPeers == null) {
userPeers = new LinkedBlockingDeque<Peer>();
peers.put(key, userPeers);
}
UserAuthorities authorities = restoreSession(key);
if (authorities != null) {
peer.setAuthorities(authorities);
}
return userPeers.offer(peer);
} finally {
lock.unlock();
}
}
public void removePeer(Peer peer) {
long sessionId = peer.getSessionId();
Lock lock = stripes.get(sessionId);
lock.lock();
try {
BlockingDeque<Peer> userPeers = peers.getIfPresent(sessionId);
if (userPeers != null && !userPeers.isEmpty()) {
UserAuthorities authorities = userPeers.getFirst().getAuthorities();
authToRestore.put(sessionId, authorities);
userPeers.remove(peer);
}
} finally {
lock.unlock();
}
}
void removePeers(long sessionId) {
Lock lock = stripes.get(sessionId);
lock.lock();
try {
peers.invalidate(sessionId);
authToRestore.invalidate(sessionId);
} finally {
lock.unlock();
}
}
private UserAuthorities restoreSession(long sessionId) {
BlockingDeque<Peer> activePeers = peers.getIfPresent(sessionId);
return (activePeers != null && !activePeers.isEmpty()) ? activePeers.getFirst().getAuthorities() : authToRestore.getIfPresent(sessionId);
}
public void resetAccessedTimeout(long sessionId) {
Lock lock = stripes.get(sessionId);
lock.lock();
try {
BlockingDeque<Peer> deque = peers.getIfPresent(sessionId);
peers.invalidate(sessionId);
peers.put(sessionId, deque);
} finally {
lock.unlock();
}
}
}
MultiThreadedTC 테스트 케이스 샘플 : [질문의 선택 섹션]
public class ProducerConsumerTest extends MultithreadedTestCase {
private LinkedTransferQueue<String> queue;
@Override
public void initialize() {
super.initialize();
queue = new LinkedTransferQueue<String>();
}
public void thread1() throws InterruptedException {
String ret = queue.take();
}
public void thread2() throws InterruptedException {
waitForTick(1);
String ret = queue.take();
}
public void thread3() {
waitForTick(1);
waitForTick(2);
queue.put("Event 1");
queue.put("Event 2");
}
@Override
public void finish() {
super.finish();
assertEquals(true, queue.size() == 0);
}
}
이유는 무엇입니까? 처음부터 자신 만의'ConcurrentHashMap'을 작성하기로 결정했다고 상상해보십시오. 하나의 공유 테이블, 3 개의 읽기 메소드 및 3 개의 쓰기 메소드가 있습니다. 다른 스레드에서 이러한 메소드를 다른 순서로 실행할 수 있습니다. 여기에서 가장 중요한 도전은 100 개의 가능한 유스 케이스를 10 가지로 줄이는 것입니다. –
테스트 케이스는 메소드 호출이 아닌 구현 바이트 코드 시퀀스의 가능한 수에 비례해야합니다. 단지 100 개의 VM 연산 코드와 4 개의 코어 만있는 작은 클래스의 경우에도 은하의 모든 별들의 해변에있는 모래알의 수보다 큽니다. – soru