update tests and add code coverage with coverage, although coverage is not required

This commit is contained in:
UpstreamData
2022-09-12 15:18:00 -06:00
parent 7377cb0d26
commit ea195b34db
7 changed files with 439 additions and 59 deletions

17
.coveragerc Normal file
View File

@@ -0,0 +1,17 @@
[report]
exclude_lines =
# Skip @abstractmethod
@abstractmethod
@abc.abstractmethod
# Don't complain if tests don't hit defensive assertion code:
raise AssertionError
raise NotImplementedError
# Don't complain about missing debug-only code:
def __repr__
if self\.debug
# Don't complain if non-runnable code isn't run:
if 0:
if __name__ == .__main__.:

View File

@@ -1,54 +0,0 @@
# Copyright 2022 Upstream Data Inc
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import unittest
from pyasic.miners.miner_factory import MINER_CLASSES
import inspect
import sys
class MinersTest(unittest.TestCase):
def test_miner_model_creation(self):
for miner_model in MINER_CLASSES.keys():
for miner_api in MINER_CLASSES[miner_model].keys():
with self.subTest(miner_model=miner_model, miner_api=miner_api):
miner = MINER_CLASSES[miner_model][miner_api]("0.0.0.0")
self.assertTrue(
isinstance(miner, MINER_CLASSES[miner_model][miner_api])
)
def test_miner_backend_backup_creation(self):
backends = inspect.getmembers(
sys.modules["pyasic.miners._backends"], inspect.isclass
)
for backend in backends:
miner_class = backend[1]
with self.subTest(miner_class=miner_class):
miner = miner_class("0.0.0.0")
self.assertTrue(isinstance(miner, miner_class))
def test_miner_type_creation_failure(self):
backends = inspect.getmembers(
sys.modules["pyasic.miners._types"], inspect.isclass
)
for backend in backends:
miner_class = backend[1]
with self.subTest(miner_class=miner_class):
with self.assertRaises(TypeError):
miner_class("0.0.0.0")
if __name__ == "__main__":
unittest.main()

View File

@@ -13,8 +13,10 @@
# limitations under the License.
import unittest
from pyasic.tests.miners_tests import MinersTest
from pyasic.tests.network_tests import NetworkTest
from tests.miners_tests import MinersTest, MinerFactoryTest
from tests.network_tests import NetworkTest
from tests.config_tests import ConfigTest
if __name__ == "__main__":
# `coverage run --source pyasic -m unittest discover` will give code coverage data
unittest.main()

View File

