|
1 | 1 | package us.abstracta.jmeter.javadsl.core;
|
2 | 2 |
|
| 3 | +import java.awt.Component; |
| 4 | +import java.awt.Dimension; |
| 5 | +import java.awt.Font; |
3 | 6 | import java.io.File;
|
4 | 7 | import java.io.FileOutputStream;
|
5 | 8 | import java.io.IOException;
|
|
10 | 13 | import java.util.concurrent.CompletableFuture;
|
11 | 14 | import java.util.concurrent.ExecutionException;
|
12 | 15 | import java.util.concurrent.TimeoutException;
|
| 16 | +import java.util.stream.Collectors; |
| 17 | +import javax.swing.BorderFactory; |
| 18 | +import javax.swing.BoxLayout; |
| 19 | +import javax.swing.JComponent; |
| 20 | +import javax.swing.JPanel; |
| 21 | +import javax.swing.JScrollPane; |
13 | 22 | import javax.swing.SwingUtilities;
|
| 23 | +import javax.swing.border.TitledBorder; |
14 | 24 | import org.apache.jmeter.config.Arguments;
|
15 | 25 | import org.apache.jmeter.control.gui.TestPlanGui;
|
16 | 26 | import org.apache.jmeter.exceptions.IllegalUserActionException;
|
|
29 | 39 | import us.abstracta.jmeter.javadsl.core.engines.JmeterEnvironment;
|
30 | 40 | import us.abstracta.jmeter.javadsl.core.engines.JmeterGui;
|
31 | 41 | import us.abstracta.jmeter.javadsl.core.testelements.TestElementContainer;
|
| 42 | +import us.abstracta.jmeter.javadsl.core.threadgroups.BaseThreadGroup; |
32 | 43 | import us.abstracta.jmeter.javadsl.core.threadgroups.DslDefaultThreadGroup;
|
| 44 | +import us.abstracta.jmeter.javadsl.core.threadgroups.LoadTimeLine; |
33 | 45 |
|
34 | 46 | /**
|
35 | 47 | * Represents a JMeter test plan, with associated thread groups and other children elements.
|
@@ -170,6 +182,73 @@ and avoid NPE while updating RSyntaxTextArea in test plan load (e.g.: when test
|
170 | 182 | }
|
171 | 183 | }
|
172 | 184 |
|
| 185 | + /** |
| 186 | + * For each thread group shows a graph with a timeline of planned load (threads or rps) to be |
| 187 | + * generated. |
| 188 | + * <p> |
| 189 | + * Graphs will be displayed in a popup window. |
| 190 | + * <p> |
| 191 | + * This method eases test plan design when working with complex thread group profiles (several |
| 192 | + * stages with ramps and holds). |
| 193 | + * |
| 194 | + * @since 1.28 |
| 195 | + */ |
| 196 | + public void showTimeline() { |
| 197 | + List<LoadTimeLine> timeLines = buildThreadGroupTimeLines(); |
| 198 | + normalizeTimelines(timeLines); |
| 199 | + JPanel panel = buildChartsContainerPanel(); |
| 200 | + int chartWidth = 800; |
| 201 | + int chartHeight = timeLines.size() > 2 ? 200 : 300; |
| 202 | + timeLines.forEach(tl -> panel.add(buildChart(tl, chartWidth, chartHeight))); |
| 203 | + showAndWaitFrameWith(buildScrollPane(panel), "Load timeline", chartWidth + 20, |
| 204 | + (chartHeight) * Math.min(3, timeLines.size())); |
| 205 | + } |
| 206 | + |
| 207 | + private List<LoadTimeLine> buildThreadGroupTimeLines() { |
| 208 | + return children.stream() |
| 209 | + .filter(c -> c instanceof BaseThreadGroup) |
| 210 | + .map(c -> ((BaseThreadGroup<?>) c).buildLoadTimeline()) |
| 211 | + .collect(Collectors.toList()); |
| 212 | + } |
| 213 | + |
| 214 | + private void normalizeTimelines(List<LoadTimeLine> timeLines) { |
| 215 | + long maxTime = timeLines.stream() |
| 216 | + .mapToLong(LoadTimeLine::getMaxTime) |
| 217 | + .max() |
| 218 | + .orElse(0L); |
| 219 | + timeLines.forEach(tl -> { |
| 220 | + tl.add(1, 0); |
| 221 | + long chartMaxTime = tl.getMaxTime(); |
| 222 | + if (chartMaxTime < maxTime) { |
| 223 | + tl.add(maxTime - chartMaxTime + 1, 0); |
| 224 | + } |
| 225 | + }); |
| 226 | + } |
| 227 | + |
| 228 | + private JPanel buildChartsContainerPanel() { |
| 229 | + JPanel ret = new JPanel(); |
| 230 | + ret.setLayout(new BoxLayout(ret, BoxLayout.Y_AXIS)); |
| 231 | + return ret; |
| 232 | + } |
| 233 | + |
| 234 | + private JComponent buildChart(LoadTimeLine timeLine, int width, int height) { |
| 235 | + JComponent ret = timeLine.buildChart(); |
| 236 | + TitledBorder border = BorderFactory.createTitledBorder(timeLine.getName()); |
| 237 | + border.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); |
| 238 | + border.setTitleFont(new Font("Arial", Font.BOLD, 14)); |
| 239 | + border.setTitleJustification(TitledBorder.CENTER); |
| 240 | + ret.setBorder(border); |
| 241 | + ret.setPreferredSize(new Dimension(width, height)); |
| 242 | + return ret; |
| 243 | + } |
| 244 | + |
| 245 | + private JScrollPane buildScrollPane(Component component) { |
| 246 | + JScrollPane ret = new JScrollPane(component); |
| 247 | + ret.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); |
| 248 | + ret.getVerticalScrollBar().setUnitIncrement(8); // makes scroll faster |
| 249 | + return ret; |
| 250 | + } |
| 251 | + |
173 | 252 | /**
|
174 | 253 | * Saves the given test plan as JMX, which allows it to be loaded in JMeter GUI.
|
175 | 254 | *
|
|
0 commit comments