File size: 4,071 Bytes
26a7c18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
"""The comparison under a figure has to be right, or it undoes the figure.

A wrong comparator is worse than none: the number above it is measured, and a
reader who catches the comparison being wrong has no reason to trust the
measurement either.
"""

from __future__ import annotations

import math

import pytest

from distinct_server.comparators import (
    COMPARATORS,
    KETTLE_EFFICIENCY,
    KWH,
    WATER_SPECIFIC_HEAT,
    basis,
    choose,
    describe,
)


def test_there_are_about_a_hundred_of_them() -> None:
    assert len(COMPARATORS) >= 100


def test_every_entry_states_its_working() -> None:
    for entry in COMPARATORS:
        assert entry.note.strip(), entry.label
        assert entry.joules > 0, entry.label
        assert math.isfinite(entry.joules), entry.label


def test_labels_are_unique() -> None:
    labels = [entry.label for entry in COMPARATORS]
    assert len(labels) == len(set(labels))


def test_the_physics_is_the_physics() -> None:
    """Spot-check the derived entries against the formula in the docstring."""

    by_label = {entry.label: entry.joules for entry in COMPARATORS}

    # 0.25 l from 20 C to 100 C, in a kettle at 80%.
    expected = 0.25 * WATER_SPECIFIC_HEAT * 80 / KETTLE_EFFICIENCY
    assert by_label["mug of tea boiled"] == pytest.approx(expected, rel=0.01)

    # 0.568 l (a pint) over the same rise.
    expected = 0.568 * WATER_SPECIFIC_HEAT * 80 / KETTLE_EFFICIENCY
    assert by_label["pint of water boiled"] == pytest.approx(expected, rel=0.01)

    # 1 kg of water, 1 degree.
    assert by_label["litre of water warmed by 1 C"] == pytest.approx(WATER_SPECIFIC_HEAT, rel=0.001)

    # A 100 g apple lifted a metre.
    assert by_label["apple lifted one metre"] == pytest.approx(0.1 * 9.81, rel=0.01)

    # Rated appliances are watts times seconds.
    assert by_label["minute in a microwave"] == pytest.approx(1000 * 60)
    assert by_label["hour of a laptop"] == pytest.approx(50 * 3600)
    assert by_label["load of washing"] == pytest.approx(0.7 * KWH, rel=0.001)
    assert by_label["day of a UK home"] == pytest.approx(7.5 * KWH, rel=0.001)


def test_a_figure_always_finds_a_comparison_it_can_carry() -> None:
    """Across nine decades, the count in front of the label stays picturable."""

    value = 1.0
    while value <= 1e10:
        chosen = choose(value)
        assert chosen is not None, value
        _, count = chosen
        assert 0.05 <= count <= 20, (value, count)
        value *= 3.0


def test_the_chosen_comparison_is_the_closest_one() -> None:
    entry, count = choose(104_650.0)
    assert entry.label == "mug of tea boiled"
    assert count == pytest.approx(1.0, rel=0.01)


def test_singular_only_when_it_reads_as_one() -> None:
    """"about 0.98 minute" reads as a mistake, so the rule follows the printed
    number rather than a tolerance around 1.0."""

    entry = next(e for e in COMPARATORS if e.label == "mug of tea boiled")
    assert entry.name(1.0) == "mug of tea boiled"
    assert entry.name(1.04) == "mug of tea boiled"
    assert entry.name(0.98) == "mugs of tea boiled"
    assert entry.name(2.0) == "mugs of tea boiled"
    assert entry.name(0.4) == "mugs of tea boiled"
    assert describe(104_650.0) == "about 1 mug of tea boiled"


def test_nothing_sensible_to_say_says_nothing() -> None:
    for bad in (0, -1, None, float("nan"), float("inf"), True, "12"):
        assert describe(bad) == ""  # type: ignore[arg-type]
        assert choose(bad) is None  # type: ignore[arg-type]


def test_the_working_travels_with_the_comparison() -> None:
    assert "4186" in basis(4186.0) or "kg" in basis(4186.0)
    assert basis(104_650.0)


def test_no_conversion_this_project_refuses_to_make() -> None:
    """No CO2, water, land or money: those need assumptions a joule does not."""

    forbidden = ("co2", "carbon", "gco2", "water footprint", "litres of water used", "£", "$")
    for entry in COMPARATORS:
        blob = f"{entry.label} {entry.note}".lower()
        for word in forbidden:
            assert word not in blob, (entry.label, word)