@@ -0,0 +1,111 @@
# Copyright 2022 Upstream Data Inc
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import unittest
from pyasic.config import MinerConfig, _PoolGroup, _Pool # noqa
from tests.test_data import (
bosminer_api_pools,
x19_api_pools,
x19_web_pools,
bosminer_config_pools,
)
class ConfigTest(unittest.TestCase):
def setUp(self) -> None:
self.test_config = MinerConfig(
pool_groups=[
_PoolGroup(
quota=1,
group_name="TEST",
pools=[
_Pool(
url="stratum+tcp://pyasic.testpool_1.pool:3333",
username="pyasic.test",
password="123",
),
_Pool(
url="stratum+tcp://pyasic.testpool_2.pool:3333",
username="pyasic.test",
password="123",
),
],
)
],
temp_mode="auto",
temp_target=70.0,
temp_hot=80.0,
temp_dangerous=100.0,
fan_speed=None,
autotuning_enabled=True,
autotuning_wattage=900,
)
def test_config_from_raw(self):
bos_config = MinerConfig().from_raw(bosminer_config_pools)
bos_config.pool_groups[0].group_name = "TEST"
with self.subTest(
msg="Testing BOSMiner config file config.", bos_config=bos_config
):
self.assertEqual(bos_config, self.test_config)
x19_cfg = MinerConfig().from_raw(x19_web_pools)
x19_cfg.pool_groups[0].group_name = "TEST"
with self.subTest(msg="Testing X19 API config.", x19_cfg=x19_cfg):
self.assertEqual(x19_cfg, self.test_config)
def test_config_from_api(self):
bos_cfg = MinerConfig().from_api(bosminer_api_pools["POOLS"])
bos_cfg.pool_groups[0].group_name = "TEST"
with self.subTest(msg="Testing BOSMiner API config.", bos_cfg=bos_cfg):
self.assertEqual(bos_cfg, self.test_config)
x19_cfg = MinerConfig().from_api(x19_api_pools["POOLS"])
x19_cfg.pool_groups[0].group_name = "TEST"
with self.subTest(msg="Testing X19 API config.", x19_cfg=x19_cfg):
self.assertEqual(x19_cfg, self.test_config)
def test_config_as_types(self):
cfg = MinerConfig().from_api(bosminer_api_pools["POOLS"])
cfg.pool_groups[0].group_name = "TEST"
commands = [
func
for func in
# each function in self
dir(cfg)
if callable(getattr(cfg, func)) and
# no __ methods
not func.startswith("__")
]
for command in [cmd for cmd in commands if cmd.startswith("as_")]:
with self.subTest():
output = getattr(cfg, command)()
self.assertEqual(output, getattr(self.test_config, command)())
if f"from_{command.split('as_')[1]}" in commands:
self.assertEqual(
getattr(MinerConfig(), f"from_{command.split('as_')[1]}")(
output
),
self.test_config,
)
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,162 @@
# Copyright 2022 Upstream Data Inc
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import unittest
from pyasic.miners.miner_factory import MINER_CLASSES
from pyasic.miners.base import BaseMiner
from pyasic.miners._backends import CGMiner
from pyasic.miners.miner_factory import MinerFactory
from pyasic.miners.miner_listener import MinerListener
import asyncio
import inspect
import sys
class MinersTest(unittest.TestCase):
def test_miner_model_creation(self):
for miner_model in MINER_CLASSES.keys():
for miner_api in MINER_CLASSES[miner_model].keys():
with self.subTest(
msg=f"Creation of miner using model={miner_model}, api={miner_api}",
miner_model=miner_model,
miner_api=miner_api,
):
miner = MINER_CLASSES[miner_model][miner_api]("0.0.0.0")
self.assertTrue(
isinstance(miner, MINER_CLASSES[miner_model][miner_api])
)
def test_miner_backend_backup_creation(self):
backends = inspect.getmembers(
sys.modules["pyasic.miners._backends"], inspect.isclass
)
for backend in backends:
miner_class = backend[1]
with self.subTest(miner_class=miner_class):
miner = miner_class("0.0.0.0")
self.assertTrue(isinstance(miner, miner_class))
def test_miner_type_creation_failure(self):
backends = inspect.getmembers(
sys.modules["pyasic.miners._types"], inspect.isclass
)
for backend in backends:
miner_class = backend[1]
with self.subTest(miner_class=miner_class):
with self.assertRaises(TypeError):
miner_class("0.0.0.0")
with self.assertRaises(TypeError):
BaseMiner("0.0.0.0")
def test_miner_comparisons(self):
miner_1 = CGMiner("1.1.1.1")
miner_2 = CGMiner("2.2.2.2")
miner_3 = CGMiner("1.1.1.1")
self.assertEqual(miner_1, miner_3)
self.assertGreater(miner_2, miner_1)
self.assertLess(miner_3, miner_2)
class MinerFactoryTest(unittest.TestCase):
def test_miner_factory_creation(self):
self.assertDictEqual(MinerFactory().miners, {})
miner_factory = MinerFactory()
self.assertIs(MinerFactory(), miner_factory)
def test_get_miner_generator(self):
async def _coro():
gen = MinerFactory().get_miner_generator([])
miners = []
async for miner in gen:
miners.append(miner)
return miners
_miners = asyncio.run(_coro())
self.assertListEqual(_miners, [])
def test_miner_selection(self):
for miner_model in MINER_CLASSES.keys():
with self.subTest():
miner = MinerFactory()._select_miner_from_classes(
"0.0.0.0", miner_model, None, None
)
self.assertIsInstance(miner, BaseMiner)
for api in ["BOSMiner+", "BOSMiner", "CGMiner", "BTMiner", "BMMiner"]:
with self.subTest():
miner = MinerFactory()._select_miner_from_classes(
"0.0.0.0", None, api, None
)
self.assertIsInstance(miner, BaseMiner)
with self.subTest():
miner = MinerFactory()._select_miner_from_classes(
"0.0.0.0", "ANTMINER S17+", "Fake API", None
)
self.assertIsInstance(miner, BaseMiner)
with self.subTest():
miner = MinerFactory()._select_miner_from_classes(
"0.0.0.0", "M30S", "BTMiner", "G20"
)
self.assertIsInstance(miner, BaseMiner)
def test_validate_command(self):
bad_test_data_returns = [
{},
{
"cmd": [
{
"STATUS": [
{"STATUS": "E", "Msg": "Command failed for some reason."}
]
}
]
},
{"STATUS": "E", "Msg": "Command failed for some reason."},
{
"STATUS": [{"STATUS": "E", "Msg": "Command failed for some reason."}],
"id": 1,
},
]
for data in bad_test_data_returns:
with self.subTest():
async def _coro(miner_ret):
_data = await MinerFactory()._validate_command(miner_ret)
return _data
ret = asyncio.run(_coro(data))
self.assertFalse(ret[0])
good_test_data_returns = [
{
"STATUS": [{"STATUS": "S", "Msg": "Yay! Command succeeded."}],
"id": 1,
},
]
for data in good_test_data_returns:
with self.subTest():
async def _coro(miner_ret):
_data = await MinerFactory()._validate_command(miner_ret)
return _data
ret = asyncio.run(_coro(data))
self.assertTrue(ret[0])
if __name__ == "__main__":
unittest.main()

