Add basic Robolectric support for EGL14

PiperOrigin-RevId: 373284290
diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowEGL14Test.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowEGL14Test.java
new file mode 100644
index 0000000..e5d5c9d
--- /dev/null
+++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowEGL14Test.java
@@ -0,0 +1,66 @@
+package org.robolectric.shadows;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.opengl.EGL14;
+import android.opengl.EGLConfig;
+import android.opengl.EGLContext;
+import android.opengl.EGLDisplay;
+import android.os.Build.VERSION_CODES;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+/** Unit tests for {@link ShadowEGL14Test} */
+@RunWith(AndroidJUnit4.class)
+@Config(minSdk = VERSION_CODES.LOLLIPOP)
+public final class ShadowEGL14Test {
+  @Test
+  public void eglGetDisplay() {
+    assertThat(EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY)).isNotNull();
+  }
+
+  @Test
+  public void eglChooseConfig() {
+    EGLDisplay display = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
+    EGLConfig[] configs = new EGLConfig[1];
+    int[] numConfig = new int[1];
+    assertThat(EGL14.eglChooseConfig(display, new int[0], 0, configs, 0, 1, numConfig, 0)).isTrue();
+    assertThat(numConfig[0]).isGreaterThan(0);
+    assertThat(configs[0]).isNotNull();
+  }
+
+  @Test
+  public void eglCreateContext_v2() {
+    EGLDisplay display = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
+    int[] attribList = {EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL14.EGL_NONE};
+    EGLContext context = EGL14.eglCreateContext(display, createEglConfig(), null, attribList, 0);
+    assertThat(context).isNotNull();
+    int[] values = new int[1];
+    EGL14.eglQueryContext(display, context, EGL14.EGL_CONTEXT_CLIENT_VERSION, values, 0);
+    assertThat(values[0]).isEqualTo(2);
+  }
+
+  @Test
+  public void eglCreatePbufferSurface() {
+    EGLDisplay display = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
+    assertThat(EGL14.eglCreatePbufferSurface(display, createEglConfig(), new int[0], 0))
+        .isNotNull();
+  }
+
+  @Test
+  public void eglCreateWindowSurface() {
+    EGLDisplay display = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
+    assertThat(EGL14.eglCreateWindowSurface(display, createEglConfig(), null, new int[0], 0))
+        .isNotNull();
+  }
+
+  private EGLConfig createEglConfig() {
+    EGLDisplay display = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
+    EGLConfig[] configs = new EGLConfig[1];
+    int[] numConfig = new int[1];
+    EGL14.eglChooseConfig(display, new int[0], 0, configs, 0, 1, numConfig, 0);
+    return configs[0];
+  }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowEGL14.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowEGL14.java
new file mode 100644
index 0000000..475607a
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowEGL14.java
@@ -0,0 +1,133 @@
+package org.robolectric.shadows;
+
+import android.opengl.EGL14;
+import android.opengl.EGLConfig;
+import android.opengl.EGLContext;
+import android.opengl.EGLDisplay;
+import android.opengl.EGLSurface;
+import android.os.Build.VERSION_CODES;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.util.ReflectionHelpers;
+import org.robolectric.util.ReflectionHelpers.ClassParameter;
+
+/** Shadow for EGL14. Currently doesn't handle real graphics work, but avoids crashing when run. */
+@Implements(value = EGL14.class, minSdk = VERSION_CODES.LOLLIPOP)
+public class ShadowEGL14 {
+  private static final long UNUSED_HANDLE_ID = 43L;
+
+  @Implementation
+  protected static EGLDisplay eglGetDisplay(int displayId) {
+    return createEglDisplay();
+  }
+
+  @Implementation
+  protected static boolean eglInitialize(
+      EGLDisplay dpy, int[] major, int majorOffset, int[] minor, int minorOffset) {
+    return true;
+  }
+
+  @Implementation
+  protected static boolean eglChooseConfig(
+      EGLDisplay dpy,
+      int[] attribList,
+      int attribListOffset,
+      EGLConfig[] configs,
+      int configsOffset,
+      int configSize,
+      int[] numConfig,
+      int numConfigOffset) {
+    configs[configsOffset] = createEglConfig();
+    numConfig[numConfigOffset] = 1;
+    return true;
+  }
+
+  @Implementation
+  protected static EGLContext eglCreateContext(
+      EGLDisplay dpy, EGLConfig config, EGLContext shareContext, int[] attribList, int offset) {
+    int majorVersion = getAttribValue(attribList, EGL14.EGL_CONTEXT_CLIENT_VERSION);
+    switch (majorVersion) {
+      case 2:
+      case 3:
+        return createEglContext(majorVersion);
+      default:
+        break;
+    }
+    return EGL14.EGL_NO_CONTEXT;
+  }
+
+  @Implementation
+  protected static boolean eglQueryContext(
+      EGLDisplay dpy, EGLContext ctx, int attribute, int[] value, int offset) {
+    value[offset] = 0;
+    switch (attribute) {
+      case EGL14.EGL_CONTEXT_CLIENT_VERSION:
+        // We stored the version in the handle field when we created the context.
+        value[offset] = (int) ctx.getNativeHandle();
+        break;
+      default:
+        // Use default output set above switch.
+    }
+    return true;
+  }
+
+  @Implementation
+  protected static EGLSurface eglCreatePbufferSurface(
+      EGLDisplay dpy, EGLConfig config, int[] attribList, int offset) {
+    return createEglSurface();
+  }
+
+  @Implementation
+  protected static EGLSurface eglCreateWindowSurface(
+      EGLDisplay dpy, EGLConfig config, Object win, int[] attribList, int offset) {
+    return createEglSurface();
+  }
+
+  @Implementation
+  protected static boolean eglMakeCurrent(
+      EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) {
+    return true;
+  }
+
+  @Implementation
+  protected static boolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) {
+    return true;
+  }
+
+  @Implementation
+  protected static int eglGetError() {
+    return EGL14.EGL_SUCCESS;
+  }
+
+  private static EGLDisplay createEglDisplay() {
+    return ReflectionHelpers.callConstructor(
+        EGLDisplay.class, ClassParameter.from(long.class, UNUSED_HANDLE_ID));
+  }
+
+  private static EGLConfig createEglConfig() {
+    return ReflectionHelpers.callConstructor(
+        EGLConfig.class, ClassParameter.from(long.class, UNUSED_HANDLE_ID));
+  }
+
+  private static EGLContext createEglContext(int version) {
+    // As a hack store the version number in the unused handle ID so we can retrieve it later
+    // if the caller queries a context.
+    return ReflectionHelpers.callConstructor(
+        EGLContext.class, ClassParameter.from(long.class, version));
+  }
+
+  private static EGLSurface createEglSurface() {
+    return ReflectionHelpers.callConstructor(
+        EGLSurface.class, ClassParameter.from(long.class, UNUSED_HANDLE_ID));
+  }
+
+  private static int getAttribValue(int[] attribList, int attribute) {
+    int attribValue = 0;
+    for (int i = 0; i < attribList.length; i += 2) {
+      if (attribList[i] == attribute) {
+        attribValue = attribList[i + 1];
+      }
+    }
+    return attribValue;
+  }
+}