2014-03-04 3 views
2

작업 정의 : 사용자 지정 동시 수집 또는 동시 환경에서 수집을 조작하는 일부 컨테이너를 테스트해야합니다. 더 정확하게 - 읽기 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); 
    } 
} 

답변

2

은 정적 분석을위한 작업 같은 소리하지 테스트 케이스를 여러 조를 실행할 시간이 없다면 테스트 할 수 있습니다. 당신은 꽤 많이 다중 스레드 동작을 테스트 할 수 없습니다. 단일 스레드에서 동작을 테스트 한 다음 스레딩 버그가 발생하지 않았 음을 증명합니다.

시도 :

http://www.contemplateltd.com/threadsafe

http://checkthread.org/

+0

이유는 무엇입니까? 처음부터 자신 만의'ConcurrentHashMap'을 작성하기로 결정했다고 상상해보십시오. 하나의 공유 테이블, 3 개의 읽기 메소드 및 3 개의 쓰기 메소드가 있습니다. 다른 스레드에서 이러한 메소드를 다른 순서로 실행할 수 있습니다. 여기에서 가장 중요한 도전은 100 개의 가능한 유스 케이스를 10 가지로 줄이는 것입니다. –

+0

테스트 케이스는 메소드 호출이 아닌 구현 바이트 코드 시퀀스의 가능한 수에 비례해야합니다. 단지 100 개의 VM 연산 코드와 4 개의 코어 만있는 작은 클래스의 경우에도 은하의 모든 별들의 해변에있는 모래알의 수보다 큽니다. – soru