View File

@@ -31,8 +31,8 @@ class NetworkTest(unittest.TestCase):
"192.168.1.60",
]
net_1 = list(MinerNetworkRange(net_range_str).hosts())
net_2 = list(MinerNetworkRange(net_range_list).hosts())
net_1 = list(MinerNetwork(net_range_str).get_network().hosts())
net_2 = list(MinerNetwork(net_range_list).get_network().hosts())
correct_net = [
ipaddress.IPv4Address("192.168.1.29"),
@@ -48,7 +48,7 @@ class NetworkTest(unittest.TestCase):
def test_net(self):
net_1_str = "192.168.1.0"
net_1_mask = 29
net_1_mask = "/29"
net_1 = list(MinerNetwork(net_1_str, mask=net_1_mask).get_network().hosts())
@@ -68,6 +68,20 @@ class NetworkTest(unittest.TestCase):
self.assertTrue(net_1 == correct_net)
self.assertTrue(net_2 == correct_net)
def test_net_len(self):
net = MinerNetwork("192.168.1.0", mask=32)
self.assertEqual(len(net), 1)
net2 = MinerNetwork("192.168.1.0", mask=31)
self.assertEqual(len(net2), 2)
def test_net_defaults(self):
net = MinerNetwork()
net_obj = net.get_network()
self.assertEqual(net_obj, MinerNetwork("192.168.1.0", mask=24).get_network())
self.assertEqual(net_obj, net.get_network())
if __name__ == "__main__":
unittest.main()

128
tests/test_data.py Normal file
View File

@@ -0,0 +1,128 @@
# Copyright 2022 Upstream Data Inc
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
bosminer_api_pools = {
"STATUS": [
{
"STATUS": "S",
"Msg": "2 Pool(s)",
"Description": "",
}
],
"POOLS": [
{
"POOL": 0,
"URL": "stratum+tcp://pyasic.testpool_1.pool:3333",
"Status": "Alive",
"Quota": 1,
"User": "pyasic.test",
"Stratum URL": "pyasic.testpool_1.pool:3333",
"AsicBoost": True,
},
{
"POOL": 1,
"URL": "stratum+tcp://pyasic.testpool_2.pool:3333",
"Status": "Alive",
"Quota": 1,
"User": "pyasic.test",
"Stratum URL": "pyasic.testpool_2.pool:3333",
"AsicBoost": True,
},
],
"id": 1,
}
x19_api_pools = {
"STATUS": [
{
"STATUS": "S",
"Msg": "2 Pool(s)",
"Description": "",
}
],
"POOLS": [
{
"POOL": 0,
"URL": "stratum+tcp://pyasic.testpool_1.pool:3333",
"Status": "Alive",
"Quota": 1,
"User": "pyasic.test",
"Stratum URL": "pyasic.testpool_1.pool:3333",
},
{
"POOL": 1,
"URL": "stratum+tcp://pyasic.testpool_2.pool:3333",
"Status": "Alive",
"Quota": 1,
"User": "pyasic.test",
"Stratum URL": "pyasic.testpool_2.pool:3333",
},
],
"id": 1,
}
x19_web_pools = {
"pools": [
{
"url": "stratum+tcp://pyasic.testpool_1.pool:3333",
"user": "pyasic.test",
"pass": "123",
},
{
"url": "stratum+tcp://pyasic.testpool_2.pool:3333",
"user": "pyasic.test",
"pass": "123",
},
],
"api-listen": True,
"api-network": True,
"api-groups": "A:stats:pools:devs:summary:version",
"api-allow": "A:0/0,W:*",
"bitmain-fan-ctrl": False,
"bitmain-fan-pwm": "100",
"bitmain-use-vil": True,
"bitmain-freq": "675",
"bitmain-voltage": "1400",
"bitmain-ccdelay": "0",
"bitmain-pwth": "0",
"bitmain-work-mode": "0",
"bitmain-freq-level": "100",
}
bosminer_config_pools = {
"format": {
"version": "1.2+",
"model": "Antminer S9",
"generator": "pyasic",
},
"group": [
{
"name": "TEST",
"quota": 1,
"pool": [
{
"enabled": True,
"url": "stratum+tcp://pyasic.testpool_1.pool:3333",
"user": "pyasic.test",
"password": "123",
},
{
"enabled": True,
"url": "stratum+tcp://pyasic.testpool_2.pool:3333",
"user": "pyasic.test",
"password": "123",
},
],
},
],
}