/** * Copyright 2014-2017 Linagora, Université Joseph Fourier, Floralis * * The present code is developed in the scope of the joint LINAGORA - * Université Joseph Fourier - Floralis research program and is designated * as a "Result" pursuant to the terms and conditions of the LINAGORA * - Université Joseph Fourier - Floralis research program. Each copyright * holder of Results enumerated here above fully & independently holds complete * ownership of the complete Intellectual Property rights applicable to the whole * of said Results, and may freely exploit it in any manner which does not infringe * the moral rights of the other copyright holders. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.roboconf.messaging.api.utils; import java.lang.reflect.Method; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.logging.Level; import org.junit.Assert; import org.junit.Test; import net.roboconf.core.model.beans.Component; import net.roboconf.core.model.beans.Instance; import net.roboconf.core.model.beans.Instance.InstanceStatus; import net.roboconf.core.model.helpers.InstanceHelpers; import net.roboconf.messaging.api.messages.Message; import net.roboconf.messaging.api.messages.from_agent_to_agent.MsgCmdAddImport; import net.roboconf.messaging.api.messages.from_agent_to_agent.MsgCmdRemoveImport; import net.roboconf.messaging.api.messages.from_agent_to_agent.MsgCmdRequestImport; import net.roboconf.messaging.api.messages.from_agent_to_dm.MsgNotifAutonomic; import net.roboconf.messaging.api.messages.from_agent_to_dm.MsgNotifHeartbeat; import net.roboconf.messaging.api.messages.from_agent_to_dm.MsgNotifInstanceChanged; import net.roboconf.messaging.api.messages.from_agent_to_dm.MsgNotifInstanceRemoved; import net.roboconf.messaging.api.messages.from_agent_to_dm.MsgNotifLogs; import net.roboconf.messaging.api.messages.from_agent_to_dm.MsgNotifMachineDown; import net.roboconf.messaging.api.messages.from_dm_to_agent.MsgCmdAddInstance; import net.roboconf.messaging.api.messages.from_dm_to_agent.MsgCmdChangeBinding; import net.roboconf.messaging.api.messages.from_dm_to_agent.MsgCmdChangeInstanceState; import net.roboconf.messaging.api.messages.from_dm_to_agent.MsgCmdChangeLogLevel; import net.roboconf.messaging.api.messages.from_dm_to_agent.MsgCmdGatherLogs; import net.roboconf.messaging.api.messages.from_dm_to_agent.MsgCmdRemoveInstance; import net.roboconf.messaging.api.messages.from_dm_to_agent.MsgCmdResynchronize; import net.roboconf.messaging.api.messages.from_dm_to_agent.MsgCmdSendInstances; import net.roboconf.messaging.api.messages.from_dm_to_agent.MsgCmdSetScopedInstance; import net.roboconf.messaging.api.messages.from_dm_to_agent.MsgCmdUpdateProbeConfiguration; import net.roboconf.messaging.api.messages.from_dm_to_dm.MsgEcho; /** * @author Vincent Zurczak - Linagora */ public class SerializationUtilsTest { // From agent @Test public void testMessage_heartbeat() throws Exception { MsgNotifHeartbeat msg = new MsgNotifHeartbeat( "app1", "instance1", "127.0.0.1" ); checkBasics( msg, MsgNotifHeartbeat.class ); msg = new MsgNotifHeartbeat( "app1", new Instance( "instance2" ), "192.168.0.11" ); checkBasics( msg, MsgNotifHeartbeat.class ); } @Test public void testMessage_autonomic() throws Exception { MsgNotifAutonomic msg = new MsgNotifAutonomic( "app1", "instance1", "too high", "oops" ); checkBasics( msg, MsgNotifAutonomic.class ); } @Test public void testMessage_logs() throws Exception { MsgNotifLogs msg = new MsgNotifLogs( "app1", "instance1", null ); checkBasics( msg, MsgNotifLogs.class ); Map<String,byte[]> map = new HashMap<> (); msg = new MsgNotifLogs( "app2", "instance2", map ); checkBasics( msg, MsgNotifLogs.class ); map.put( "file1", new byte[ 0 ]); map.put( "file2", "test".getBytes( "UTF-8" )); msg = new MsgNotifLogs( "app5", "instance4", map ); checkBasics( msg, MsgNotifLogs.class ); } @Test public void testMessage_machineDown() throws Exception { MsgNotifMachineDown msg = new MsgNotifMachineDown( "app1", "instance1" ); checkBasics( msg, MsgNotifMachineDown.class ); } @Test public void testMessage_instanceChanged() throws Exception { MsgNotifInstanceChanged msg = new MsgNotifInstanceChanged( "app2", new Instance( "instance1" )); checkBasics( msg, MsgNotifInstanceChanged.class ); } @Test public void testMessage_instanceRemoved() throws Exception { MsgNotifInstanceRemoved msg = new MsgNotifInstanceRemoved( "app2", new Instance( "instance1" )); checkBasics( msg, MsgNotifInstanceRemoved.class ); } @Test public void testMessage_removeImport() throws Exception { MsgCmdRemoveImport msg = new MsgCmdRemoveImport( "app", "change-me", "anything" ); checkBasics( msg, MsgCmdRemoveImport.class ); } @Test public void testMessage_addImport() throws Exception { Map<String,String> map = new HashMap<> (); map.put( "yeah", "value" ); MsgCmdAddImport msg = new MsgCmdAddImport( "app", "change-me", "anything", map ); checkBasics( msg, MsgCmdAddImport.class ); } @Test public void testMessage_requestImport() throws Exception { MsgCmdRequestImport msg = new MsgCmdRequestImport( "app", "dsf" ); checkBasics( msg, MsgCmdRequestImport.class ); } // From DM @Test public void testMessage_setRootInstance() throws Exception { MsgCmdSetScopedInstance msg = new MsgCmdSetScopedInstance( new Instance( "instance1" )); checkBasics( msg, MsgCmdSetScopedInstance.class ); Map<String,String> map1 = new HashMap<> (); map1.put( "test", "t1" ); map1.put( "another", "t2" ); Map<String,Set<String>> map2 = new HashMap<> (); Set<String> appNames = new LinkedHashSet<> (); appNames.add( "app1" ); appNames.add( "app2" ); map2.put( "app_prefix", appNames ); Map<String,byte[]> map3 = new HashMap<> (); map3.put("script", "toto".getBytes( "UTF-8" )); msg = new MsgCmdSetScopedInstance( new Instance( "instance1" ), map1, map2, map3 ); checkBasics( msg, MsgCmdSetScopedInstance.class ); } @Test public void testMessage_resynchronize() throws Exception { MsgCmdResynchronize msg = new MsgCmdResynchronize(); checkBasics( msg, MsgCmdResynchronize.class ); } @Test public void testMessage_changeLogLevel() throws Exception { MsgCmdChangeLogLevel msg = new MsgCmdChangeLogLevel( Level.FINER ); checkBasics( msg, MsgCmdChangeLogLevel.class ); } @Test public void testMessage_gatherLogs() throws Exception { MsgCmdGatherLogs msg = new MsgCmdGatherLogs(); checkBasics( msg, MsgCmdGatherLogs.class ); } @Test public void testMessage_updateProbeConfiguration() throws Exception { MsgCmdUpdateProbeConfiguration msg = new MsgCmdUpdateProbeConfiguration( "/inst", null ); checkBasics( msg, MsgCmdUpdateProbeConfiguration.class ); msg = new MsgCmdUpdateProbeConfiguration( new Instance( "inst" ), new HashMap<String,byte[]>( 0 )); checkBasics( msg, MsgCmdUpdateProbeConfiguration.class ); } @Test public void testMessage_changeBinding() throws Exception { MsgCmdChangeBinding msg = new MsgCmdChangeBinding( "tpl", new HashSet<>( Arrays.asList( "app" ))); checkBasics( msg, MsgCmdChangeBinding.class ); msg = new MsgCmdChangeBinding( "tpl", new HashSet<>( Arrays.asList( "app1", "app2" ))); checkBasics( msg, MsgCmdChangeBinding.class ); } @Test public void testMessage_removeInstance() throws Exception { MsgCmdRemoveInstance msg = new MsgCmdRemoveInstance( "/inst1" ); checkBasics( msg, MsgCmdRemoveInstance.class ); msg = new MsgCmdRemoveInstance( new Instance( "root" )); checkBasics( msg, MsgCmdRemoveInstance.class ); } @Test public void testMessage_addInstance() throws Exception { Instance child = new Instance( "child" ).channel( "channel 4" ).status( InstanceStatus.DEPLOYED_STOPPED ); child.component( new Component( "comp_child" ).installerName( "whatever" )); MsgCmdAddInstance msg = new MsgCmdAddInstance( child ); checkBasics( msg, MsgCmdAddInstance.class ); Instance root = new Instance( "root" ).status( InstanceStatus.DEPLOYED_STARTED ).channel( "channel1" ).channel( "channel2" ); root.component( new Component( "comp_root" ).installerName( "whatever" )); InstanceHelpers.insertChild( root, child ); msg = new MsgCmdAddInstance( child ); checkBasics( msg, MsgCmdAddInstance.class ); msg = new MsgCmdAddInstance( new Instance( "instance without component" )); checkBasics( msg, MsgCmdAddInstance.class ); } @Test public void testMessage_restoreInstance() throws Exception { MsgCmdSendInstances msg = new MsgCmdSendInstances(); checkBasics( msg, MsgCmdSendInstances.class ); } @Test public void testMessage_changeInstanceState() throws Exception { MsgCmdChangeInstanceState msg = new MsgCmdChangeInstanceState( "/o/mp/k", InstanceStatus.DEPLOYED_STARTED ); checkBasics( msg, MsgCmdChangeInstanceState.class ); msg = new MsgCmdChangeInstanceState((String) null, InstanceStatus.NOT_DEPLOYED ); checkBasics( msg, MsgCmdChangeInstanceState.class ); msg = new MsgCmdChangeInstanceState( new Instance( "test" ), InstanceStatus.NOT_DEPLOYED ); checkBasics( msg, MsgCmdChangeInstanceState.class ); Map<String,byte[]> fileNameToFileContent = new HashMap<> (); fileNameToFileContent.put( "readme.txt", new byte[ 90 ]); msg = new MsgCmdChangeInstanceState( "/oops", InstanceStatus.NOT_DEPLOYED, fileNameToFileContent ); checkBasics( msg, MsgCmdChangeInstanceState.class ); msg = new MsgCmdChangeInstanceState((Instance) null, InstanceStatus.NOT_DEPLOYED, fileNameToFileContent ); checkBasics( msg, MsgCmdChangeInstanceState.class ); } @Test public void testMessage_msgEcho() throws Exception { MsgEcho msg = new MsgEcho( "coucou", UUID.randomUUID()); checkBasics( msg, MsgEcho.class ); msg = new MsgEcho( "hello" ); checkBasics( msg, MsgEcho.class ); } /** * Serializes, deserializes and compares messages. * @param msg * @param clazz * @return * @throws Exception */ public static <T extends Message> T checkBasics( Message msg, Class<T> clazz ) throws Exception { String prefix = "Class " + clazz.getSimpleName(); Assert.assertTrue( prefix + ": invalid invocation. First parameter must be of type " + clazz.getSimpleName() + ".", clazz.isAssignableFrom( msg.getClass())); // Write and read byte[] bytes = SerializationUtils.serializeObject( msg ); Message newMsg = SerializationUtils.deserializeObject( bytes ); // Compare classes Assert.assertEquals( prefix, clazz.getName(), newMsg.getClass().getName()); Assert.assertTrue( prefix, clazz.isAssignableFrom( newMsg.getClass())); // Compare internal fields for( Method m : clazz.getMethods()) { if( ! m.getName().startsWith( "get" ) || m.getParameterTypes().length != 0 ) continue; Object expectedValue = m.invoke( msg ); Object value = m.invoke( newMsg ); // Maps gets a special treatment if( value instanceof Map ) { Map<?,?> expectedMap = (Map<?,?>) expectedValue; Map<?,?> map = (Map<?,?>) value; Assert.assertEquals( prefix, expectedMap.size(), map.size()); for( Map.Entry<?,?> entry : expectedMap.entrySet()) { Assert.assertTrue( prefix + ": key was not found. " + entry.getKey(), map.containsKey( entry.getKey())); if( entry.getValue() instanceof String ) Assert.assertEquals( prefix + ": value did not match. " + entry.getKey(), entry.getValue(), map.get( entry.getKey())); } } // Other objects are compared directly else { Assert.assertEquals( prefix + ": invalid match for " + m.getName() + ".", expectedValue, value ); } } return clazz.cast( newMsg ); } }