package com.googlecode.mp4parser.boxes; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.mp4parser.Box; import org.mp4parser.IsoFile; import org.mp4parser.ParsableBox; import org.mp4parser.support.AbstractContainerBox; import org.mp4parser.tools.ByteBufferByteChannel; import java.beans.BeanInfo; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.io.ByteArrayOutputStream; import java.nio.channels.Channels; import java.nio.channels.WritableByteChannel; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Map; @RunWith(Parameterized.class) public abstract class BoxRoundtripTest { /* @Parameterized.Parameters public static Collection<Object[]> data() { return Collections.singletonList( new Object[]{new Box(), new Map.Entry[]{ new E("prop", 1), new E("prop2", 21)} }); } */ private static final Collection<String> skipList = Arrays.asList( "class", "flags", "isoFile", "parent", "parsed", "path", "size", "offset", "type", "userType", "version"); String dummyParent = null; ParsableBox parsableBoxUnderTest; Map<String, Object> props; public BoxRoundtripTest(ParsableBox parsableBoxUnderTest, Map.Entry<String, Object>... properties) { this.parsableBoxUnderTest = parsableBoxUnderTest; this.props = new HashMap<String, Object>(); for (Map.Entry<String, Object> property : properties) { props.put(property.getKey(), property.getValue()); } } @Test public void roundtrip() throws Exception { BeanInfo beanInfo = Introspector.getBeanInfo(parsableBoxUnderTest.getClass()); PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); for (String property : props.keySet()) { boolean found = false; for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { if (property.equals(propertyDescriptor.getName())) { found = true; try { propertyDescriptor.getWriteMethod().invoke(parsableBoxUnderTest, props.get(property)); } catch (IllegalArgumentException e) { System.err.println(propertyDescriptor.getWriteMethod().getName() + "(" + propertyDescriptor.getWriteMethod().getParameterTypes()[0].getSimpleName() + ");"); System.err.println("Called with " + props.get(property).getClass()); throw e; } // do the next assertion on string level to not trap into the long vs java.lang.Long pitfall Assert.assertEquals("The symmetry between getter/setter of " + property + " is not given.", props.get(property), propertyDescriptor.getReadMethod().invoke(parsableBoxUnderTest)); } } if (!found) { Assert.fail("Could not find any property descriptor for " + property); } } ByteArrayOutputStream baos = new ByteArrayOutputStream(); WritableByteChannel wbc = Channels.newChannel(baos); parsableBoxUnderTest.getBox(wbc); wbc.close(); baos.close(); IsoFile singleBoxIsoFile = new IsoFile(new ByteBufferByteChannel(baos.toByteArray())); Assert.assertEquals("Expected box and file size to be the same", parsableBoxUnderTest.getSize(), baos.size()); Assert.assertEquals("Expected a single box in the IsoFile structure", 1, singleBoxIsoFile.getBoxes().size()); Assert.assertEquals("Expected to find a box of different type ", parsableBoxUnderTest.getClass(), singleBoxIsoFile.getBoxes().get(0).getClass()); Box parsedBox = singleBoxIsoFile.getBoxes().get(0); for (String property : props.keySet()) { boolean found = false; for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { if (property.equals(propertyDescriptor.getName())) { found = true; if (props.get(property) instanceof int[]) { Assert.assertArrayEquals("Writing and parsing changed the value of " + property, (int[]) props.get(property), (int[]) propertyDescriptor.getReadMethod().invoke(parsedBox)); } else if (props.get(property) instanceof byte[]) { Assert.assertArrayEquals("Writing and parsing changed the value of " + property, (byte[]) props.get(property), (byte[]) propertyDescriptor.getReadMethod().invoke(parsedBox)); } else if (props.get(property) instanceof long[]) { Assert.assertArrayEquals("Writing and parsing changed the value of " + property, (long[]) props.get(property), (long[]) propertyDescriptor.getReadMethod().invoke(parsedBox)); } else { Assert.assertEquals("Writing and parsing changed the value of " + property, props.get(property), propertyDescriptor.getReadMethod().invoke(parsedBox)); } } } if (!found) { Assert.fail("Could not find any property descriptor for " + property); } } Assert.assertEquals("Writing and parsing should not change the box size.", parsableBoxUnderTest.getSize(), parsedBox.getSize()); boolean output = false; for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { if (!props.containsKey(propertyDescriptor.getName())) { if (!skipList.contains(propertyDescriptor.getName())) { if (!output) { System.out.println("No value given for the following properties: "); output = true; } System.out.println(String.format("new E(\"%s\", (%s)) ),", propertyDescriptor.getName(), propertyDescriptor.getPropertyType().getSimpleName())); } } } } protected static class E implements Map.Entry<String,Object> { private String key; private Object value; public E(String key, Object value) { this.key = key; this.value = value; } public boolean equals(Object o) { if (!(o instanceof E)) { return false; } E other = (E) o; return other.key.equals(key) && (value == null ? other.value == null : other.value.equals(value)); } public String getKey() { return key; } public Object getValue() { return value; } public Object setValue(Object newValue) { throw new UnsupportedOperationException(); } public int hashCode() { return key.hashCode() ^ (value==null ? 0 : value.hashCode()); } public String toString() { return key +"="+value.toString(); } } class DummyContainerBox extends AbstractContainerBox { public DummyContainerBox(String type) { super(type); } } }