The Problem

Internet speed tests give you a single snapshot. If you want to know whether your connection degrades at peak hours or after a router change, you need to run tests repeatedly and compare results over time. This repo automates that: it runs speedtest-cli, appends results to a text file, and plots the trend.

What This Does

speedtest.py is a single-file script with five functions. It shells out to speedtest-cli, parses the JSON output, appends the result to speed_test_results.txt, and uses matplotlib to render download speed, upload speed, and latency over time.

The repo is minimal: one Python file, a requirements.txt for dependencies, a GitHub Actions workflow, and a README. There are no tests in the repo despite the pipeline detecting two test files—verify that against the actual tree before relying on it.

How It Is Wired

Execution starts at the bottom of speedtest.py when run directly. The flow is:

  1. perform_speed_test() invokes speedtest-cli --json via subprocess and parses the output.
  2. parse_speed_test_result() extracts download speed, upload speed, and latency from the returned dict.
  3. save_speed_test_result() appends those values to speed_test_results.txt in append mode.
  4. load_speed_test_results() reads that file back as a list of lists.
  5. plot_speed_test_results() feeds that list to matplotlib and displays a chart.

The script touches exactly two things outside itself: the speedtest-cli binary (via subprocess) and the local results file. There are no databases, no network calls beyond the speed test itself, and no internal module imports—the module graph shows one module with zero edges.

File responsibilities:

  • speedtest.py — all logic, from subprocess call to plotting.
  • requirements.txt — declares speedtest-cli and matplotlib.
  • .github/workflows/python-package.yml — CI that runs the script on push.
  • speed_test_results.txt — the append-only data store; this is the state that accumulates over time.

The GitHub Actions workflow is the only automation. It runs the script, but since the script only appends to a local file, CI runs don't persist results anywhere—the file is ephemeral in the runner.

How To Use It

Setup:

pip install -r requirements.txt
git clone https://github.com/moses-y/speedtest
cd speedtest

Running it:

python speed_test.py

The README names speed_test.py but the actual file is speedtest.py. Use the latter. The script takes no arguments, needs no environment variables, and writes results to speed_test_results.txt in the working directory.

Real-World Use

Schedule it with cron on a home server:

0 * * * * cd /path/to/speedtest && python speedtest.py

Run it hourly for a week, then open the plot to see whether your ISP's "up to" speed holds at peak hours. The append-mode file design means you can run it indefinitely without data loss, and the plot function gives you a visual trend without any external dashboard.

Code Health & Issues

Static analysis found 5 issues (0 critical, 0 high, 4 medium, 1 low), all in the GitHub Actions workflow:

  • Medium — No least-privilege permissions on GITHUB_TOKEN in .github/workflows/python-package.yml. Add permissions: contents: read.
  • Medium — No Dependabot or Renovate configured. Add .github/dependabot.yml.
  • Medium — No dependency vulnerability scan in CI. Add dependency-review-action or osv-scanner.
  • Mediumcheckout keeps the token in .git/config. Add persist-credentials: false.
  • Low — No timeout-minutes on jobs. A wedged step runs to the six-hour platform default.

Beyond that: the repo has a license and no committed secrets, but no lockfile, so pip install -r requirements.txt is not reproducible across time.

The Bottom Line

A straightforward, working script for personal speed-test tracking. The code is simple and does what it says. The CI workflow is the weakest part—it adds moving parts without real value, and its security posture needs the fixes above. Use it if you want a zero-dependency-other-than-pip way to log and plot your connection over time; skip it if you need something production-grade or multi-user.