Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
ARG BASE_IMAGE
FROM ${BASE_IMAGE}
RUN dnf install -y python3 && dnf clean all
COPY pi.py /opt/azl-tests/pi.py
72 changes: 72 additions & 0 deletions base/images/tests/cases/runtime/container-base/test_math/pi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# SPDX-License-Identifier: MIT
# Pi calculation adapted from https://github.com/MrBlaise/learnpython/blob/master/Numbers/pi.py
"""Spigot-algorithm Pi calculation, run inside the container for compute validation."""

from __future__ import annotations

import time
from collections.abc import Iterator

PI_1000 = (
"3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412"
"737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051"
"320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473"
"035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989"
Comment thread
MadhurAggarwal marked this conversation as resolved.
)

MAX_SECONDS_PER_COMPUTE = 20.0


def calc_pi(limit: int) -> Iterator[object]:
Comment thread
MadhurAggarwal marked this conversation as resolved.
"""Calculate Pi digits one at a time via the spigot algorithm."""
q, r, t, k, n, step = 1, 0, 1, 1, 3, 3
counter = 0
while counter != limit + 1:
if 4 * q + r - t < n * t:
yield n
if counter == 0:
yield "."
if limit == counter:
break
counter += 1
nr = 10 * (r - n * t)
n = ((10 * (3 * q + r)) // t) - 10 * n
q *= 10
r = nr
else:
nr = (2 * q + r) * step
nn = (q * (7 * k) + 2 + (r * step)) // (t * step)
q *= k
t *= step
step += 2
k += 1
n = nn
r = nr


def pi_to_places(places: int) -> str:
"""Return Pi, accurate to the given number of decimal places."""
return "".join(str(d) for d in calc_pi(places))


def verify_pi_1000() -> bool:
"""Check that Pi to 1000 places matches the known reference value."""
return pi_to_places(1000) == PI_1000


def verify_pi_n_times_1000(nrange: int = 10, mult: int = 1000) -> bool:
"""Repeatedly compute Pi at growing precision and assert performance (max 20s per computation)."""
for count in range(nrange + 1):
places = max(count * mult, 3)
start = time.time()
answer = pi_to_places(places)
if len(answer) != places + 2 or time.time() - start > MAX_SECONDS_PER_COMPUTE:
Comment on lines +61 to +63
return False
return True


if __name__ == "__main__":
import sys

checks = {"1000": verify_pi_1000, "n1000": verify_pi_n_times_1000}
sys.exit(0 if checks[sys.argv[1]]() else 1)
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# SPDX-License-Identifier: MIT
"""Verify the container's python3 computes Pi correctly (Dockerfile adds python3)."""

from __future__ import annotations

import pytest

_PI = "/opt/azl-tests/pi.py"


@pytest.mark.dockerfile()
def test_pi_1000_places(container_exec_shell) -> None:
"""Pi computed to 1000 places must match the known value."""
result = container_exec_shell(f"python3 {_PI} 1000")
assert result.exit_code == 0, f"Pi(1000) verification failed: {result.output}"


@pytest.mark.dockerfile()
def test_pi_n_times_1000_places(container_exec_shell) -> None:
"""Sustained Pi compute (10 x 1000 places) must stay correct and fast."""
result = container_exec_shell(f"python3 {_PI} n1000")
assert result.exit_code == 0, f"Pi series verification failed: {result.output}"
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
ARG BASE_IMAGE
FROM ${BASE_IMAGE}
RUN dnf install -y python3 && dnf clean all
COPY netcheck.py /opt/azl-tests/netcheck.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# SPDX-License-Identifier: MIT
"""Outbound HTTPS fetch + parse helpers, run inside the container to validate networking."""

from __future__ import annotations

import re
import time
import urllib.request
from urllib.error import HTTPError, URLError

WEATHER_URLS = (
"https://iotadbselfhostreportstor.blob.core.windows.net/marinerextendedtests/mockweathergov.html",
"https://forecast.weather.gov/MapClick.php?lat=47.6786&lon=-122.1316",
)
REPO_CONFIG_URLS = (
"https://packages.microsoft.com/azurelinux/3.0/prod/base/x86_64/config.repo",
"https://packages.microsoft.com/cbl-mariner/2.0/prod/base/x86_64/config.repo",
)


def fetch(url: str, retries: int = 4) -> str:
"""Fetch a URL, retrying transient HTTP errors with exponential backoff, and a 30s timeout to avoid hangs."""
for attempt in range(1, retries + 1):
try:
with urllib.request.urlopen(urllib.request.Request(url), timeout=30) as resp: # noqa: S310 — https literals only
return resp.read().decode()
except HTTPError:
time.sleep(attempt * 2)
except URLError:
break
return ""


def fetch_first(urls: tuple[str, ...]) -> str:
"""Return the first non-empty page across the given URLs."""
for url in urls:
page = fetch(url)
if page:
return page
return ""


def substring_between(source: str, before: str, after: str) -> str:
"""Return the markup-stripped text between two markers, or empty string."""
beg = source.find(before)
if beg < 0:
return ""
sub = source[beg + len(before) :]
end = sub.find(after)
if end >= 0:
sub = sub[:end]
return re.sub(r"<.*?>", "", sub).strip()


def verify_weather(*, strict: bool = True) -> bool:
"""Fetch Redmond weather; strict requires all forecast fields present."""
page = fetch_first(WEATHER_URLS)
visibility = substring_between(page, "<b>Visibility</b></td>", "</td>")
dewpoint = substring_between(page, "<b>Dewpoint</b></td>", "</td>")
humidity = substring_between(page, "<b>Humidity</b></td>", "</td>")
wind = substring_between(page, "<b>Wind Speed</b></td>", "</td>")
forecast = substring_between(page, 'alt="Today:', '"')
print(
f"weather: bytes={len(page)} humidity={humidity!r} wind={wind!r} "
f"visibility={visibility!r} dewpoint={dewpoint!r} forecast={forecast!r}"
)
if not (wind and humidity and visibility and dewpoint) and strict:
return False
return len(page) > 0


def verify_sustained_https(iterations: int = 50, *, strict: bool = True) -> bool:
"""Repeat repo-config fetch; strict requires name+enabled fields each time."""
for i in range(iterations):
page = fetch_first(REPO_CONFIG_URLS)
name = substring_between(page, "name", "\n")
enabled = substring_between(page, "enabled", "\n")
print(f"fetch {i + 1}/{iterations}: bytes={len(page)} name={name!r} enabled={enabled!r}")
if not (name and enabled) and strict:
return False
if len(page) == 0:
return False
Comment on lines +79 to +82
time.sleep(1)
return True


if __name__ == "__main__":
import sys

checks = {"weather": verify_weather, "sustained": verify_sustained_https}
strict = "--strict" in sys.argv
sys.exit(0 if checks[sys.argv[1]](strict=strict) else 1)
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# SPDX-License-Identifier: MIT
"""Verify outbound networking from the container (Dockerfile adds python3)."""

from __future__ import annotations

import pytest

_NET = "/opt/azl-tests/netcheck.py"


@pytest.mark.dockerfile()
def test_online_service_weather(container_exec_shell) -> None:
"""Test Online Services: a single outbound HTTPS fetch must return a non-empty page."""
result = container_exec_shell(f"python3 {_NET} weather")
assert result.exit_code == 0, f"Outbound HTTPS fetch failed: {result.output}"


@pytest.mark.dockerfile()
def test_sustained_https_fetch(container_exec_shell) -> None:
"""Test Core Networking: 50 sequential outbound HTTPS fetches must all return valid repo config."""
result = container_exec_shell(f"python3 {_NET} sustained --strict")
assert result.exit_code == 0, f"Sustained HTTPS fetch failed: {result.output}"
Loading