2012-01-15 8 views
2

MainActivityは、リモートサーバーからUDPコールを受信できるUdpListenerという別のスレッドを最初に起動します。コンテンツ "UPDATE"を持つパケットを受信した場合、UdpListenerはMainActivityに何かを行うよう通知する必要があります。Android JUnitテストでタイミング問題が発生しました

(実際のアプリケーションでは、私のアプリケーションがリモートサーバーでリッスンしているように見えます)リモートサーバーで利用できる新しいデータがある場合は、すべてのクライアント(アプリケーション)にUDPで通知します。 HTTPを使用して新しいデータをダウンロードできることがわかります)。

これをJUnitテストでシミュレートしようとしました。テストは同様にそれがUdpListenerにUDPコールを送るようMainActivityを皮肉っ内部クラスが含まれています。良いことは、私のコンセプトの作品ということである

@RunWith(RobolectricTestRunner.class) 
public class UdpListenerTest { 

    private MainActivityMock mainActivityMock = new MainActivityMock(); 

    @Before 
    public void setUp() throws Exception { 
     mainActivityMock.setUpdate(false); 
    } 

    @After 
    public void tearDown() throws Exception { 
     mainActivityMock.setUpdate(false); 
    } 

    @Test 
    public void canNotifyObserver() throws IOException, InterruptedException { 
     UdpListener udpListener = new UdpListener(mainActivityMock); 
     udpListener.setRunning(true); 
     udpListener.start();  

     InetAddress ipAddress = InetAddress.getByName("localhost"); 
     DatagramSocket datagramSocket = new DatagramSocket(); 
     DatagramPacket sendPacket = new DatagramPacket("UPDATE".getBytes(), "UPDATE".length(), ipAddress, 9800); 
     datagramSocket.send(sendPacket); 
     datagramSocket.close(); 

     assertTrue(mainActivityMock.isUpdate()); 

     udpListener.setRunning(false); 
    } 

    private class MainActivityMock implements Observer { 

     private boolean update = false; 

     @Override 
     public void update() { 
      update = true; 
     } 

     public boolean isUpdate() { 
      return update; 
     } 

     public void setUpdate(boolean update) { 
      this.update = update; 
     } 
    } 
} 

public class UdpListener extends Thread implements Subject { 
    private DatagramSocket serverSocket; 
    private DatagramPacket receivedPacket; 
    private boolean running = false; 
    private String sentence = ""; 

    private Observer observer; 

    private static final String TAG = "UdpListener"; 

    public UdpListener(Observer o) throws SocketException { 
     serverSocket = new DatagramSocket(9800); 
     setRunning(true); 

     observer = o; 
    } 

    @Override 
    public void run() { 
     setName(TAG); 
     while (isRunning()) { 
      byte[] receivedData = new byte[1024]; 
      receivedPacket = new DatagramPacket(receivedData, receivedData.length); 
      try { 
       serverSocket.receive(receivedPacket); 
      } 
      catch (IOException e) { 
       Log.w(TAG, e.getMessage()); 
      } 

      try { 
       sentence = new String(receivedPacket.getData(), 0, receivedPacket.getLength(), "UTF-8"); 
       if ("UPDATE".equals(sentence)) { 
        notifyObserver(); 
       } 
      } 
      catch (UnsupportedEncodingException e) { 
       Log.w(TAG, e.getMessage()); 
      } 
     } 
    } 

    private boolean isRunning() { 
     return running; 
    } 

    public void setRunning(boolean running) { 
     this.running = running; 
    } 

    @Override 
    public void notifyObserver() { 
     observer.update(); 
    } 
} 

これは、対応するテストです。しかし、このテストはしません。これは、私がこの行のブレークポイントで停止してdatagramSocket.close();を停止してから約1秒待つときにのみ実行されることを意味します。なぜこれが起こるのかは明らかです。しかし、どうすれば自動的にそれを行うことができますか?私はwait()を使うことを考えましたが、そのために他のスレッドからnotify()を呼び出さなければなりません。 CountDownLatchと同じ問題。私はそれを解決する方法がわかりませんUdpListenerを変更せずに。

答えて

0

指定したタイムアウトで簡単なループを書くことができます。

try { 
    long timeout = 500; // ms 
    long lastTime = System.currentTimeMillis(); 
    while(timeout > 0 && !mainActivityMock.isUpdate()) { 
    Thread.sleep(timeout); 
    timeout -= System.currentTimeMillis() - lastTime; 
    lastTime = System.currentTimeMillis(); 
    } 
} catch(InterruptedException e) { 

} finally { 
    assertTrue(mainActivityMock.isUpdate()); 
} 

ところで - あなたはvolatileにごrunning属性を宣言する必要があります。

+0

が完璧です。 Btw、あなたは揮発性で正しいです。私はちょうどそれを追加した。 – Bevor

0

解決方法の1つは、受信した結果を格納するためにサイズ1のブロッキングキューを使用することです。

更新パッケージ(または他のパッケージ)がキューに入れられるまで、isUpdate(ブロック化キューから要素を取ります)の要求はブロックされます。

あなたのすべての通話をノンブロッキングにしたい場合は、結果を受け取るために未来を使用することができます。 Future.get()は、結果が受信されたときにontilをブロックします。

関連する問題