1 package autotest.tko; 2 3 import autotest.common.JsonRpcCallback; 4 import autotest.common.JsonRpcProxy; 5 import autotest.common.PaddedJsonRpcProxy; 6 import autotest.common.Utils; 7 import autotest.common.ui.DetailView; 8 import autotest.common.ui.NotifyManager; 9 import autotest.common.ui.RealHyperlink; 10 11 import com.google.gwt.event.logical.shared.OpenEvent; 12 import com.google.gwt.event.logical.shared.OpenHandler; 13 import com.google.gwt.event.logical.shared.ResizeEvent; 14 import com.google.gwt.event.logical.shared.ResizeHandler; 15 import com.google.gwt.json.client.JSONNumber; 16 import com.google.gwt.json.client.JSONObject; 17 import com.google.gwt.json.client.JSONString; 18 import com.google.gwt.json.client.JSONValue; 19 import com.google.gwt.user.client.Window; 20 import com.google.gwt.user.client.ui.Composite; 21 import com.google.gwt.user.client.ui.DisclosurePanel; 22 import com.google.gwt.user.client.ui.FlexTable; 23 import com.google.gwt.user.client.ui.FlowPanel; 24 import com.google.gwt.user.client.ui.HTML; 25 import com.google.gwt.user.client.ui.Panel; 26 import com.google.gwt.user.client.ui.ScrollPanel; 27 import com.google.gwt.user.client.ui.SimplePanel; 28 29 import java.util.ArrayList; 30 import java.util.Arrays; 31 import java.util.Collections; 32 import java.util.List; 33 34 class TestDetailView extends DetailView { 35 private static final int NO_TEST_ID = -1; 36 37 private static final JsonRpcProxy logLoadingProxy = 38 new PaddedJsonRpcProxy(Utils.RETRIEVE_LOGS_URL); 39 40 private int testId = NO_TEST_ID; 41 private String jobTag; 42 private List<LogFileViewer> logFileViewers = new ArrayList<LogFileViewer>(); 43 private RealHyperlink logLink = new RealHyperlink("(view all logs)"); 44 private RealHyperlink testLogLink = new RealHyperlink("(view test logs)"); 45 private Panel logPanel; 46 private Panel attributePanel = new SimplePanel(); 47 48 private class LogFileViewer extends Composite 49 implements OpenHandler<DisclosurePanel>, ResizeHandler { 50 private DisclosurePanel panel; 51 private ScrollPanel scroller; // ScrollPanel wrapping log contents 52 private String logFilePath; 53 54 private JsonRpcCallback rpcCallback = new JsonRpcCallback() { 55 @Override 56 public void onError(JSONObject errorObject) { 57 super.onError(errorObject); 58 String errorString = getErrorString(errorObject); 59 if (errorString.equals("")) { 60 errorString = "Failed to load log "+ logFilePath; 61 } 62 setStatusText(errorString); 63 } 64 65 @Override 66 public void onSuccess(JSONValue result) { 67 handle(result); 68 } 69 }; 70 LogFileViewer(String logFilePath, String logFileName)71 public LogFileViewer(String logFilePath, String logFileName) { 72 this.logFilePath = logFilePath; 73 panel = new DisclosurePanel(logFileName); 74 panel.addOpenHandler(this); 75 panel.addStyleName("log-file-panel"); 76 initWidget(panel); 77 78 Window.addResizeHandler(this); 79 } 80 81 @Override onOpen(OpenEvent<DisclosurePanel> event)82 public void onOpen(OpenEvent<DisclosurePanel> event) { 83 JSONObject params = new JSONObject(); 84 params.put("path", new JSONString(getLogUrl())); 85 logLoadingProxy.rpcCall("dummy", params, rpcCallback); 86 87 setStatusText("Loading..."); 88 } 89 getLogUrl()90 private String getLogUrl() { 91 return Utils.getLogsUrl(jobTag + "/" + logFilePath); 92 } 93 handle(JSONValue value)94 public void handle(JSONValue value) { 95 String logContents = value.isString().stringValue(); 96 if (logContents.equals("")) { 97 setStatusText("Log file is empty"); 98 } else { 99 setLogText(logContents); 100 } 101 } 102 setLogText(String text)103 private void setLogText(String text) { 104 panel.clear(); 105 scroller = new ScrollPanel(); 106 scroller.getElement().setInnerText(text); 107 panel.add(scroller); 108 setScrollerWidth(); 109 } 110 111 /** 112 * Firefox fails to set relative widths correctly for elements with overflow: scroll (or 113 * auto, or hidden). Instead, it just expands the element to fit the contents. So we use 114 * this trick to dynamically implement width: 100%. 115 */ setScrollerWidth()116 private void setScrollerWidth() { 117 assert panel.isOpen(); 118 scroller.setWidth("0px"); // allow the parent to assume its natural size 119 int targetWidthPx = scroller.getParent().getOffsetWidth(); 120 NotifyManager.getInstance().log(targetWidthPx + "px"); 121 scroller.setWidth(targetWidthPx + "px"); 122 } 123 124 @Override onResize(ResizeEvent event)125 public void onResize(ResizeEvent event) { 126 if (panel.isOpen()) { 127 setScrollerWidth(); 128 } 129 } 130 setStatusText(String status)131 private void setStatusText(String status) { 132 panel.clear(); 133 panel.add(new HTML("<i>" + status + "</i>")); 134 } 135 } 136 137 private static class AttributeTable extends Composite { 138 private DisclosurePanel container = new DisclosurePanel("Test attributes"); 139 private FlexTable table = new FlexTable(); 140 AttributeTable(JSONObject attributes)141 public AttributeTable(JSONObject attributes) { 142 processAttributes(attributes); 143 setupTableStyle(); 144 container.add(table); 145 initWidget(container); 146 } 147 processAttributes(JSONObject attributes)148 private void processAttributes(JSONObject attributes) { 149 if (attributes.size() == 0) { 150 table.setText(0, 0, "No test attributes"); 151 return; 152 } 153 154 List<String> sortedKeys = new ArrayList<String>(attributes.keySet()); 155 Collections.sort(sortedKeys); 156 for (String key : sortedKeys) { 157 String value = Utils.jsonToString(attributes.get(key)); 158 int row = table.getRowCount(); 159 table.setText(row, 0, key); 160 table.setText(row, 1, value); 161 } 162 } 163 setupTableStyle()164 private void setupTableStyle() { 165 container.addStyleName("test-attributes"); 166 } 167 } 168 169 @Override initialize()170 public void initialize() { 171 super.initialize(); 172 173 addWidget(attributePanel, "td_attributes"); 174 logPanel = new FlowPanel(); 175 addWidget(logPanel, "td_log_files"); 176 testLogLink.setOpensNewWindow(true); 177 addWidget(testLogLink, "td_view_logs_link"); 178 logLink.setOpensNewWindow(true); 179 addWidget(logLink, "td_view_logs_link"); 180 } 181 addLogViewers(String testName)182 private void addLogViewers(String testName) { 183 logPanel.clear(); 184 addLogFileViewer(testName + "/debug/" + testName + ".DEBUG", "Test debug log"); 185 addLogFileViewer(testName + "/debug/" + testName + ".ERROR", "Test error log"); 186 addLogFileViewer("status.log", "Job status log"); 187 addLogFileViewer("debug/autoserv.DEBUG", "Job debug log"); 188 addLogFileViewer("debug/autoserv.ERROR", "Job error log"); 189 } 190 addLogFileViewer(String logPath, String logName)191 private void addLogFileViewer(String logPath, String logName) { 192 LogFileViewer viewer = new LogFileViewer(logPath, logName); 193 logFileViewers.add(viewer); 194 logPanel.add(viewer); 195 } 196 197 @Override fetchData()198 protected void fetchData() { 199 JSONObject params = new JSONObject(); 200 params.put("test_idx", new JSONNumber(testId)); 201 rpcProxy.rpcCall("get_detailed_test_views", params, new JsonRpcCallback() { 202 @Override 203 public void onSuccess(JSONValue result) { 204 JSONObject test; 205 try { 206 test = Utils.getSingleObjectFromArray(result.isArray()); 207 } 208 catch (IllegalArgumentException exc) { 209 NotifyManager.getInstance().showError("No such job found"); 210 resetPage(); 211 return; 212 } 213 214 showTest(test); 215 } 216 217 @Override 218 public void onError(JSONObject errorObject) { 219 super.onError(errorObject); 220 resetPage(); 221 } 222 }); 223 } 224 225 @Override setObjectId(String id)226 protected void setObjectId(String id) { 227 try { 228 testId = Integer.parseInt(id); 229 } 230 catch (NumberFormatException exc) { 231 throw new IllegalArgumentException(); 232 } 233 } 234 235 @Override getObjectId()236 protected String getObjectId() { 237 if (testId == NO_TEST_ID) { 238 return NO_OBJECT; 239 } 240 return Integer.toString(testId); 241 } 242 243 @Override getDataElementId()244 protected String getDataElementId() { 245 return "td_data"; 246 } 247 248 @Override getFetchControlsElementId()249 protected String getFetchControlsElementId() { 250 return "td_fetch_controls"; 251 } 252 253 @Override getNoObjectText()254 protected String getNoObjectText() { 255 return "No test selected"; 256 } 257 258 @Override getTitleElementId()259 protected String getTitleElementId() { 260 return "td_title"; 261 } 262 263 @Override getElementId()264 public String getElementId() { 265 return "test_detail_view"; 266 } 267 268 @Override display()269 public void display() { 270 super.display(); 271 CommonPanel.getPanel().setConditionVisible(false); 272 } 273 showTest(JSONObject test)274 protected void showTest(JSONObject test) { 275 String testName = test.get("test_name").isString().stringValue(); 276 jobTag = test.get("job_tag").isString().stringValue(); 277 278 showText(testName, "td_test"); 279 showText(jobTag, "td_job_tag"); 280 showField(test, "job_name", "td_job_name"); 281 showField(test, "status", "td_status"); 282 showField(test, "reason", "td_reason"); 283 showField(test, "test_started_time", "td_test_started"); 284 showField(test, "test_finished_time", "td_test_finished"); 285 showField(test, "hostname", "td_hostname"); 286 showField(test, "platform", "td_platform"); 287 showField(test, "kernel", "td_kernel"); 288 289 String[] labels = Utils.JSONtoStrings(test.get("labels").isArray()); 290 String labelList = Utils.joinStrings(", ", Arrays.asList(labels)); 291 if (labelList.equals("")) { 292 labelList = "none"; 293 } 294 showText(labelList, "td_test_labels"); 295 296 JSONObject attributes = test.get("attributes").isObject(); 297 attributePanel.clear(); 298 attributePanel.add(new AttributeTable(attributes)); 299 300 logLink.setHref(Utils.getRetrieveLogsUrl(jobTag)); 301 testLogLink.setHref(Utils.getRetrieveLogsUrl(jobTag) + "/" + testName); 302 addLogViewers(testName); 303 304 displayObjectData("Test " + testName + " (job " + jobTag + ")"); 305 } 306 @Override resetPage()307 public void resetPage() { 308 super.resetPage(); 309 } 310 } 311