@@ -253,6 +253,119 @@ def test_library_schema_header_attributes(self):
253253 # Version number might be different for unmerged (without library prefix)
254254 self .assertEqual (schema .with_standard , schema_unmerged .with_standard )
255255
256+ def test_json_empty_list_attributes_omitted (self ):
257+ """Test that empty list attributes (suggestedTag, relatedTag, etc.) are omitted from JSON."""
258+ import json
259+
260+ schema = load_schema_version ("8.4.0" )
261+ json_path = os .path .join (self .temp_dir , "empty_lists.json" )
262+ schema .save_as_json (json_path )
263+
264+ # Read the JSON file and check for empty list attributes
265+ with open (json_path , "r" , encoding = "utf-8" ) as f :
266+ json_data = json .load (f )
267+
268+ # Check ALL tags for empty lists
269+ tags_with_empty_lists = []
270+
271+ for tag_name , tag_data in json_data .get ("tags" , {}).items ():
272+ # Check attributes dict
273+ attrs = tag_data .get ("attributes" , {})
274+ for list_attr in ["suggestedTag" , "relatedTag" , "valueClass" , "unitClass" ]:
275+ if list_attr in attrs and attrs [list_attr ] == []:
276+ tags_with_empty_lists .append (f"{ tag_name } .attributes.{ list_attr } " )
277+
278+ # Check explicitAttributes dict
279+ explicit_attrs = tag_data .get ("explicitAttributes" , {})
280+ for list_attr in ["suggestedTag" , "relatedTag" , "valueClass" , "unitClass" ]:
281+ if list_attr in explicit_attrs and explicit_attrs [list_attr ] == []:
282+ tags_with_empty_lists .append (f"{ tag_name } .explicitAttributes.{ list_attr } " )
283+
284+ self .assertEqual (
285+ len (tags_with_empty_lists ),
286+ 0 ,
287+ f"Found { len (tags_with_empty_lists )} empty list attributes: { tags_with_empty_lists [:5 ]} " ,
288+ )
289+
290+ # Verify that tags WITH these attributes have non-empty lists
291+ if "Sensory-event" in json_data .get ("tags" , {}):
292+ sensory_attrs = json_data ["tags" ]["Sensory-event" ].get ("attributes" , {})
293+ if "suggestedTag" in sensory_attrs :
294+ self .assertTrue (
295+ len (sensory_attrs ["suggestedTag" ]) > 0 , "Sensory-event suggestedTag should be non-empty if present"
296+ )
297+
298+ def test_extras_sections_roundtrip (self ):
299+ """Test that extras sections (Sources, Prefixes, AnnotationPropertyExternal) roundtrip correctly."""
300+ schema = load_schema_version ("8.4.0" )
301+
302+ # Check that original has extras
303+ orig_extras = getattr (schema , "extras" , {}) or {}
304+ self .assertGreater (len (orig_extras ), 0 , "Schema should have extras sections" )
305+
306+ # Save and reload
307+ json_path = os .path .join (self .temp_dir , "with_extras.json" )
308+ schema .save_as_json (json_path )
309+ reloaded = load_schema (json_path )
310+
311+ # Check reloaded has extras
312+ reloaded_extras = getattr (reloaded , "extras" , {}) or {}
313+
314+ # Compare each extras section
315+ self .assertEqual (set (orig_extras .keys ()), set (reloaded_extras .keys ()), "Extras sections should match" )
316+
317+ for key in orig_extras .keys ():
318+ orig_df = orig_extras [key ]
319+ reloaded_df = reloaded_extras [key ]
320+ self .assertTrue (orig_df .equals (reloaded_df ), f"Extras section '{ key } ' should match after roundtrip" )
321+
322+ def test_library_schema_extras_roundtrip (self ):
323+ """Test that library schema extras (external annotations, etc.) roundtrip correctly."""
324+ schema = load_schema_version ("score_2.1.0" )
325+
326+ # Check that library schema has extras
327+ orig_extras = getattr (schema , "extras" , {}) or {}
328+ self .assertGreater (len (orig_extras ), 0 , "Library schema should have extras sections" )
329+
330+ # Check for external annotations specifically
331+ self .assertIn ("AnnotationPropertyExternal" , orig_extras , "Library schema should have external annotations" )
332+
333+ # Save and reload
334+ json_path = os .path .join (self .temp_dir , "library_with_extras.json" )
335+ schema .save_as_json (json_path , save_merged = False )
336+ reloaded = load_schema (json_path )
337+
338+ # Check reloaded has all extras
339+ reloaded_extras = getattr (reloaded , "extras" , {}) or {}
340+ self .assertEqual (set (orig_extras .keys ()), set (reloaded_extras .keys ()), "Library schema extras sections should match" )
341+
342+ # Verify each extras dataframe matches
343+ for key in orig_extras .keys ():
344+ orig_df = orig_extras [key ]
345+ reloaded_df = reloaded_extras [key ]
346+ self .assertTrue (orig_df .equals (reloaded_df ), f"Library schema extras '{ key } ' should match after roundtrip" )
347+
348+ def test_library_schema_score (self ):
349+ """Test score library schema roundtrip specifically."""
350+ schema = load_schema_version ("score_2.1.0" )
351+
352+ # Test unmerged format
353+ json_path = os .path .join (self .temp_dir , "score_unmerged.json" )
354+ schema .save_as_json (json_path , save_merged = False )
355+ reloaded = load_schema (json_path )
356+
357+ # Verify library attributes
358+ self .assertEqual (schema .library , reloaded .library )
359+ self .assertEqual (schema .version , reloaded .version )
360+ self .assertEqual (schema .with_standard , reloaded .with_standard )
361+
362+ # Verify tag counts match
363+ self .assertEqual (len (schema .tags .all_entries ), len (reloaded .tags .all_entries ))
364+
365+ # Verify prologue and epilogue
366+ self .assertEqual (schema .prologue , reloaded .prologue )
367+ self .assertEqual (schema .epilogue , reloaded .epilogue )
368+
256369
257370if __name__ == "__main__" :
258371 unittest .main ()
0 commit comments