package org.example; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.awt.geom.Path2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.List; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.entity.ChartEntity; import org.jfree.chart.entity.XYItemEntity; import org.jfree.chart.labels.XYToolTipGenerator; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.jfree.data.xy.XYDataset; import org.jfree.chart.ChartFactory; import org.jfree.chart.ChartMouseEvent; import org.jfree.chart.ChartMouseListener; import org.jfree.chart.ChartPanel; import org.jfree.chart.JFreeChart; public class InternetSpeedTest extends JFrame { private XYSeries uploadSeries; private XYSeries downloadSeries; private JLabel status; private JButton startButton; private JLabel testNum; private XYSeriesCollection dataset; private JFreeChart chart; private JSlider tests; private int numberOfTests; private int testsLeft; public String timings; private JLabel timeLeft; private JLabel tlabel; private JProgressBar bar; private int onTest; private JLabel uploadSpeedLabel; private JLabel downloadSpeedLabel; private long startTime; private double etime; private List elapsedTimes; private JWindow tooltipWindow; private JLabel tooltipLabel; private Point cursorPosition; public InternetSpeedTest() { setTitle("Connectivity Speed Test"); setSize(1800, 600); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new BorderLayout()); // Initialize variables elapsedTimes = new ArrayList<>(); // Setup graph uploadSeries = new XYSeries("Upload Speed"); downloadSeries = new XYSeries("Download Speed"); dataset = new XYSeriesCollection(); dataset.addSeries(uploadSeries); dataset.addSeries(downloadSeries); chart = ChartFactory.createXYLineChart( "Speed Test", "Time", "Speed (Mbps)", dataset, PlotOrientation.VERTICAL, true, true, false ); ChartPanel chartPanel = new ChartPanel(chart) { @Override public void paintComponent(Graphics g) { super.paintComponent(g); if (cursorPosition != null) { XYPlot plot = chart.getXYPlot(); Rectangle2D plotArea = getScreenDataArea(); Point2D p = translateScreenToJava2D(cursorPosition); if (plotArea.contains(p)) { Graphics2D g2 = (Graphics2D) g; g2.setColor(new Color(42, 50, 58)); g2.setStroke(new BasicStroke(1)); // Get data area coordinates from screen coordinates Rectangle2D dataArea = getScreenDataArea(); int x = cursorPosition.x; int y = cursorPosition.y; // Draw vertical line within plot area g2.drawLine(x, (int) dataArea.getMinY(), x, (int) dataArea.getMaxY()); // Draw horizontal line within plot area g2.drawLine((int) dataArea.getMinX(), y, (int) dataArea.getMaxX(), y); } } } }; // Add a mouse motion listener for drawing dynamic axis lines chartPanel.addMouseMotionListener(new MouseMotionListener() { @Override public void mouseDragged(MouseEvent e) { // Not used } @Override public void mouseMoved(MouseEvent e) { cursorPosition = e.getPoint(); chartPanel.repaint(); // Trigger a repaint to draw lines } }); // Add a mouse listener to clear cursor position when mouse exits the chart chartPanel.addMouseListener(new MouseAdapter() { @Override public void mouseExited(MouseEvent e) { cursorPosition = null; chartPanel.repaint(); // Clear the lines when the mouse exits } }); chartPanel.setDomainZoomable(true); chartPanel.setRangeZoomable(true); add(chartPanel, BorderLayout.CENTER); // Customize point shapes and sizes XYPlot plot = chart.getXYPlot(); // Set the x-axis to show time in seconds NumberAxis domainAxis = new NumberAxis("Time (s)"); domainAxis.setTickUnit(new org.jfree.chart.axis.NumberTickUnit(1)); plot.setDomainAxis(domainAxis); // Set geom shape for up and down points XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(); renderer.setSeriesShapesVisible(0, true); renderer.setSeriesShapesVisible(1, true); renderer.setSeriesShape(0, createTriangleUpShape(6)); // Triangle facing up renderer.setSeriesShape(1, createTriangleDownShape(6)); // Triangle facing down plot.setRenderer(renderer); // Add tooltips // Create a custom tooltip window tooltipWindow = new JWindow(); tooltipWindow.setAlwaysOnTop(true); tooltipWindow.setFocusableWindowState(false); // Configure the tooltip label tooltipLabel = new JLabel(); tooltipLabel.setOpaque(true); tooltipLabel.setBackground(new Color(58, 64, 71)); // Light grey background tooltipLabel.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createLineBorder(new Color(0, 120, 215)), // Blue border BorderFactory.createEmptyBorder(5, 10, 5, 10) // Padding inside the label )); tooltipLabel.setFont(new Font("SansSerif", Font.PLAIN, 12)); // Set font tooltipLabel.setForeground(new Color(255, 255, 255)); // Black text color tooltipLabel.setVerticalAlignment(SwingConstants.CENTER); tooltipLabel.setHorizontalAlignment(SwingConstants.LEFT); // Align text to the left tooltipWindow.getContentPane().add(tooltipLabel); tooltipWindow.pack(); // Add a mouse motion listener to show custom tooltips chartPanel.addChartMouseListener(new ChartMouseListener() { @Override public void chartMouseClicked(ChartMouseEvent event) { // Not used } @Override public void chartMouseMoved(ChartMouseEvent event) { ChartEntity entity = event.getEntity(); if (entity instanceof XYItemEntity) { XYItemEntity itemEntity = (XYItemEntity) entity; XYDataset dataset = itemEntity.getDataset(); int series = itemEntity.getSeriesIndex(); int item = itemEntity.getItem(); double x = dataset.getXValue(series, item); double y = dataset.getYValue(series, item); String seriesName = dataset.getSeriesKey(series).toString(); // Get the specific time taken for this point double test = elapsedTimes.get(item)/1e9; double elapsedTime = test; String tooltipText = String.format( "%s
Elapsed: %.2f s
Speed: %.2f Mbps
Time: %.2f s", seriesName, x, y, elapsedTime ); Point p = event.getTrigger().getPoint(); SwingUtilities.convertPointToScreen(p, chartPanel); tooltipLabel.setText(tooltipText); tooltipWindow.pack(); tooltipWindow.setLocation(p.x - 130, p.y + 15); tooltipWindow.setVisible(true); } else { tooltipWindow.setVisible(false); } } }); // Setup controls JPanel controlPanel = new JPanel(); startButton = new JButton("Start Test"); tests = new JSlider(); tlabel = new JLabel("Tests: 2"); controlPanel.add(tlabel); tests.setMaximum(12); tests.setMinimum(2); tests.setValue(2); tests.revalidate(); controlPanel.add(tests); startButton.addActionListener(new StartButtonListener()); tests.addChangeListener(e -> setTestsNum()); tests.addChangeListener((e -> startButton.setText("Queue Tests"))); tests.addChangeListener((e -> testNum.setText(" | Test#: " + onTest + "/" + numberOfTests))); controlPanel.add(startButton); uploadSpeedLabel = new JLabel("Up: N/A"); controlPanel.add(uploadSpeedLabel); downloadSpeedLabel = new JLabel(" | Down: N/A"); controlPanel.add(downloadSpeedLabel); status = new JLabel(" | Status: Not Running"); controlPanel.add(status); testNum = new JLabel(" | Test#: N/A | "); controlPanel.add(testNum); bar = new JProgressBar(0, tests.getValue()); controlPanel.add(bar); timeLeft = new JLabel(" | est time"); controlPanel.add(timeLeft); add(controlPanel, BorderLayout.SOUTH); //customize chart data } public void disableActions() { startButton.setEnabled(false); tests.setEnabled(false); } public void enableActions() { startButton.setEnabled(true); tests.setEnabled(true); } public void setTestsNum() { numberOfTests = tests.getValue(); tlabel.setText("Tests: " + tests.getValue()); bar.setMaximum(numberOfTests); } private class StartButtonListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { setTestsNum(); new Thread(new SpeedTestTask()).start(); new Thread(new ElapsedTimeTask()).start(); //new Thread(new calculateTime()).start(); elapsedTimes.clear(); downloadSeries.clear(); uploadSeries.clear(); System.out.println("tests queued: "+tests.getValue()); } } private Shape createTriangleUpShape(int size) { Path2D.Double path = new Path2D.Double(); path.moveTo(-size / 2.0, size / 2.0); path.lineTo(size / 2.0, size / 2.0); path.lineTo(0, -size / 2.0); path.closePath(); return path; } private Shape createTriangleDownShape(int size) { Path2D.Double path = new Path2D.Double(); path.moveTo(-size / 2.0, -size / 2.0); path.lineTo(size / 2.0, -size / 2.0); path.lineTo(0, size / 2.0); path.closePath(); return path; } private class SpeedTestTask implements Runnable { @Override public void run() { try { disableActions(); long previousTime = System.nanoTime(); for (int i = 1; i <= numberOfTests; i++) { int tn = i; onTest = tn; testNum.setText(" | Test#: " + tn + "/" + numberOfTests); bar.setValue(i); double downloadSpeed = performDownloadTest(); double uploadSpeed = performUploadTest(); long currentTime = System.nanoTime(); double elapsedTime = (currentTime - startTime) / 1e9; // convert to seconds long timeDiff = currentTime - previousTime; previousTime = currentTime; downloadSeries.add(elapsedTime, downloadSpeed,true); uploadSeries.add(elapsedTime, uploadSpeed,true); //lastTime = (int) elapsedTime; testsLeft = numberOfTests - tn; downloadSpeedLabel.setText(String.format(" | Down: %.2f Mbps", downloadSpeed)); uploadSpeedLabel.setText(String.format("Up: %.2f Mbps", uploadSpeed)); if (elapsedTimes.size() > 0) { elapsedTimes.add(timeDiff); } else { elapsedTimes.add(currentTime - startTime); } System.out.println("last time: " + elapsedTime); System.out.println("tests left to complete: " + testsLeft); System.out.println("math: " + doMath()); System.out.println("took: "+timeDiff); System.out.println(elapsedTimes); timeLeft.setText(doMath()); Thread.sleep(100); } startButton.setText("Complete"); enableActions(); } catch (InterruptedException ex) { ex.printStackTrace(); status.setText(" | Status: Failed"); } catch (Exception ex) { ex.printStackTrace(); status.setText(" | Status: Failed"); } } private String doMath() { if (elapsedTimes.size() < 1) { return "Calculating..."; } // Calculate the average elapsed time between points long totalElapsedTime = 0; for (long elapsedTime : elapsedTimes) { totalElapsedTime += elapsedTime; } double averageTimePerTest = totalElapsedTime / (double) elapsedTimes.size() / 1e9; // convert to seconds // Estimate the remaining time double estimatedRemainingTime = averageTimePerTest * testsLeft; return String.format(" | ~Time Remaining: %.2f s", estimatedRemainingTime); } private double performDownloadTest() throws Exception { startButton.setText("Running"); URL url = new URL("http://speedtest.tele2.net/20MB.zip"); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.connect(); int fileSize = connection.getContentLength(); BufferedInputStream inputStream = new BufferedInputStream(connection.getInputStream()); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; long startTime = System.nanoTime(); int bytesRead; while ((bytesRead = inputStream.read(buffer, 0, 1024)) != -1) { byteArrayOutputStream.write(buffer, 0, bytesRead); } long endTime = System.nanoTime(); double timeTaken = (endTime - startTime) / 1e9; // convert to seconds double downloadSpeed = (fileSize * 8) / (timeTaken * 1e6); // in Mbps inputStream.close(); return downloadSpeed; } private double performUploadTest() throws Exception { startButton.setText("Running"); // Perform a real upload test byte[] uploadData = new byte[3 * 1024 * 1024]; // 10 MB of data URL url = new URL("https://httpbin.org/post"); // Replace with a valid endpoint HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("POST"); connection.setDoOutput(true); long startTime = System.nanoTime(); OutputStream outputStream = connection.getOutputStream(); outputStream.write(uploadData); outputStream.flush(); outputStream.close(); int responseCode = connection.getResponseCode(); long endTime = System.nanoTime(); double timeTaken = (endTime - startTime) / 1e9; // convert to seconds if (responseCode == HttpURLConnection.HTTP_OK) { double uploadSpeed = (uploadData.length * 8) / (timeTaken * 1e6); // in Mbps return uploadSpeed; } else { throw new Exception("Failed to upload data: " + responseCode); } } } private class ElapsedTimeTask implements Runnable { @Override public void run() { startTime = System.nanoTime(); while (true) { long elapsedTime = (System.nanoTime() - startTime) / 1_000_000_000; status.setText(String.format(" | Elapsed Time: %d s", elapsedTime)); etime = elapsedTime; try { Thread.sleep(1000); //etime = (int) elapsedTime; if (startButton.getText().equals("Complete")) { break; } } catch (InterruptedException e) { e.printStackTrace(); break; } } } } public static void main(String[] args) { SwingUtilities.invokeLater(() -> { InternetSpeedTest frame = new InternetSpeedTest(); frame.setVisible(true); }); } }