Skip to content

Commit 62e1451

Browse files
add speedtest implementation
1 parent 22e0807 commit 62e1451

File tree

2 files changed

+176
-0
lines changed

2 files changed

+176
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .speedtest import CloudflareSpeedTest, CloudflareSpeedTestLinuxImplementation
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
import os
2+
import random
3+
import subprocess
4+
import time
5+
from dataclasses import dataclass
6+
from typing import Optional
7+
8+
from netunicorn.base import Result, Task, TaskDispatcher, Node, Architecture, Success
9+
10+
11+
@dataclass
12+
class CloudflareSpeedTestResults:
13+
summary: dict
14+
unloaded_latency: float
15+
unloaded_jitter: float
16+
unloaded_latency_points: list[float]
17+
down_loaded_latency: float
18+
down_loaded_jitter: float
19+
down_loaded_latency_points: list[float]
20+
up_loaded_latency: float
21+
up_loaded_jitter: float
22+
up_loaded_latency_points: list[float]
23+
download_bandwidth: float
24+
download_bandwidth_points: list[dict]
25+
upload_bandwidth: float
26+
upload_bandwidth_points: list[dict]
27+
packet_loss: float
28+
packet_loss_details: dict
29+
scores: dict
30+
31+
32+
def get_measurements(driver) -> CloudflareSpeedTestResults:
33+
return CloudflareSpeedTestResults(
34+
summary=driver.execute_script(
35+
"return window.testInstance.results.getSummary()"
36+
),
37+
unloaded_latency=driver.execute_script(
38+
"return window.testInstance.results.getUnloadedLatency()"
39+
),
40+
unloaded_jitter=driver.execute_script(
41+
"return window.testInstance.results.getUnloadedJitter()"
42+
),
43+
unloaded_latency_points=driver.execute_script(
44+
"return window.testInstance.results.getUnloadedLatencyPoints()"
45+
),
46+
down_loaded_latency=driver.execute_script(
47+
"return window.testInstance.results.getDownLoadedLatency()"
48+
),
49+
down_loaded_jitter=driver.execute_script(
50+
"return window.testInstance.results.getDownLoadedJitter()"
51+
),
52+
down_loaded_latency_points=driver.execute_script(
53+
"return window.testInstance.results.getDownLoadedLatencyPoints()"
54+
),
55+
up_loaded_latency=driver.execute_script(
56+
"return window.testInstance.results.getUpLoadedLatency()"
57+
),
58+
up_loaded_jitter=driver.execute_script(
59+
"return window.testInstance.results.getUpLoadedJitter()"
60+
),
61+
up_loaded_latency_points=driver.execute_script(
62+
"return window.testInstance.results.getUpLoadedLatencyPoints()"
63+
),
64+
download_bandwidth=driver.execute_script(
65+
"return window.testInstance.results.getDownloadBandwidth()"
66+
),
67+
download_bandwidth_points=driver.execute_script(
68+
"return window.testInstance.results.getDownloadBandwidthPoints()"
69+
),
70+
upload_bandwidth=driver.execute_script(
71+
"return window.testInstance.results.getUploadBandwidth()"
72+
),
73+
upload_bandwidth_points=driver.execute_script(
74+
"return window.testInstance.results.getUploadBandwidthPoints()"
75+
),
76+
packet_loss=driver.execute_script(
77+
"return window.testInstance.results.getPacketLoss()"
78+
),
79+
packet_loss_details=driver.execute_script(
80+
"return window.testInstance.results.getPacketLossDetails()"
81+
),
82+
scores=driver.execute_script("return window.testInstance.results.getScores()"),
83+
)
84+
85+
86+
def measure(
87+
chrome_location: Optional[str] = None, webdriver_arguments: Optional[list] = None
88+
) -> CloudflareSpeedTestResults:
89+
from selenium import webdriver
90+
from selenium.webdriver.chrome.options import Options
91+
from selenium.webdriver.chrome.service import Service
92+
93+
# start python http.server
94+
http_process = subprocess.Popen(
95+
["python3", "-m", "http.server", "44345"], cwd="/tmp/cloudflare/speedtest"
96+
)
97+
98+
# start Xvfb display for the browser
99+
display_number = random.randint(100, 500)
100+
xvfb_process = subprocess.Popen(
101+
["Xvfb", f":{display_number}", "-screen", "0", "1920x1080x24"]
102+
)
103+
os.environ["DISPLAY"] = f":{display_number}"
104+
105+
options = Options()
106+
options.add_argument("--no-sandbox")
107+
options.add_argument("--disable-dev-shm-usage")
108+
if webdriver_arguments:
109+
for argument in webdriver_arguments:
110+
options.add_argument(argument)
111+
if chrome_location:
112+
options.binary_location = chrome_location
113+
114+
driver = webdriver.Chrome(service=Service(), options=options)
115+
time.sleep(1)
116+
driver.get("http://localhost:44345/test.html")
117+
time.sleep(1)
118+
119+
# check "window.testInstance.isFinished" attribute
120+
while not driver.execute_script("return window.testInstance.isFinished"):
121+
time.sleep(1)
122+
123+
# get results
124+
results = get_measurements(driver)
125+
126+
driver.close()
127+
xvfb_process.kill()
128+
http_process.kill()
129+
return results
130+
131+
132+
class CloudflareSpeedTest(TaskDispatcher):
133+
def __init__(self, *args, **kwargs):
134+
super().__init__(*args, **kwargs)
135+
self.linux_instance = CloudflareSpeedTestLinuxImplementation(name=self.name)
136+
137+
def dispatch(self, node: Node) -> Task:
138+
if node.architecture in {Architecture.LINUX_AMD64, Architecture.LINUX_ARM64}:
139+
return self.linux_instance
140+
141+
raise NotImplementedError(
142+
f"CLoudflareSpeedTest is not implemented for architecture: {node.architecture}"
143+
)
144+
145+
146+
class CloudflareSpeedTestLinuxImplementation(Task):
147+
requirements = [
148+
"apt install -y python3-pip wget xvfb procps chromium chromium-driver",
149+
"pip3 install selenium webdriver-manager",
150+
"mkdir -p /tmp/cloudflare/speedtest"
151+
"wget https://github.com/netunicorn/netunicorn-library/releases/download/cloudflare-speedtest-0.1/bundle.js -O /tmp/cloudflare/speedtest/bundle.js",
152+
"wget https://github.com/netunicorn/netunicorn-library/releases/download/cloudflare-speedtest-0.1/test.html -O /tmp/cloudflare/speedtest/test.html",
153+
]
154+
155+
def __init__(
156+
self,
157+
chrome_location: Optional[str] = None,
158+
webdriver_arguments: Optional[list] = None,
159+
*args,
160+
**kwargs,
161+
):
162+
self.chrome_location = chrome_location
163+
if not self.chrome_location:
164+
self.chrome_location = "/usr/bin/chromium"
165+
self.webdriver_arguments = webdriver_arguments
166+
super().__init__(*args, **kwargs)
167+
168+
def run(self) -> Result[CloudflareSpeedTestResults, str]:
169+
return Success(measure(self.chrome_location, self.webdriver_arguments))
170+
171+
172+
if __name__ == "__main__":
173+
CloudflareSpeedTestLinuxImplementation(
174+
chrome_location="/usr/bin/chromium-browser"
175+
).run()

0 commit comments

Comments
 (0)