Merge remote-tracking branch 'origin/master' into amakeev/debugger clion/162.282
authorAnton Makeev <Anton.Makeev@jetbrains.com>
Wed, 18 May 2016 10:45:50 +0000 (12:45 +0200)
committerAnton Makeev <Anton.Makeev@jetbrains.com>
Wed, 18 May 2016 10:45:50 +0000 (12:45 +0200)
platform/util/src/com/intellij/util/io/BaseDataReader.java
platform/util/src/com/intellij/util/io/BaseOutputReader.java
platform/util/src/com/intellij/util/io/OutputReader.java
platform/util/testSrc/com/intellij/util/io/BaseOutputReaderTest.java
platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XDebuggerTestUtil.java
platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XTestValueNode.java

index 6f974b5d8ff7cb8c294243179e83a5244a21bf6f..1d63b9609570d8ac567fd05367929c13e8c95eaf 100644 (file)
@@ -17,7 +17,6 @@ package com.intellij.util.io;
 
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.util.TimeoutUtil;
 import org.jetbrains.annotations.NotNull;
 
 import java.io.IOException;
@@ -31,6 +30,7 @@ public abstract class BaseDataReader {
   private static final Logger LOG = Logger.getInstance(BaseDataReader.class);
 
   protected final SleepingPolicy mySleepingPolicy;
+  protected final Object mySleepMonitor = new Object();
   protected volatile boolean isStopped;
 
   private Future<?> myFinishedFuture;
@@ -138,7 +138,10 @@ public abstract class BaseDataReader {
         if (!stopSignalled) {
           // if process stopped, there is no sense to sleep, 
           // just check if there is unread output in the stream
-          TimeoutUtil.sleep(mySleepingPolicy.getTimeToSleep(read));
+
+          synchronized (mySleepMonitor) {
+            mySleepMonitor.wait(mySleepingPolicy.getTimeToSleep(read));
+          }
         }
       }
     }
@@ -157,6 +160,12 @@ public abstract class BaseDataReader {
       }
     }
   }
+  
+  protected void resumeReading() {
+    synchronized (mySleepMonitor) {
+      mySleepMonitor.notifyAll();
+    }
+  }
 
   protected abstract boolean readAvailable() throws IOException;
   protected abstract void close() throws IOException;
index 7d794301fc84341ba3c30103c14edb89e2c0b097..e96b037789ebf59814ed23744d518d286b7ac354 100644 (file)
@@ -48,6 +48,7 @@ public abstract class BaseOutputReader extends BaseDataReader {
   }
 
   protected final Reader myReader;
+  protected final Object myBufferExhausted = new Object();
 
   private final Options myOptions;
   private final char[] myInputBuffer = new char[8192];
@@ -102,6 +103,9 @@ public abstract class BaseOutputReader extends BaseDataReader {
           read = true;
           processInput(myInputBuffer, myLineBuffer, n);
         }
