| OLD | NEW |
| 1 /* | 1 /* |
| 2 * libjingle | 2 * libjingle |
| 3 * Copyright 2013 Google Inc. | 3 * Copyright 2013 Google Inc. |
| 4 * | 4 * |
| 5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
| 6 * modification, are permitted provided that the following conditions are met: | 6 * modification, are permitted provided that the following conditions are met: |
| 7 * | 7 * |
| 8 * 1. Redistributions of source code must retain the above copyright notice, | 8 * 1. Redistributions of source code must retain the above copyright notice, |
| 9 * this list of conditions and the following disclaimer. | 9 * this list of conditions and the following disclaimer. |
| 10 * 2. Redistributions in binary form must reproduce the above copyright notice, | 10 * 2. Redistributions in binary form must reproduce the above copyright notice, |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 43 import java.util.TreeSet; | 43 import java.util.TreeSet; |
| 44 import java.util.concurrent.CountDownLatch; | 44 import java.util.concurrent.CountDownLatch; |
| 45 import java.util.concurrent.TimeUnit; | 45 import java.util.concurrent.TimeUnit; |
| 46 | 46 |
| 47 import static junit.framework.Assert.*; | 47 import static junit.framework.Assert.*; |
| 48 | 48 |
| 49 /** End-to-end tests for PeerConnection.java. */ | 49 /** End-to-end tests for PeerConnection.java. */ |
| 50 public class PeerConnectionTest { | 50 public class PeerConnectionTest { |
| 51 // Set to true to render video. | 51 // Set to true to render video. |
| 52 private static final boolean RENDER_TO_GUI = false; | 52 private static final boolean RENDER_TO_GUI = false; |
| 53 private static final int TIMEOUT_SECONDS = 20; |
| 53 private TreeSet<String> threadsBeforeTest = null; | 54 private TreeSet<String> threadsBeforeTest = null; |
| 54 | 55 |
| 55 private static class ObserverExpectations implements PeerConnection.Observer, | 56 private static class ObserverExpectations implements PeerConnection.Observer, |
| 56 VideoRenderer.Callbacks, | 57 VideoRenderer.Callbacks, |
| 57 DataChannel.Observer, | 58 DataChannel.Observer, |
| 58 StatsObserver { | 59 StatsObserver { |
| 59 private final String name; | 60 private final String name; |
| 60 private int expectedIceCandidates = 0; | 61 private int expectedIceCandidates = 0; |
| 61 private int expectedErrors = 0; | 62 private int expectedErrors = 0; |
| 62 private int expectedRenegotiations = 0; | 63 private int expectedRenegotiations = 0; |
| (...skipping 277 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 340 stillWaitingForExpectations.add("expectedRemoteDataChannelLabels: " + | 341 stillWaitingForExpectations.add("expectedRemoteDataChannelLabels: " + |
| 341 expectedRemoteDataChannelLabels.size()); | 342 expectedRemoteDataChannelLabels.size()); |
| 342 } | 343 } |
| 343 if (expectedStatsCallbacks != 0) { | 344 if (expectedStatsCallbacks != 0) { |
| 344 stillWaitingForExpectations.add( | 345 stillWaitingForExpectations.add( |
| 345 "expectedStatsCallbacks: " + expectedStatsCallbacks); | 346 "expectedStatsCallbacks: " + expectedStatsCallbacks); |
| 346 } | 347 } |
| 347 return stillWaitingForExpectations; | 348 return stillWaitingForExpectations; |
| 348 } | 349 } |
| 349 | 350 |
| 350 public void waitForAllExpectationsToBeSatisfied() { | 351 public boolean waitForAllExpectationsToBeSatisfied(int timeoutSeconds) { |
| 351 // TODO(fischman): problems with this approach: | 352 // TODO(fischman): problems with this approach: |
| 352 // - come up with something better than a poll loop | 353 // - come up with something better than a poll loop |
| 353 // - avoid serializing expectations explicitly; the test is not as robust | 354 // - avoid serializing expectations explicitly; the test is not as robust |
| 354 // as it could be because it must place expectations between wait | 355 // as it could be because it must place expectations between wait |
| 355 // statements very precisely (e.g. frame must not arrive before its | 356 // statements very precisely (e.g. frame must not arrive before its |
| 356 // expectation, and expectation must not be registered so early as to | 357 // expectation, and expectation must not be registered so early as to |
| 357 // stall a wait). Use callbacks to fire off dependent steps instead of | 358 // stall a wait). Use callbacks to fire off dependent steps instead of |
| 358 // explicitly waiting, so there can be just a single wait at the end of | 359 // explicitly waiting, so there can be just a single wait at the end of |
| 359 // the test. | 360 // the test. |
| 361 long endTime = System.currentTimeMillis() + 1000 * timeoutSeconds; |
| 360 TreeSet<String> prev = null; | 362 TreeSet<String> prev = null; |
| 361 TreeSet<String> stillWaitingForExpectations = unsatisfiedExpectations(); | 363 TreeSet<String> stillWaitingForExpectations = unsatisfiedExpectations(); |
| 362 while (!stillWaitingForExpectations.isEmpty()) { | 364 while (!stillWaitingForExpectations.isEmpty()) { |
| 363 if (!stillWaitingForExpectations.equals(prev)) { | 365 if (!stillWaitingForExpectations.equals(prev)) { |
| 364 System.out.println( | 366 System.out.println( |
| 365 name + " still waiting at\n " + | 367 name + " still waiting at\n " + |
| 366 (new Throwable()).getStackTrace()[1] + | 368 (new Throwable()).getStackTrace()[1] + |
| 367 "\n for: " + | 369 "\n for: " + |
| 368 Arrays.toString(stillWaitingForExpectations.toArray())); | 370 Arrays.toString(stillWaitingForExpectations.toArray())); |
| 369 } | 371 } |
| 372 if (endTime < System.currentTimeMillis()) { |
| 373 System.out.println(name + " timed out waiting for: " |
| 374 + Arrays.toString(stillWaitingForExpectations.toArray())); |
| 375 return false; |
| 376 } |
| 370 try { | 377 try { |
| 371 Thread.sleep(10); | 378 Thread.sleep(10); |
| 372 } catch (InterruptedException e) { | 379 } catch (InterruptedException e) { |
| 373 throw new RuntimeException(e); | 380 throw new RuntimeException(e); |
| 374 } | 381 } |
| 375 prev = stillWaitingForExpectations; | 382 prev = stillWaitingForExpectations; |
| 376 stillWaitingForExpectations = unsatisfiedExpectations(); | 383 stillWaitingForExpectations = unsatisfiedExpectations(); |
| 377 } | 384 } |
| 378 if (prev == null) { | 385 if (prev == null) { |
| 379 System.out.println(name + " didn't need to wait at\n " + | 386 System.out.println(name + " didn't need to wait at\n " + |
| 380 (new Throwable()).getStackTrace()[1]); | 387 (new Throwable()).getStackTrace()[1]); |
| 381 } | 388 } |
| 389 return true; |
| 382 } | 390 } |
| 383 | 391 |
| 384 // This methods return a list of all currently gathered ice candidates or wa
its until | 392 // This methods return a list of all currently gathered ice candidates or wa
its until |
| 385 // 1 candidate have been gathered. | 393 // 1 candidate have been gathered. |
| 386 public List<IceCandidate> getAtLeastOneIceCandidate() throws InterruptedExce
ption { | 394 public List<IceCandidate> getAtLeastOneIceCandidate() throws InterruptedExce
ption { |
| 387 synchronized (gotIceCandidates) { | 395 synchronized (gotIceCandidates) { |
| 388 while (gotIceCandidates.isEmpty()) { | 396 while (gotIceCandidates.isEmpty()) { |
| 389 gotIceCandidates.wait(); | 397 gotIceCandidates.wait(); |
| 390 } | 398 } |
| 391 return new LinkedList<IceCandidate>(gotIceCandidates); | 399 return new LinkedList<IceCandidate>(gotIceCandidates); |
| (...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 654 for (IceCandidate candidate : offeringExpectations.getAtLeastOneIceCandidate
()) { | 662 for (IceCandidate candidate : offeringExpectations.getAtLeastOneIceCandidate
()) { |
| 655 answeringPC.addIceCandidate(candidate); | 663 answeringPC.addIceCandidate(candidate); |
| 656 } | 664 } |
| 657 | 665 |
| 658 // Wait for at least one ice candidate from the answering PC and forward the
m to the offering | 666 // Wait for at least one ice candidate from the answering PC and forward the
m to the offering |
| 659 // PC. | 667 // PC. |
| 660 for (IceCandidate candidate : answeringExpectations.getAtLeastOneIceCandidat
e()) { | 668 for (IceCandidate candidate : answeringExpectations.getAtLeastOneIceCandidat
e()) { |
| 661 offeringPC.addIceCandidate(candidate); | 669 offeringPC.addIceCandidate(candidate); |
| 662 } | 670 } |
| 663 | 671 |
| 664 offeringExpectations.waitForAllExpectationsToBeSatisfied(); | 672 assertTrue(offeringExpectations.waitForAllExpectationsToBeSatisfied(TIMEOUT_
SECONDS)); |
| 665 answeringExpectations.waitForAllExpectationsToBeSatisfied(); | 673 assertTrue(answeringExpectations.waitForAllExpectationsToBeSatisfied(TIMEOUT
_SECONDS)); |
| 666 | 674 |
| 667 assertEquals( | 675 assertEquals( |
| 668 PeerConnection.SignalingState.STABLE, offeringPC.signalingState()); | 676 PeerConnection.SignalingState.STABLE, offeringPC.signalingState()); |
| 669 assertEquals( | 677 assertEquals( |
| 670 PeerConnection.SignalingState.STABLE, answeringPC.signalingState()); | 678 PeerConnection.SignalingState.STABLE, answeringPC.signalingState()); |
| 671 | 679 |
| 672 // Test send & receive UTF-8 text. | 680 // Test send & receive UTF-8 text. |
| 673 answeringExpectations.expectMessage( | 681 answeringExpectations.expectMessage( |
| 674 ByteBuffer.wrap("hello!".getBytes(Charset.forName("UTF-8"))), false); | 682 ByteBuffer.wrap("hello!".getBytes(Charset.forName("UTF-8"))), false); |
| 675 DataChannel.Buffer buffer = new DataChannel.Buffer( | 683 DataChannel.Buffer buffer = new DataChannel.Buffer( |
| 676 ByteBuffer.wrap("hello!".getBytes(Charset.forName("UTF-8"))), false); | 684 ByteBuffer.wrap("hello!".getBytes(Charset.forName("UTF-8"))), false); |
| 677 assertTrue(offeringExpectations.dataChannel.send(buffer)); | 685 assertTrue(offeringExpectations.dataChannel.send(buffer)); |
| 678 answeringExpectations.waitForAllExpectationsToBeSatisfied(); | 686 assertTrue(answeringExpectations.waitForAllExpectationsToBeSatisfied(TIMEOUT
_SECONDS)); |
| 679 | 687 |
| 680 // Construct this binary message two different ways to ensure no | 688 // Construct this binary message two different ways to ensure no |
| 681 // shortcuts are taken. | 689 // shortcuts are taken. |
| 682 ByteBuffer expectedBinaryMessage = ByteBuffer.allocateDirect(5); | 690 ByteBuffer expectedBinaryMessage = ByteBuffer.allocateDirect(5); |
| 683 for (byte i = 1; i < 6; ++i) { | 691 for (byte i = 1; i < 6; ++i) { |
| 684 expectedBinaryMessage.put(i); | 692 expectedBinaryMessage.put(i); |
| 685 } | 693 } |
| 686 expectedBinaryMessage.flip(); | 694 expectedBinaryMessage.flip(); |
| 687 offeringExpectations.expectMessage(expectedBinaryMessage, true); | 695 offeringExpectations.expectMessage(expectedBinaryMessage, true); |
| 688 assertTrue(answeringExpectations.dataChannel.send( | 696 assertTrue(answeringExpectations.dataChannel.send( |
| 689 new DataChannel.Buffer( | 697 new DataChannel.Buffer( |
| 690 ByteBuffer.wrap(new byte[] { 1, 2, 3, 4, 5 }), true))); | 698 ByteBuffer.wrap(new byte[] { 1, 2, 3, 4, 5 }), true))); |
| 691 offeringExpectations.waitForAllExpectationsToBeSatisfied(); | 699 assertTrue(offeringExpectations.waitForAllExpectationsToBeSatisfied(TIMEOUT_
SECONDS)); |
| 692 | 700 |
| 693 offeringExpectations.expectStateChange(DataChannel.State.CLOSING); | 701 offeringExpectations.expectStateChange(DataChannel.State.CLOSING); |
| 694 answeringExpectations.expectStateChange(DataChannel.State.CLOSING); | 702 answeringExpectations.expectStateChange(DataChannel.State.CLOSING); |
| 695 offeringExpectations.expectStateChange(DataChannel.State.CLOSED); | 703 offeringExpectations.expectStateChange(DataChannel.State.CLOSED); |
| 696 answeringExpectations.expectStateChange(DataChannel.State.CLOSED); | 704 answeringExpectations.expectStateChange(DataChannel.State.CLOSED); |
| 697 answeringExpectations.dataChannel.close(); | 705 answeringExpectations.dataChannel.close(); |
| 698 offeringExpectations.dataChannel.close(); | 706 offeringExpectations.dataChannel.close(); |
| 699 | 707 |
| 700 if (RENDER_TO_GUI) { | 708 if (RENDER_TO_GUI) { |
| 701 try { | 709 try { |
| (...skipping 25 matching lines...) Expand all Loading... |
| 727 factory.dispose(); | 735 factory.dispose(); |
| 728 System.gc(); | 736 System.gc(); |
| 729 } | 737 } |
| 730 | 738 |
| 731 private static void shutdownPC( | 739 private static void shutdownPC( |
| 732 PeerConnection pc, ObserverExpectations expectations) { | 740 PeerConnection pc, ObserverExpectations expectations) { |
| 733 expectations.dataChannel.unregisterObserver(); | 741 expectations.dataChannel.unregisterObserver(); |
| 734 expectations.dataChannel.dispose(); | 742 expectations.dataChannel.dispose(); |
| 735 expectations.expectStatsCallback(); | 743 expectations.expectStatsCallback(); |
| 736 assertTrue(pc.getStats(expectations, null)); | 744 assertTrue(pc.getStats(expectations, null)); |
| 737 expectations.waitForAllExpectationsToBeSatisfied(); | 745 assertTrue(expectations.waitForAllExpectationsToBeSatisfied(TIMEOUT_SECONDS)
); |
| 738 expectations.expectIceConnectionChange(IceConnectionState.CLOSED); | 746 expectations.expectIceConnectionChange(IceConnectionState.CLOSED); |
| 739 expectations.expectSignalingChange(SignalingState.CLOSED); | 747 expectations.expectSignalingChange(SignalingState.CLOSED); |
| 740 pc.close(); | 748 pc.close(); |
| 741 expectations.waitForAllExpectationsToBeSatisfied(); | 749 assertTrue(expectations.waitForAllExpectationsToBeSatisfied(TIMEOUT_SECONDS)
); |
| 742 expectations.expectStatsCallback(); | 750 expectations.expectStatsCallback(); |
| 743 assertTrue(pc.getStats(expectations, null)); | 751 assertTrue(pc.getStats(expectations, null)); |
| 744 expectations.waitForAllExpectationsToBeSatisfied(); | 752 assertTrue(expectations.waitForAllExpectationsToBeSatisfied(TIMEOUT_SECONDS)
); |
| 745 | 753 |
| 746 System.out.println("FYI stats: "); | 754 System.out.println("FYI stats: "); |
| 747 int reportIndex = -1; | 755 int reportIndex = -1; |
| 748 for (StatsReport[] reports : expectations.takeStatsReports()) { | 756 for (StatsReport[] reports : expectations.takeStatsReports()) { |
| 749 System.out.println(" Report #" + (++reportIndex)); | 757 System.out.println(" Report #" + (++reportIndex)); |
| 750 for (int i = 0; i < reports.length; ++i) { | 758 for (int i = 0; i < reports.length; ++i) { |
| 751 System.out.println(" " + reports[i].toString()); | 759 System.out.println(" " + reports[i].toString()); |
| 752 } | 760 } |
| 753 } | 761 } |
| 754 assertEquals(1, reportIndex); | 762 assertEquals(1, reportIndex); |
| 755 System.out.println("End stats."); | 763 System.out.println("End stats."); |
| 756 | 764 |
| 757 pc.dispose(); | 765 pc.dispose(); |
| 758 } | 766 } |
| 759 | 767 |
| 760 // Returns a set of thread IDs belonging to this process, as Strings. | 768 // Returns a set of thread IDs belonging to this process, as Strings. |
| 761 private static TreeSet<String> allThreads() { | 769 private static TreeSet<String> allThreads() { |
| 762 TreeSet<String> threads = new TreeSet<String>(); | 770 TreeSet<String> threads = new TreeSet<String>(); |
| 763 // This pokes at /proc instead of using the Java APIs because we're also | 771 // This pokes at /proc instead of using the Java APIs because we're also |
| 764 // looking for libjingle/webrtc native threads, most of which won't have | 772 // looking for libjingle/webrtc native threads, most of which won't have |
| 765 // attached to the JVM. | 773 // attached to the JVM. |
| 766 for (String threadId : (new File("/proc/self/task")).list()) { | 774 for (String threadId : (new File("/proc/self/task")).list()) { |
| 767 threads.add(threadId); | 775 threads.add(threadId); |
| 768 } | 776 } |
| 769 return threads; | 777 return threads; |
| 770 } | 778 } |
| 771 } | 779 } |
| OLD | NEW |