package alma.acs.container.corba; import java.util.ArrayList; import java.util.List; import java.util.logging.LogRecord; import junit.framework.TestCase; import org.jacorb.orb.giop.ServiceContextTransportingOutputStream; import org.omg.CORBA.MARSHAL; import org.omg.CORBA.StringHolder; import org.omg.CORBA.portable.OutputStream; import alma.acs.container.ContainerSealant; import alma.acs.logging.AcsLogger; import alma.acs.logging.ClientLogManager; import alma.acs.testsupport.LogRecordCollectingLogger; import alma.jconttest.ComponentWithBadNullsOperations; import alma.jconttest.ComponentWithBadNullsImpl.ComponentWithBadNullsImpl; import alma.jconttest.ComponentWithBadNullsPackage.Enum1; import alma.jconttest.ComponentWithBadNullsPackage.Struct1; import alma.jconttest.ComponentWithBadNullsPackage.Struct1Holder; import alma.jconttest.ComponentWithBadNullsPackage.Struct2; import alma.jconttest.ComponentWithBadNullsPackage.Struct2Helper; /** * Tests CorbaNullFinder. See COMP-4592 and COMP-6091. * @author hsommer */ public class CorbaNullFinderTest extends TestCase { /** * Tests jacorb's reaction to null data inside structs. * If this test fails after a jacorb update, the logic of the Null Finder must be revisited. */ public void testJacorbNullBehavior() throws Exception { // Below we'll need an ORB... AcsLogger logger = ClientLogManager.getAcsLogManager().getLoggerForApplication("testOrbLogger", false); AcsCorba acsCorba = new AcsCorba(logger); acsCorba.initCorbaForClient(false); // Jacorb uses ReplyOutputStream, but its base class ServiceContextTransportingOutputStream is easier to construct // and should be similar enough for this test. OutputStream out = new ServiceContextTransportingOutputStream(acsCorba.getORB()); Struct2 myStruct2 = ComponentWithBadNullsImpl.createGoodStruct2(); // the good data should marshal without exception Struct2Helper.write(out, myStruct2); // null string try { myStruct2.mystruct1.mystring = null; Struct2Helper.write(out, myStruct2); fail("null strings in structs should marshal with exception."); } catch (MARSHAL ex) { // expected assertEquals("org.omg.CORBA.MARSHAL: Cannot marshall null string.", ex.toString()); } // null enum myStruct2 = ComponentWithBadNullsImpl.createGoodStruct2(); try { myStruct2.mystruct1.myenum1 = null; Struct2Helper.write(out, myStruct2); fail("null strings in structs should marshal with NPE."); } catch (NullPointerException ex) { // expected... this is a really mean case, because we get NPE instead of MARSHAL. Maybe a jacorb bug? } // null struct myStruct2 = ComponentWithBadNullsImpl.createGoodStruct2(); try { myStruct2.mystruct1 = null; Struct2Helper.write(out, myStruct2); fail("null structs inside structs should marshal with NPE."); } catch (NullPointerException ex) { // expected... this is a really mean case, because we get NPE instead of MARSHAL. Maybe a jacorb bug? } // top-level struct itself is null try { Struct2Helper.write(out, null); fail("top-level null structs should marshal with NPE."); } catch (NullPointerException ex) { // expected... } // null sequence of structs myStruct2 = ComponentWithBadNullsImpl.createGoodStruct2(); try { myStruct2.seqOfStruct1 = null; Struct2Helper.write(out, myStruct2); fail("null sequence of structs inside structs should marshal with NPE."); } catch (NullPointerException ex) { // expected... this is a really mean case, because we get NPE instead of MARSHAL. Maybe a jacorb bug? } // sequence with null struct myStruct2 = ComponentWithBadNullsImpl.createGoodStruct2(); try { myStruct2.seqOfStruct1 = new Struct1[1]; // with null inside Struct2Helper.write(out, myStruct2); fail("sequence of structs with nulls should marshal with NPE."); } catch (NullPointerException ex) { // expected... this is a really mean case, because we get NPE instead of MARSHAL. Maybe a jacorb bug? } } /** * Tests how java classes for IDL interfaces, structs, and enums are distinguished. */ public void testIdlTypeInfering() { assertTrue(CorbaNullFinder.isIDLEnumClass(Enum1.class)); assertFalse(CorbaNullFinder.isIDLEnumClass(Struct1.class)); assertTrue(CorbaNullFinder.isIDLStructClass(Struct1.class)); assertFalse(CorbaNullFinder.isIDLStructClass(Enum1.class)); assertTrue(CorbaNullFinder.isIDLInterfaceClass(alma.ACS.Property.class)); assertFalse(CorbaNullFinder.isIDLStructClass(alma.ACS.Property.class)); assertFalse(CorbaNullFinder.isIDLEnumClass(alma.ACS.Property.class)); } /** * Tests the errors found and reported by CorbaNullFinder */ public void testNullFinder() { List<String> expected = new ArrayList<String>(); Struct2 myStruct2 = ComponentWithBadNullsImpl.createGoodStruct2(); // all fine CorbaNullFinder finder = new CorbaNullFinder(myStruct2); assertFalse(finder.hasErrors()); assertNullFinderErrors(expected, finder.getErrors()); // struct missing myStruct2.mystruct1 = null; finder = new CorbaNullFinder(myStruct2); assertTrue(finder.hasErrors()); expected.add("Null struct in field Struct2/mystruct1"); assertNullFinderErrors(expected, finder.getErrors()); // string and enum in struct missing myStruct2 = ComponentWithBadNullsImpl.createGoodStruct2(); myStruct2.mystruct1.mystring = null; myStruct2.mystruct1.myenum1 = null; finder = new CorbaNullFinder(myStruct2); assertTrue(finder.hasErrors()); expected.clear(); expected.add("Null string in field Struct2/mystruct1/mystring"); expected.add("Null enum in field Struct2/mystruct1/myenum1"); expected.add("Null string in field Struct2/seqOfStruct1[0]/mystring"); // mystruct1 is also part of the sequence expected.add("Null enum in field Struct2/seqOfStruct1[0]/myenum1"); assertNullFinderErrors(expected, finder.getErrors()); // null sequence of structs myStruct2 = ComponentWithBadNullsImpl.createGoodStruct2(); myStruct2.seqOfStruct1 = null; finder = new CorbaNullFinder(myStruct2); assertTrue(finder.hasErrors()); expected.clear(); expected.add("Null array in field Struct2/seqOfStruct1"); assertNullFinderErrors(expected, finder.getErrors()); // sequence with null struct myStruct2 = ComponentWithBadNullsImpl.createGoodStruct2(); myStruct2.seqOfStruct1[0] = null; finder = new CorbaNullFinder(myStruct2); assertTrue(finder.hasErrors()); expected.clear(); expected.add("Null object in field Struct2/seqOfStruct1[0]"); assertNullFinderErrors(expected, finder.getErrors()); // Arrays outside of structs, with good values or with null values (see COMP-6091) finder = new CorbaNullFinder(new String[] {"goodString"}); assertFalse(finder.hasErrors()); finder = new CorbaNullFinder(new String[] {"goodStringAtFirst", null, "anotherGoodString"}); assertTrue(finder.hasErrors()); expected.clear(); expected.add("Null object in field String[1]"); assertNullFinderErrors(expected, finder.getErrors()); finder = new CorbaNullFinder(new int[] {1,2}); // CorbaNullFinder will see them as Integer objects. assertFalse(finder.hasErrors()); // main object null finder = new CorbaNullFinder(null); assertTrue(finder.hasErrors()); expected.clear(); expected.add("Top-level object is null; cannot distinguish between a legal null object reference and an illegal null data item."); assertNullFinderErrors(expected, finder.getErrors()); } private void assertNullFinderErrors(List<String> expected, List<String> actual) { assertEquals("Number of errors found by CorbaNullFinder", expected.size(), actual.size()); for (int i = 0; i < expected.size(); i++) { String error1 = expected.get(i); String error2 = actual.get(i); assertEquals("Error message", error1, error2); } } /** * Strictly speaking this is a test for {@link ContainerSealant}, but practically fits well in here. */ public void testNullFinderInContainerSealant() throws Exception { // activate the null checker System.setProperty(ContainerSealant.CHECK_NULLS_CORBA_OUT_PROPERTYNAME, "true"); // The test component, with the real interceptor around it LogRecordCollectingLogger collectingLogger = LogRecordCollectingLogger.getCollectingLogger("Collecting_" + getName()); ComponentWithBadNullsImpl compImpl = new ComponentWithBadNullsImpl(); ComponentWithBadNullsOperations sealedComponent = ContainerSealant.createContainerSealant( ComponentWithBadNullsOperations.class, compImpl, "InstanceForUnitTest", false, collectingLogger, null, null ); // invocation of methodWithReturnData String instring = null; Struct1 instruct1 = ComponentWithBadNullsImpl.createGoodStruct1(); instruct1.myenum1 = null; instruct1.mystring = null; StringHolder inoutstringHolder = new StringHolder(); Struct1Holder inoutstruct1Holder = new Struct1Holder(); StringHolder outstringHolder = new StringHolder(); Struct1Holder outstruct1Holder = new Struct1Holder(); sealedComponent.methodWithReturnData(instring, instruct1, inoutstringHolder, inoutstruct1Holder, outstringHolder, outstruct1Holder); // check the logs produced by the ContainerSealant who uses CorbaNullFinder LogRecord[] logRecords = collectingLogger.getCollectedLogRecords(); assertEquals(3, logRecords.length); assertEquals("intercepted a call to 'InstanceForUnitTest#methodWithReturnData'.", logRecords[0].getMessage()); assertTrue(logRecords[1].getMessage().startsWith("returning from InstanceForUnitTest#methodWithReturnData after")); System.out.println(logRecords[2].getMessage()); assertEquals( "Illegal null value in out parameter(s) of method methodWithReturnData:\n" + " Parameter StringHolder: \n" + " Null string in field StringHolder/value\n" + " Parameter Struct1Holder: \n" + " Null string in field Struct1Holder/value/mystring\n" + " Null enum in field Struct1Holder/value/myenum1\n" + " Parameter StringHolder: \n" + " Null string in field StringHolder/value\n" + " Parameter Struct1Holder: \n" + " Null string in field Struct1Holder/value/mystring\n" + " Null enum in field Struct1Holder/value/myenum1\n", logRecords[2].getMessage() ); } }