+        if (!myReader.ready()) {
+          onBufferExhaustion();
+        }
       }
     }
     finally {
@@ -188,18 +192,42 @@ public abstract class BaseOutputReader extends BaseDataReader {
     myReader.close();
   }
 
-  protected void onBufferExhaustion() { }
+  public void readFully() throws InterruptedException {
+    // 1) no obvious way to implement this in the blocking mode
+    // 2) output is expected to be processed right away in the blocking mode
+    
+    if (mySleepingPolicy == SleepingPolicy.BLOCKING) return;
+    
+    synchronized (myBufferExhausted) {
+      try {
+        if (myReader.ready()) {
+          resumeReading();
+          
+          //noinspection WaitNotInLoop
+          myBufferExhausted.wait();
+        }
+      }
+      catch (IOException ignore) {
+      }
+    }
+  }
+
+  protected void onBufferExhaustion() {
+    synchronized (myBufferExhausted) {
+      myBufferExhausted.notifyAll();
+    }
+  }
 
   protected abstract void onTextAvailable(@NotNull String text);
 
   //<editor-fold desc="Deprecated stuff.">
-  /** @deprecated use {@link #BaseOutputReader(InputStream, Charset, Options)} (to be removed in IDEA 18) */
+  /** @deprecated use {@link #BaseOutputReader(InputStream, Charset, Options)} (to be removed in IDEA 2018.1) */
   @SuppressWarnings("unused")
   public BaseOutputReader(@NotNull InputStream inputStream, @Nullable Charset charset, @Nullable SleepingPolicy policy) {
     this(inputStream, charset, Options.withPolicy(policy));
   }
 
-  /** @deprecated use {@link #BaseOutputReader(Reader, Options)} (to be removed in IDEA 18) */
+  /** @deprecated use {@link #BaseOutputReader(Reader, Options)} (to be removed in IDEA 2018.1) */
   @SuppressWarnings("unused")
   public BaseOutputReader(@NotNull Reader reader, @Nullable SleepingPolicy policy) {
     this(reader, Options.withPolicy(policy));
index f34e86572cb0697f8982445c583c192dd57bd9a0..f22df4b27142bda9d1b3e9891546161c60c7d350 100644 (file)
  */
 package com.intellij.util.io;
 
-import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.util.TimeoutUtil;
-import com.intellij.util.concurrency.Semaphore;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
-import java.io.IOException;
 import java.io.InputStream;
-import java.io.Reader;
 import java.nio.charset.Charset;
 
+/** @deprecated use {@link BaseOutputReader}. to be removed in IDEA 2018.1 */
+@Deprecated 
 public abstract class OutputReader extends BaseOutputReader {
-  private static final Logger LOG = Logger.getInstance("#com.intellij.util.io.OutputReader");
-  private static final int READ_FULLY_TIMEOUT = 10;
-
-  private final Semaphore myReadFullySemaphore = new Semaphore();
-
-  /** @deprecated use {@link #OutputReader(InputStream, Charset, String)} to be removed in IDEA 16 */
-  @Deprecated
-  public OutputReader(@NotNull InputStream inputStream, @Nullable Charset charset) {
-    this(inputStream, charset, "");
-  }
-
-  /** @deprecated use {@link #OutputReader(InputStream, Charset, SleepingPolicy, String)} to be removed in IDEA 16 */
-  @Deprecated
-  public OutputReader(@NotNull InputStream inputStream, @Nullable Charset charset, @Nullable SleepingPolicy sleepingPolicy) {
-    this(inputStream, charset, sleepingPolicy, "");
-  }
-
   public OutputReader(@NotNull InputStream stream, @Nullable Charset charset, @NotNull String name) {
     super(stream, charset);
     start(name);
@@ -53,73 +33,4 @@ public abstract class OutputReader extends BaseOutputReader {
     super(stream, charset, Options.withPolicy(policy));
     start(name);
   }
-
-  /** @deprecated to be removed in IDEA 16 */
-  @Deprecated
-  public OutputReader(@NotNull Reader reader) {
-    super(reader);
-    start("");
-  }
-
-  @Override
-  protected void doRun() {
-    try {
-      while (true) {
-        boolean read = readAvailable();
-
-        if (!read) {
-          myReadFullySemaphore.up();
-        }
-
-        if (isStopped) {
-          break;
-        }
-
-        TimeoutUtil.sleep(mySleepingPolicy.getTimeToSleep(read));
-      }
-    }
-    catch (IOException e) {
-      LOG.info(e);
-    }
-    catch (Exception e) {
-      LOG.error(e);
-    }
-    finally {
-      try {
-        myReader.close();
-      }
-      catch (IOException e) {
-        LOG.warn("Can't close reader", e);
-      }
-    }
-  }
-
-  @Override
-  protected void onBufferExhaustion() {
-    if (mySleepingPolicy == SleepingPolicy.BLOCKING) {
-      myReadFullySemaphore.up();
-    }
-  }
-
-  public void readFully() throws InterruptedException {
-    if (mySleepingPolicy != SleepingPolicy.BLOCKING) {
-      myReadFullySemaphore.down();
-      while (!myReadFullySemaphore.waitForUnsafe(READ_FULLY_TIMEOUT)) {
-        if (isStopped) {
-          waitFor();
-          return;
-        }
-      }
-    }
-    else {
-      do {
-        myReadFullySemaphore.down();
-      }
-      while (myReadFullySemaphore.waitForUnsafe(READ_FULLY_TIMEOUT));
-
-      myReadFullySemaphore.up();
-
-      if (isStopped) waitFor();
-    }
-  }
 }
\ No newline at end of file
index 808a428d62bf811658ac3fe343936936e176d7de..0c40134a20bac43a53434d8890bf4c9e094bfc2f 100644 (file)
@@ -17,6 +17,7 @@ package com.intellij.util.io;
 
 import com.intellij.openapi.util.SystemInfo;
 import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.ArrayUtil;
 import com.intellij.util.concurrency.AppExecutorUtil;
 import org.jetbrains.annotations.NotNull;
 import org.junit.Test;
@@ -30,6 +31,7 @@ import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.Future;
 
+import static com.intellij.util.io.BaseOutputReaderTest.Runner.TEST_DATA_OUTPUT;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -97,11 +99,31 @@ public class BaseOutputReaderTest {
     assertThat(StringUtil.join(lines, "")).isEqualTo(StringUtil.join(Arrays.asList(TEST_DATA), ""));
   }
 
-  // Stopping is not supported for an open stream in blocking mode
-  //@Test(timeout = 30000)
-  //public void testBlockingStop() throws Exception {
-  //  doStopTest(BaseDataReader.SleepingPolicy.BLOCKING);
-  //}
+  @Test(timeout = 30000)
+  public void testBlockingReadFully() throws Exception {
+    doTestReadFully(BaseDataReader.SleepingPolicy.BLOCKING);
+  }
+
+  @Test(timeout = 30000)
+  public void testNonBlockingReadFully() throws Exception {
+    doTestReadFully(BaseDataReader.SleepingPolicy.SIMPLE);
+  }
+
+  private void doTestReadFully(BaseDataReader.SleepingPolicy policy) throws Exception {
+    int numLines = 100000;
+    Process process = launchTest("times", String.valueOf(numLines));
+    TestOutputReader reader = new TestOutputReader(process.getInputStream(), BaseOutputReader.Options.withPolicy(policy));
+    process.waitFor();
+
+    reader.readFully();
+    ArrayList<String> lines = new ArrayList<>(reader.myLines);
+
+    StringBuilder expected = new StringBuilder();
+    for (int i = 0; i < numLines; i++) {
+      expected.append(TEST_DATA_OUTPUT + "\n");
+    }
+    assertThat(StringUtil.join(lines, "")).isEqualTo(expected.toString());
+  }
 
   @Test(timeout = 30000)
   public void testNonBlockingStop() throws Exception {
@@ -140,7 +162,7 @@ public class BaseOutputReaderTest {
     }
   }
 
-  private Process launchTest(String mode) throws Exception {
+  private Process launchTest(String... args) throws Exception {
     String java = System.getProperty("java.home") + (SystemInfo.isWindows ? "\\bin\\java.exe" : "/bin/java");
 
     String className = BaseOutputReaderTest.Runner.class.getName();
@@ -149,13 +171,16 @@ public class BaseOutputReaderTest {
     File dir = new File(url.toURI());
     for (int i = 0; i < StringUtil.countChars(className, '.') + 1; i++) dir = dir.getParentFile();
 
-    String[] cmd = {java, "-cp", dir.getPath(), className, mode};
+    String[] cmd = ArrayUtil.mergeArrays(new String[] {java, "-cp", dir.getPath(), className}, args);
+    
     return new ProcessBuilder(cmd).redirectErrorStream(true).start();
   }
 
   public static class Runner {
-    private static final String[] TEST_DATA =
+    public static final String[] TEST_DATA =
       {"first\n", "incomplete", "-continuation\n", new String(new char[16*1024]).replace('\0', 'x') + '\n', "last"};
+    
+    public static final String TEST_DATA_OUTPUT = "output";
 
     private static final int SEND_TIMEOUT = 500;
     private static final int SLEEP_TIMEOUT = 60000;
@@ -171,6 +196,11 @@ public class BaseOutputReaderTest {
           Thread.sleep(SEND_TIMEOUT);
         }
       }
+      else if (args.length > 1 && "times".equals(args[0])) {
+        for (int i = 0; i < Integer.valueOf(args[1]); i++) {
+          System.out.println(TEST_DATA_OUTPUT);
+        }
+      }
       else {
         throw new IllegalArgumentException(Arrays.toString(args));
       }
index 00b8ba027381562fbe0e6a212166f91503f21425..286cdfe43606d21a64ce5a346f0a4ef04f6870bb 100644 (file)
@@ -28,6 +28,7 @@ import com.intellij.openapi.util.Ref;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.testFramework.UsefulTestCase;
+import com.intellij.util.concurrency.FutureResult;
 import com.intellij.util.ui.TextTransferable;
 import com.intellij.util.ui.UIUtil;
 import com.intellij.xdebugger.breakpoints.*;
@@ -109,6 +110,16 @@ public class XDebuggerTestUtil {
     return session.getSuspendContext().getActiveExecutionStack();
   }
 
+  public static List<XExecutionStack> collectThreads(@NotNull XDebugSession session) throws InterruptedException {
+    return collectThreadsWithErrors(session).first;
+  }
+
+  public static Pair<List<XExecutionStack>, String> collectThreadsWithErrors(@NotNull XDebugSession session) throws InterruptedException {
+    XTestExecutionStackContainer container = new XTestExecutionStackContainer();
+    session.getSuspendContext().computeExecutionStacks(container);
+    return container.waitFor(TIMEOUT);
+  }
+
   public static List<XStackFrame> collectFrames(@NotNull XDebugSession session) throws InterruptedException {
     return collectFrames(null, session);
   }
@@ -129,9 +140,13 @@ public class XDebuggerTestUtil {
   }
 
   public static List<XStackFrame> collectStacks(XExecutionStack thread, long timeout) throws InterruptedException {
+    return collectStacksWithError(thread, timeout).first;
+  }
+
+  public static Pair<List<XStackFrame>, String> collectStacksWithError(XExecutionStack thread, long timeout) throws InterruptedException {
     XTestStackFrameContainer container = new XTestStackFrameContainer();
     thread.computeStackFrames(0, container);
-    return container.waitFor(timeout).first;
+    return container.waitFor(timeout);
   }
 
   public static List<XValue> collectVariables(XStackFrame frame) throws InterruptedException {
@@ -230,6 +245,21 @@ public class XDebuggerTestUtil {
     assertVariableValueMatches(findVar(vars, name), name, valuePattern);
   }
 
+  public static void assertVariableValueMatches(@NotNull Collection<XValue> vars,
+                                                @Nullable String name,
+                                                @Nullable String type,
+                                                @Nullable @Language("RegExp") String valuePattern) throws InterruptedException {
+    assertVariableValueMatches(findVar(vars, name), name, type, valuePattern);
+  }
+
+  public static void assertVariableValueMatches(@NotNull Collection<XValue> vars,
+                                                @Nullable String name,
+                                                @Nullable String type,
+                                                @Nullable @Language("RegExp") String valuePattern,
+                                                @Nullable Boolean hasChildren) throws InterruptedException {
+    assertVariableValueMatches(findVar(vars, name), name, type, valuePattern, hasChildren);
+  }
+
   public static void assertVariableValueMatches(@NotNull XValue var,
                                                 @Nullable String name,
                                                 @Nullable @Language("RegExp") String valuePattern) throws InterruptedException {
@@ -240,12 +270,21 @@ public class XDebuggerTestUtil {
                                                 @Nullable String name,
                                                 @Nullable String type,
                                                 @Nullable @Language("RegExp") String valuePattern) throws InterruptedException {
+    assertVariableValueMatches(var, name, type, valuePattern, null);
+  }
+
+  public static void assertVariableValueMatches(@NotNull XValue var,
+                                                @Nullable String name,
+                                                @Nullable String type,
+                                                @Nullable @Language("RegExp") String valuePattern,
+                                                @Nullable Boolean hasChildren) throws InterruptedException {
     XTestValueNode node = computePresentation(var);
     if (name != null) assertEquals(name, node.myName);
     if (type != null) assertEquals(type, node.myType);
     if (valuePattern != null) {
-      assertTrue("Expected value" + valuePattern + " Actual value: " + node.myValue, node.myValue.matches(valuePattern));
+      assertTrue("Expected value" + valuePattern + " Actual value: " + node.myValue, node.myValue.matches(valuePattern));
     }
+    if (hasChildren != null) assertEquals(hasChildren, node.myHasChildren);
   }
 
   public static void assertVariableTypeMatches(@NotNull Collection<XValue> vars,
@@ -267,37 +306,42 @@ public class XDebuggerTestUtil {
   }
 
   public static void assertVariableFullValue(@NotNull XValue var,
-                                             @Nullable String value) throws InterruptedException {
+                                             @Nullable String value) throws Exception {
     XTestValueNode node = computePresentation(var);
-    final String[] result = new String[1];
 
-    node.myFullValueEvaluator.startEvaluation(new XFullValueEvaluator.XFullValueEvaluationCallback() {
-      @Override
-      public void evaluated(@NotNull String fullValue) {
-        result[0] = fullValue;
-      }
+    if (value == null) {
+      assertNull("full value evaluator should be null", node.myFullValueEvaluator);
+    }
+    else {
+      final FutureResult<String> result = new FutureResult<>();
+      node.myFullValueEvaluator.startEvaluation(new XFullValueEvaluator.XFullValueEvaluationCallback() {
+        @Override
+        public void evaluated(@NotNull String fullValue) {
+          result.set(fullValue);
+        }
 
-      @Override
-      public void evaluated(@NotNull String fullValue, @Nullable Font font) {
-        result[0] = fullValue;
-      }
+        @Override
+        public void evaluated(@NotNull String fullValue, @Nullable Font font) {
+          result.set(fullValue);
+        }
 
-      @Override
-      public void errorOccurred(@NotNull String errorMessage) {
-        result[0] = errorMessage;
-      }
+        @Override
+        public void errorOccurred(@NotNull String errorMessage) {
+          result.set(errorMessage);
+        }
 
-      @Override
-      public boolean isObsolete() {
-        return false;
-      }
-    });
+        @Override
+        public boolean isObsolete() {
+          return false;
+        }
+      });
 
-    assertEquals(value, result[0]);
+      assertEquals(value, result.get(TIMEOUT, TimeUnit.MILLISECONDS));
+    }
   }
 
   public static void assertVariableFullValue(Collection<XValue> vars, @Nullable String name, @Nullable String value)
-    throws InterruptedException {
+    throws Exception {
     assertVariableFullValue(findVar(vars, name), value);
   }
 
@@ -485,6 +529,18 @@ public class XDebuggerTestUtil {
     assertEquals(expectedExpression, expression);
     return expression;
   }
+  
+  public static class XTestExecutionStackContainer extends XTestContainer<XExecutionStack> implements XSuspendContext.XExecutionStackContainer {
+    @Override
+    public void errorOccurred(@NotNull String errorMessage) {
+      setErrorMessage(errorMessage);
+    }
+
+    @Override
+    public void addExecutionStack(@NotNull List<? extends XExecutionStack> executionStacks, boolean last) {
+      addChildren(executionStacks, last);
+    }
+  } 
 
   public static class XTestStackFrameContainer extends XTestContainer<XStackFrame> implements XExecutionStack.XStackFrameContainer {
     public void addStackFrames(@NotNull List<? extends XStackFrame> stackFrames, boolean last) {
index ea4684e69d8bbd794887fec817f83db02314ddd1..ced45522ba6ca1cff1000a2a86759f45c53ad12a 100644 (file)
@@ -48,4 +48,9 @@ public class XTestValueNode extends XValueNodePresentationConfigurator.Configura
       throw new AssertionError("Waiting timed out");
     }
   }
+
+  @Override
+  public String toString() {
+    return myName + "{" + myType + "} = " + myValue + ", hasChildren = " + myHasChildren;
+  }
 }