[jnigen] Add tests on summary content (https://github.com/dart-lang/jnigen/issues/353)

* Add tests on summary content
* Emit char as integer
diff --git a/pkgs/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmClassVisitor.java b/pkgs/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmClassVisitor.java
index 33fecbd..d5a730b 100644
--- a/pkgs/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmClassVisitor.java
+++ b/pkgs/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmClassVisitor.java
@@ -4,6 +4,8 @@
 
 package com.github.dart_lang.jnigen.apisummarizer.disasm;
 
+import static org.objectweb.asm.Opcodes.ACC_ENUM;
+
 import com.github.dart_lang.jnigen.apisummarizer.elements.*;
 import com.github.dart_lang.jnigen.apisummarizer.util.SkipException;
 import com.github.dart_lang.jnigen.apisummarizer.util.StreamUtil;
@@ -45,7 +47,6 @@
       String[] interfaces) {
     var current = new ClassDecl();
     visiting.push(current);
-    var type = Type.getObjectType(name);
     current.binaryName = name.replace('/', '.');
     current.modifiers = TypeUtils.access(actualAccess.getOrDefault(current.binaryName, access));
     current.declKind = TypeUtils.declKind(access);
@@ -86,6 +87,9 @@
     field.type = TypeUtils.typeUsage(Type.getType(descriptor), signature);
     field.defaultValue = value;
     field.modifiers = TypeUtils.access(access);
+    if ((access & ACC_ENUM) != 0) {
+      peekVisiting().values.add(name);
+    }
     if (signature != null) {
       var reader = new SignatureReader(signature);
       reader.accept(new AsmTypeUsageSignatureVisitor(field.type));
diff --git a/pkgs/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/ElementBuilders.java b/pkgs/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/ElementBuilders.java
index 7ca8bb6..675da7b 100644
--- a/pkgs/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/ElementBuilders.java
+++ b/pkgs/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/ElementBuilders.java
@@ -62,6 +62,9 @@
     field.name = e.getSimpleName().toString();
     field.modifiers = e.getModifiers().stream().map(Modifier::toString).collect(Collectors.toSet());
     field.defaultValue = e.getConstantValue();
+    if (field.defaultValue instanceof Character) {
+      field.defaultValue = (int) (Character) field.defaultValue;
+    }
     field.type = typeUsage(e.asType());
     field.javadoc = docComment(env.trees.getDocCommentTree(e));
     field.annotations = annotations(e.getAnnotationMirrors());
diff --git a/pkgs/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/elements/TypeUsage.java b/pkgs/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/elements/TypeUsage.java
index a41b8ea..a60407c 100644
--- a/pkgs/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/elements/TypeUsage.java
+++ b/pkgs/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/elements/TypeUsage.java
@@ -7,6 +7,14 @@
 import java.util.List;
 
 public class TypeUsage {
+  public TypeUsage(String shorthand, Kind kind, ReferredType type) {
+    this.shorthand = shorthand;
+    this.kind = kind;
+    this.type = type;
+  }
+
+  public TypeUsage() {}
+
   public enum Kind {
     DECLARED,
     TYPE_VARIABLE,
diff --git a/pkgs/jnigen/java/src/test/resources/exampleClassSummary.json b/pkgs/jnigen/java/src/test/resources/exampleClassSummary.json
index 208e7ae..6786a26 100644
--- a/pkgs/jnigen/java/src/test/resources/exampleClassSummary.json
+++ b/pkgs/jnigen/java/src/test/resources/exampleClassSummary.json
@@ -139,7 +139,6 @@
   "declKind" : "CLASS",
   "modifiers" : [ "static", "public" ],
   "binaryName" : "com.example.Example$Aux",
-  "parentName" : "com.example.Example",
   "methods" : [ {
     "modifiers" : [ "public" ],
     "name" : "<init>",
diff --git a/pkgs/jnigen/test/simple_package_test/c_based/c_bindings/simple_package.c b/pkgs/jnigen/test/simple_package_test/c_based/c_bindings/simple_package.c
index 18621f8..62b150e 100644
--- a/pkgs/jnigen/test/simple_package_test/c_based/c_bindings/simple_package.c
+++ b/pkgs/jnigen/test/simple_package_test/c_based/c_bindings/simple_package.c
@@ -19,6 +19,44 @@
   env_getter = eg;
 }
 
+// com.github.dart_lang.jnigen.simple_package.Color
+jclass _c_Color = NULL;
+
+jmethodID _m_Color__values = NULL;
+FFI_PLUGIN_EXPORT
+JniResult Color__values() {
+  load_env();
+  load_class_global_ref(&_c_Color,
+                        "com/github/dart_lang/jnigen/simple_package/Color");
+  if (_c_Color == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  load_static_method(_c_Color, &_m_Color__values, "values",
+                     "()[Lcom/github/dart_lang/jnigen/simple_package/Color;");
+  if (_m_Color__values == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  jobject _result =
+      (*jniEnv)->CallStaticObjectMethod(jniEnv, _c_Color, _m_Color__values);
+  return to_global_ref_result(_result);
+}
+
+jmethodID _m_Color__valueOf = NULL;
+FFI_PLUGIN_EXPORT
+JniResult Color__valueOf(jobject name) {
+  load_env();
+  load_class_global_ref(&_c_Color,
+                        "com/github/dart_lang/jnigen/simple_package/Color");
+  if (_c_Color == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  load_static_method(
+      _c_Color, &_m_Color__valueOf, "valueOf",
+      "(Ljava/lang/String;)Lcom/github/dart_lang/jnigen/simple_package/Color;");
+  if (_m_Color__valueOf == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  jobject _result = (*jniEnv)->CallStaticObjectMethod(jniEnv, _c_Color,
+                                                      _m_Color__valueOf, name);
+  return to_global_ref_result(_result);
+}
+
 // com.github.dart_lang.jnigen.simple_package.Example
 jclass _c_Example = NULL;
 
@@ -402,6 +440,96 @@
   return to_global_ref_result(_result);
 }
 
+jmethodID _m_Example__protectedMethod = NULL;
+FFI_PLUGIN_EXPORT
+JniResult Example__protectedMethod(jobject self_, jobject a, jobject b) {
+  load_env();
+  load_class_global_ref(&_c_Example,
+                        "com/github/dart_lang/jnigen/simple_package/Example");
+  if (_c_Example == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  load_method(_c_Example, &_m_Example__protectedMethod, "protectedMethod",
+              "(Ljava/lang/String;Ljava/lang/String;)V");
+  if (_m_Example__protectedMethod == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_Example__protectedMethod, a, b);
+  return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+}
+
+jmethodID _m_Example__finalMethod = NULL;
+FFI_PLUGIN_EXPORT
+JniResult Example__finalMethod(jobject self_) {
+  load_env();
+  load_class_global_ref(&_c_Example,
+                        "com/github/dart_lang/jnigen/simple_package/Example");
+  if (_c_Example == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  load_method(_c_Example, &_m_Example__finalMethod, "finalMethod", "()V");
+  if (_m_Example__finalMethod == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_Example__finalMethod);
+  return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+}
+
+jmethodID _m_Example__getList = NULL;
+FFI_PLUGIN_EXPORT
+JniResult Example__getList(jobject self_) {
+  load_env();
+  load_class_global_ref(&_c_Example,
+                        "com/github/dart_lang/jnigen/simple_package/Example");
+  if (_c_Example == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  load_method(_c_Example, &_m_Example__getList, "getList",
+              "()Ljava/util/List;");
+  if (_m_Example__getList == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  jobject _result =
+      (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_Example__getList);
+  return to_global_ref_result(_result);
+}
+
+jmethodID _m_Example__joinStrings = NULL;
+FFI_PLUGIN_EXPORT
+JniResult Example__joinStrings(jobject self_, jobject values, jobject delim) {
+  load_env();
+  load_class_global_ref(&_c_Example,
+                        "com/github/dart_lang/jnigen/simple_package/Example");
+  if (_c_Example == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  load_method(_c_Example, &_m_Example__joinStrings, "joinStrings",
+              "(Ljava/util/List;Ljava/lang/String;)Ljava/lang/String;");
+  if (_m_Example__joinStrings == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  jobject _result = (*jniEnv)->CallObjectMethod(
+      jniEnv, self_, _m_Example__joinStrings, values, delim);
+  return to_global_ref_result(_result);
+}
+
+jmethodID _m_Example__methodWithSeveralParams = NULL;
+FFI_PLUGIN_EXPORT
+JniResult Example__methodWithSeveralParams(jobject self_,
+                                           uint16_t ch,
+                                           jobject s,
+                                           jobject a,
+                                           jobject t,
+                                           jobject lt,
+                                           jobject wm) {
+  load_env();
+  load_class_global_ref(&_c_Example,
+                        "com/github/dart_lang/jnigen/simple_package/Example");
+  if (_c_Example == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  load_method(_c_Example, &_m_Example__methodWithSeveralParams,
+              "methodWithSeveralParams",
+              "(CLjava/lang/String;[ILjava/lang/CharSequence;Ljava/util/"
+              "List;Ljava/util/Map;)V");
+  if (_m_Example__methodWithSeveralParams == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_Example__methodWithSeveralParams,
+                            ch, s, a, t, lt, wm);
+  return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+}
+
 jmethodID _m_Example__new0 = NULL;
 FFI_PLUGIN_EXPORT
 JniResult Example__new0() {
@@ -587,6 +715,127 @@
   return (JniResult){.value = {.j = 0}, .exception = check_exception()};
 }
 
+jmethodID _m_Example__overloaded = NULL;
+FFI_PLUGIN_EXPORT
+JniResult Example__overloaded(jobject self_) {
+  load_env();
+  load_class_global_ref(&_c_Example,
+                        "com/github/dart_lang/jnigen/simple_package/Example");
+  if (_c_Example == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  load_method(_c_Example, &_m_Example__overloaded, "overloaded", "()V");
+  if (_m_Example__overloaded == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_Example__overloaded);
+  return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+}
+
+jmethodID _m_Example__overloaded1 = NULL;
+FFI_PLUGIN_EXPORT
+JniResult Example__overloaded1(jobject self_, int32_t a, jobject b) {
+  load_env();
+  load_class_global_ref(&_c_Example,
+                        "com/github/dart_lang/jnigen/simple_package/Example");
+  if (_c_Example == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  load_method(_c_Example, &_m_Example__overloaded1, "overloaded",
+              "(ILjava/lang/String;)V");
+  if (_m_Example__overloaded1 == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_Example__overloaded1, a, b);
+  return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+}
+
+jmethodID _m_Example__overloaded2 = NULL;
+FFI_PLUGIN_EXPORT
+JniResult Example__overloaded2(jobject self_, int32_t a) {
+  load_env();
+  load_class_global_ref(&_c_Example,
+                        "com/github/dart_lang/jnigen/simple_package/Example");
+  if (_c_Example == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  load_method(_c_Example, &_m_Example__overloaded2, "overloaded", "(I)V");
+  if (_m_Example__overloaded2 == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_Example__overloaded2, a);
+  return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+}
+
+jmethodID _m_Example__overloaded3 = NULL;
+FFI_PLUGIN_EXPORT
+JniResult Example__overloaded3(jobject self_, jobject a, jobject b) {
+  load_env();
+  load_class_global_ref(&_c_Example,
+                        "com/github/dart_lang/jnigen/simple_package/Example");
+  if (_c_Example == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  load_method(_c_Example, &_m_Example__overloaded3, "overloaded",
+              "(Ljava/util/List;Ljava/lang/String;)V");
+  if (_m_Example__overloaded3 == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_Example__overloaded3, a, b);
+  return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+}
+
+jmethodID _m_Example__overloaded4 = NULL;
+FFI_PLUGIN_EXPORT
+JniResult Example__overloaded4(jobject self_, jobject a) {
+  load_env();
+  load_class_global_ref(&_c_Example,
+                        "com/github/dart_lang/jnigen/simple_package/Example");
+  if (_c_Example == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  load_method(_c_Example, &_m_Example__overloaded4, "overloaded",
+              "(Ljava/util/List;)V");
+  if (_m_Example__overloaded4 == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_Example__overloaded4, a);
+  return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+}
+
+jfieldID _f_Example__unusedRandom = NULL;
+FFI_PLUGIN_EXPORT
+JniResult get_Example__unusedRandom() {
+  load_env();
+  load_class_global_ref(&_c_Example,
+                        "com/github/dart_lang/jnigen/simple_package/Example");
+  if (_c_Example == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  load_static_field(_c_Example, &_f_Example__unusedRandom, "unusedRandom",
+                    "Ljava/util/Random;");
+  jobject _result = (*jniEnv)->GetStaticObjectField(jniEnv, _c_Example,
+                                                    _f_Example__unusedRandom);
+  return to_global_ref_result(_result);
+}
+
+jfieldID _f_Example__protectedField = NULL;
+FFI_PLUGIN_EXPORT
+JniResult get_Example__protectedField(jobject self_) {
+  load_env();
+  load_class_global_ref(&_c_Example,
+                        "com/github/dart_lang/jnigen/simple_package/Example");
+  if (_c_Example == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  load_field(_c_Example, &_f_Example__protectedField, "protectedField",
+             "Ljava/util/Random;");
+  jobject _result =
+      (*jniEnv)->GetObjectField(jniEnv, self_, _f_Example__protectedField);
+  return to_global_ref_result(_result);
+}
+
+FFI_PLUGIN_EXPORT
+JniResult set_Example__protectedField(jobject self_, jobject value) {
+  load_env();
+  load_class_global_ref(&_c_Example,
+                        "com/github/dart_lang/jnigen/simple_package/Example");
+  if (_c_Example == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  load_field(_c_Example, &_f_Example__protectedField, "protectedField",
+             "Ljava/util/Random;");
+  (*jniEnv)->SetObjectField(jniEnv, self_, _f_Example__protectedField, value);
+  return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+}
+
 // com.github.dart_lang.jnigen.simple_package.Example$Nested
 jclass _c_Example_Nested = NULL;
 
@@ -713,6 +962,60 @@
   return (JniResult){.value = {.j = 0}, .exception = check_exception()};
 }
 
+// com.github.dart_lang.jnigen.simple_package.Example$NonStaticNested
+jclass _c_Example_NonStaticNested = NULL;
+
+jmethodID _m_Example_NonStaticNested__new0 = NULL;
+FFI_PLUGIN_EXPORT
+JniResult Example_NonStaticNested__new0(jobject _parent) {
+  load_env();
+  load_class_global_ref(
+      &_c_Example_NonStaticNested,
+      "com/github/dart_lang/jnigen/simple_package/Example$NonStaticNested");
+  if (_c_Example_NonStaticNested == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  load_method(_c_Example_NonStaticNested, &_m_Example_NonStaticNested__new0,
+              "<init>",
+              "(Lcom/github/dart_lang/jnigen/simple_package/Example;)V");
+  if (_m_Example_NonStaticNested__new0 == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  jobject _result =
+      (*jniEnv)->NewObject(jniEnv, _c_Example_NonStaticNested,
+                           _m_Example_NonStaticNested__new0, _parent);
+  return to_global_ref_result(_result);
+}
+
+jfieldID _f_Example_NonStaticNested__ok = NULL;
+FFI_PLUGIN_EXPORT
+JniResult get_Example_NonStaticNested__ok(jobject self_) {
+  load_env();
+  load_class_global_ref(
+      &_c_Example_NonStaticNested,
+      "com/github/dart_lang/jnigen/simple_package/Example$NonStaticNested");
+  if (_c_Example_NonStaticNested == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  load_field(_c_Example_NonStaticNested, &_f_Example_NonStaticNested__ok, "ok",
+             "Z");
+  uint8_t _result =
+      (*jniEnv)->GetBooleanField(jniEnv, self_, _f_Example_NonStaticNested__ok);
+  return (JniResult){.value = {.z = _result}, .exception = check_exception()};
+}
+
+FFI_PLUGIN_EXPORT
+JniResult set_Example_NonStaticNested__ok(jobject self_, uint8_t value) {
+  load_env();
+  load_class_global_ref(
+      &_c_Example_NonStaticNested,
+      "com/github/dart_lang/jnigen/simple_package/Example$NonStaticNested");
+  if (_c_Example_NonStaticNested == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  load_field(_c_Example_NonStaticNested, &_f_Example_NonStaticNested__ok, "ok",
+             "Z");
+  (*jniEnv)->SetBooleanField(jniEnv, self_, _f_Example_NonStaticNested__ok,
+                             value);
+  return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+}
+
 // com.github.dart_lang.jnigen.simple_package.Exceptions
 jclass _c_Exceptions = NULL;
 
@@ -1440,6 +1743,27 @@
   return (JniResult){.value = {.i = _result}, .exception = check_exception()};
 }
 
+// com.github.dart_lang.jnigen.generics.GenericTypeParams
+jclass _c_GenericTypeParams = NULL;
+
+jmethodID _m_GenericTypeParams__new0 = NULL;
+FFI_PLUGIN_EXPORT
+JniResult GenericTypeParams__new0() {
+  load_env();
+  load_class_global_ref(
+      &_c_GenericTypeParams,
+      "com/github/dart_lang/jnigen/generics/GenericTypeParams");
+  if (_c_GenericTypeParams == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  load_method(_c_GenericTypeParams, &_m_GenericTypeParams__new0, "<init>",
+              "()V");
+  if (_m_GenericTypeParams__new0 == NULL)
+    return (JniResult){.value = {.j = 0}, .exception = check_exception()};
+  jobject _result = (*jniEnv)->NewObject(jniEnv, _c_GenericTypeParams,
+                                         _m_GenericTypeParams__new0);
+  return to_global_ref_result(_result);
+}
+
 // com.github.dart_lang.jnigen.generics.GrandParent
 jclass _c_GrandParent = NULL;
 
diff --git a/pkgs/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart b/pkgs/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart
index 10e3990..06112a1 100644
--- a/pkgs/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart
+++ b/pkgs/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart
@@ -29,6 +29,66 @@
 final ffi.Pointer<T> Function<T extends ffi.NativeType>(String sym) jniLookup =
     ProtectedJniExtensions.initGeneratedLibrary("simple_package");
 
+/// from: com.github.dart_lang.jnigen.simple_package.Color
+class Color extends jni.JObject {
+  @override
+  late final jni.JObjType<Color> $type = type;
+
+  Color.fromRef(
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
+
+  /// The type which includes information such as the signature of this class.
+  static const type = $ColorType();
+  static final _values =
+      jniLookup<ffi.NativeFunction<jni.JniResult Function()>>("Color__values")
+          .asFunction<jni.JniResult Function()>();
+
+  /// from: static public com.github.dart_lang.jnigen.simple_package.Color[] values()
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  static jni.JArray<Color> values() {
+    return const jni.JArrayType($ColorType()).fromRef(_values().object);
+  }
+
+  static final _valueOf = jniLookup<
+              ffi
+              .NativeFunction<jni.JniResult Function(ffi.Pointer<ffi.Void>)>>(
+          "Color__valueOf")
+      .asFunction<jni.JniResult Function(ffi.Pointer<ffi.Void>)>();
+
+  /// from: static public com.github.dart_lang.jnigen.simple_package.Color valueOf(java.lang.String name)
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  static Color valueOf(
+    jni.JString name,
+  ) {
+    return const $ColorType().fromRef(_valueOf(name.reference).object);
+  }
+}
+
+class $ColorType extends jni.JObjType<Color> {
+  const $ColorType();
+
+  @override
+  String get signature => r"Lcom/github/dart_lang/jnigen/simple_package/Color;";
+
+  @override
+  Color fromRef(jni.JObjectPtr ref) => Color.fromRef(ref);
+
+  @override
+  jni.JObjType get superType => const jni.JObjectType();
+
+  @override
+  final superCount = 1;
+
+  @override
+  int get hashCode => ($ColorType).hashCode;
+
+  @override
+  bool operator ==(Object other) {
+    return other.runtimeType == ($ColorType) && other is $ColorType;
+  }
+}
+
 /// from: com.github.dart_lang.jnigen.simple_package.Example
 class Example extends jni.JObject {
   @override
@@ -51,11 +111,48 @@
   static const PI = 3.14159;
 
   /// from: static public final char SEMICOLON
-  static const SEMICOLON = r""";""";
+  static const SEMICOLON = 59;
 
   /// from: static public final java.lang.String SEMICOLON_STRING
   static const SEMICOLON_STRING = r""";""";
 
+  static final _get_unusedRandom =
+      jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
+              "get_Example__unusedRandom")
+          .asFunction<jni.JniResult Function()>();
+
+  /// from: static public final java.util.Random unusedRandom
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  static jni.JObject get unusedRandom =>
+      const jni.JObjectType().fromRef(_get_unusedRandom().object);
+
+  static final _get_protectedField = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(
+                jni.JObjectPtr,
+              )>>("get_Example__protectedField")
+      .asFunction<
+          jni.JniResult Function(
+            jni.JObjectPtr,
+          )>();
+
+  static final _set_protectedField = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(jni.JObjectPtr,
+                  ffi.Pointer<ffi.Void>)>>("set_Example__protectedField")
+      .asFunction<
+          jni.JniResult Function(jni.JObjectPtr, ffi.Pointer<ffi.Void>)>();
+
+  /// from: protected java.util.Random protectedField
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  jni.JObject get protectedField =>
+      const jni.JObjectType().fromRef(_get_protectedField(reference).object);
+
+  /// from: protected java.util.Random protectedField
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  set protectedField(jni.JObject value) =>
+      _set_protectedField(reference, value.reference).check();
+
   static final _getAmount =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
               "Example__getAmount")
@@ -359,6 +456,109 @@
         .fromRef(_getRandomNumericString(reference, random.reference).object);
   }
 
+  static final _protectedMethod = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(
+                  ffi.Pointer<ffi.Void>,
+                  ffi.Pointer<ffi.Void>,
+                  ffi.Pointer<ffi.Void>)>>("Example__protectedMethod")
+      .asFunction<
+          jni.JniResult Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>,
+              ffi.Pointer<ffi.Void>)>();
+
+  /// from: protected void protectedMethod(java.lang.String a, java.lang.String b)
+  void protectedMethod(
+    jni.JString a,
+    jni.JString b,
+  ) {
+    return _protectedMethod(reference, a.reference, b.reference).check();
+  }
+
+  static final _finalMethod = jniLookup<
+              ffi
+              .NativeFunction<jni.JniResult Function(ffi.Pointer<ffi.Void>)>>(
+          "Example__finalMethod")
+      .asFunction<jni.JniResult Function(ffi.Pointer<ffi.Void>)>();
+
+  /// from: public final void finalMethod()
+  void finalMethod() {
+    return _finalMethod(reference).check();
+  }
+
+  static final _getList = jniLookup<
+              ffi
+              .NativeFunction<jni.JniResult Function(ffi.Pointer<ffi.Void>)>>(
+          "Example__getList")
+      .asFunction<jni.JniResult Function(ffi.Pointer<ffi.Void>)>();
+
+  /// from: public java.util.List<java.lang.String> getList()
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  jni.JList<jni.JString> getList() {
+    return const jni.JListType(jni.JStringType())
+        .fromRef(_getList(reference).object);
+  }
+
+  static final _joinStrings = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(
+                  ffi.Pointer<ffi.Void>,
+                  ffi.Pointer<ffi.Void>,
+                  ffi.Pointer<ffi.Void>)>>("Example__joinStrings")
+      .asFunction<
+          jni.JniResult Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>,
+              ffi.Pointer<ffi.Void>)>();
+
+  /// from: public java.lang.String joinStrings(java.util.List<java.lang.String> values, java.lang.String delim)
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  ///
+  /// Joins the strings in the list using the given delimiter.
+  jni.JString joinStrings(
+    jni.JList<jni.JString> values,
+    jni.JString delim,
+  ) {
+    return const jni.JStringType().fromRef(
+        _joinStrings(reference, values.reference, delim.reference).object);
+  }
+
+  static final _methodWithSeveralParams = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(
+                  ffi.Pointer<ffi.Void>,
+                  ffi.Uint16,
+                  ffi.Pointer<ffi.Void>,
+                  ffi.Pointer<ffi.Void>,
+                  ffi.Pointer<ffi.Void>,
+                  ffi.Pointer<ffi.Void>,
+                  ffi.Pointer<ffi.Void>)>>("Example__methodWithSeveralParams")
+      .asFunction<
+          jni.JniResult Function(
+              ffi.Pointer<ffi.Void>,
+              int,
+              ffi.Pointer<ffi.Void>,
+              ffi.Pointer<ffi.Void>,
+              ffi.Pointer<ffi.Void>,
+              ffi.Pointer<ffi.Void>,
+              ffi.Pointer<ffi.Void>)>();
+
+  /// from: public void methodWithSeveralParams(char ch, java.lang.String s, int[] a, T t, java.util.List<T> lt, java.util.Map<java.lang.String,? extends java.lang.CharSequence> wm)
+  void methodWithSeveralParams<$T extends jni.JObject>(
+    int ch,
+    jni.JString s,
+    jni.JArray<jni.jint> a,
+    $T t,
+    jni.JList<$T> lt,
+    jni.JMap<jni.JString, jni.JObject> wm, {
+    jni.JObjType<$T>? T,
+  }) {
+    T ??= jni.lowestCommonSuperType([
+      (lt.$type as jni.JListType).E,
+      t.$type,
+    ]) as jni.JObjType<$T>;
+    return _methodWithSeveralParams(reference, ch, s.reference, a.reference,
+            t.reference, lt.reference, wm.reference)
+        .check();
+  }
+
   static final _new0 =
       jniLookup<ffi.NativeFunction<jni.JniResult Function()>>("Example__new0")
           .asFunction<jni.JniResult Function()>();
@@ -503,6 +703,79 @@
   static void throwException() {
     return _throwException().check();
   }
+
+  static final _overloaded = jniLookup<
+              ffi
+              .NativeFunction<jni.JniResult Function(ffi.Pointer<ffi.Void>)>>(
+          "Example__overloaded")
+      .asFunction<jni.JniResult Function(ffi.Pointer<ffi.Void>)>();
+
+  /// from: public void overloaded()
+  void overloaded() {
+    return _overloaded(reference).check();
+  }
+
+  static final _overloaded1 = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(ffi.Pointer<ffi.Void>, ffi.Int32,
+                  ffi.Pointer<ffi.Void>)>>("Example__overloaded1")
+      .asFunction<
+          jni.JniResult Function(
+              ffi.Pointer<ffi.Void>, int, ffi.Pointer<ffi.Void>)>();
+
+  /// from: public void overloaded(int a, java.lang.String b)
+  void overloaded1(
+    int a,
+    jni.JString b,
+  ) {
+    return _overloaded1(reference, a, b.reference).check();
+  }
+
+  static final _overloaded2 = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(
+                  ffi.Pointer<ffi.Void>, ffi.Int32)>>("Example__overloaded2")
+      .asFunction<jni.JniResult Function(ffi.Pointer<ffi.Void>, int)>();
+
+  /// from: public void overloaded(int a)
+  void overloaded2(
+    int a,
+  ) {
+    return _overloaded2(reference, a).check();
+  }
+
+  static final _overloaded3 = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(
+                  ffi.Pointer<ffi.Void>,
+                  ffi.Pointer<ffi.Void>,
+                  ffi.Pointer<ffi.Void>)>>("Example__overloaded3")
+      .asFunction<
+          jni.JniResult Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>,
+              ffi.Pointer<ffi.Void>)>();
+
+  /// from: public void overloaded(java.util.List<java.lang.Integer> a, java.lang.String b)
+  void overloaded3(
+    jni.JList<jni.JInteger> a,
+    jni.JString b,
+  ) {
+    return _overloaded3(reference, a.reference, b.reference).check();
+  }
+
+  static final _overloaded4 = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(ffi.Pointer<ffi.Void>,
+                  ffi.Pointer<ffi.Void>)>>("Example__overloaded4")
+      .asFunction<
+          jni.JniResult Function(
+              ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)>();
+
+  /// from: public void overloaded(java.util.List<java.lang.Integer> a)
+  void overloaded4(
+    jni.JList<jni.JInteger> a,
+  ) {
+    return _overloaded4(reference, a.reference).check();
+  }
 }
 
 class $ExampleType extends jni.JObjType<Example> {
@@ -682,6 +955,82 @@
   }
 }
 
+/// from: com.github.dart_lang.jnigen.simple_package.Example$NonStaticNested
+class Example_NonStaticNested extends jni.JObject {
+  @override
+  late final jni.JObjType<Example_NonStaticNested> $type = type;
+
+  Example_NonStaticNested.fromRef(
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
+
+  /// The type which includes information such as the signature of this class.
+  static const type = $Example_NonStaticNestedType();
+  static final _get_ok = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(
+                jni.JObjectPtr,
+              )>>("get_Example_NonStaticNested__ok")
+      .asFunction<
+          jni.JniResult Function(
+            jni.JObjectPtr,
+          )>();
+
+  static final _set_ok = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(jni.JObjectPtr,
+                  ffi.Uint8)>>("set_Example_NonStaticNested__ok")
+      .asFunction<jni.JniResult Function(jni.JObjectPtr, int)>();
+
+  /// from: public boolean ok
+  bool get ok => _get_ok(reference).boolean;
+
+  /// from: public boolean ok
+  set ok(bool value) => _set_ok(reference, value ? 1 : 0).check();
+
+  static final _new0 = jniLookup<
+              ffi
+              .NativeFunction<jni.JniResult Function(ffi.Pointer<ffi.Void>)>>(
+          "Example_NonStaticNested__new0")
+      .asFunction<jni.JniResult Function(ffi.Pointer<ffi.Void>)>();
+
+  /// from: public void <init>(com.github.dart_lang.jnigen.simple_package.Example $parent)
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  factory Example_NonStaticNested(
+    Example $parent,
+  ) {
+    return Example_NonStaticNested.fromRef(_new0($parent.reference).object);
+  }
+}
+
+class $Example_NonStaticNestedType
+    extends jni.JObjType<Example_NonStaticNested> {
+  const $Example_NonStaticNestedType();
+
+  @override
+  String get signature =>
+      r"Lcom/github/dart_lang/jnigen/simple_package/Example$NonStaticNested;";
+
+  @override
+  Example_NonStaticNested fromRef(jni.JObjectPtr ref) =>
+      Example_NonStaticNested.fromRef(ref);
+
+  @override
+  jni.JObjType get superType => const jni.JObjectType();
+
+  @override
+  final superCount = 1;
+
+  @override
+  int get hashCode => ($Example_NonStaticNestedType).hashCode;
+
+  @override
+  bool operator ==(Object other) {
+    return other.runtimeType == ($Example_NonStaticNestedType) &&
+        other is $Example_NonStaticNestedType;
+  }
+}
+
 /// from: com.github.dart_lang.jnigen.simple_package.Exceptions
 class Exceptions extends jni.JObject {
   @override
@@ -1389,6 +1738,83 @@
   }
 }
 
+/// from: com.github.dart_lang.jnigen.generics.GenericTypeParams
+class GenericTypeParams<$S extends jni.JObject, $K extends jni.JObject>
+    extends jni.JObject {
+  @override
+  late final jni.JObjType<GenericTypeParams<$S, $K>> $type = type(S, K);
+
+  final jni.JObjType<$S> S;
+  final jni.JObjType<$K> K;
+
+  GenericTypeParams.fromRef(
+    this.S,
+    this.K,
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
+
+  /// The type which includes information such as the signature of this class.
+  static $GenericTypeParamsType<$S, $K>
+      type<$S extends jni.JObject, $K extends jni.JObject>(
+    jni.JObjType<$S> S,
+    jni.JObjType<$K> K,
+  ) {
+    return $GenericTypeParamsType(
+      S,
+      K,
+    );
+  }
+
+  static final _new0 = jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
+          "GenericTypeParams__new0")
+      .asFunction<jni.JniResult Function()>();
+
+  /// from: public void <init>()
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  factory GenericTypeParams({
+    required jni.JObjType<$S> S,
+    required jni.JObjType<$K> K,
+  }) {
+    return GenericTypeParams.fromRef(S, K, _new0().object);
+  }
+}
+
+class $GenericTypeParamsType<$S extends jni.JObject, $K extends jni.JObject>
+    extends jni.JObjType<GenericTypeParams<$S, $K>> {
+  final jni.JObjType<$S> S;
+  final jni.JObjType<$K> K;
+
+  const $GenericTypeParamsType(
+    this.S,
+    this.K,
+  );
+
+  @override
+  String get signature =>
+      r"Lcom/github/dart_lang/jnigen/generics/GenericTypeParams;";
+
+  @override
+  GenericTypeParams<$S, $K> fromRef(jni.JObjectPtr ref) =>
+      GenericTypeParams.fromRef(S, K, ref);
+
+  @override
+  jni.JObjType get superType => const jni.JObjectType();
+
+  @override
+  final superCount = 1;
+
+  @override
+  int get hashCode => Object.hash($GenericTypeParamsType, S, K);
+
+  @override
+  bool operator ==(Object other) {
+    return other.runtimeType == ($GenericTypeParamsType<$S, $K>) &&
+        other is $GenericTypeParamsType<$S, $K> &&
+        S == other.S &&
+        K == other.K;
+  }
+}
+
 /// from: com.github.dart_lang.jnigen.generics.GrandParent
 class GrandParent<$T extends jni.JObject> extends jni.JObject {
   @override
diff --git a/pkgs/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart b/pkgs/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart
index e3667c1..73d8b74 100644
--- a/pkgs/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart
+++ b/pkgs/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart
@@ -24,6 +24,73 @@
 import "package:jni/internal_helpers_for_jnigen.dart";
 import "package:jni/jni.dart" as jni;
 
+/// from: com.github.dart_lang.jnigen.simple_package.Color
+class Color extends jni.JObject {
+  @override
+  late final jni.JObjType<Color> $type = type;
+
+  Color.fromRef(
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
+
+  static final _class =
+      jni.Jni.findJClass(r"com/github/dart_lang/jnigen/simple_package/Color");
+
+  /// The type which includes information such as the signature of this class.
+  static const type = $ColorType();
+  static final _id_values = jni.Jni.accessors.getStaticMethodIDOf(
+      _class.reference,
+      r"values",
+      r"()[Lcom/github/dart_lang/jnigen/simple_package/Color;");
+
+  /// from: static public com.github.dart_lang.jnigen.simple_package.Color[] values()
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  static jni.JArray<Color> values() {
+    return const jni.JArrayType($ColorType()).fromRef(jni.Jni.accessors
+        .callStaticMethodWithArgs(_class.reference, _id_values,
+            jni.JniCallType.objectType, []).object);
+  }
+
+  static final _id_valueOf = jni.Jni.accessors.getStaticMethodIDOf(
+      _class.reference,
+      r"valueOf",
+      r"(Ljava/lang/String;)Lcom/github/dart_lang/jnigen/simple_package/Color;");
+
+  /// from: static public com.github.dart_lang.jnigen.simple_package.Color valueOf(java.lang.String name)
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  static Color valueOf(
+    jni.JString name,
+  ) {
+    return const $ColorType().fromRef(jni.Jni.accessors
+        .callStaticMethodWithArgs(_class.reference, _id_valueOf,
+            jni.JniCallType.objectType, [name.reference]).object);
+  }
+}
+
+class $ColorType extends jni.JObjType<Color> {
+  const $ColorType();
+
+  @override
+  String get signature => r"Lcom/github/dart_lang/jnigen/simple_package/Color;";
+
+  @override
+  Color fromRef(jni.JObjectPtr ref) => Color.fromRef(ref);
+
+  @override
+  jni.JObjType get superType => const jni.JObjectType();
+
+  @override
+  final superCount = 1;
+
+  @override
+  int get hashCode => ($ColorType).hashCode;
+
+  @override
+  bool operator ==(Object other) {
+    return other.runtimeType == ($ColorType) && other is $ColorType;
+  }
+}
+
 /// from: com.github.dart_lang.jnigen.simple_package.Example
 class Example extends jni.JObject {
   @override
@@ -49,11 +116,43 @@
   static const PI = 3.14159;
 
   /// from: static public final char SEMICOLON
-  static const SEMICOLON = r""";""";
+  static const SEMICOLON = 59;
 
   /// from: static public final java.lang.String SEMICOLON_STRING
   static const SEMICOLON_STRING = r""";""";
 
+  static final _id_unusedRandom = jni.Jni.accessors.getStaticFieldIDOf(
+    _class.reference,
+    r"unusedRandom",
+    r"Ljava/util/Random;",
+  );
+
+  /// from: static public final java.util.Random unusedRandom
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  static jni.JObject get unusedRandom =>
+      const jni.JObjectType().fromRef(jni.Jni.accessors
+          .getStaticField(
+              _class.reference, _id_unusedRandom, jni.JniCallType.objectType)
+          .object);
+
+  static final _id_protectedField = jni.Jni.accessors.getFieldIDOf(
+    _class.reference,
+    r"protectedField",
+    r"Ljava/util/Random;",
+  );
+
+  /// from: protected java.util.Random protectedField
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  jni.JObject get protectedField =>
+      const jni.JObjectType().fromRef(jni.Jni.accessors
+          .getField(reference, _id_protectedField, jni.JniCallType.objectType)
+          .object);
+
+  /// from: protected java.util.Random protectedField
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  set protectedField(jni.JObject value) => jni.Jni.env
+      .SetObjectField(reference, _id_protectedField, value.reference);
+
   static final _id_getAmount = jni.Jni.accessors
       .getStaticMethodIDOf(_class.reference, r"getAmount", r"()I");
 
@@ -332,6 +431,90 @@
         [random.reference]).object);
   }
 
+  static final _id_protectedMethod = jni.Jni.accessors.getMethodIDOf(
+      _class.reference,
+      r"protectedMethod",
+      r"(Ljava/lang/String;Ljava/lang/String;)V");
+
+  /// from: protected void protectedMethod(java.lang.String a, java.lang.String b)
+  void protectedMethod(
+    jni.JString a,
+    jni.JString b,
+  ) {
+    return jni.Jni.accessors.callMethodWithArgs(reference, _id_protectedMethod,
+        jni.JniCallType.voidType, [a.reference, b.reference]).check();
+  }
+
+  static final _id_finalMethod =
+      jni.Jni.accessors.getMethodIDOf(_class.reference, r"finalMethod", r"()V");
+
+  /// from: public final void finalMethod()
+  void finalMethod() {
+    return jni.Jni.accessors.callMethodWithArgs(
+        reference, _id_finalMethod, jni.JniCallType.voidType, []).check();
+  }
+
+  static final _id_getList = jni.Jni.accessors
+      .getMethodIDOf(_class.reference, r"getList", r"()Ljava/util/List;");
+
+  /// from: public java.util.List<java.lang.String> getList()
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  jni.JList<jni.JString> getList() {
+    return const jni.JListType(jni.JStringType()).fromRef(jni.Jni.accessors
+        .callMethodWithArgs(
+            reference, _id_getList, jni.JniCallType.objectType, []).object);
+  }
+
+  static final _id_joinStrings = jni.Jni.accessors.getMethodIDOf(
+      _class.reference,
+      r"joinStrings",
+      r"(Ljava/util/List;Ljava/lang/String;)Ljava/lang/String;");
+
+  /// from: public java.lang.String joinStrings(java.util.List<java.lang.String> values, java.lang.String delim)
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  ///
+  /// Joins the strings in the list using the given delimiter.
+  jni.JString joinStrings(
+    jni.JList<jni.JString> values,
+    jni.JString delim,
+  ) {
+    return const jni.JStringType().fromRef(jni.Jni.accessors.callMethodWithArgs(
+        reference,
+        _id_joinStrings,
+        jni.JniCallType.objectType,
+        [values.reference, delim.reference]).object);
+  }
+
+  static final _id_methodWithSeveralParams = jni.Jni.accessors.getMethodIDOf(
+      _class.reference,
+      r"methodWithSeveralParams",
+      r"(CLjava/lang/String;[ILjava/lang/CharSequence;Ljava/util/List;Ljava/util/Map;)V");
+
+  /// from: public void methodWithSeveralParams(char ch, java.lang.String s, int[] a, T t, java.util.List<T> lt, java.util.Map<java.lang.String,? extends java.lang.CharSequence> wm)
+  void methodWithSeveralParams<$T extends jni.JObject>(
+    int ch,
+    jni.JString s,
+    jni.JArray<jni.jint> a,
+    $T t,
+    jni.JList<$T> lt,
+    jni.JMap<jni.JString, jni.JObject> wm, {
+    jni.JObjType<$T>? T,
+  }) {
+    T ??= jni.lowestCommonSuperType([
+      (lt.$type as jni.JListType).E,
+      t.$type,
+    ]) as jni.JObjType<$T>;
+    return jni.Jni.accessors.callMethodWithArgs(
+        reference, _id_methodWithSeveralParams, jni.JniCallType.voidType, [
+      jni.JValueChar(ch),
+      s.reference,
+      a.reference,
+      t.reference,
+      lt.reference,
+      wm.reference
+    ]).check();
+  }
+
   static final _id_new0 =
       jni.Jni.accessors.getMethodIDOf(_class.reference, r"<init>", r"()V");
 
@@ -475,6 +658,63 @@
     return jni.Jni.accessors.callStaticMethodWithArgs(_class.reference,
         _id_throwException, jni.JniCallType.voidType, []).check();
   }
+
+  static final _id_overloaded =
+      jni.Jni.accessors.getMethodIDOf(_class.reference, r"overloaded", r"()V");
+
+  /// from: public void overloaded()
+  void overloaded() {
+    return jni.Jni.accessors.callMethodWithArgs(
+        reference, _id_overloaded, jni.JniCallType.voidType, []).check();
+  }
+
+  static final _id_overloaded1 = jni.Jni.accessors.getMethodIDOf(
+      _class.reference, r"overloaded", r"(ILjava/lang/String;)V");
+
+  /// from: public void overloaded(int a, java.lang.String b)
+  void overloaded1(
+    int a,
+    jni.JString b,
+  ) {
+    return jni.Jni.accessors.callMethodWithArgs(reference, _id_overloaded1,
+        jni.JniCallType.voidType, [jni.JValueInt(a), b.reference]).check();
+  }
+
+  static final _id_overloaded2 =
+      jni.Jni.accessors.getMethodIDOf(_class.reference, r"overloaded", r"(I)V");
+
+  /// from: public void overloaded(int a)
+  void overloaded2(
+    int a,
+  ) {
+    return jni.Jni.accessors.callMethodWithArgs(reference, _id_overloaded2,
+        jni.JniCallType.voidType, [jni.JValueInt(a)]).check();
+  }
+
+  static final _id_overloaded3 = jni.Jni.accessors.getMethodIDOf(
+      _class.reference,
+      r"overloaded",
+      r"(Ljava/util/List;Ljava/lang/String;)V");
+
+  /// from: public void overloaded(java.util.List<java.lang.Integer> a, java.lang.String b)
+  void overloaded3(
+    jni.JList<jni.JInteger> a,
+    jni.JString b,
+  ) {
+    return jni.Jni.accessors.callMethodWithArgs(reference, _id_overloaded3,
+        jni.JniCallType.voidType, [a.reference, b.reference]).check();
+  }
+
+  static final _id_overloaded4 = jni.Jni.accessors
+      .getMethodIDOf(_class.reference, r"overloaded", r"(Ljava/util/List;)V");
+
+  /// from: public void overloaded(java.util.List<java.lang.Integer> a)
+  void overloaded4(
+    jni.JList<jni.JInteger> a,
+  ) {
+    return jni.Jni.accessors.callMethodWithArgs(reference, _id_overloaded4,
+        jni.JniCallType.voidType, [a.reference]).check();
+  }
 }
 
 class $ExampleType extends jni.JObjType<Example> {
@@ -652,6 +892,76 @@
   }
 }
 
+/// from: com.github.dart_lang.jnigen.simple_package.Example$NonStaticNested
+class Example_NonStaticNested extends jni.JObject {
+  @override
+  late final jni.JObjType<Example_NonStaticNested> $type = type;
+
+  Example_NonStaticNested.fromRef(
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
+
+  static final _class = jni.Jni.findJClass(
+      r"com/github/dart_lang/jnigen/simple_package/Example$NonStaticNested");
+
+  /// The type which includes information such as the signature of this class.
+  static const type = $Example_NonStaticNestedType();
+  static final _id_ok = jni.Jni.accessors.getFieldIDOf(
+    _class.reference,
+    r"ok",
+    r"Z",
+  );
+
+  /// from: public boolean ok
+  bool get ok => jni.Jni.accessors
+      .getField(reference, _id_ok, jni.JniCallType.booleanType)
+      .boolean;
+
+  /// from: public boolean ok
+  set ok(bool value) =>
+      jni.Jni.env.SetBooleanField(reference, _id_ok, value ? 1 : 0);
+
+  static final _id_new0 = jni.Jni.accessors.getMethodIDOf(_class.reference,
+      r"<init>", r"(Lcom/github/dart_lang/jnigen/simple_package/Example;)V");
+
+  /// from: public void <init>(com.github.dart_lang.jnigen.simple_package.Example $parent)
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  factory Example_NonStaticNested(
+    Example $parent,
+  ) {
+    return Example_NonStaticNested.fromRef(jni.Jni.accessors.newObjectWithArgs(
+        _class.reference, _id_new0, [$parent.reference]).object);
+  }
+}
+
+class $Example_NonStaticNestedType
+    extends jni.JObjType<Example_NonStaticNested> {
+  const $Example_NonStaticNestedType();
+
+  @override
+  String get signature =>
+      r"Lcom/github/dart_lang/jnigen/simple_package/Example$NonStaticNested;";
+
+  @override
+  Example_NonStaticNested fromRef(jni.JObjectPtr ref) =>
+      Example_NonStaticNested.fromRef(ref);
+
+  @override
+  jni.JObjType get superType => const jni.JObjectType();
+
+  @override
+  final superCount = 1;
+
+  @override
+  int get hashCode => ($Example_NonStaticNestedType).hashCode;
+
+  @override
+  bool operator ==(Object other) {
+    return other.runtimeType == ($Example_NonStaticNestedType) &&
+        other is $Example_NonStaticNestedType;
+  }
+}
+
 /// from: com.github.dart_lang.jnigen.simple_package.Exceptions
 class Exceptions extends jni.JObject {
   @override
@@ -1304,6 +1614,89 @@
   }
 }
 
+/// from: com.github.dart_lang.jnigen.generics.GenericTypeParams
+class GenericTypeParams<$S extends jni.JObject, $K extends jni.JObject>
+    extends jni.JObject {
+  @override
+  late final jni.JObjType<GenericTypeParams<$S, $K>> $type = type(S, K);
+
+  final jni.JObjType<$S> S;
+  final jni.JObjType<$K> K;
+
+  GenericTypeParams.fromRef(
+    this.S,
+    this.K,
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
+
+  static final _class = jni.Jni.findJClass(
+      r"com/github/dart_lang/jnigen/generics/GenericTypeParams");
+
+  /// The type which includes information such as the signature of this class.
+  static $GenericTypeParamsType<$S, $K>
+      type<$S extends jni.JObject, $K extends jni.JObject>(
+    jni.JObjType<$S> S,
+    jni.JObjType<$K> K,
+  ) {
+    return $GenericTypeParamsType(
+      S,
+      K,
+    );
+  }
+
+  static final _id_new0 =
+      jni.Jni.accessors.getMethodIDOf(_class.reference, r"<init>", r"()V");
+
+  /// from: public void <init>()
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  factory GenericTypeParams({
+    required jni.JObjType<$S> S,
+    required jni.JObjType<$K> K,
+  }) {
+    return GenericTypeParams.fromRef(
+        S,
+        K,
+        jni.Jni.accessors
+            .newObjectWithArgs(_class.reference, _id_new0, []).object);
+  }
+}
+
+class $GenericTypeParamsType<$S extends jni.JObject, $K extends jni.JObject>
+    extends jni.JObjType<GenericTypeParams<$S, $K>> {
+  final jni.JObjType<$S> S;
+  final jni.JObjType<$K> K;
+
+  const $GenericTypeParamsType(
+    this.S,
+    this.K,
+  );
+
+  @override
+  String get signature =>
+      r"Lcom/github/dart_lang/jnigen/generics/GenericTypeParams;";
+
+  @override
+  GenericTypeParams<$S, $K> fromRef(jni.JObjectPtr ref) =>
+      GenericTypeParams.fromRef(S, K, ref);
+
+  @override
+  jni.JObjType get superType => const jni.JObjectType();
+
+  @override
+  final superCount = 1;
+
+  @override
+  int get hashCode => Object.hash($GenericTypeParamsType, S, K);
+
+  @override
+  bool operator ==(Object other) {
+    return other.runtimeType == ($GenericTypeParamsType<$S, $K>) &&
+        other is $GenericTypeParamsType<$S, $K> &&
+        S == other.S &&
+        K == other.K;
+  }
+}
+
 /// from: com.github.dart_lang.jnigen.generics.GrandParent
 class GrandParent<$T extends jni.JObject> extends jni.JObject {
   @override
diff --git a/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/generics/GenericTypeParams.java b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/generics/GenericTypeParams.java
new file mode 100644
index 0000000..2a41875
--- /dev/null
+++ b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/generics/GenericTypeParams.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2023, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.github.dart_lang.jnigen.generics;
+
+public class GenericTypeParams<S extends CharSequence, K extends GenericTypeParams<S, K>> {}
+
+// TODO: Add more cases of generic parameters
diff --git a/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/inheritance/BaseClass.java b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/inheritance/BaseClass.java
new file mode 100644
index 0000000..f9157e4
--- /dev/null
+++ b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/inheritance/BaseClass.java
@@ -0,0 +1,7 @@
+// Copyright (c) 2023, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.github.dart_lang.jnigen.inheritance;
+
+public class BaseClass<T extends CharSequence> {}
diff --git a/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/inheritance/GenericDerivedClass.java b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/inheritance/GenericDerivedClass.java
new file mode 100644
index 0000000..5829657
--- /dev/null
+++ b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/inheritance/GenericDerivedClass.java
@@ -0,0 +1,7 @@
+// Copyright (c) 2023, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.github.dart_lang.jnigen.inheritance;
+
+public class GenericDerivedClass<T extends CharSequence> extends BaseClass<T> {}
diff --git a/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/inheritance/SpecificDerivedClass.java b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/inheritance/SpecificDerivedClass.java
new file mode 100644
index 0000000..6c33e83
--- /dev/null
+++ b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/inheritance/SpecificDerivedClass.java
@@ -0,0 +1,7 @@
+// Copyright (c) 2023, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.github.dart_lang.jnigen.inheritance;
+
+public class SpecificDerivedClass extends BaseClass<String> {}
diff --git a/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/simple_package/Color.java b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/simple_package/Color.java
new file mode 100644
index 0000000..efb96c6
--- /dev/null
+++ b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/simple_package/Color.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2023, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.github.dart_lang.jnigen.simple_package;
+
+public enum Color {
+  RED,
+  BLUE,
+  BLACK,
+  GREEN,
+  YELLOW,
+  LIME
+}
diff --git a/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/simple_package/Example.java b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/simple_package/Example.java
index 47fab9d..b96156e 100644
--- a/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/simple_package/Example.java
+++ b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/simple_package/Example.java
@@ -14,6 +14,8 @@
   public static final char SEMICOLON = ';';
   public static final String SEMICOLON_STRING = ";";
 
+  public static final Random unusedRandom = new Random();
+
   private static int amount = 500;
   private static double pi = 3.14159;
   private static char asterisk = '*';
@@ -123,6 +125,26 @@
         "%d%d%d%d", random.nextInt(10), random.nextInt(10), random.nextInt(10), random.nextInt(10));
   }
 
+  private void privateMethod(String a, String b) {}
+
+  protected void protectedMethod(String a, String b) {}
+
+  protected Random protectedField;
+
+  public final void finalMethod() {}
+
+  public List<String> getList() {
+    return null;
+  }
+
+  /** Joins the strings in the list using the given delimiter. */
+  public String joinStrings(List<String> values, String delim) {
+    return null;
+  }
+
+  public <T extends CharSequence> void methodWithSeveralParams(
+      char ch, String s, int[] a, T t, List<T> lt, Map<String, ? extends CharSequence> wm) {}
+
   public Example() {
     this(0);
   }
@@ -169,6 +191,20 @@
     throw new RuntimeException("Hello");
   }
 
+  public void overloaded() {}
+
+  public void overloaded(int a, String b) {}
+
+  public void overloaded(int a) {}
+
+  public void overloaded(List<Integer> a, String b) {}
+
+  public void overloaded(List<Integer> a) {}
+
+  public class NonStaticNested {
+    public boolean ok;
+  }
+
   public static class Nested {
     private boolean value;
 
diff --git a/pkgs/jnigen/test/simple_package_test/runtime_test_registrant.dart b/pkgs/jnigen/test/simple_package_test/runtime_test_registrant.dart
index e14dbfb..3e7d657 100644
--- a/pkgs/jnigen/test/simple_package_test/runtime_test_registrant.dart
+++ b/pkgs/jnigen/test/simple_package_test/runtime_test_registrant.dart
@@ -22,7 +22,7 @@
       expect(Example.ON, equals(1));
       expect(Example.OFF, equals(0));
       expect(Example.PI, closeTo(pi, fpDelta));
-      expect(Example.SEMICOLON, equals(';'));
+      expect(Example.SEMICOLON, equals(';'.codeUnitAt(0)));
       expect(Example.SEMICOLON_STRING, equals(';'));
     });
 
diff --git a/pkgs/jnigen/test/summary_generation_test.dart b/pkgs/jnigen/test/summary_generation_test.dart
index e1657f1..62eee3b 100644
--- a/pkgs/jnigen/test/summary_generation_test.dart
+++ b/pkgs/jnigen/test/summary_generation_test.dart
@@ -1,4 +1,4 @@
-// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
@@ -7,18 +7,16 @@
 
 @Tags(['summarizer_test'])
 
-import 'dart:io';
 import 'dart:math';
 
 import 'package:jnigen/src/config/config.dart';
 import 'package:jnigen/src/elements/elements.dart';
-import 'package:jnigen/src/logging/logging.dart';
 import 'package:jnigen/src/summary/summary.dart';
-import 'package:logging/logging.dart';
 
 import 'package:path/path.dart' hide equals;
 import 'package:test/test.dart';
 
+import 'test_util/summary_util.dart';
 import 'test_util/test_util.dart';
 
 const nestedClasses = [
@@ -43,18 +41,6 @@
   expect(declNames, containsAll(expectedClasses));
 }
 
-void deleteTempDir(Directory directory) {
-  try {
-    if (Platform.isWindows) {
-      // This appears to avoid "file used by another process" errors.
-      sleep(const Duration(seconds: 1));
-    }
-    directory.deleteSync(recursive: true);
-  } on FileSystemException catch (e) {
-    log.warning("Cannot delete directory: $e");
-  }
-}
-
 /// Packs files indicated by [artifacts], each relative to [artifactDir] into
 /// a JAR file at [jarPath].
 Future<void> createJar({
@@ -69,44 +55,6 @@
   );
 }
 
-String getClassNameFromPath(String path) {
-  if (!path.endsWith('.java')) {
-    throw ArgumentError('Filename must end with java');
-  }
-  return path
-      .replaceAll('/', '.')
-      .replaceAll('\\', '.')
-      .substring(0, path.length - 5);
-}
-
-final simplePackagePath = join('test', 'simple_package_test', 'java');
-final simplePackageDir = Directory(simplePackagePath);
-final javaFiles = findFilesWithSuffix(simplePackageDir, '.java');
-final javaClasses = javaFiles.map(getClassNameFromPath).toList();
-// remove individual class listings from one package,
-// and add the package name instead, for testing.
-const _removalPackage = 'com.github.dart_lang.jnigen.pkg2';
-final summarizerClassesSpec = [
-  ...javaClasses.where((e) => !e.startsWith('$_removalPackage.')),
-  _removalPackage,
-];
-
-Config getConfig({List<String>? sourcePath, List<String>? classPath}) {
-  return Config(
-    outputConfig: OutputConfig(
-      bindingsType: BindingsType.dartOnly,
-      dartConfig: DartCodeOutputConfig(
-        path: Uri.file('unused.dart'),
-        structure: OutputStructure.singleFile,
-      ),
-    ),
-    classes: summarizerClassesSpec,
-    sourcePath: sourcePath?.map((e) => Uri.file(e)).toList(),
-    classPath: classPath?.map((e) => Uri.file(e)).toList(),
-    logLevel: Level.WARNING,
-  );
-}
-
 final random = Random.secure();
 
 void testSuccessCase(String description, Config config) {
@@ -141,16 +89,16 @@
 }) {
   testSuccessCase(
     '- valid config',
-    getConfig(sourcePath: sourcePath, classPath: classPath),
+    getSummaryGenerationConfig(sourcePath: sourcePath, classPath: classPath),
   );
   testFailureCase(
     '- should fail with non-existing class',
-    getConfig(sourcePath: sourcePath, classPath: classPath),
+    getSummaryGenerationConfig(sourcePath: sourcePath, classPath: classPath),
     'com.github.dart_lang.jnigen.DoesNotExist',
   );
   testFailureCase(
     '- should fail with non-existing package',
-    getConfig(sourcePath: sourcePath, classPath: classPath),
+    getSummaryGenerationConfig(sourcePath: sourcePath, classPath: classPath),
     'com.github.dart_lang.notexist',
   );
 }
@@ -222,5 +170,5 @@
     );
   });
 
-  tearDownAll(() => deleteTempDir(tempDir));
+  tearDownAll(() => deleteTempDirWithDelay(tempDir));
 }
diff --git a/pkgs/jnigen/test/summary_test.dart b/pkgs/jnigen/test/summary_test.dart
new file mode 100644
index 0000000..c83e798
--- /dev/null
+++ b/pkgs/jnigen/test/summary_test.dart
@@ -0,0 +1,323 @@
+// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// These tests validate individual characteristics in summary
+// For example, the values of methods, arguments, types, generic params etc...
+@Tags(['summarizer_test'])
+
+import 'package:jnigen/src/elements/elements.dart';
+import 'package:jnigen/src/summary/summary.dart';
+import 'package:test/test.dart';
+
+import 'test_util/summary_util.dart';
+import 'test_util/test_util.dart';
+
+const jnigenPackage = 'com.github.dart_lang.jnigen';
+const simplePackage = "$jnigenPackage.simple_package";
+
+extension on Classes {
+  String _getSimpleName(ClassDecl c) {
+    return c.binaryName.split(".").last;
+  }
+
+  ClassDecl getClassBySimpleName(String simpleName) {
+    return decls.values.firstWhere((c) => _getSimpleName(c) == simpleName);
+  }
+
+  ClassDecl getClass(String dirName, String className) {
+    return decls['$jnigenPackage.$dirName.$className']!;
+  }
+
+  ClassDecl getExampleClass() {
+    return getClass('simple_package', 'Example');
+  }
+}
+
+extension on ClassDecl {
+  Method getMethod(String name) => methods.firstWhere((m) => m.name == name);
+  Field getField(String name) => fields.firstWhere((f) => f.name == name);
+}
+
+void registerCommonTests(Classes classes) {
+  test('static modifier', () {
+    final example = classes.getExampleClass();
+    final containsStatic = contains("static");
+    final notContainsStatic = isNot(containsStatic);
+    expect(example.getMethod("max4").modifiers, containsStatic);
+    expect(example.getMethod("getCodename").modifiers, notContainsStatic);
+    expect(example.getField("ON").modifiers, containsStatic);
+    expect(example.getField("codename").modifiers, notContainsStatic);
+    final nested = classes.getClassBySimpleName("Example\$Nested");
+    expect(nested.modifiers, containsStatic);
+    final nonStaticNested =
+        classes.getClassBySimpleName("Example\$NonStaticNested");
+    expect(nonStaticNested.modifiers, notContainsStatic);
+  });
+
+  test('Public, protected and private modifiers', () {
+    final example = classes.getExampleClass();
+    final hasPrivate = contains("private");
+    final hasProtected = contains("protected");
+    final hasPublic = contains("public");
+    final isPrivate = allOf(hasPrivate, isNot(hasProtected), isNot(hasPublic));
+    final isProtected =
+        allOf(isNot(hasPrivate), hasProtected, isNot(hasPublic));
+    final isPublic = allOf(isNot(hasPrivate), isNot(hasProtected), hasPublic);
+    expect(example.getMethod("getNumber").modifiers, isPublic);
+    expect(example.getMethod("privateMethod").modifiers, isPrivate);
+    expect(example.getMethod("protectedMethod").modifiers, isProtected);
+    expect(example.getField("OFF").modifiers, isPublic);
+    expect(example.getField("number").modifiers, isPrivate);
+    expect(example.getField("protectedField").modifiers, isProtected);
+  });
+
+  test('final modifier', () {
+    final example = classes.getExampleClass();
+    final isFinal = contains('final');
+    expect(example.getField("PI").modifiers, isFinal);
+    expect(example.getField("unusedRandom").modifiers, isFinal);
+    expect(example.getField("number").modifiers, isNot(isFinal));
+    expect(example.getMethod("finalMethod").modifiers, isFinal);
+  });
+
+  void assertToBeStringListType(TypeUsage listType) {
+    expect(listType.kind, equals(Kind.declared));
+    final listClassType = listType.type as DeclaredType;
+    expect(listClassType.binaryName, equals('java.util.List'));
+    expect(listClassType.params, hasLength(1));
+    final listTypeParam = listClassType.params[0];
+    expect(listTypeParam.kind, equals(Kind.declared));
+    expect(listTypeParam.type.name, equals('java.lang.String'));
+  }
+
+  test('return types', () {
+    final example = classes.getExampleClass();
+    expect(example.getMethod("getNumber").returnType.shorthand, equals("int"));
+    expect(example.getMethod("getName").returnType.shorthand,
+        equals("java.lang.String"));
+    expect(example.getMethod("getNestedInstance").returnType.name,
+        equals("$simplePackage.Example\$Nested"));
+    final listType = example.getMethod("getList").returnType;
+    assertToBeStringListType(listType);
+  });
+
+  test('parameter types', () {
+    final example = classes.getExampleClass();
+    final joinStrings = example.getMethod('joinStrings');
+    final listType = joinStrings.params[0].type;
+    assertToBeStringListType(listType);
+    final stringType = joinStrings.params[1].type;
+    expect(stringType.kind, Kind.declared);
+    expect((stringType.type as DeclaredType).binaryName, 'java.lang.String');
+  });
+
+  test('Parameters of several types', () {
+    final example = classes.getExampleClass();
+    final method = example.getMethod('methodWithSeveralParams');
+    expect(method.typeParams, hasLength(1));
+    expect(method.typeParams[0].name, 'T');
+    expect(method.typeParams[0].bounds[0].name, 'java.lang.CharSequence');
+
+    final charParam = method.params[0];
+    expect(charParam.type.kind, equals(Kind.primitive));
+    expect(charParam.type.name, equals('char'));
+
+    final stringParam = method.params[1];
+    expect(stringParam.type.kind, equals(Kind.declared));
+    expect((stringParam.type.type as DeclaredType).binaryName,
+        equals('java.lang.String'));
+
+    final arrayParam = method.params[2];
+    expect(arrayParam.type.kind, equals(Kind.array));
+    expect((arrayParam.type.type as ArrayType).type.name, equals('int'));
+
+    final typeVarParam = method.params[3];
+    expect(typeVarParam.type.kind, equals(Kind.typeVariable));
+    expect((typeVarParam.type.type as TypeVar).name, equals('T'));
+
+    final listParam = method.params[4];
+    expect(listParam.type.kind, equals(Kind.declared));
+    final listType = (listParam.type.type as DeclaredType);
+    expect(listType.binaryName, equals('java.util.List'));
+    expect(listType.params, hasLength(1));
+    final tType = listType.params[0];
+    expect(tType.kind, Kind.typeVariable);
+    expect((tType.type as TypeVar).name, equals('T'));
+
+    final wildcardMapParam = method.params[5];
+    expect(wildcardMapParam.type.kind, equals(Kind.declared));
+    final mapType = (wildcardMapParam.type.type as DeclaredType);
+    expect(mapType.binaryName, equals('java.util.Map'));
+    expect(mapType.params, hasLength(2));
+    final strType = mapType.params[0];
+    expect(strType.name, 'java.lang.String');
+    // TODO(#141): Wildcard implementation.
+    /*
+    final wildcardType = mapType.params[1];
+    expect(wildcardType.kind, equals(Kind.wildcard));
+    expect((wildcardType.type as Wildcard).extendsBound?.name,
+        equals('java.lang.CharSequence'));
+    */
+  });
+
+  test('superclass', () {
+    final baseClass = classes.getClass('inheritance', 'BaseClass');
+    expect(baseClass.typeParams, hasLength(1));
+    final typeParam = baseClass.typeParams.single;
+    expect(typeParam.bounds.map((b) => b.name).toList(),
+        ['java.lang.CharSequence']);
+
+    final specific = classes.getClass('inheritance', 'SpecificDerivedClass');
+    expect(specific.typeParams, hasLength(0));
+    expect(specific.superclass, isNotNull);
+    final specificSuper = specific.superclass!.type as DeclaredType;
+    expect(specificSuper.params[0].type, isA<DeclaredType>());
+    expect(specificSuper.params[0].type.name, equals('java.lang.String'));
+
+    final generic = classes.getClass('inheritance', 'GenericDerivedClass');
+    expect(generic.typeParams, hasLength(1));
+    expect(generic.typeParams[0].name, equals('T'));
+    expect(generic.typeParams[0].bounds.map((b) => b.name).toList(),
+        ['java.lang.CharSequence']);
+    expect(generic.superclass, isNotNull);
+    final genericSuper = generic.superclass!.type as DeclaredType;
+    expect(genericSuper.params[0].type, isA<TypeVar>());
+    expect(genericSuper.params[0].type.name, equals('T'));
+  });
+
+  test('constructor is included', () {
+    final example = classes.getExampleClass();
+    void assertOneCtorExistsWithArity(List<String> paramTypes) {
+      final arityCtors = example.methods
+          .where(
+              (m) => m.name == '<init>' && m.params.length == paramTypes.length)
+          .toList();
+      expect(arityCtors, hasLength(1));
+      final ctor = arityCtors[0];
+      expect(ctor.params.map((p) => p.type.name), equals(paramTypes));
+    }
+
+    assertOneCtorExistsWithArity([]);
+    assertOneCtorExistsWithArity(['int']);
+    assertOneCtorExistsWithArity(['int', 'boolean']);
+    assertOneCtorExistsWithArity(['int', 'boolean', 'java.lang.String']);
+  });
+
+  test('Overloaded methods', () {
+    final methods = classes
+        .getExampleClass()
+        .methods
+        .where((m) => m.name == 'overloaded')
+        .toList();
+    final signatures =
+        methods.map((m) => m.params.map((p) => p.type.name).toList()).toSet();
+    expect(
+      signatures,
+      equals({
+        <String>[],
+        ['int'],
+        ['int', 'java.lang.String'],
+        ['java.util.List'],
+        ['java.util.List', 'java.lang.String'],
+      }),
+    );
+  });
+
+  test('Declaration type (class vs interface vs enum)', () {
+    final example = classes.getExampleClass();
+    expect(example.declKind, DeclKind.classKind);
+    final myInterface = classes.getClass('interfaces', 'MyInterface');
+    expect(myInterface.declKind, DeclKind.interfaceKind);
+    final color = classes.getClass('simple_package', 'Color');
+    expect(color.declKind, DeclKind.enumKind);
+  });
+
+  test('Enum values', () {
+    final example = classes.getExampleClass();
+    expect(example.values, anyOf(isNull, isEmpty));
+    final color = classes.getClass('simple_package', 'Color');
+    const expectedEnumValues = {
+      'RED',
+      'BLUE',
+      'BLACK',
+      'GREEN',
+      'YELLOW',
+      'LIME'
+    };
+    expect(color.values?.toSet(), expectedEnumValues);
+  });
+
+  test('Static final field values', () {
+    final example = classes.getExampleClass();
+    expect(example.getField("ON").defaultValue, equals(1));
+    expect(example.getField("OFF").defaultValue, equals(0));
+    expect(example.getField("PI").defaultValue, closeTo(3.14159, 0.001));
+    expect(
+        example.getField("SEMICOLON").defaultValue, equals(';'.codeUnitAt(0)));
+    expect(example.getField("SEMICOLON_STRING").defaultValue, equals(';'));
+  });
+
+  test('self referencing generic parameters', () {
+    final gp = classes.getClass('generics', 'GenericTypeParams');
+    final typeParams = gp.typeParams;
+    expect(typeParams[0].name, equals('S'));
+    expect(typeParams[0].bounds.map((e) => e.name), ['java.lang.CharSequence']);
+    expect(typeParams[1].name, equals('K'));
+    final selfBound = typeParams[1].bounds[0];
+    expect(selfBound.kind, Kind.declared);
+    expect(selfBound.name,
+        equals('com.github.dart_lang.jnigen.generics.GenericTypeParams'));
+    final selfBoundType = selfBound.type as DeclaredType;
+    expect(selfBoundType.params, hasLength(2));
+    expect(selfBoundType.params.map((e) => e.name), ['S', 'K']);
+    expect(selfBoundType.params.map((e) => e.kind),
+        [Kind.typeVariable, Kind.typeVariable]);
+  });
+}
+
+void main() async {
+  await checkLocallyBuiltDependencies();
+
+  final tempDir = getTempDir("jnigen_summary_tests_");
+
+  final sourceConfig =
+      getSummaryGenerationConfig(sourcePath: [simplePackagePath]);
+  final parsedFromSource = await getSummary(sourceConfig);
+
+  final targetDir = tempDir.createTempSync("compiled_classes_test_");
+  await compileJavaFiles(simplePackageDir, targetDir);
+  final classConfig = getSummaryGenerationConfig(classPath: [targetDir.path]);
+  final parsedFromClasses = await getSummary(classConfig);
+
+  group('source summary', () {
+    registerCommonTests(parsedFromSource);
+  });
+
+  group('compiled summary', () {
+    registerCommonTests(parsedFromClasses);
+  });
+
+  group('source-based summary features', () {
+    final classes = parsedFromSource;
+    test('Parameter names', () {
+      final example = classes.getExampleClass();
+      final joinStrings = example.getMethod('joinStrings');
+      expect(
+          joinStrings.params.map((p) => p.name).toList(), ['values', 'delim']);
+      final methodWithSeveralParams =
+          example.getMethod("methodWithSeveralParams");
+      expect(methodWithSeveralParams.params.map((p) => p.name).toList(),
+          ['ch', 's', 'a', 't', 'lt', 'wm']);
+    });
+
+    test('Javadoc comment', () {
+      final example = classes.getExampleClass();
+      final joinStrings = example.getMethod('joinStrings');
+      expect(joinStrings.javadoc?.comment,
+          contains("Joins the strings in the list using the given delimiter."));
+    });
+  });
+
+  tearDownAll(() => tempDir.deleteSync(recursive: true));
+}
diff --git a/pkgs/jnigen/test/test_util/summary_util.dart b/pkgs/jnigen/test/test_util/summary_util.dart
new file mode 100644
index 0000000..0d44583
--- /dev/null
+++ b/pkgs/jnigen/test/test_util/summary_util.dart
@@ -0,0 +1,75 @@
+// Copyright (c) 2023, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:io';
+
+import 'package:jnigen/src/config/config.dart';
+import 'package:jnigen/src/logging/logging.dart';
+import 'package:logging/logging.dart';
+import 'package:path/path.dart';
+
+import 'test_util.dart';
+
+String getClassNameFromPath(String path) {
+  if (!path.endsWith('.java')) {
+    throw ArgumentError('Filename must end with java');
+  }
+  return path
+      .replaceAll('/', '.')
+      .replaceAll('\\', '.')
+      .substring(0, path.length - 5);
+}
+
+/// test/simple_package_test/java
+final simplePackagePath = join('test', 'simple_package_test', 'java');
+
+/// Directory(test/simple_package_test/java)
+final simplePackageDir = Directory(simplePackagePath);
+
+/// All Java files in simple_package_test/java
+final javaFiles = findFilesWithSuffix(simplePackageDir, '.java');
+
+/// All Java classes in simple_package_test/java
+final javaClasses = javaFiles.map(getClassNameFromPath).toList();
+
+// Remove individual class listings from one package,
+// and add the package name instead, for testing.
+
+const removalPackageForSummaryTests = 'com.github.dart_lang.jnigen.pkg2';
+
+/// List of FQNs passed to summarizer for simple_package_test.
+final summarizerClassesSpec = [
+  ...javaClasses.where((e) => !e.startsWith('$removalPackageForSummaryTests.')),
+  removalPackageForSummaryTests,
+];
+
+Config getSummaryGenerationConfig(
+    {List<String>? sourcePath, List<String>? classPath}) {
+  return Config(
+    outputConfig: OutputConfig(
+      bindingsType: BindingsType.dartOnly,
+      dartConfig: DartCodeOutputConfig(
+        path: Uri.file('unused.dart'),
+        structure: OutputStructure.singleFile,
+      ),
+    ),
+    // Make a defensive copy of class list, if some test mutates the list...
+    classes: summarizerClassesSpec.toList(),
+    sourcePath: sourcePath?.map((e) => Uri.file(e)).toList(),
+    classPath: classPath?.map((e) => Uri.file(e)).toList(),
+    logLevel: Level.WARNING,
+  );
+}
+
+void deleteTempDirWithDelay(Directory directory) {
+  try {
+    if (Platform.isWindows) {
+      // This appears to avoid "file used by another process" errors.
+      sleep(const Duration(seconds: 1));
+    }
+    directory.deleteSync(recursive: true);
+  } on FileSystemException catch (e) {
+    log.warning("Cannot delete directory: $e");
+  }
+}