Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 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
66 changes: 66 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,66 @@
# 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.
)


def calc_pi(limit: int) -> Iterator[object]:
Comment thread
MadhurAggarwal marked this conversation as resolved.
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 "".join(str(d) for d in calc_pi(places))


def verify_pi_1000() -> bool:
return pi_to_places(1000) == PI_1000


def verify_pi_n_times_1000(nrange: int = 10, mult: int = 1000) -> bool:
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 > 20.0:
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,89 @@
# SPDX-License-Identifier: MIT
"""Outbound HTTP 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 backoff."""
for attempt in range(1, retries + 1):
try:
with urllib.request.urlopen(urllib.request.Request(url)) as resp:
Comment thread
MadhurAggarwal marked this conversation as resolved.
Outdated
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} visibility={visibility!r} dewpoint={dewpoint!r} forecast={forecast!r}")
Comment thread
MadhurAggarwal marked this conversation as resolved.
Outdated
if not (wind and humidity and visibility and dewpoint) and strict:
return False
return len(page) > 0


def verify_sustained_http(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_http}
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 HTTP fetch failed: {result.output}"


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