1 package org.testng.remote.strprotocol; 2 3 4 import org.testng.ITestResult; 5 import org.testng.collections.Lists; 6 7 import java.util.List; 8 import java.util.regex.Pattern; 9 10 11 /** 12 * Marshal/unmarshal tool for <code>IStringMessage</code>s. 13 * 14 * @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a> 15 */ 16 public class MessageHelper { 17 public static final char DELIMITER = '\u0001'; 18 public static final char PARAM_DELIMITER = '\u0004'; 19 private static final char LINE_SEP_DELIMITER_1 = '\u0002'; 20 private static final char LINE_SEP_DELIMITER_2 = '\u0003'; 21 22 public static final int GENERIC_SUITE_COUNT = 1; 23 24 public static final int SUITE = 10; 25 public static final int SUITE_START = 11; 26 public static final int SUITE_FINISH = 12; 27 28 public static final int TEST = 100; 29 public static final int TEST_START = 101; 30 public static final int TEST_FINISH = 102; 31 32 public static final int TEST_RESULT = 1000; 33 public static final int PASSED_TEST = TEST_RESULT + ITestResult.SUCCESS; 34 public static final int FAILED_TEST = TEST_RESULT + ITestResult.FAILURE; 35 public static final int SKIPPED_TEST = TEST_RESULT + ITestResult.SKIP; 36 public static final int FAILED_ON_PERCENTAGE_TEST = TEST_RESULT + ITestResult.SUCCESS_PERCENTAGE_FAILURE; 37 public static final int TEST_STARTED = TEST_RESULT + ITestResult.STARTED; 38 39 public static final String STOP_MSG = ">STOP"; 40 public static final String ACK_MSG = ">ACK"; 41 getMessageType(final String message)42 public static int getMessageType(final String message) { 43 int idx = message.indexOf(DELIMITER); 44 45 return idx == -1 ? Integer.parseInt(message) : Integer.parseInt(message.substring(0, idx)); 46 } 47 unmarshallGenericMessage(final String message)48 public static GenericMessage unmarshallGenericMessage(final String message) { 49 String[] messageParts = parseMessage(message); 50 if(messageParts.length == 1) { 51 return new GenericMessage(Integer.parseInt(messageParts[0])); 52 } 53 else { 54 GenericMessage result = new GenericMessage(Integer.parseInt(messageParts[0])); 55 56 for(int i = 1; i < messageParts.length; i+=2) { 57 if ("testCount".equals(messageParts[i])) { 58 result.setTestCount(Integer.parseInt(messageParts[i + 1])); 59 } else if ("suiteCount".equals(messageParts[i])) { 60 result.setSuiteCount(Integer.parseInt(messageParts[i + 1])); 61 } 62 } 63 64 return result; 65 } 66 } 67 createSuiteMessage(final String message)68 public static SuiteMessage createSuiteMessage(final String message) { 69 int type = getMessageType(message); 70 String[] messageParts = parseMessage(message); 71 72 SuiteMessage result = new SuiteMessage(messageParts[1], 73 MessageHelper.SUITE_START == type, 74 Integer.parseInt(messageParts[2])); 75 // Any excluded methods? 76 if (messageParts.length > 3) { 77 int count = Integer.parseInt(messageParts[3]); 78 if (count > 0) { 79 List<String> methods = Lists.newArrayList(); 80 int i = 4; 81 while (count-- > 0) { 82 methods.add(messageParts[i++]); 83 } 84 result.setExcludedMethods(methods); 85 } 86 } 87 88 return result; 89 } 90 createTestMessage(final String message)91 public static TestMessage createTestMessage(final String message) { 92 int type = getMessageType(message); 93 String[] messageParts = parseMessage(message); 94 95 return new TestMessage(MessageHelper.TEST_START == type, 96 messageParts[1], 97 messageParts[2], 98 Integer.parseInt(messageParts[3]), 99 Integer.parseInt(messageParts[4]), 100 Integer.parseInt(messageParts[5]), 101 Integer.parseInt(messageParts[6]), 102 Integer.parseInt(messageParts[7])); 103 } 104 unmarshallTestResultMessage(final String message)105 public static TestResultMessage unmarshallTestResultMessage(final String message) { 106 String[] messageParts = parseMessage(message); 107 108 String parametersFragment= null; 109 String startTimestampFragment= null; 110 String stopTimestampFragment= null; 111 String stackTraceFragment= null; 112 String testDescriptor= null; 113 switch(messageParts.length) { 114 case 10: 115 { 116 parametersFragment= messageParts[5]; 117 startTimestampFragment= messageParts[6]; 118 stopTimestampFragment= messageParts[7]; 119 stackTraceFragment= messageParts[8]; 120 testDescriptor= messageParts[9]; 121 } 122 break; 123 case 9: 124 { 125 parametersFragment= messageParts[5]; 126 startTimestampFragment= messageParts[6]; 127 stopTimestampFragment= messageParts[7]; 128 stackTraceFragment= messageParts[8]; 129 } 130 break; 131 default: 132 { 133 // HINT: old protocol without parameters 134 parametersFragment= null; 135 startTimestampFragment= messageParts[5]; 136 stopTimestampFragment= messageParts[6]; 137 stackTraceFragment= messageParts[7]; 138 } 139 } 140 return new TestResultMessage(Integer.parseInt(messageParts[0]), 141 messageParts[1], 142 messageParts[2], 143 messageParts[3], 144 messageParts[4], 145 replaceAsciiCharactersWithUnicode(replaceNewLineReplacer(testDescriptor)), 146 replaceAsciiCharactersWithUnicode(replaceNewLineReplacer(testDescriptor)), 147 parseParameters(parametersFragment), 148 Long.parseLong(startTimestampFragment), 149 Long.parseLong(stopTimestampFragment), 150 replaceAsciiCharactersWithUnicode(replaceNewLineReplacer(stackTraceFragment)), 151 0, 0 /* invocation counts not supported by this protocol */ 152 ); 153 } 154 replaceNewLine(String message)155 public static String replaceNewLine(String message) { 156 if(null == message) { 157 return message; 158 } 159 160 return message.replace('\n', LINE_SEP_DELIMITER_1).replace('\r', LINE_SEP_DELIMITER_2); 161 } 162 replaceUnicodeCharactersWithAscii(String message)163 public static String replaceUnicodeCharactersWithAscii(String message) { 164 if(null == message) { 165 return message; 166 } 167 168 return replace( 169 replace( 170 replace( 171 replace(message, "\u0004", "\\u0004"), 172 "\u0003", "\\u0003"), 173 "\u0002", "\\u0002"), 174 "\u0001", "\\u0001"); 175 } 176 replaceAsciiCharactersWithUnicode(String message)177 public static String replaceAsciiCharactersWithUnicode(String message) { 178 if(null == message) { 179 return message; 180 } 181 182 return replace( 183 replace( 184 replace( 185 replace(message, "\\u0004", "\u0004"), 186 "\\u0003", "\u0003"), 187 "\\u0002", "\u0002"), 188 "\\u0001", "\u0001"); 189 } 190 replaceNewLineReplacer(String message)191 public static String replaceNewLineReplacer(String message) { 192 if(null == message) { 193 return message; 194 } 195 196 return message.replace(LINE_SEP_DELIMITER_1, '\n').replace(LINE_SEP_DELIMITER_2, '\r'); 197 } 198 parseParameters(final String messagePart)199 private static String[] parseParameters(final String messagePart) { 200 return tokenize(messagePart, PARAM_DELIMITER); 201 } 202 parseMessage(final String message)203 private static String[] parseMessage(final String message) { 204 return tokenize(message, DELIMITER); 205 } 206 tokenize(final String message, final char separator)207 private static String[] tokenize(final String message, final char separator) { 208 if(null == message) { 209 return new String[0]; 210 } 211 212 List<String> tokens = Lists.newArrayList(); 213 int start = 0; 214 for(int i = 0; i < message.length(); i++) { 215 if(separator == message.charAt(i)) { 216 tokens.add(message.substring(start, i)); 217 start = i + 1; 218 } 219 } 220 if(start < message.length()) { 221 tokens.add(message.substring(start, message.length())); 222 } 223 224 return tokens.toArray(new String[tokens.size()]); 225 } 226 227 /** 228 * Implementation according to JDK5 String.replace(CharSequence,CharSequence) 229 */ replace(String original, CharSequence target, CharSequence replacement)230 private static final String replace(String original, CharSequence target, CharSequence replacement) { 231 return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(original) 232 .replaceAll(quoteReplacement(replacement.toString())); 233 } 234 235 /** 236 * Implementation according to JDK5 String.replace(CharSequence,CharSequence) 237 */ quoteReplacement(String s)238 private static String quoteReplacement(String s) { 239 if ((s.indexOf('\\') == -1) && (s.indexOf('$') == -1)) { 240 return s; 241 } 242 StringBuffer sb = new StringBuffer(); 243 for (int i=0; i<s.length(); i++) { 244 char c = s.charAt(i); 245 if (c == '\\') { 246 sb.append('\\'); sb.append('\\'); 247 } else if (c == '$') { 248 sb.append('\\'); sb.append('$'); 249 } else { 250 sb.append(c); 251 } 252 } 253 return sb.toString(); 254 } 255 } 256