@Test
  public void testThreads() throws Exception {
    final Analyzer analyzer = new MockAnalyzer(random());
    RandomIndexWriter iw =
        new RandomIndexWriter(
            random(),
            dir,
            iwcWithSuggestField(analyzer, "suggest_field_1", "suggest_field_2", "suggest_field_3"));
    int num = Math.min(1000, atLeast(100));
    final String prefix1 = "abc1_";
    final String prefix2 = "abc2_";
    final String prefix3 = "abc3_";
    final Entry[] entries1 = new Entry[num];
    final Entry[] entries2 = new Entry[num];
    final Entry[] entries3 = new Entry[num];
    for (int i = 0; i < num; i++) {
      int weight = num - (i + 1);
      entries1[i] = new Entry(prefix1 + weight, weight);
      entries2[i] = new Entry(prefix2 + weight, weight);
      entries3[i] = new Entry(prefix3 + weight, weight);
    }
    for (int i = 0; i < num; i++) {
      Document doc = new Document();
      doc.add(new SuggestField("suggest_field_1", prefix1 + i, i));
      doc.add(new SuggestField("suggest_field_2", prefix2 + i, i));
      doc.add(new SuggestField("suggest_field_3", prefix3 + i, i));
      iw.addDocument(doc);

      if (rarely()) {
        iw.commit();
      }
    }

    DirectoryReader reader = iw.getReader();
    int numThreads = TestUtil.nextInt(random(), 2, 7);
    Thread threads[] = new Thread[numThreads];
    final CyclicBarrier startingGun = new CyclicBarrier(numThreads + 1);
    final CopyOnWriteArrayList<Throwable> errors = new CopyOnWriteArrayList<>();
    final SuggestIndexSearcher indexSearcher = new SuggestIndexSearcher(reader);
    for (int i = 0; i < threads.length; i++) {
      threads[i] =
          new Thread() {
            @Override
            public void run() {
              try {
                startingGun.await();
                PrefixCompletionQuery query =
                    new PrefixCompletionQuery(analyzer, new Term("suggest_field_1", prefix1));
                TopSuggestDocs suggest = indexSearcher.suggest(query, num);
                assertSuggestions(suggest, entries1);
                query = new PrefixCompletionQuery(analyzer, new Term("suggest_field_2", prefix2));
                suggest = indexSearcher.suggest(query, num);
                assertSuggestions(suggest, entries2);
                query = new PrefixCompletionQuery(analyzer, new Term("suggest_field_3", prefix3));
                suggest = indexSearcher.suggest(query, num);
                assertSuggestions(suggest, entries3);
              } catch (Throwable e) {
                errors.add(e);
              }
            }
          };
      threads[i].start();
    }

    startingGun.await();
    for (Thread t : threads) {
      t.join();
    }
    assertTrue(errors.toString(), errors.isEmpty());

    reader.close();
    iw.close();
  }
 @Override
 public String toString() {
   return copyOnWriteArrayList.toString();
 }
  @Test
  public void testPushResourceAreSentNonInterleaved() throws Exception {
    final CountDownLatch allExpectedPushesReceivedLatch = new CountDownLatch(4);
    final CountDownLatch allPushDataReceivedLatch = new CountDownLatch(4);
    final CopyOnWriteArrayList<Integer> dataReceivedOrder = new CopyOnWriteArrayList<>();

    InetSocketAddress bigResponseServerAddress =
        startHTTPServer(
            version,
            new AbstractHandler() {
              @Override
              public void handle(
                  String target,
                  Request baseRequest,
                  HttpServletRequest request,
                  HttpServletResponse response)
                  throws IOException, ServletException {
                byte[] bytes = new byte[32768];
                new Random().nextBytes(bytes);
                ServletOutputStream outputStream = response.getOutputStream();
                outputStream.write(bytes);
                baseRequest.setHandled(true);
              }
            });
    Session pushCacheBuildSession = startClient(version, bigResponseServerAddress, null);

    Fields mainResourceHeaders = createHeadersWithoutReferrer(mainResource);
    sendRequest(pushCacheBuildSession, mainResourceHeaders, null, null);
    sendRequest(pushCacheBuildSession, createHeaders("/style.css", mainResource), null, null);
    sendRequest(pushCacheBuildSession, createHeaders("/javascript.js", mainResource), null, null);
    sendRequest(pushCacheBuildSession, createHeaders("/image1.jpg", mainResource), null, null);
    sendRequest(pushCacheBuildSession, createHeaders("/image2.jpg", mainResource), null, null);

    Session session = startClient(version, bigResponseServerAddress, null);

    session.syn(
        new SynInfo(mainResourceHeaders, true),
        new StreamFrameListener.Adapter() {
          AtomicInteger currentStreamId = new AtomicInteger(2);

          @Override
          public StreamFrameListener onPush(Stream stream, PushInfo pushInfo) {
            LOG.info("Received push for stream: {} {}", stream.getId(), pushInfo);
            String uriHeader = pushInfo.getHeaders().get(HTTPSPDYHeader.URI.name(version)).value();
            switch ((int) allExpectedPushesReceivedLatch.getCount()) {
              case 4:
                assertThat("1st pushed resource is the css", uriHeader.endsWith("css"), is(true));
                break;
              case 3:
                assertThat("2nd pushed resource is the js", uriHeader.endsWith("js"), is(true));
                break;
              case 2:
                assertThat(
                    "3rd pushed resource is image1", uriHeader.endsWith("image1.jpg"), is(true));
                break;
              case 1:
                assertThat(
                    "4th pushed resource is image2", uriHeader.endsWith("image2.jpg"), is(true));
                break;
            }
            allExpectedPushesReceivedLatch.countDown();
            return new Adapter() {
              @Override
              public void onData(Stream stream, DataInfo dataInfo) {
                if (stream.getId() != currentStreamId.get())
                  throw new IllegalStateException(
                      "Streams interleaved. Expected StreamId: "
                          + currentStreamId
                          + " but was: "
                          + stream.getId());
                dataInfo.consume(dataInfo.available());
                if (dataInfo.isClose()) {
                  currentStreamId.compareAndSet(currentStreamId.get(), currentStreamId.get() + 2);
                  dataReceivedOrder.add(stream.getId());
                  allPushDataReceivedLatch.countDown();
                }
                LOG.info(stream.getId() + ":" + dataInfo);
              }
            };
          }
        });

    assertThat(
        "All push resources received",
        allExpectedPushesReceivedLatch.await(5, TimeUnit.SECONDS),
        is(true));
    assertThat(
        "All pushData received", allPushDataReceivedLatch.await(5, TimeUnit.SECONDS), is(true));
    assertThat(
        "The data for different push streams has not been interleaved",
        dataReceivedOrder.toString(),
        equalTo("[2, 4, 6, 8]"));
    LOG.info(dataReceivedOrder.toString());
  }