am e944d1bd: Merge "media: centralize signaling of skipped tests" into lmp-mr1-dev

* commit 'e944d1bd26b3403073f349b25d839d77b1febdf5':
  media: centralize signaling of skipped tests
diff --git a/libs/deviceutil/src/android/cts/util/MediaUtils.java b/libs/deviceutil/src/android/cts/util/MediaUtils.java
index 18e804f..8e88b47 100644
--- a/libs/deviceutil/src/android/cts/util/MediaUtils.java
+++ b/libs/deviceutil/src/android/cts/util/MediaUtils.java
@@ -20,6 +20,10 @@
 import android.media.MediaCodecList;
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
+import java.lang.reflect.Method;
+import static java.lang.reflect.Modifier.isPublic;
+import static java.lang.reflect.Modifier.isStatic;
+import java.util.Map;
 import android.util.Log;
 
 import java.io.IOException;
@@ -28,6 +32,92 @@
     final public static String TAG = "MediaUtils";
 
     /**
+     * Finds test name (heuristically) and prints out standard skip message.
+     *
+     * Since it uses heuristics, this method has only been verified for media
+     * tests. This centralizes the way to signal a skipped test.
+     */
+    public static void skipTest(String tag, String reason) {
+        int bestScore = -1;
+        String testName = "test???";
+        Map<Thread, StackTraceElement[]> traces = Thread.getAllStackTraces();
+        for (Map.Entry<Thread, StackTraceElement[]> entry : traces.entrySet()) {
+            StackTraceElement[] stack = entry.getValue();
+            for (int index = 0; index < stack.length; ++index) {
+                // method name must start with "test"
+                String methodName = stack[index].getMethodName();
+                if (!methodName.startsWith("test")) {
+                    continue;
+                }
+
+                int score = 0;
+                // see if there is a public non-static void method that takes no argument
+                Class<?> clazz;
+                try {
+                    clazz = Class.forName(stack[index].getClassName());
+                    ++score;
+                    for (final Method method : clazz.getDeclaredMethods()) {
+                        if (method.getName().equals(methodName)
+                                && isPublic(method.getModifiers())
+                                && !isStatic(method.getModifiers())
+                                && method.getParameterTypes().length == 0
+                                && method.getReturnType().equals(Void.TYPE)) {
+                            ++score;
+                            break;
+                        }
+                    }
+                    if (score == 1) {
+                        // if we could read the class, but method is not public void, it is
+                        // not a candidate
+                        continue;
+                    }
+                } catch (ClassNotFoundException e) {
+                }
+
+                // even if we cannot verify the method signature, there are signals in the stack
+
+                // usually test method is invoked by reflection
+                int depth = 1;
+                while (index + depth < stack.length
+                        && stack[index + depth].getMethodName().equals("invoke")
+                        && stack[index + depth].getClassName().equals(
+                                "java.lang.reflect.Method")) {
+                    ++depth;
+                }
+                if (depth > 1) {
+                    ++score;
+                    // and usually test method is run by runMethod method in android.test package
+                    if (index + depth < stack.length) {
+                        if (stack[index + depth].getClassName().startsWith("android.test.")) {
+                            ++score;
+                        }
+                        if (stack[index + depth].getMethodName().equals("runMethod")) {
+                            ++score;
+                        }
+                    }
+                }
+
+                if (score > bestScore) {
+                    bestScore = score;
+                    testName = methodName;
+                }
+            }
+        }
+
+        Log.i(tag, "SKIPPING " + testName + "(): " + reason);
+    }
+
+    /**
+     * Finds test name (heuristically) and prints out standard skip message.
+     *
+     * Since it uses heuristics, this method has only been verified for media
+     * tests.  This centralizes the way to signal a skipped test.
+     */
+    public static void skipTest(String reason) {
+        skipTest(TAG, reason);
+    }
+
+    /**
      * return true iff all audio and video tracks are supported
      */
     public static boolean hasCodecsForMedia(MediaExtractor ex) {