Skip to content

Commit

Permalink
Merge pull request #68 from Snailed/master
Browse files Browse the repository at this point in the history
Fix tests and add them to CI
  • Loading branch information
PedramBakh committed Mar 25, 2024
2 parents 992072e + 733c75b commit b1d56a7
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 33 deletions.
8 changes: 5 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ on:
- dev

jobs:
build:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.7, 3.8, 3.9, '3.10']
python-version: ['3.12']

steps:
- uses: actions/checkout@v3
Expand All @@ -27,9 +27,11 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install flake8 black
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
pip install .[test]
- name: Lint with flake8
run: |
flake8 carbontracker --count --select=E9,F63,F7,F82 --show-source --statistics
- name: Formatting with Black
run: black --line-length 120 carbontracker
- name: Run tests
run: python -m unittest discover -v
1 change: 0 additions & 1 deletion carbontracker/emissions/intensity/intensity.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ def get_default_intensity():

default_intensity = get_default_intensity()


class CarbonIntensity:
def __init__(
self,
Expand Down
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ repository = "https://github.com/lfwa/carbontracker"

[tool.setuptools_scm]

[project.optional-dependencies]
test = ["pyfakefs"]

[project.scripts]
carbontracker = "carbontracker.cli:main"

58 changes: 30 additions & 28 deletions tests/intensity/test_intensity.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import geocoder
import unittest
from unittest.mock import patch, MagicMock
import numpy as np
import pandas as pd
import pkg_resources
import importlib.resources

from carbontracker import constants
from carbontracker.emissions.intensity import intensity

from carbontracker.emissions.intensity.intensity import carbon_intensity, default_intensity
from carbontracker.emissions.intensity.intensity import carbon_intensity


class TestIntensity(unittest.TestCase):
Expand All @@ -20,9 +21,9 @@ def test_get_default_intensity_success(self, mock_geocoder_ip):
mock_geocoder_ip.return_value = mock_location

result = intensity.get_default_intensity()

carbon_intensities_df = pd.read_csv(
pkg_resources.resource_filename("carbontracker", "data/carbon-intensities.csv"))
ref = importlib.resources.files("carbontracker") / "data/carbon-intensities.csv"
with importlib.resources.as_file(ref) as path:
carbon_intensities_df = pd.read_csv(path)
intensity_row = carbon_intensities_df[carbon_intensities_df["alpha-2"] == mock_location.country].iloc[0]
expected_intensity = intensity_row["Carbon intensity of electricity (gCO2/kWh)"]

Expand Down Expand Up @@ -106,27 +107,26 @@ def test_carbon_intensity_location_failure(self, mock_geocoder_ip):

logger = MagicMock()

result = intensity.carbon_intensity(logger)
with patch('carbontracker.emissions.intensity.intensity.default_intensity', intensity.get_default_intensity()) as C:
result = intensity.carbon_intensity(logger)
default_intensity = intensity.get_default_intensity()

self.assertEqual(result.carbon_intensity, default_intensity["carbon_intensity"])
self.assertEqual(result.address, "UNDETECTED")
self.assertEqual(result.success, False)
self.assertIn("Live carbon intensity could not be fetched at detected location", result.message)
self.assertEqual(result.carbon_intensity, default_intensity["carbon_intensity"])
self.assertEqual(result.address, "UNDETECTED")
self.assertEqual(result.success, False)
self.assertIn("Live carbon intensity could not be fetched at detected location", result.message)

@patch("carbontracker.emissions.intensity.intensity.geocoder.ip")
def test_set_carbon_intensity_message(self, mock_geocoder):
ci = intensity.CarbonIntensity()
def test_set_carbon_intensity_message(self, mock_geocoder_ip):
time_dur = 3600

mock_location = MagicMock()
# Assuming the actual function logic uses a specific fallback or detected location
detected_address = "Zürich, Zurich, CH" # The detected location that appears in the error
detected_address = "Aarhus, Capital Region, DK" # The detected location that appears in the error
fallback_address = "Generic Location, Country" # The fallback or generic location
mock_location.address = detected_address
mock_geocoder.ip.return_value = mock_location

ci.address = fallback_address # Set to the fallback or generic address for the test case

mock_location.country = 'DK'
mock_geocoder_ip.ok = True
mock_geocoder_ip.return_value = mock_location
# Adjust the set_expected_message function to match the error details
def set_expected_message(is_prediction, success, carbon_intensity):
if is_prediction:
Expand All @@ -139,10 +139,9 @@ def set_expected_message(is_prediction, success, carbon_intensity):
message = f"Current carbon intensity is {carbon_intensity:.2f} gCO2/kWh at detected location: {fallback_address}."
else:
message = (f"Live carbon intensity could not be fetched at detected location: {detected_address}. "
f"Defaulted to average carbon intensity for CH in 2021 of 57.77 gCO2/kWh. "
f"Defaulted to average carbon intensity for DK in 2021 of 149.75 gCO2/kWh. "
f"at detected location: {fallback_address}.")
return message

# Test scenarios
scenarios = [
(True, True, 100.0),
Expand All @@ -151,13 +150,16 @@ def set_expected_message(is_prediction, success, carbon_intensity):
(False, False, None) # The scenario corresponding to the failure message
]

for is_prediction, success, carbon_intensity in scenarios:
ci.is_prediction = is_prediction
ci.success = success
ci.carbon_intensity = carbon_intensity if carbon_intensity is not None else 0.0
intensity.set_carbon_intensity_message(ci, time_dur)
expected_message = set_expected_message(is_prediction, success, carbon_intensity)
self.assertEqual(ci.message, expected_message)
with patch('carbontracker.emissions.intensity.intensity.default_intensity', intensity.get_default_intensity()) as C:
ci = intensity.CarbonIntensity()
ci.address = fallback_address # Set to the fallback or generic address for the test case
for is_prediction, success, carbon_intensity in scenarios:
ci.is_prediction = is_prediction
ci.success = success
ci.carbon_intensity = carbon_intensity if carbon_intensity is not None else 0.0
intensity.set_carbon_intensity_message(ci, time_dur)
expected_message = set_expected_message(is_prediction, success, carbon_intensity)
self.assertEqual(ci.message, expected_message)

@patch("geocoder.ip")
@patch("carbontracker.emissions.intensity.fetchers.electricitymaps.ElectricityMap.suitable")
Expand Down Expand Up @@ -248,4 +250,4 @@ def test_carbon_intensity_nan(self, mock_electricity_map, mock_geocoder):

self.assertFalse(result.success)
self.assertTrue(np.isnan(result.carbon_intensity))
self.assertEqual(mock_location.address, "Sample Address")
self.assertEqual(mock_location.address, "Sample Address")
3 changes: 3 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import subprocess
import unittest
from unittest import skipIf
from time import sleep
from unittest.mock import patch
from io import StringIO
import sys
from carbontracker import cli
import os

def mock_password_input(prompt):
# Simulate password entry based on the prompt
Expand All @@ -14,6 +16,7 @@ def mock_password_input(prompt):
# Handle other prompts or return None for unexpected prompts
return None

@skipIf(os.environ.get('CI') == 'true', 'Skipped due to CI')
class TestCLI(unittest.TestCase):

@patch("builtins.input", side_effect=mock_password_input)
Expand Down
3 changes: 3 additions & 0 deletions tests/test_loggerutil.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import unittest
from unittest import skipIf
from carbontracker import loggerutil
from carbontracker.loggerutil import Logger, convert_to_timestring
import unittest.mock
Expand Down Expand Up @@ -33,6 +34,7 @@ def test_convert_to_timestring_rounding_float_seconds(self):
time_s = 3659.9955 # Very close to 3660, and should round off to it
self.assertEqual(convert_to_timestring(time_s, add_milliseconds=True), "1:01:00.00")

@skipIf(os.environ.get('CI') == 'true', 'Skipped due to CI')
def test_formatTime_with_datefmt(self):
formatter = loggerutil.TrackerFormatter()
record = MagicMock()
Expand All @@ -44,6 +46,7 @@ def test_formatTime_with_datefmt(self):

self.assertEqual(formatted_time, "2023-03-15 14-20-00")

@skipIf(os.environ.get('CI') == 'true', 'Skipped due to CI')
def test_formatTime_without_datefmt(self):
formatter = loggerutil.TrackerFormatter()
record = MagicMock()
Expand Down
5 changes: 4 additions & 1 deletion tests/test_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import time
import traceback
import unittest
from unittest import mock
from unittest import mock, skipIf
from unittest.mock import Mock, patch, MagicMock
from threading import Event
import numpy as np
Expand Down Expand Up @@ -476,6 +476,7 @@ def test_epoch_start_deleted(self, mock_handle_error):

mock_handle_error.assert_not_called()

@skipIf(os.environ.get('CI') == 'true', 'Skipped due to CI')
@patch('carbontracker.tracker.CarbonTrackerThread.epoch_start')
@patch('carbontracker.tracker.CarbonTracker._handle_error')
def test_epoch_start_exception(self, mock_handle_error, mock_tracker_thread_epoch_start):
Expand Down Expand Up @@ -513,6 +514,7 @@ def test_handle_error_no_ignore_errors(self):
with self.assertRaises(SystemExit):
self.tracker._handle_error(Exception('Test exception'))

@skipIf(os.environ.get('CI') == 'true', 'Skipped due to CI')
@patch('carbontracker.emissions.intensity.fetchers.electricitymaps.ElectricityMap.set_api_key')
def test_set_api_keys_electricitymaps(self, mock_set_api_key):
tracker = CarbonTracker(epochs=1)
Expand All @@ -521,6 +523,7 @@ def test_set_api_keys_electricitymaps(self, mock_set_api_key):

mock_set_api_key.assert_called_once_with("mock_api_key")

@skipIf(os.environ.get('CI') == 'true', 'Skipped due to CI')
@patch('carbontracker.tracker.CarbonTracker.set_api_keys')
def test_carbontracker_api_key(self, mock_set_api_keys):
api_dict = {"ElectricityMaps": "mock_api_key"}
Expand Down

0 comments on commit b1d56a7

Please sign in to comment.