@@ -250,6 +250,145 @@ func TestExtractSpec_Location(t *testing.T) {
250250 require .Empty (t , lastField .Condition )
251251}
252252
253+ func TestExtractSpecs_WriteStringList (t * testing.T ) {
254+ src := `
255+ package com.example;
256+
257+ public class Desc implements Parcelable {
258+ @Override
259+ public void writeToParcel(Parcel dest, int flags) {
260+ dest.writeStringList(mMimeTypes);
261+ dest.writeLong(mTimeStamp);
262+ }
263+ }
264+ `
265+
266+ specs := ExtractSpecs (src , "com.example" )
267+ require .Len (t , specs , 1 )
268+
269+ fields := specs [0 ].Fields
270+ require .Len (t , fields , 2 )
271+
272+ require .Equal (t , "MimeTypes" , fields [0 ].Name )
273+ require .Equal (t , "string_list" , fields [0 ].Type )
274+
275+ require .Equal (t , "TimeStamp" , fields [1 ].Name )
276+ require .Equal (t , "int64" , fields [1 ].Type )
277+ }
278+
279+ func TestExtractSpecs_ForLoop (t * testing.T ) {
280+ src := `
281+ package com.example;
282+
283+ public class Container implements Parcelable {
284+ private int mFlags;
285+ private ArrayList<Item> mItems;
286+
287+ @Override
288+ public void writeToParcel(Parcel dest, int flags) {
289+ dest.writeInt(mFlags);
290+ final int N = mItems.size();
291+ dest.writeInt(N);
292+ for (int i=0; i<N; i++) {
293+ Item item = mItems.get(i);
294+ dest.writeString8(item.mName);
295+ dest.writeInt(item.mValue);
296+ }
297+ }
298+ }
299+ `
300+
301+ specs := ExtractSpecs (src , "com.example" )
302+ require .Len (t , specs , 1 )
303+
304+ fields := specs [0 ].Fields
305+ require .Len (t , fields , 2 , "expected mFlags + repeated Items" )
306+
307+ require .Equal (t , "Flags" , fields [0 ].Name )
308+ require .Equal (t , "int32" , fields [0 ].Type )
309+
310+ require .Equal (t , "Items" , fields [1 ].Name )
311+ require .Equal (t , "repeated" , fields [1 ].Type )
312+ require .Len (t , fields [1 ].Elements , 2 )
313+ require .Equal (t , "Name" , fields [1 ].Elements [0 ].Name )
314+ require .Equal (t , "string8" , fields [1 ].Elements [0 ].Type )
315+ require .Equal (t , "Value" , fields [1 ].Elements [1 ].Name )
316+ require .Equal (t , "int32" , fields [1 ].Elements [1 ].Type )
317+ }
318+
319+ func TestExtractSpec_ClipDescription (t * testing.T ) {
320+ src , err := os .ReadFile (
321+ "../3rdparty/frameworks-base/core/java/android/content/ClipDescription.java" ,
322+ )
323+ if err != nil {
324+ t .Skip ("3rdparty submodules not available" )
325+ }
326+
327+ specs := ExtractSpecs (string (src ), "android.content" )
328+ require .Len (t , specs , 1 )
329+
330+ spec := specs [0 ]
331+ require .Equal (t , "ClipDescription" , spec .Type )
332+
333+ // Find MimeTypes field -- should be string_list, not opaque.
334+ var mimeField * FieldSpec
335+ for i := range spec .Fields {
336+ if spec .Fields [i ].Name == "MimeTypes" {
337+ mimeField = & spec .Fields [i ]
338+ break
339+ }
340+ }
341+ require .NotNil (t , mimeField , "MimeTypes field must exist" )
342+ require .Equal (t , "string_list" , mimeField .Type )
343+ }
344+
345+ func TestExtractSpec_ClipData (t * testing.T ) {
346+ src , err := os .ReadFile (
347+ "../3rdparty/frameworks-base/core/java/android/content/ClipData.java" ,
348+ )
349+ if err != nil {
350+ t .Skip ("3rdparty submodules not available" )
351+ }
352+
353+ specs := ExtractSpecs (string (src ), "android.content" )
354+ require .NotEmpty (t , specs )
355+
356+ // Find the ClipData spec (might have inner classes too).
357+ var clipSpec * ParcelableSpec
358+ for i := range specs {
359+ if specs [i ].Type == "ClipData" {
360+ clipSpec = & specs [i ]
361+ break
362+ }
363+ }
364+ require .NotNil (t , clipSpec , "ClipData spec must exist" )
365+
366+ // Find the repeated Items field.
367+ var itemsField * FieldSpec
368+ for i := range clipSpec .Fields {
369+ if clipSpec .Fields [i ].Name == "Items" {
370+ itemsField = & clipSpec .Fields [i ]
371+ break
372+ }
373+ }
374+ require .NotNil (t , itemsField , "Items repeated field must exist" )
375+ require .Equal (t , "repeated" , itemsField .Type )
376+ require .NotEmpty (t , itemsField .Elements , "Items must have element fields" )
377+
378+ // The first element should be the Text char_sequence.
379+ require .Equal (t , "Text" , itemsField .Elements [0 ].Name )
380+ require .Equal (t , "char_sequence" , itemsField .Elements [0 ].Type )
381+
382+ // HtmlText is the second element.
383+ require .Equal (t , "HtmlText" , itemsField .Elements [1 ].Name )
384+ require .Equal (t , "string8" , itemsField .Elements [1 ].Type )
385+
386+ // There should be no standalone "N" int32 field (it was subsumed by repeated).
387+ for _ , f := range clipSpec .Fields {
388+ require .NotEqual (t , "N" , f .Name , "count field N should be subsumed by repeated" )
389+ }
390+ }
391+
253392func TestDeriveFieldName (t * testing.T ) {
254393 tests := []struct {
255394 input string
@@ -261,6 +400,8 @@ func TestDeriveFieldName(t *testing.T) {
261400 {"provider" , "Provider" },
262401 {"x" , "x" },
263402 {"mX" , "X" },
403+ {"item.mText" , "Text" },
404+ {"item.mHtmlText" , "HtmlText" },
264405 }
265406
266407 for _ , tc := range tests {
0 commit comments