diff --git a/maro/simulator/scenarios/supply_chain/units/distribution.py b/maro/simulator/scenarios/supply_chain/units/distribution.py index 335d6fb6b..29ea8a263 100644 --- a/maro/simulator/scenarios/supply_chain/units/distribution.py +++ b/maro/simulator/scenarios/supply_chain/units/distribution.py @@ -29,11 +29,23 @@ class DistributionUnit(UnitBase): """ def __init__( - self, id: int, data_model_name: Optional[str], data_model_index: Optional[int], - facility: FacilityBase, parent: Union[FacilityBase, UnitBase], world: World, config: dict, + self, + id: int, + data_model_name: Optional[str], + data_model_index: Optional[int], + facility: FacilityBase, + parent: Union[FacilityBase, UnitBase], + world: World, + config: dict, ) -> None: super(DistributionUnit, self).__init__( - id, data_model_name, data_model_index, facility, parent, world, config, + id, + data_model_name, + data_model_index, + facility, + parent, + world, + config, ) self._vehicle_num: Dict[str, Optional[int]] = {} @@ -88,6 +100,7 @@ def initialize(self) -> None: self._busy_vehicle_num[vehicle_type] = 0 # TODO: add vehicle patient setting if needed + self.data_model.initialize() for sku_id in self.facility.products.keys(): @@ -109,12 +122,14 @@ def place_order(self, order: Order) -> float: float: The corresponding total order fee, will paid by the consumer. """ # TODO: to indicate whether it is a valid order or not in Return value? - if all([ - order.sku_id in self.facility.downstream_vlt_infos, - order.dest_facility.id in self.facility.downstream_vlt_infos[order.sku_id], - order.vehicle_type in self.facility.downstream_vlt_infos[order.sku_id][order.dest_facility.id], - order.required_quantity > 0 - ]): + if all( + [ + order.sku_id in self.facility.downstream_vlt_infos, + order.dest_facility.id in self.facility.downstream_vlt_infos[order.sku_id], + order.vehicle_type in self.facility.downstream_vlt_infos[order.sku_id][order.dest_facility.id], + order.required_quantity > 0, + ], + ): self._order_queues[order.vehicle_type].append(order) self._maintain_pending_order_info(order, is_increase=True) self.total_order_num += 1 @@ -230,9 +245,7 @@ def step(self, tick: int) -> None: # TODO: here do not distinguish on the way & pending unloading. for order_list in self._order_on_the_way.values(): for order in order_list: - self.transportation_cost[order.sku_id] += ( - order.unit_transportation_cost_per_day * order.payload - ) + self.transportation_cost[order.sku_id] += order.unit_transportation_cost_per_day * order.payload # Schedule orders self.try_schedule_orders(tick) diff --git a/tests/data/supply_chain/case_01/config.yml b/tests/data/supply_chain/case_01/config.yml index 7730960e4..0d21ac8c5 100644 --- a/tests/data/supply_chain/case_01/config.yml +++ b/tests/data/supply_chain/case_01/config.yml @@ -88,6 +88,7 @@ world: sku3: init_stock: 80 has_manufacture: True + has_consumer: True max_manufacture_rate: 50 manufacture_leading_time: 1 unit_product_cost: 1 @@ -105,6 +106,7 @@ world: init_stock: 96 has_manufacture: True max_manufacture_rate: 50 + has_consumer: True manufacture_leading_time: 1 unit_product_cost: 1 price: 100 diff --git a/tests/data/supply_chain/case_05/config.yml b/tests/data/supply_chain/case_05/config.yml index 93f2fdf51..93c663234 100644 --- a/tests/data/supply_chain/case_05/config.yml +++ b/tests/data/supply_chain/case_05/config.yml @@ -146,6 +146,25 @@ world: unit_delay_order_penalty: 20 unit_order_cost: 0 + - name: "Supplier_SKU1" + definition_ref: "SupplierFacility" + skus: + sku3: + init_stock: 80 + has_manufacture: True + max_manufacture_rate: 50 + manufacture_leading_time: 1 + unit_product_cost: 1 + price: 10 + unit_delay_order_penalty: 10 + has_consumer: True + children: + storage: *small_storage + distribution: *normal_distribution + config: + unit_delay_order_penalty: 20 + unit_order_cost: 0 + - name: "Warehouse_001" definition_ref: "WarehouseFacility" skus: @@ -232,7 +251,7 @@ world: storage: *single_storage config: unit_order_cost: 200 - file_path: "tests/data/supply_chain/case_05/test_case_05.csv" + file_path: "tests/data/supply_chain/case_04/test_case_04.csv" topology: @@ -258,6 +277,10 @@ world: Warehouse_001: sku3: + "Supplier_SKU1": + "train": + vlt: 3 + cost: 1 "Supplier_SKU3": "train": vlt: 3 diff --git a/tests/supply_chain/test_action_reset.py b/tests/supply_chain/test_action_reset.py new file mode 100644 index 000000000..d87c6b914 --- /dev/null +++ b/tests/supply_chain/test_action_reset.py @@ -0,0 +1,593 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +import random +import unittest +from collections import defaultdict +from typing import Dict, List + +import numpy as np + +from maro.simulator.scenarios.supply_chain import ( + ConsumerAction, + ConsumerUnit, + FacilityBase, + ManufactureAction, + ManufactureUnit, + StorageUnit, +) +from maro.simulator.scenarios.supply_chain.business_engine import SupplyChainBusinessEngine +from maro.simulator.scenarios.supply_chain.order import Order + +from tests.supply_chain.common import SKU1_ID, SKU3_ID, build_env, get_product_dict_from_storage + + +class MyTestCase(unittest.TestCase): + """ + . consumer unit test + . distribution unit test + . manufacture unit test + . seller unit test + . storage unit test + """ + + def test_env_reset_with_none_action(self) -> None: + """test_env_reset_with_none_action""" + env = build_env("case_05", 500) + be = env.business_engine + assert isinstance(be, SupplyChainBusinessEngine) + + env.step(None) + + supplier_1: FacilityBase = be.world._get_facility_by_name("Supplier_SKU1") + warehouse_1 = be.world._get_facility_by_name("Warehouse_001") + Store_001: FacilityBase = be.world._get_facility_by_name("Store_001") + consumer_unit: ConsumerUnit = supplier_1.products[SKU3_ID].consumer + storage_unit: StorageUnit = supplier_1.storage + seller_unit = Store_001.products[SKU3_ID].seller + manufacture_unit = supplier_1.products[SKU3_ID].manufacture + distribution_unit = supplier_1.distribution + + consumer_nodes = env.snapshot_list["consumer"] + storage_nodes = env.snapshot_list["storage"] + seller_nodes = env.snapshot_list["seller"] + manufacture_nodes = env.snapshot_list["manufacture"] + distribution_nodes = env.snapshot_list["distribution"] + + consumer_node_index = consumer_unit.data_model_index + storage_node_index = storage_unit.data_model_index + seller_node_index = seller_unit.data_model_index + manufacture_node_index = manufacture_unit.data_model_index + distribution_node_index = distribution_unit.data_model_index + + consumer_features = ( + "id", + "facility_id", + "sku_id", + "order_base_cost", + "purchased", + "received", + "order_product_cost", + "latest_consumptions", + "in_transit_quantity", + ) + + storage_features = ("id", "facility_id") + + seller_features = ( + "sold", + "demand", + "total_sold", + "id", + "total_demand", + "backlog_ratio", + "facility_id", + "product_unit_id", + ) + + manufacture_features = ( + "id", + "facility_id", + "start_manufacture_quantity", + "sku_id", + "in_pipeline_quantity", + "finished_quantity", + "product_unit_id", + ) + + distribution_features = ("id", "facility_id", "pending_order_number", "pending_product_quantity") + + # ##################################### Before reset ##################################### + + expect_tick = 10 + + # Save the env.metric of each tick into env_metric_1 + env_metric_1: Dict[int, dict] = defaultdict(dict) + + random_tick: List[int] = [] + + # The purpose is to randomly perform the order operation + for i in range(10): + random_tick.append(random.randint(1, 30)) + + # Store the information about the snapshot of each tick in states_1_x + states_1_consumer: Dict[int, list] = defaultdict(list) + states_1_storage: Dict[int, list] = defaultdict(list) + states_1_seller: Dict[int, list] = defaultdict(list) + states_1_manufacture: Dict[int, list] = defaultdict(list) + states_1_distribution: Dict[int, list] = defaultdict(list) + + for i in range(expect_tick): + env.step(None) + if i in random_tick: + order = Order( + src_facility=supplier_1, + dest_facility=warehouse_1, + sku_id=SKU3_ID, + quantity=10, + vehicle_type="train", + creation_tick=env.tick, + expected_finish_tick=env.tick + 7, + ) + distribution_unit.place_order(order) + distribution_unit.try_schedule_orders(env.tick) + env_metric_1[i] = env.metrics + states_1_consumer[i] = consumer_nodes[i:consumer_node_index:consumer_features].flatten().astype(np.int) + states_1_manufacture[i] = ( + manufacture_nodes[i:manufacture_node_index:manufacture_features] + .flatten() + .astype( + np.int, + ) + ) + env_metric_1[i] = env.metrics + states_1_storage[i] = list(storage_nodes[i:storage_node_index:storage_features].flatten().astype(np.int)) + states_1_storage[i].append( + storage_nodes[i:storage_node_index:"product_id_list"].flatten().astype(np.int).sum(), + ) + states_1_storage[i].append( + storage_nodes[i:storage_node_index:"product_quantity"].flatten().astype(np.int).sum(), + ) + states_1_storage[i].append( + storage_nodes[i:storage_node_index:"remaining_space"].flatten().astype(np.int).sum(), + ) + states_1_seller[i] = seller_nodes[i:seller_node_index:seller_features].flatten().astype(np.int) + states_1_manufacture[i] = ( + manufacture_nodes[i:manufacture_node_index:manufacture_features] + .flatten() + .astype( + np.int, + ) + ) + states_1_distribution[i] = ( + distribution_nodes[i:distribution_node_index:distribution_features] + .flatten() + .astype( + np.int, + ) + ) + + # ############################### Test whether reset updates the storage unit completely ################ + env.reset() + env.step(None) + + # snapshot should reset after env.reset(). + consumer_states = consumer_nodes[1:consumer_node_index:consumer_features].flatten().astype(np.int) + storage_states = storage_nodes[1:storage_node_index:storage_features].flatten().astype(np.int) + seller_states = seller_nodes[1:seller_node_index:seller_features].flatten().astype(np.int) + manufacture_states = manufacture_nodes[1:manufacture_node_index:manufacture_features].flatten().astype(np.int) + distribution_states = ( + distribution_nodes[1:distribution_node_index:distribution_features] + .flatten() + .astype( + np.int, + ) + ) + + self.assertEqual([0, 0, 0, 0, 0, 0, 0, 0, 0], list(consumer_states)) + self.assertEqual([0, 0], list(storage_states)) + self.assertEqual([0, 0, 0, 0, 0, 0, 0, 0], list(seller_states)) + self.assertEqual([0, 0, 0, 0, 0, 0, 0], list(manufacture_states)) + self.assertEqual([0, 0, 0, 0], list(distribution_states)) + + expect_tick = 10 + + # Save the env.metric of each tick into env_metric_2 + env_metric_2: Dict[int, dict] = defaultdict(dict) + + # Store the information about the snapshot storage unit of each tick in states_2 + + states_2_consumer: Dict[int, list] = defaultdict(list) + states_2_storage: Dict[int, list] = defaultdict(list) + states_2_seller: Dict[int, list] = defaultdict(list) + states_2_manufacture: Dict[int, list] = defaultdict(list) + states_2_distribution: Dict[int, list] = defaultdict(list) + + for i in range(expect_tick): + env.step(None) + if i in random_tick: + order = Order( + src_facility=supplier_1, + dest_facility=warehouse_1, + sku_id=SKU3_ID, + quantity=10, + vehicle_type="train", + creation_tick=env.tick, + expected_finish_tick=env.tick + 7, + ) + distribution_unit.place_order(order) + distribution_unit.try_schedule_orders(env.tick) + env_metric_2[i] = env.metrics + states_2_consumer[i] = consumer_nodes[i:consumer_node_index:consumer_features].flatten().astype(np.int) + states_2_storage[i] = list(storage_nodes[i:storage_node_index:storage_features].flatten().astype(np.int)) + states_2_storage[i].append( + storage_nodes[i:storage_node_index:"product_id_list"].flatten().astype(np.int).sum(), + ) + states_2_storage[i].append( + storage_nodes[i:storage_node_index:"product_quantity"].flatten().astype(np.int).sum(), + ) + states_2_storage[i].append( + storage_nodes[i:storage_node_index:"remaining_space"].flatten().astype(np.int).sum(), + ) + states_2_seller[i] = seller_nodes[i:seller_node_index:seller_features].flatten().astype(np.int) + states_2_manufacture[i] = ( + manufacture_nodes[i:manufacture_node_index:manufacture_features] + .flatten() + .astype( + np.int, + ) + ) + states_2_distribution[i] = ( + distribution_nodes[i:distribution_node_index:distribution_features].flatten().astype(np.int) + ) + + for i in range(expect_tick): + self.assertEqual(list(states_1_consumer[i]), list(states_2_consumer[i])) + self.assertEqual(list(states_1_storage[i]), list(states_2_storage[i])) + self.assertEqual(list(states_1_seller[i]), list(states_2_seller[i])) + self.assertEqual(list(states_1_manufacture[i]), list(states_2_manufacture[i])) + self.assertEqual(list(states_1_distribution[i]), list(states_2_distribution[i])) + self.assertEqual(list(env_metric_1[i].values()), list(env_metric_2[i].values())) + + def test_env_reset_with_ManufactureAction_only(self) -> None: + """test env reset with ManufactureAction only""" + env = build_env("case_01", 100) + be = env.business_engine + assert isinstance(be, SupplyChainBusinessEngine) + + supplier_3: FacilityBase = be.world._get_facility_by_name("Supplier_SKU3") + sku3_storage_index = supplier_3.storage.data_model_index + manufacture_sku3_unit = supplier_3.products[SKU3_ID].manufacture + sku3_manufacture_index = manufacture_sku3_unit.data_model_index + + storage_nodes = env.snapshot_list["storage"] + manufacture_nodes = env.snapshot_list["manufacture"] + manufacture_features = ( + "id", + "facility_id", + "start_manufacture_quantity", + "sku_id", + "in_pipeline_quantity", + "finished_quantity", + "product_unit_id", + ) + # ##################################### Before reset ##################################### + + env.step(None) + + capacities = storage_nodes[env.frame_index : sku3_storage_index : "capacity"].flatten().astype(np.int) + remaining_spaces = ( + storage_nodes[env.frame_index : sku3_storage_index : "remaining_space"].flatten().astype(np.int) + ) + + # there should be 80 units been taken at the beginning according to the config file. + # so remaining space should be 20 + self.assertEqual(20, remaining_spaces.sum()) + # capacity is 100 by config + self.assertEqual(100, capacities.sum()) + + product_dict = get_product_dict_from_storage(env, env.frame_index, sku3_storage_index) + + # The product quantity should be same as configuration at beginning. + # 80 sku3 + self.assertEqual(80, product_dict[SKU3_ID]) + + # all the id is greater than 0 + self.assertGreater(manufacture_sku3_unit.id, 0) + + action = ManufactureAction(manufacture_sku3_unit.id, 1) + + expect_tick = 30 + + # Save the env.metric of each tick into env_metric_1 + env_metric_1: Dict[int, dict] = defaultdict(dict) + + # Store the information about the snapshot manufacture unit of each tick in states_1 + states_1: Dict[int, list] = defaultdict(list) + + random_tick: List[int] = [] + + # The purpose is to randomly perform the order operation + for i in range(10): + random_tick.append(random.randint(1, 30)) + + for i in range(expect_tick): + env.step([action]) + if i in random_tick: + env.step([ManufactureAction(manufacture_sku3_unit.id, 1)]) + env_metric_1[i] = env.metrics + states_1[i] = manufacture_nodes[i:sku3_manufacture_index:manufacture_features].flatten().astype(np.int) + + # ############################### Test whether reset updates the manufacture unit completely ################ + env.reset() + env.step(None) + + # snapshot should reset after env.reset(). + states = manufacture_nodes[1:sku3_manufacture_index:manufacture_features].flatten().astype(np.int) + self.assertEqual([0, 0, 0, 0, 0, 0, 0], list(states)) + + storage_nodes = env.snapshot_list["storage"] + manufacture_nodes = env.snapshot_list["manufacture"] + + capacities = storage_nodes[env.frame_index : sku3_storage_index : "capacity"].flatten().astype(np.int) + remaining_spaces = ( + storage_nodes[env.frame_index : sku3_storage_index : "remaining_space"].flatten().astype(np.int) + ) + + # there should be 80 units been taken at the beginning according to the config file. + # so remaining space should be 20 + self.assertEqual(20, remaining_spaces.sum()) + # capacity is 100 by config + self.assertEqual(100, capacities.sum()) + + product_dict = get_product_dict_from_storage(env, env.frame_index, sku3_storage_index) + + # The product quantity should be same as configuration at beginning. + # 80 sku3 + self.assertEqual(80, product_dict[SKU3_ID]) + + # all the id is greater than 0 + self.assertGreater(manufacture_sku3_unit.id, 0) + + expect_tick = 30 + + # Save the env.metric of each tick into env_metric_2 + env_metric_2: Dict[int, dict] = defaultdict(dict) + + # Store the information about the snapshot manufacture unit of each tick in states_2 + states_2: Dict[int, list] = defaultdict(list) + + for i in range(expect_tick): + env.step([action]) + if i in random_tick: + env.step([ManufactureAction(manufacture_sku3_unit.id, 1)]) + env_metric_2[i] = env.metrics + states_2[i] = manufacture_nodes[i:sku3_manufacture_index:manufacture_features].flatten().astype(np.int) + + expect_tick = 30 + for i in range(expect_tick): + self.assertEqual(list(states_1[i]), list(states_2[i])) + self.assertEqual(list(env_metric_1[i].values()), list(env_metric_2[i].values())) + + def test_env_reset_with_ConsumerAction_only(self) -> None: + """ "test env reset with ConsumerAction only""" + env = build_env("case_01", 500) + be = env.business_engine + assert isinstance(be, SupplyChainBusinessEngine) + + env.step(None) + + supplier_1: FacilityBase = be.world._get_facility_by_name("Supplier_SKU1") + supplier_3: FacilityBase = be.world._get_facility_by_name("Supplier_SKU3") + sku3_consumer_unit = supplier_1.products[SKU3_ID].consumer + + consumer_node_index = sku3_consumer_unit.data_model_index + + features = ( + "id", + "facility_id", + "sku_id", + "order_base_cost", + "purchased", + "received", + "order_product_cost", + "latest_consumptions", + "in_transit_quantity", + ) + + # ##################################### Before reset ##################################### + consumer_nodes = env.snapshot_list["consumer"] + action = ConsumerAction(sku3_consumer_unit.id, SKU3_ID, supplier_3.id, 1, "train") + expect_tick = 100 + + # Save the env.metric of each tick into env_metric_1 + env_metric_1: Dict[int, dict] = defaultdict(dict) + + # Store the information about the snapshot consumer unit of each tick in states_1 + states_1: Dict[int, list] = defaultdict(list) + + for i in range(expect_tick): + env.step([action]) + env_metric_1[i] = env.metrics + states_1[i] = consumer_nodes[i:consumer_node_index:features].flatten().astype(np.int) + + # ############### Test whether reset updates the consumer unit completely ################ + env.reset() + env.step(None) + + # snapshot should reset after env.reset() + states = consumer_nodes[1:consumer_node_index:features].flatten().astype(np.int) + self.assertEqual([0, 0, 0, 0, 0, 0, 0, 0, 0], list(states)) + + expect_tick = 100 + + # Save the env.metric of each tick into env_metric_2 + env_metric_2: Dict[int, dict] = defaultdict(dict) + + # Store the information about the snapshot consumer unit of each tick in states_2 + states_2: Dict[int, list] = defaultdict(list) + for i in range(expect_tick): + env.step([action]) + env_metric_2[i] = env.metrics + states_2[i] = consumer_nodes[i:consumer_node_index:features].flatten().astype(np.int) + + expect_tick = 100 + for i in range(expect_tick): + self.assertEqual(list(states_1[i]), list(states_2[i])) + self.assertEqual(list(env_metric_1[i].values()), list(env_metric_2[i].values())) + + def test_env_reset_with_both_ManufactureAction_and_ConsumerAction(self) -> None: + """test env reset with both ManufactureAction and ConsumerAction""" + env = build_env("case_01", 100) + be = env.business_engine + assert isinstance(be, SupplyChainBusinessEngine) + + env.step(None) + + supplier_1: FacilityBase = be.world._get_facility_by_name("Supplier_SKU1") + supplier_3: FacilityBase = be.world._get_facility_by_name("Supplier_SKU3") + consumer_unit: ConsumerUnit = supplier_1.products[SKU3_ID].consumer + manufacture_unit: ManufactureUnit = supplier_1.products[SKU1_ID].manufacture + storage_unit: StorageUnit = supplier_1.storage + + consumer_node_index = consumer_unit.data_model_index + manufacture_node_index = manufacture_unit.data_model_index + storage_node_index = storage_unit.data_model_index + + consumer_features = ( + "id", + "facility_id", + "sku_id", + "order_base_cost", + "purchased", + "received", + "order_product_cost", + "latest_consumptions", + "in_transit_quantity", + ) + + manufacture_features = ( + "id", + "facility_id", + "start_manufacture_quantity", + "sku_id", + "in_pipeline_quantity", + "finished_quantity", + "product_unit_id", + ) + storage_features = ("id", "facility_id") + + consumer_nodes = env.snapshot_list["consumer"] + manufacture_nodes = env.snapshot_list["manufacture"] + storage_nodes = env.snapshot_list["storage"] + + # ##################################### Before reset ##################################### + action_consumer = ConsumerAction(consumer_unit.id, SKU3_ID, supplier_3.id, 20, "train") + action_manufacture = ManufactureAction(manufacture_unit.id, 5) + + expect_tick = 100 + + # Save the env.metric of each tick into env_metric_1 + env_metric_1: Dict[int, dict] = defaultdict(dict) + + random_tick: List[int] = [] + + # The purpose is to randomly perform the order operation + for i in range(30): + random_tick.append(random.randint(0, 90)) + + # Store the information about the snapshot unit of each tick in states_1 + states_1_consumer: Dict[int, list] = defaultdict(list) + states_1_manufacture: Dict[int, list] = defaultdict(list) + states_1_storage: Dict[int, list] = defaultdict(list) + + for i in range(expect_tick): + + if i in random_tick: + env.step([action_manufacture]) + i += 1 + states_1_manufacture[i] = list( + manufacture_nodes[i:manufacture_node_index:manufacture_features] + .flatten() + .astype( + np.int, + ), + ) + env_metric_1[i] = env.metrics + continue + + env.step([action_consumer]) + env_metric_1[i] = env.metrics + states_1_consumer[i] = list( + consumer_nodes[i:consumer_node_index:consumer_features].flatten().astype(np.int), + ) + + states_1_storage[i] = list(storage_nodes[i:storage_node_index:storage_features].flatten().astype(np.int)) + states_1_storage[i].append( + list(storage_nodes[i:storage_node_index:"product_quantity"].flatten().astype(np.int)), + ) + states_1_storage[i].append( + list(storage_nodes[i:storage_node_index:"remaining_space"].flatten().astype(np.int)), + ) + + # ############### Test whether reset updates the consumer unit completely ################ + env.reset() + env.step(None) + + # snapshot should reset after env.reset() + consumer_states = consumer_nodes[1:consumer_node_index:consumer_features].flatten().astype(np.int) + manufacture_states = manufacture_nodes[1:manufacture_node_index:manufacture_features].flatten().astype(np.int) + self.assertEqual([0, 0, 0, 0, 0, 0, 0, 0, 0], list(consumer_states)) + self.assertEqual([0, 0, 0, 0, 0, 0, 0], list(manufacture_states)) + + expect_tick = 100 + + # Save the env.metric of each tick into env_metric_2 + env_metric_2: Dict[int, dict] = defaultdict(dict) + + # Store the information about the snapshot consumer unit of each tick in states_2 + states_2_consumer: Dict[int, list] = defaultdict(list) + states_2_manufacture: Dict[int, list] = defaultdict(list) + states_2_storage: Dict[int, list] = defaultdict(list) + + for i in range(expect_tick): + + if i in random_tick: + env.step([action_manufacture]) + i += 1 + states_2_manufacture[i] = list( + manufacture_nodes[i:manufacture_node_index:manufacture_features] + .flatten() + .astype( + np.int, + ), + ) + env_metric_2[i] = env.metrics + continue + + env.step([action_consumer]) + env_metric_2[i] = env.metrics + states_2_consumer[i] = list( + consumer_nodes[i:consumer_node_index:consumer_features].flatten().astype(np.int), + ) + + states_2_storage[i] = list(storage_nodes[i:storage_node_index:storage_features].flatten().astype(np.int)) + states_2_storage[i].append( + list( + storage_nodes[i:storage_node_index:"product_quantity"].flatten().astype(np.int), + ), + ) + states_2_storage[i].append( + list(storage_nodes[i:storage_node_index:"remaining_space"].flatten().astype(np.int)), + ) + + expect_tick = 100 + for i in range(expect_tick): + self.assertEqual(list(states_1_consumer[i]), list(states_2_consumer[i])) + self.assertEqual(list(states_1_manufacture[i]), list(states_2_manufacture[i])) + self.assertEqual(list(states_1_storage[i]), list(states_2_storage[i])) + self.assertEqual(list(env_metric_1[i].values()), list(env_metric_2[i].values())) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/supply_chain/test_consumer_unit.py b/tests/supply_chain/test_consumer_unit.py index 38a3cdb36..8ffe75dfd 100644 --- a/tests/supply_chain/test_consumer_unit.py +++ b/tests/supply_chain/test_consumer_unit.py @@ -2,27 +2,28 @@ # Licensed under the MIT license. import unittest + import numpy as np -from maro.simulator.scenarios.supply_chain import FacilityBase, ConsumerAction +from maro.simulator.scenarios.supply_chain import ConsumerAction, FacilityBase from maro.simulator.scenarios.supply_chain.business_engine import SupplyChainBusinessEngine from maro.simulator.scenarios.supply_chain.order import Order -from tests.supply_chain.common import build_env, SKU3_ID, FOOD_1_ID +from tests.supply_chain.common import FOOD_1_ID, SKU3_ID, build_env class MyTestCase(unittest.TestCase): """ - Consumer test: - - . initial state - . state after reset - . set_action directly from code - . set_action by env.step - . call on_order_reception directly to simulation order arrived - . call update_open_orders directly - . with dynamics sampler - """ + Consumer test: + + . initial state + . state after reset + . set_action directly from code + . set_action by env.step + . call on_order_reception directly to simulation order arrived + . call update_open_orders directly + . with dynamics sampler + """ def test_consumer_init_state(self) -> None: """Consumer of sku3 in Supplier_SKU1.""" @@ -65,7 +66,7 @@ def test_consumer_init_state(self) -> None: env.step(None) # check state - states = consumer_nodes[env.frame_index:consumer_node_index:features].flatten().astype(np.int) + states = consumer_nodes[env.frame_index : consumer_node_index : features].flatten().astype(np.int) self.assertEqual(sku3_consumer_unit.id, states[IDX_ID]) self.assertEqual(sku3_consumer_unit.facility.id, states[IDX_FACILITY_ID]) @@ -75,7 +76,7 @@ def test_consumer_init_state(self) -> None: env.reset() env.step(None) - states = consumer_nodes[env.frame_index:consumer_node_index:features].flatten().astype(np.int) + states = consumer_nodes[env.frame_index : consumer_node_index : features].flatten().astype(np.int) # Nothing happened at tick 0, so most states will be 0 self.assertEqual(0, states[IDX_PURCHASED]) @@ -110,7 +111,7 @@ def test_consumer_action(self) -> None: action_with_zero = ConsumerAction(sku3_consumer_unit.id, SKU3_ID, supplier_3.id, 0, "train") env.step([action_with_zero]) - states = consumer_nodes[env.frame_index:consumer_node_index:features].flatten().astype(np.int) + states = consumer_nodes[env.frame_index : consumer_node_index : features].flatten().astype(np.int) # Nothing happened at tick 0, at the action will be recorded self.assertEqual(action_with_zero.sku_id, states[IDX_SKU_ID]) @@ -178,7 +179,7 @@ def test_consumer_on_order_reception(self) -> None: def test_consumer_unit_dynamics_sampler(self): """Tested the store_001 Interaction between consumer unit and dynamics csv data. - The data file of this test is test_case_ 04.csv""" + The data file of this test is test_case_ 04.csv""" env = build_env("case_04", 100) be = env.business_engine assert isinstance(be, SupplyChainBusinessEngine) @@ -218,7 +219,7 @@ def test_consumer_unit_dynamics_sampler(self): env.step(None) # check state - states = consumer_nodes[env.frame_index:consumer_node_index:features].flatten().astype(np.int) + states = consumer_nodes[env.frame_index : consumer_node_index : features].flatten().astype(np.int) self.assertEqual(FOOD_1_consumer_unit.id, states[IDX_ID]) self.assertEqual(FOOD_1_consumer_unit.facility.id, states[IDX_FACILITY_ID]) @@ -228,7 +229,7 @@ def test_consumer_unit_dynamics_sampler(self): env.reset() env.step(None) - states = consumer_nodes[env.frame_index:consumer_node_index:features].flatten().astype(np.int) + states = consumer_nodes[env.frame_index : consumer_node_index : features].flatten().astype(np.int) # Nothing happened at tick 0, so most states will be 0 self.assertEqual(0, states[IDX_PURCHASED]) @@ -251,7 +252,7 @@ def test_consumer_unit_dynamics_sampler(self): action_with_zero = ConsumerAction(FOOD_1_consumer_unit.id, FOOD_1_ID, Store_001.id, 0, "train") env.step([action_with_zero]) - states = consumer_nodes[env.frame_index:consumer_node_index:features].flatten().astype(np.int) + states = consumer_nodes[env.frame_index : consumer_node_index : features].flatten().astype(np.int) # Nothing happened at tick 0, at the action will be recorded self.assertEqual(action_with_zero.sku_id, states[IDX_SKU_ID]) @@ -267,7 +268,7 @@ def test_consumer_unit_dynamics_sampler(self): self.assertEqual(action.quantity, FOOD_1_consumer_unit._purchased) self.assertEqual(0, FOOD_1_consumer_unit._received) - states = consumer_nodes[env.frame_index:consumer_node_index:features].flatten().astype(np.int) + states = consumer_nodes[env.frame_index : consumer_node_index : features].flatten().astype(np.int) # action field should be recorded self.assertEqual(action.sku_id, states[IDX_SKU_ID]) @@ -278,5 +279,5 @@ def test_consumer_unit_dynamics_sampler(self): self.assertEqual(0, states[IDX_RECEIVED]) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/tests/supply_chain/test_distribution_unit.py b/tests/supply_chain/test_distribution_unit.py index 2e1fca19d..dd3e7b9ac 100644 --- a/tests/supply_chain/test_distribution_unit.py +++ b/tests/supply_chain/test_distribution_unit.py @@ -2,26 +2,27 @@ # Licensed under the MIT license. import unittest + import numpy as np -from maro.simulator.scenarios.supply_chain import FacilityBase, ConsumerAction +from maro.simulator.scenarios.supply_chain import ConsumerAction, FacilityBase from maro.simulator.scenarios.supply_chain.business_engine import SupplyChainBusinessEngine from maro.simulator.scenarios.supply_chain.order import Order -from tests.supply_chain.common import build_env, get_product_dict_from_storage, SKU1_ID, SKU3_ID +from tests.supply_chain.common import SKU1_ID, SKU3_ID, build_env, get_product_dict_from_storage class MyTestCase(unittest.TestCase): """ - Distribution unit test: - - . initial state - . place order and dispatch with available vehicle - . place order but has no available vehicle - . if arrive at destination within special vlt - . if support for 0-vlt - . try_unload if target storage cannot take all - """ + Distribution unit test: + + . initial state + . place order and dispatch with available vehicle + . place order but has no available vehicle + . if arrive at destination within special vlt + . if support for 0-vlt + . try_unload if target storage cannot take all + """ def test_distribution_unit_initial_state(self) -> None: """Test initial state of the DistributionUnit of Supplier_SKU3.""" @@ -53,7 +54,7 @@ def test_distribution_unit_dispatch_order(self) -> None: warehouse_1 = be.world._get_facility_by_name("Warehouse_001") distribution_unit = supplier_3.distribution - consumer_unit = warehouse_1.products[SKU3_ID].consumer + warehouse_1.products[SKU3_ID].consumer order_1 = Order( src_facility=supplier_3, @@ -166,7 +167,7 @@ def test_distribution_unit_cannot_unload_at_destination(self) -> None: supplier_3 = be.world._get_facility_by_name("Supplier_SKU3") warehouse_1 = be.world._get_facility_by_name("Warehouse_001") distribution_unit = supplier_3.distribution - consumer_unit = warehouse_1.products[SKU3_ID].consumer + warehouse_1.products[SKU3_ID].consumer warehouse_storage_unit = warehouse_1.storage env.step(None) @@ -188,7 +189,9 @@ def test_distribution_unit_cannot_unload_at_destination(self) -> None: while env.tick <= expected_tick: # Check the inventory level in target storage quantity = get_product_dict_from_storage( - env, env.frame_index, warehouse_storage_unit.data_model_index + env, + env.frame_index, + warehouse_storage_unit.data_model_index, )[SKU3_ID] self.assertEqual(10, quantity) @@ -207,7 +210,9 @@ def test_distribution_unit_cannot_unload_at_destination(self) -> None: while not is_done: # Check the inventory level in target storage quantity = get_product_dict_from_storage( - env, env.frame_index, warehouse_storage_unit.data_model_index + env, + env.frame_index, + warehouse_storage_unit.data_model_index, )[SKU3_ID] self.assertEqual(10 + 70, quantity) @@ -222,5 +227,5 @@ def test_distribution_unit_cannot_unload_at_destination(self) -> None: _, _, is_done = env.step(None) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/tests/supply_chain/test_env_reset.py b/tests/supply_chain/test_env_reset.py index 1cbffa1f5..2a5ab982d 100644 --- a/tests/supply_chain/test_env_reset.py +++ b/tests/supply_chain/test_env_reset.py @@ -1,5 +1,6 @@ # Copyright (c) Microsoft Corporation. -# Licensed under the MIT license +# Licensed under the MIT license. + import random import unittest from collections import defaultdict @@ -7,21 +8,21 @@ import numpy as np -from maro.simulator.scenarios.supply_chain import FacilityBase, ConsumerAction, ManufactureAction, StorageUnit +from maro.simulator.scenarios.supply_chain import ConsumerAction, FacilityBase, ManufactureAction, StorageUnit from maro.simulator.scenarios.supply_chain.business_engine import SupplyChainBusinessEngine from maro.simulator.scenarios.supply_chain.order import Order -from tests.supply_chain.common import build_env, SKU3_ID, FOOD_1_ID, get_product_dict_from_storage +from tests.supply_chain.common import FOOD_1_ID, SKU3_ID, build_env, get_product_dict_from_storage class MyTestCase(unittest.TestCase): """ - . consumer unit test - . distribution unit test - . manufacture unit test - . seller unit test - . storage unit test - """ + . consumer unit test + . distribution unit test + . manufacture unit test + . seller unit test + . storage unit test + """ def test_consumer_unit_reset(self) -> None: """Test whether reset updates the consumer unit completely""" @@ -37,8 +38,17 @@ def test_consumer_unit_reset(self) -> None: consumer_node_index = sku3_consumer_unit.data_model_index - features = ("id", "facility_id", "sku_id", "order_base_cost", "purchased", "received", "order_product_cost", - "latest_consumptions", "in_transit_quantity") + features = ( + "id", + "facility_id", + "sku_id", + "order_base_cost", + "purchased", + "received", + "order_product_cost", + "latest_consumptions", + "in_transit_quantity", + ) # ##################################### Before reset ##################################### consumer_nodes = env.snapshot_list["consumer"] @@ -107,7 +117,8 @@ def test_distribution_unit_reset(self) -> None: quantity=10, vehicle_type="train", creation_tick=env.tick, - expected_finish_tick=env.tick + 7, ) + expected_finish_tick=env.tick + 7, + ) # There are 2 "train" in total, and 1 left after scheduling this order. distribution_unit.place_order(order_1) @@ -247,7 +258,12 @@ def test_manufacture_unit_reset(self) -> None: manufacture_nodes = env.snapshot_list["manufacture"] manufacture_features = ( - "id", "facility_id", "start_manufacture_quantity", "sku_id", "in_pipeline_quantity", "finished_quantity", + "id", + "facility_id", + "start_manufacture_quantity", + "sku_id", + "in_pipeline_quantity", + "finished_quantity", "product_unit_id", ) @@ -255,8 +271,10 @@ def test_manufacture_unit_reset(self) -> None: env.step(None) - capacities = storage_nodes[env.frame_index:sku3_storage_index:"capacity"].flatten().astype(np.int) - remaining_spaces = storage_nodes[env.frame_index:sku3_storage_index:"remaining_space"].flatten().astype(np.int) + capacities = storage_nodes[env.frame_index : sku3_storage_index : "capacity"].flatten().astype(np.int) + remaining_spaces = ( + storage_nodes[env.frame_index : sku3_storage_index : "remaining_space"].flatten().astype(np.int) + ) # there should be 80 units been taken at the beginning according to the config file. # so remaining space should be 20 @@ -307,8 +325,10 @@ def test_manufacture_unit_reset(self) -> None: storage_nodes = env.snapshot_list["storage"] manufacture_nodes = env.snapshot_list["manufacture"] - capacities = storage_nodes[env.frame_index:sku3_storage_index:"capacity"].flatten().astype(np.int) - remaining_spaces = storage_nodes[env.frame_index:sku3_storage_index:"remaining_space"].flatten().astype(np.int) + capacities = storage_nodes[env.frame_index : sku3_storage_index : "capacity"].flatten().astype(np.int) + remaining_spaces = ( + storage_nodes[env.frame_index : sku3_storage_index : "remaining_space"].flatten().astype(np.int) + ) # there should be 80 units been taken at the beginning according to the config file. # so remaining space should be 20 @@ -347,7 +367,7 @@ def test_manufacture_unit_reset(self) -> None: def test_seller_unit_dynamics_sampler(self): """Tested the store_001 Interaction between seller unit and dynamics csv data. - The data file of this test is test_case_ 04.csv""" + The data file of this test is test_case_ 04.csv""" env = build_env("case_04", 600) be = env.business_engine assert isinstance(be, SupplyChainBusinessEngine) @@ -359,7 +379,16 @@ def test_seller_unit_dynamics_sampler(self): seller_node_index = seller_unit.data_model_index seller_nodes = env.snapshot_list["seller"] - features = ("sold", "demand", "total_sold", "id", "total_demand", "backlog_ratio", "facility_id", "product_unit_id",) + features = ( + "sold", + "demand", + "total_sold", + "id", + "total_demand", + "backlog_ratio", + "facility_id", + "product_unit_id", + ) # ##################################### Before reset ##################################### self.assertEqual(20, seller_unit.sku_id) @@ -422,7 +451,7 @@ def test_storage_unit_reset(self) -> None: storage_unit: StorageUnit = supplier_3.storage storage_node_index = storage_unit.data_model_index storage_nodes = env.snapshot_list["storage"] - features = ("id", "facility_id",) + features = ("id", "facility_id") # ##################################### Before reset ##################################### @@ -470,5 +499,5 @@ def test_storage_unit_reset(self) -> None: self.assertEqual(list(env_metric_1[i].values()), list(env_metric_2[i].values())) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/tests/supply_chain/test_manufacture_unit.py b/tests/supply_chain/test_manufacture_unit.py index 8e7bbff70..7f6e1a5f4 100644 --- a/tests/supply_chain/test_manufacture_unit.py +++ b/tests/supply_chain/test_manufacture_unit.py @@ -2,12 +2,13 @@ # Licensed under the MIT license. import unittest + import numpy as np from maro.simulator.scenarios.supply_chain import FacilityBase, ManufactureAction from maro.simulator.scenarios.supply_chain.business_engine import SupplyChainBusinessEngine -from tests.supply_chain.common import build_env, get_product_dict_from_storage, SKU1_ID, SKU2_ID, SKU3_ID, SKU4_ID +from tests.supply_chain.common import SKU1_ID, SKU2_ID, SKU3_ID, SKU4_ID, build_env, get_product_dict_from_storage class MyTestCase(unittest.TestCase): @@ -43,7 +44,10 @@ def test_manufacture_meet_storage_limitation(self) -> None: manufacture_nodes = env.snapshot_list["manufacture"] manufacture_features = ( - "id", "facility_id", "start_manufacture_quantity", "sku_id", + "id", + "facility_id", + "start_manufacture_quantity", + "sku_id", ) IDX_ID, IDX_FACILITY_ID, IDX_START_MANUFACTURE_QUANTITY, IDX_SKU_ID = 0, 1, 2, 3 @@ -52,8 +56,10 @@ def test_manufacture_meet_storage_limitation(self) -> None: # tick 0 passed, no product manufacturing. env.step(None) - capacities = storage_nodes[env.frame_index:sku3_storage_index:"capacity"].flatten().astype(np.int) - remaining_spaces = storage_nodes[env.frame_index:sku3_storage_index:"remaining_space"].flatten().astype(np.int) + capacities = storage_nodes[env.frame_index : sku3_storage_index : "capacity"].flatten().astype(np.int) + remaining_spaces = ( + storage_nodes[env.frame_index : sku3_storage_index : "remaining_space"].flatten().astype(np.int) + ) # there should be 80 units been taken at the beginning according to the config file. # so remaining space should be 20 @@ -84,9 +90,11 @@ def test_manufacture_meet_storage_limitation(self) -> None: env.step(None) start_frame = env.business_engine.frame_index(start_tick) - states = manufacture_nodes[ - start_frame:manufacture_sku3_unit.data_model_index:manufacture_features - ].flatten().astype(np.int) + states = ( + manufacture_nodes[start_frame : manufacture_sku3_unit.data_model_index : manufacture_features] + .flatten() + .astype(np.int) + ) # Sku3 produce rate is 1 per tick, so start_manufacture_quantity should be 1. self.assertEqual(1, states[IDX_START_MANUFACTURE_QUANTITY]) @@ -107,9 +115,11 @@ def test_manufacture_meet_storage_limitation(self) -> None: # leave the action as None will cause manufacture unit stop manufacturing. env.step(None) - states = manufacture_nodes[ - env.frame_index:manufacture_sku3_unit.data_model_index:manufacture_features - ].flatten().astype(np.int) + states = ( + manufacture_nodes[env.frame_index : manufacture_sku3_unit.data_model_index : manufacture_features] + .flatten() + .astype(np.int) + ) # so start_manufacture_quantity should be 0 self.assertEqual(0, states[IDX_START_MANUFACTURE_QUANTITY]) @@ -131,9 +141,11 @@ def test_manufacture_meet_storage_limitation(self) -> None: env.step(None) start_frame = env.business_engine.frame_index(start_tick) - states = manufacture_nodes[ - start_frame:manufacture_sku3_unit.data_model_index:manufacture_features - ].flatten().astype(np.int) + states = ( + manufacture_nodes[start_frame : manufacture_sku3_unit.data_model_index : manufacture_features] + .flatten() + .astype(np.int) + ) # so start_manufacture_number should be 19 instead 20 self.assertEqual(19, states[IDX_START_MANUFACTURE_QUANTITY]) @@ -151,8 +163,8 @@ def test_manufacture_meet_storage_limitation(self) -> None: def test_manufacture_meet_source_lack(self) -> None: """Test sku4 manufacturing. -- Supplier_SKU4. - This sku supplier does not have enough source material at the beginning, - so it cannot produce anything without consumer purchase.""" + This sku supplier does not have enough source material at the beginning, + so it cannot produce anything without consumer purchase.""" env = build_env("case_01", 100) be = env.business_engine assert isinstance(be, SupplyChainBusinessEngine) @@ -165,7 +177,10 @@ def test_manufacture_meet_source_lack(self) -> None: manufacture_nodes = env.snapshot_list["manufacture"] manufacture_features = ( - "id", "facility_id", "start_manufacture_quantity", "sku_id" + "id", + "facility_id", + "start_manufacture_quantity", + "sku_id", ) IDX_ID, IDX_FACILITY_ID, IDX_START_MANUFACTURE_QUANTITY, IDX_SKU_ID = 0, 1, 2, 3 @@ -175,17 +190,21 @@ def test_manufacture_meet_source_lack(self) -> None: env.step(None) # capacity is same as configured. - capacities = storage_nodes[env.frame_index:sku4_storage_index:"capacity"].flatten().astype(np.int) + capacities = storage_nodes[env.frame_index : sku4_storage_index : "capacity"].flatten().astype(np.int) self.assertEqual(200, capacities.sum()) # remaining space should be capacity 200 - (sku4 50 + sku2 0) - remaining_spaces = storage_nodes[env.frame_index:sku4_storage_index:"remaining_space"].flatten().astype(np.int) + remaining_spaces = ( + storage_nodes[env.frame_index : sku4_storage_index : "remaining_space"].flatten().astype(np.int) + ) self.assertEqual(200 - (50 + 0 + 50), remaining_spaces.sum()) # no manufacture number as we have not pass any action - manufacture_states = manufacture_nodes[ - env.frame_index:manufacture_sku4_unit.data_model_index:manufacture_features - ].flatten().astype(np.int) + manufacture_states = ( + manufacture_nodes[env.frame_index : manufacture_sku4_unit.data_model_index : manufacture_features] + .flatten() + .astype(np.int) + ) # manufacture_quantity should be 0 self.assertEqual(0, manufacture_states[IDX_START_MANUFACTURE_QUANTITY]) @@ -209,9 +228,11 @@ def test_manufacture_meet_source_lack(self) -> None: # push to the end, the storage should not br changed, no matter what production rate we give it. _, _, is_done = env.step([ManufactureAction(manufacture_sku4_unit.id, 10)]) - manufacture_states = manufacture_nodes[ - env.frame_index:manufacture_sku4_unit.data_model_index:manufacture_features - ].flatten().astype(np.int) + manufacture_states = ( + manufacture_nodes[env.frame_index : manufacture_sku4_unit.data_model_index : manufacture_features] + .flatten() + .astype(np.int) + ) # manufacture_quantity should be 0 self.assertEqual(0, manufacture_states[IDX_START_MANUFACTURE_QUANTITY]) @@ -243,7 +264,10 @@ def test_manufacture_meet_avg_storage_limitation(self) -> None: manufacture_nodes = env.snapshot_list["manufacture"] manufacture_features = ( - "id", "facility_id", "start_manufacture_quantity", "sku_id" + "id", + "facility_id", + "start_manufacture_quantity", + "sku_id", ) IDX_ID, IDX_FACILITY_ID, IDX_START_MANUFACTURE_QUANTITY, IDX_SKU_ID = 0, 1, 2, 3 @@ -264,18 +288,22 @@ def test_manufacture_meet_avg_storage_limitation(self) -> None: env.step(None) start_frame = env.business_engine.frame_index(start_tick) - manufacture_states = manufacture_nodes[ - start_frame:manufacture_sku1_unit.data_model_index:manufacture_features - ].flatten().astype(np.int) + manufacture_states = ( + manufacture_nodes[start_frame : manufacture_sku1_unit.data_model_index : manufacture_features] + .flatten() + .astype(np.int) + ) # we can produce 4 sku1, as it will meet storage avg limitation per sku. 4 = 200//2 - 96 self.assertEqual(200 // 2 - 96, manufacture_states[IDX_START_MANUFACTURE_QUANTITY]) # so storage remaining space should be 200 - ((96 + 4) + (100 - 4 * 2 sku3/sku1)) expected_frame = env.business_engine.frame_index(expected_tick) - remaining_spaces = storage_nodes[ - expected_frame:manufacture_sku1_unit.data_model_index:"remaining_space" - ].flatten().astype(np.int) + remaining_spaces = ( + storage_nodes[expected_frame : manufacture_sku1_unit.data_model_index : "remaining_space"] + .flatten() + .astype(np.int) + ) self.assertEqual(200 - ((96 + 4) + (100 - 4 * 2)), remaining_spaces.sum()) product_dict = get_product_dict_from_storage(env, env.frame_index, sku1_storage_index) @@ -296,17 +324,19 @@ def test_manufacture_meet_avg_storage_limitation(self) -> None: while not is_done: _, _, is_done = env.step(None) - manufacture_states = manufacture_nodes[ - env.frame_index:manufacture_sku1_unit.data_model_index:manufacture_features - ].flatten().astype(np.int) + manufacture_states = ( + manufacture_nodes[env.frame_index : manufacture_sku1_unit.data_model_index : manufacture_features] + .flatten() + .astype(np.int) + ) # but manufacture number is 0 self.assertEqual(0, manufacture_states[IDX_START_MANUFACTURE_QUANTITY]) # so storage remaining space should be 200 - ((96 + 4) + (100 - 4*2)) - remaining_spaces = storage_nodes[ - env.frame_index:sku1_storage_index:"remaining_space" - ].flatten().astype(np.int) + remaining_spaces = ( + storage_nodes[env.frame_index : sku1_storage_index : "remaining_space"].flatten().astype(np.int) + ) self.assertEqual(200 - ((96 + 4) + (100 - 4 * 2)), remaining_spaces.sum()) product_dict = get_product_dict_from_storage(env, env.frame_index, sku1_storage_index) @@ -331,7 +361,10 @@ def test_simple_manufacture_without_using_source(self) -> None: manufacture_nodes = env.snapshot_list["manufacture"] manufacture_features = ( - "id", "facility_id", "start_manufacture_quantity", "sku_id", + "id", + "facility_id", + "start_manufacture_quantity", + "sku_id", ) IDX_ID, IDX_FACILITY_ID, IDX_START_MANUFACTURE_QUANTITY, IDX_SKU_ID = 0, 1, 2, 3 @@ -340,8 +373,10 @@ def test_simple_manufacture_without_using_source(self) -> None: # tick 0 passed, no product manufacturing. env.step(None) - capacities = storage_nodes[env.frame_index:sku2_storage_index:"capacity"].flatten().astype(np.int) - remaining_spaces = storage_nodes[env.frame_index:sku2_storage_index:"remaining_space"].flatten().astype(np.int) + capacities = storage_nodes[env.frame_index : sku2_storage_index : "capacity"].flatten().astype(np.int) + remaining_spaces = ( + storage_nodes[env.frame_index : sku2_storage_index : "remaining_space"].flatten().astype(np.int) + ) # there should be 50 + 50 units been taken at the beginning according to the config file. # so remaining space should be 200 - (50 + 50) = 100 @@ -373,9 +408,11 @@ def test_simple_manufacture_without_using_source(self) -> None: env.step(None) start_frame = env.business_engine.frame_index(start_tick) - states = manufacture_nodes[ - start_frame:manufacture_sku2_unit.data_model_index:manufacture_features - ].flatten().astype(np.int) + states = ( + manufacture_nodes[start_frame : manufacture_sku2_unit.data_model_index : manufacture_features] + .flatten() + .astype(np.int) + ) # Sku2 produce rate is 1 per tick, and the output per lot is 2, so manufacture_quantity should be 2. self.assertEqual(1 * 2, states[IDX_START_MANUFACTURE_QUANTITY]) @@ -398,9 +435,11 @@ def test_simple_manufacture_without_using_source(self) -> None: # leave the action as None will keep the manufacture rate as 0, so at to stop manufacturing. env.step(None) - states = manufacture_nodes[ - env.frame_index:manufacture_sku2_unit.data_model_index:manufacture_features - ].flatten().astype(np.int) + states = ( + manufacture_nodes[env.frame_index : manufacture_sku2_unit.data_model_index : manufacture_features] + .flatten() + .astype(np.int) + ) # so manufacture_quantity should be 0 self.assertEqual(0, states[IDX_START_MANUFACTURE_QUANTITY]) @@ -411,5 +450,5 @@ def test_simple_manufacture_without_using_source(self) -> None: self.assertEqual(50 + 1 * 2, product_dict[SKU2_ID]) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/tests/supply_chain/test_read_data.py b/tests/supply_chain/test_read_data.py index d15b8108e..c7a0d1c74 100644 --- a/tests/supply_chain/test_read_data.py +++ b/tests/supply_chain/test_read_data.py @@ -2,30 +2,41 @@ # Licensed under the MIT license. import unittest + import numpy as np from maro.simulator.scenarios.supply_chain import FacilityBase, StorageUnit from maro.simulator.scenarios.supply_chain.business_engine import SupplyChainBusinessEngine from maro.simulator.scenarios.supply_chain.sku_dynamics_sampler import ( - OneTimeSkuPriceDemandSampler, OneTimeSkuDynamicsSampler, StreamSkuPriceDemandSampler, DataFileDemandSampler + DataFileDemandSampler, + OneTimeSkuDynamicsSampler, + OneTimeSkuPriceDemandSampler, + StreamSkuPriceDemandSampler, ) from tests.supply_chain.common import ( - build_env, get_product_dict_from_storage, SKU1_ID, SKU2_ID, SKU3_ID, SKU4_ID, FOOD_1_ID, HOBBY_1_ID + FOOD_1_ID, + HOBBY_1_ID, + SKU1_ID, + SKU2_ID, + SKU3_ID, + SKU4_ID, + build_env, + get_product_dict_from_storage, ) class MyTestCase(unittest.TestCase): """ - read date test: - . initial state - . with snapshot - . with storage unit - . with dynamics sampler - . OneTimeSkuPriceDemandSampler - . StreamSkuPriceDemandSampler - . DataFileDemandSampler - """ + read date test: + . initial state + . with snapshot + . with storage unit + . with dynamics sampler + . OneTimeSkuPriceDemandSampler + . StreamSkuPriceDemandSampler + . DataFileDemandSampler + """ def test_read_the_supplier_from_snapshot_sku_data(self) -> None: """This test mainly tests reading supplier from snapshot SKU data""" @@ -45,8 +56,10 @@ def test_read_the_supplier_from_snapshot_sku_data(self) -> None: # tick 0 passed, no product manufacturing, verified in above case, pass checking it here. - capacities = storage_nodes[env.frame_index:sku1_storage_index:"capacity"].flatten().astype(np.int) - remaining_spaces = storage_nodes[env.frame_index:sku1_storage_index:"remaining_space"].flatten().astype(np.int) + capacities = storage_nodes[env.frame_index : sku1_storage_index : "capacity"].flatten().astype(np.int) + remaining_spaces = ( + storage_nodes[env.frame_index : sku1_storage_index : "remaining_space"].flatten().astype(np.int) + ) # capacity is 200 by config self.assertEqual(200, capacities.sum()) @@ -73,8 +86,10 @@ def test_read_the_supplier_from_snapshot_sku_data(self) -> None: # tick 0 passed, no product manufacturing. - capacities = storage_nodes[env.frame_index:sku2_storage_index:"capacity"].flatten().astype(np.int) - remaining_spaces = storage_nodes[env.frame_index:sku2_storage_index:"remaining_space"].flatten().astype(np.int) + capacities = storage_nodes[env.frame_index : sku2_storage_index : "capacity"].flatten().astype(np.int) + remaining_spaces = ( + storage_nodes[env.frame_index : sku2_storage_index : "remaining_space"].flatten().astype(np.int) + ) # there should be 50 + 50 units been taken at the beginning according to the config file. # so remaining space should be 200 - (50 + 50) = 100 @@ -102,8 +117,10 @@ def test_read_the_supplier_from_snapshot_sku_data(self) -> None: # tick 0 passed, no product manufacturing. - capacities = storage_nodes[env.frame_index:sku3_storage_index:"capacity"].flatten().astype(np.int) - remaining_spaces = storage_nodes[env.frame_index:sku3_storage_index:"remaining_space"].flatten().astype(np.int) + capacities = storage_nodes[env.frame_index : sku3_storage_index : "capacity"].flatten().astype(np.int) + remaining_spaces = ( + storage_nodes[env.frame_index : sku3_storage_index : "remaining_space"].flatten().astype(np.int) + ) # there should be 80 units been taken at the beginning according to the config file. # so remaining space should be 20 @@ -130,24 +147,31 @@ def test_read_the_supplier_from_snapshot_sku_data(self) -> None: manufacture_nodes = env.snapshot_list["manufacture"] manufacture_features = ( - "id", "facility_id", "start_manufacture_quantity", "sku_id" + "id", + "facility_id", + "start_manufacture_quantity", + "sku_id", ) IDX_ID, IDX_FACILITY_ID, IDX_START_MANUFACTURE_QUANTITY, IDX_SKU_ID = 0, 1, 2, 3 # tick 0 passed, no product manufacturing. # capacity is same as configured. - capacities = storage_nodes[env.frame_index:sku4_storage_index:"capacity"].flatten().astype(np.int) + capacities = storage_nodes[env.frame_index : sku4_storage_index : "capacity"].flatten().astype(np.int) self.assertEqual(200, capacities.sum()) # remaining space should be capacity 200 - (sku4 50 + sku2 0) - remaining_spaces = storage_nodes[env.frame_index:sku4_storage_index:"remaining_space"].flatten().astype(np.int) + remaining_spaces = ( + storage_nodes[env.frame_index : sku4_storage_index : "remaining_space"].flatten().astype(np.int) + ) self.assertEqual(200 - (50 + 0 + 50), remaining_spaces.sum()) # no manufacture number as we have not pass any action - manufacture_states = manufacture_nodes[ - env.frame_index:manufacture_sku4_unit.data_model_index:manufacture_features - ].flatten().astype(np.int) + manufacture_states = ( + manufacture_nodes[env.frame_index : manufacture_sku4_unit.data_model_index : manufacture_features] + .flatten() + .astype(np.int) + ) # manufacture_quantity should be 0 self.assertEqual(0, manufacture_states[IDX_START_MANUFACTURE_QUANTITY]) @@ -262,7 +286,7 @@ def test_read_the_supplier_from_storage_unit_sku_data(self) -> None: def test_init_sku_dynamics_one_time_sku_price_demand_sampler(self): """Test the reading of store_001 SKU information of OneTimeSkuPriceDemandSampler. - The data file of this test is test_case_ 04.csv""" + The data file of this test is test_case_ 04.csv""" env = build_env("case_04", 100) be = env.business_engine assert isinstance(be, SupplyChainBusinessEngine) @@ -275,16 +299,16 @@ def test_init_sku_dynamics_one_time_sku_price_demand_sampler(self): # Store_001 has two SKUs. self.assertEqual(2, len(sku._cache[0])) # The price of the first item of SKU with product ID 20 is 43.11954545 and the demand is 10. - self.assertEqual(43.0, sku._cache[0][FOOD_1_ID]['Price']) - self.assertEqual(10, sku._cache[0][FOOD_1_ID]['Demand']) + self.assertEqual(43.0, sku._cache[0][FOOD_1_ID]["Price"]) + self.assertEqual(10, sku._cache[0][FOOD_1_ID]["Demand"]) # The price of the first item of SKU with product ID 30 is 28.32 and the demand is 80. - self.assertEqual(28.32, sku._cache[0][HOBBY_1_ID]['Price']) - self.assertEqual(100, sku._cache[0][HOBBY_1_ID]['Demand']) + self.assertEqual(28.32, sku._cache[0][HOBBY_1_ID]["Price"]) + self.assertEqual(100, sku._cache[0][HOBBY_1_ID]["Demand"]) # The price of the second item of SKU with product ID 20 is 43.63277778 and the demand is 41. - self.assertEqual(43.1, sku._cache[1][FOOD_1_ID]['Price']) - self.assertEqual(20, sku._cache[1][FOOD_1_ID]['Demand']) + self.assertEqual(43.1, sku._cache[1][FOOD_1_ID]["Price"]) + self.assertEqual(20, sku._cache[1][FOOD_1_ID]["Demand"]) # Test sample_price() method of onetimeskupricedemandsampler. product_FOOD_1_price = sku.sample_price(4, FOOD_1_ID) @@ -301,7 +325,7 @@ def test_init_sku_dynamics_one_time_sku_price_demand_sampler(self): def test_init_sku_dynamics_stream_sku_price_demand_sampler(self): """Test the reading of store_001 SKU information of StreamSkuPriceDemandSampler. - The data file of this test is test_case_04.csv""" + The data file of this test is test_case_04.csv""" env = build_env("case_04", 600) be = env.business_engine assert isinstance(be, SupplyChainBusinessEngine) @@ -328,7 +352,7 @@ def test_init_sku_dynamics_stream_sku_price_demand_sampler(self): def test_init_sku_dynamics_data_file_demand_sampler(self): """Test the reading of store_001 SKU information of DataFileDemandSampler. - The data file of this test is test_case_04.csv""" + The data file of this test is test_case_04.csv""" env = build_env("case_04", 600) be = env.business_engine assert isinstance(be, SupplyChainBusinessEngine) @@ -342,7 +366,7 @@ def test_init_sku_dynamics_data_file_demand_sampler(self): def test_storage_unit_dynamics_data_file_demand_sampler(self): """Under the DataFileDemandSampler class,test the store between the storage unit and the dynamics CSV data - interaction. The data file of this test is test_case_04.csv """ + interaction. The data file of this test is test_case_04.csv""" env = build_env("case_04", 600) be = env.business_engine assert isinstance(be, SupplyChainBusinessEngine) @@ -372,13 +396,18 @@ def test_storage_unit_dynamics_data_file_demand_sampler(self): self.assertEqual(4900, init_product_dict[HOBBY_1_ID]) # ######################### Capacity ########################### - capacities = storage_nodes[env.frame_index:storage_node_index:"capacity"].flatten().astype(np.int) + capacities = storage_nodes[env.frame_index : storage_node_index : "capacity"].flatten().astype(np.int) self.assertEqual(80000, storage_unit.capacity) self.assertEqual(80000, capacities.sum()) # ######################### Remaining Space ########################### - init_remaining_spaces = storage_nodes[env.frame_index:storage_node_index:"remaining_space"].flatten().astype( - np.int) + init_remaining_spaces = ( + storage_nodes[env.frame_index : storage_node_index : "remaining_space"] + .flatten() + .astype( + np.int, + ) + ) self.assertEqual(80000 - (10000 - 10) - (5000 - 100), init_remaining_spaces.sum()) # ######################### tick 1 ########################### env.step(None) @@ -404,18 +433,22 @@ def test_storage_unit_dynamics_data_file_demand_sampler(self): self.assertEqual(5000 - (100 + 200 + 300 + 400 + 500), product_dict[HOBBY_1_ID]) # ######################### Capacity ########################### - capacities = storage_nodes[env.frame_index:storage_node_index:"capacity"].flatten().astype(np.int) + capacities = storage_nodes[env.frame_index : storage_node_index : "capacity"].flatten().astype(np.int) self.assertEqual(80000, storage_unit.capacity) self.assertEqual(80000, capacities.sum()) # ######################### Remaining Space ########################### - init_remaining_spaces = storage_nodes[ - env.frame_index:storage_node_index:"remaining_space" - ].flatten().astype(np.int) - self.assertEqual(80000 - (10000 - (10 + 20 + 30 + 40 + 50)) - (5000 - (100 + 200 + 300 + 400 + 500)), - storage_unit.remaining_space) - self.assertEqual(80000 - (10000 - (10 + 20 + 30 + 40 + 50)) - (5000 - (100 + 200 + 300 + 400 + 500)), - init_remaining_spaces.sum()) + init_remaining_spaces = ( + storage_nodes[env.frame_index : storage_node_index : "remaining_space"].flatten().astype(np.int) + ) + self.assertEqual( + 80000 - (10000 - (10 + 20 + 30 + 40 + 50)) - (5000 - (100 + 200 + 300 + 400 + 500)), + storage_unit.remaining_space, + ) + self.assertEqual( + 80000 - (10000 - (10 + 20 + 30 + 40 + 50)) - (5000 - (100 + 200 + 300 + 400 + 500)), + init_remaining_spaces.sum(), + ) # Should change after reset env.reset() @@ -432,17 +465,17 @@ def test_storage_unit_dynamics_data_file_demand_sampler(self): self.assertEqual(5000 - 100, product_dict[HOBBY_1_ID]) # ######################### Capacity ########################### - capacities = storage_nodes[env.frame_index:storage_node_index:"capacity"].flatten().astype(np.int) + capacities = storage_nodes[env.frame_index : storage_node_index : "capacity"].flatten().astype(np.int) self.assertEqual(80000, storage_unit.capacity) self.assertEqual(80000, capacities.sum()) # ######################### Remaining Space ########################### - init_remaining_spaces = storage_nodes[ - env.frame_index:storage_node_index:"remaining_space" - ].flatten().astype(np.int) + init_remaining_spaces = ( + storage_nodes[env.frame_index : storage_node_index : "remaining_space"].flatten().astype(np.int) + ) self.assertEqual(80000 - (10000 - 10) - (5000 - 100), storage_unit.remaining_space) self.assertEqual(80000 - (10000 - 10) - (5000 - 100), init_remaining_spaces.sum()) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/tests/supply_chain/test_seller_unit.py b/tests/supply_chain/test_seller_unit.py index bd139cc1d..3a78e1a52 100644 --- a/tests/supply_chain/test_seller_unit.py +++ b/tests/supply_chain/test_seller_unit.py @@ -2,22 +2,23 @@ # Licensed under the MIT license. import unittest + import numpy as np from maro.simulator.scenarios.supply_chain import FacilityBase from maro.simulator.scenarios.supply_chain.business_engine import SupplyChainBusinessEngine -from tests.supply_chain.common import build_env, SKU3_ID, FOOD_1_ID +from tests.supply_chain.common import FOOD_1_ID, SKU3_ID, build_env class MyTestCase(unittest.TestCase): """ - Seller unit test: - . initial state - . with a customized seller unit - . with built-in one - . with dynamics sampler - """ + Seller unit test: + . initial state + . with a customized seller unit + . with built-in one + . with dynamics sampler + """ def test_seller_unit_initial_states(self) -> None: """Test the initial states of sku3's SellerUnit of Retailer_001.""" @@ -93,7 +94,7 @@ def test_seller_unit_demand_states(self) -> None: self.assertEqual(actual_sold, seller_unit._total_sold) self.assertEqual(actual_sold, seller_unit.data_model.total_sold) - states = seller_nodes[env.frame_index:seller_node_index:features].flatten().astype(np.int) + states = seller_nodes[env.frame_index : seller_node_index : features].flatten().astype(np.int) self.assertEqual(actual_sold, states[IDX_SOLD]) self.assertEqual(seller_unit._demand, states[IDX_DEMAND]) @@ -113,7 +114,7 @@ def test_seller_unit_demand_states(self) -> None: self.assertEqual(actual_sold + actual_sold_2, seller_unit._total_sold) self.assertEqual(actual_sold + actual_sold_2, seller_unit.data_model.total_sold) - states = seller_nodes[env.frame_index:seller_node_index:features].flatten().astype(np.int) + states = seller_nodes[env.frame_index : seller_node_index : features].flatten().astype(np.int) self.assertEqual(actual_sold_2, states[IDX_SOLD]) self.assertEqual(seller_unit._demand, states[IDX_DEMAND]) @@ -156,22 +157,22 @@ def test_seller_unit_customized(self) -> None: while not is_done: _, _, is_done = env.step(None) - states = seller_nodes[:seller_node_index:features[IDX_DEMAND]].flatten().astype(np.int) + states = seller_nodes[: seller_node_index : features[IDX_DEMAND]].flatten().astype(np.int) # Check demand history, it should be same as tick self.assertListEqual([i for i in range(100)], list(states)) # Check sold states. Since the init stock 10 = 1 + 2 + 3 + 4, sold value should be 0 after tick 4. - states = seller_nodes[:seller_node_index:features[IDX_SOLD]].flatten().astype(np.int) + states = seller_nodes[: seller_node_index : features[IDX_SOLD]].flatten().astype(np.int) self.assertListEqual([0, 1, 2, 3, 4] + [0] * 95, list(states)) # Check total sold, should be: 0, 0 + 1, 0 + 1 + 2, 0 + 1 + 2 + 3, 0 + 1 + 2 + 3 + 4, 10, 10, ... - states = seller_nodes[:seller_node_index:features[IDX_TOTAL_SOLD]].flatten().astype(np.int) + states = seller_nodes[: seller_node_index : features[IDX_TOTAL_SOLD]].flatten().astype(np.int) self.assertListEqual([0, 1, 3, 6, 10] + [10] * 95, list(states)) def test_seller_unit_dynamics_sampler(self): """Tested the store_001 Interaction between seller unit and dynamics csv data. - The data file of this test is test_case_ 04.csv""" + The data file of this test is test_case_ 04.csv""" env = build_env("case_04", 600) be = env.business_engine assert isinstance(be, SupplyChainBusinessEngine) @@ -205,9 +206,9 @@ def test_seller_unit_dynamics_sampler(self): while env.tick < expected_tick - 1: env.step(None) - states = seller_nodes[:seller_node_index:features[IDX_SOLD]].flatten().astype(np.int) + states = seller_nodes[: seller_node_index : features[IDX_SOLD]].flatten().astype(np.int) self.assertListEqual([10, 20, 30, 40, 50], list(states)) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/tests/supply_chain/test_state_only.py b/tests/supply_chain/test_state_only.py index 1762b700b..93aa95231 100644 --- a/tests/supply_chain/test_state_only.py +++ b/tests/supply_chain/test_state_only.py @@ -1,5 +1,6 @@ import os import unittest + import numpy as np from maro.simulator import Env @@ -33,19 +34,18 @@ def get_product_dict_from_storage(env: Env, frame_index: int, node_index: int): class MyTestCase(unittest.TestCase): """ - state only test: - . seller_state_only - . "sale_mean" - . "sale_hist" - . distribution_state_only - . "pending_order" - . "in_transit_orders" - . "pending_order_daily" + state only test: + . seller_state_only + . "sale_mean" + . "sale_hist" + . distribution_state_only + . "pending_order" + . "in_transit_orders" + . "pending_order_daily" """ def test_distribution_state_only_small_vlt(self) -> None: - """Test the "pending_order_daily" of the distribution unit when vlt is less than "pending_order_daily" length. - """ + """Test the "pending_order_daily" of the distribution unit when vlt is less than "pending_order_daily" length.""" env = build_env("case_05", 100) be = env.business_engine assert isinstance(be, SupplyChainBusinessEngine) @@ -54,7 +54,7 @@ def test_distribution_state_only_small_vlt(self) -> None: warehouse_1 = be.world._get_facility_by_name("Warehouse_001") distribution_unit = supplier_3.distribution - consumer_unit = warehouse_1.products[SKU3_ID].consumer + warehouse_1.products[SKU3_ID].consumer env.step(None) # vlt is greater than len(pending_order_len), which will cause the pending order to increase @@ -67,7 +67,8 @@ def test_distribution_state_only_small_vlt(self) -> None: env.step(None) # Here the vlt of "train" is less than "pending_order_daily" length self.assertEqual( - [0, 0, 1, 0], list(env.metrics["products"][warehouse_1.products[SKU3_ID].id]["pending_order_daily"]), + [0, 0, 1, 0], + list(env.metrics["products"][warehouse_1.products[SKU3_ID].id]["pending_order_daily"]), ) self.assertEqual(1 * 1, distribution_unit.transportation_cost[SKU3_ID]) @@ -78,7 +79,8 @@ def test_distribution_state_only_small_vlt(self) -> None: self.assertEqual(2, sum([order.required_quantity for order in distribution_unit._order_queues["train"]])) self.assertEqual( - [0, 0, 1, 0], list(env.metrics["products"][warehouse_1.products[SKU3_ID].id]["pending_order_daily"]), + [0, 0, 1, 0], + list(env.metrics["products"][warehouse_1.products[SKU3_ID].id]["pending_order_daily"]), ) start_tick = env.tick @@ -96,36 +98,40 @@ def test_distribution_state_only_small_vlt(self) -> None: env.step(None) self.assertEqual( - [0, 1, 2, 0], list(env.metrics["products"][warehouse_1.products[SKU3_ID].id]["pending_order_daily"]), + [0, 1, 2, 0], + list(env.metrics["products"][warehouse_1.products[SKU3_ID].id]["pending_order_daily"]), ) - self.assertEqual(1 * (1+2), distribution_unit.transportation_cost[SKU3_ID]) + self.assertEqual(1 * (1 + 2), distribution_unit.transportation_cost[SKU3_ID]) env.step(None) self.assertEqual( - [1, 2, 0, 0], list(env.metrics["products"][warehouse_1.products[SKU3_ID].id]["pending_order_daily"]), + [1, 2, 0, 0], + list(env.metrics["products"][warehouse_1.products[SKU3_ID].id]["pending_order_daily"]), ) self.assertEqual(1, len(distribution_unit._order_queues["train"])) self.assertEqual(3, sum([order.required_quantity for order in distribution_unit._order_queues["train"]])) - self.assertEqual(1 * (1+2), distribution_unit.transportation_cost[SKU3_ID]) + self.assertEqual(1 * (1 + 2), distribution_unit.transportation_cost[SKU3_ID]) env.step(None) self.assertEqual( - [2, 0, 0, 3], list(env.metrics["products"][warehouse_1.products[SKU3_ID].id]["pending_order_daily"]), + [2, 0, 0, 3], + list(env.metrics["products"][warehouse_1.products[SKU3_ID].id]["pending_order_daily"]), ) # will arrive at the end of this tick, still on the way. self.assertEqual(0, len(distribution_unit._order_queues["train"])) self.assertEqual(0, sum([order.required_quantity for order in distribution_unit._order_queues["train"]])) - self.assertEqual(1 * (2+3), distribution_unit.transportation_cost[SKU3_ID]) + self.assertEqual(1 * (2 + 3), distribution_unit.transportation_cost[SKU3_ID]) self.assertEqual(10 * 0, distribution_unit.delay_order_penalty[SKU3_ID]) assert env.tick == expected_tick env.step(None) self.assertEqual( - [0, 0, 3, 0], list(env.metrics["products"][warehouse_1.products[SKU3_ID].id]["pending_order_daily"]), + [0, 0, 3, 0], + list(env.metrics["products"][warehouse_1.products[SKU3_ID].id]["pending_order_daily"]), ) self.assertEqual(0, len(distribution_unit._order_queues["train"])) @@ -139,7 +145,8 @@ def test_distribution_state_only_small_vlt(self) -> None: distribution_unit.place_order(order_1) self.assertEqual( - [0, 3, 0, 0], list(env.metrics["products"][warehouse_1.products[SKU3_ID].id]["pending_order_daily"]), + [0, 3, 0, 0], + list(env.metrics["products"][warehouse_1.products[SKU3_ID].id]["pending_order_daily"]), ) self.assertEqual(1, len(distribution_unit._order_queues["train"])) @@ -153,7 +160,8 @@ def test_distribution_state_only_small_vlt(self) -> None: self.assertEqual(1 * 3 + 1 * 1, distribution_unit.transportation_cost[SKU3_ID]) self.assertEqual( - [3, 0, 1, 0], list(env.metrics["products"][warehouse_1.products[SKU3_ID].id]["pending_order_daily"]), + [3, 0, 1, 0], + list(env.metrics["products"][warehouse_1.products[SKU3_ID].id]["pending_order_daily"]), ) start_tick = env.tick @@ -162,13 +170,14 @@ def test_distribution_state_only_small_vlt(self) -> None: env.step(None) self.assertEqual( - [1, 0, 0, 0], list(env.metrics["products"][warehouse_1.products[SKU3_ID].id]["pending_order_daily"]), + [1, 0, 0, 0], + list(env.metrics["products"][warehouse_1.products[SKU3_ID].id]["pending_order_daily"]), ) self.assertEqual(1 * 1 * 1, distribution_unit.transportation_cost[SKU3_ID]) def test_distribution_state_only_bigger_vlt(self) -> None: """Tests the "pending_order_daily" of the distribution unit when vlt is greater than the - "pending_order_daily" length. """ + "pending_order_daily" length.""" env = build_env("case_05", 100) be = env.business_engine @@ -176,14 +185,14 @@ def test_distribution_state_only_bigger_vlt(self) -> None: warehouse_1 = be.world._get_facility_by_name("Warehouse_001") retailer_1: FacilityBase = be.world._get_facility_by_name("Retailer_001") - warehouse_1_id, retailer_1_id = 6, 13 + warehouse_1_id, retailer_1_id = 12, 19 warehouse_1_distribution_unit = warehouse_1.distribution self.assertEqual(0, len(warehouse_1_distribution_unit._order_queues["train"])) env.step(None) - consumer_unit = retailer_1.products[SKU2_ID].consumer + retailer_1.products[SKU2_ID].consumer order_1 = Order(warehouse_1, retailer_1, SKU2_ID, 1, "train", env.tick, None) warehouse_1_distribution_unit.place_order(order_1) @@ -195,7 +204,8 @@ def test_distribution_state_only_bigger_vlt(self) -> None: self.assertEqual(0, len(warehouse_1_distribution_unit._order_queues["train"])) self.assertEqual( - [0, 0, 0, 0], list(env.metrics["products"][retailer_1.products[SKU2_ID].id]["pending_order_daily"]), + [0, 0, 0, 0], + list(env.metrics["products"][retailer_1.products[SKU2_ID].id]["pending_order_daily"]), ) order_2 = Order(warehouse_1, retailer_1, SKU2_ID, 2, "train", env.tick, None) @@ -208,14 +218,16 @@ def test_distribution_state_only_bigger_vlt(self) -> None: env.step(None) self.assertEqual( - [0, 0, 0, 1], list(env.metrics["products"][retailer_1.products[SKU2_ID].id]["pending_order_daily"]), + [0, 0, 0, 1], + list(env.metrics["products"][retailer_1.products[SKU2_ID].id]["pending_order_daily"]), ) # After env.step runs, where tick is 3. order_1 will arrive at tick=5. order_2 will arrive at tick=6. env.step(None) self.assertEqual( - [0, 0, 1, 2], list(env.metrics["products"][retailer_1.products[SKU2_ID].id]["pending_order_daily"]), + [0, 0, 1, 2], + list(env.metrics["products"][retailer_1.products[SKU2_ID].id]["pending_order_daily"]), ) self.assertEqual(3, env.metrics["facilities"][retailer_1_id]["in_transit_orders"][SKU2_ID]) @@ -229,7 +241,8 @@ def test_distribution_state_only_bigger_vlt(self) -> None: # order_2 will arrive at tick=6.order_3 is expected to arrive at tick=8 under normal circumstances. env.step(None) self.assertEqual( - [0, 1, 2, 0], list(env.metrics["products"][retailer_1.products[SKU2_ID].id]["pending_order_daily"]), + [0, 1, 2, 0], + list(env.metrics["products"][retailer_1.products[SKU2_ID].id]["pending_order_daily"]), ) self.assertEqual(6, env.metrics["facilities"][retailer_1_id]["in_transit_orders"][SKU2_ID]) @@ -240,14 +253,16 @@ def test_distribution_state_only_bigger_vlt(self) -> None: # order_2 will arrive at tick=6.order_3 is expected to arrive at tick=8 under normal circumstances. env.step(None) self.assertEqual( - [1, 2, 0, 0], list(env.metrics["products"][retailer_1.products[SKU2_ID].id]["pending_order_daily"]), + [1, 2, 0, 0], + list(env.metrics["products"][retailer_1.products[SKU2_ID].id]["pending_order_daily"]), ) # After env.step runs, where tick is 6. order_2 arrives after env.step. # There are empty cars at this time, order_3 will arrive at tick = 11. env.step(None) self.assertEqual( - [2, 0, 0, 0], list(env.metrics["products"][retailer_1.products[SKU2_ID].id]["pending_order_daily"]), + [2, 0, 0, 0], + list(env.metrics["products"][retailer_1.products[SKU2_ID].id]["pending_order_daily"]), ) # When order_1 arrives at the next step, the in_transit_orders of retailer_1 should be the negative number @@ -269,32 +284,36 @@ def test_distribution_state_only_bigger_vlt(self) -> None: # order_4 is expected to arrive at tick=12 under normal circumstances. env.step(None) self.assertEqual( - [0, 0, 0, 3], list(env.metrics["products"][retailer_1.products[SKU2_ID].id]["pending_order_daily"]), + [0, 0, 0, 3], + list(env.metrics["products"][retailer_1.products[SKU2_ID].id]["pending_order_daily"]), ) # After env.step runs, where tick is 9. order_3 will arrive at tick = 11. # order_4 is expected to arrive at tick=12 under normal circumstances. env.step(None) self.assertEqual( - [0, 0, 3, 4], list(env.metrics["products"][retailer_1.products[SKU2_ID].id]["pending_order_daily"]), + [0, 0, 3, 4], + list(env.metrics["products"][retailer_1.products[SKU2_ID].id]["pending_order_daily"]), ) # After env.step runs, where tick is 10. order_3 will arrive at tick = 11. # order_4 is expected to arrive at tick=12 under normal circumstances. env.step(None) self.assertEqual( - [0, 3, 4, 0], list(env.metrics["products"][retailer_1.products[SKU2_ID].id]["pending_order_daily"]), + [0, 3, 4, 0], + list(env.metrics["products"][retailer_1.products[SKU2_ID].id]["pending_order_daily"]), ) # After env.step runs, where tick is 11. order_3 arrives after env.step. # order_4 is expected to arrive at tick=12 under normal circumstances. env.step(None) self.assertEqual( - [3, 4, 0, 0], list(env.metrics["products"][retailer_1.products[SKU2_ID].id]["pending_order_daily"]), + [3, 4, 0, 0], + list(env.metrics["products"][retailer_1.products[SKU2_ID].id]["pending_order_daily"]), ) def test_seller_state_only(self) -> None: - """Test "sale_mean" and "_sale_hist """ + """Test "sale_mean" and "_sale_hist""" env = build_env("case_05", 600) be = env.business_engine @@ -324,12 +343,12 @@ def test_seller_state_only(self) -> None: self.assertEqual(5, env.metrics["products"][store_001.products[SKU1_ID].id]["demand_mean"]) self.assertEqual(43.0, env.metrics["products"][store_001.products[SKU1_ID].id]["selling_price"]) - self.assertEqual(5*2+3, env.metrics["products"][store_001.products[SKU3_ID].id]["sale_mean"]) - self.assertEqual(5*2+3, env.metrics["products"][store_001.products[SKU3_ID].id]["demand_mean"]) + self.assertEqual(5 * 2 + 3, env.metrics["products"][store_001.products[SKU3_ID].id]["sale_mean"]) + self.assertEqual(5 * 2 + 3, env.metrics["products"][store_001.products[SKU3_ID].id]["demand_mean"]) self.assertEqual(28.0, env.metrics["products"][store_001.products[SKU3_ID].id]["selling_price"]) - self.assertEqual(0+2, env.metrics["products"][store_001.products[SKU2_ID].id]["sale_mean"]) - self.assertEqual(0+2, env.metrics["products"][store_001.products[SKU2_ID].id]["demand_mean"]) + self.assertEqual(0 + 2, env.metrics["products"][store_001.products[SKU2_ID].id]["sale_mean"]) + self.assertEqual(0 + 2, env.metrics["products"][store_001.products[SKU2_ID].id]["demand_mean"]) self.assertEqual(17.0, env.metrics["products"][store_001.products[SKU2_ID].id]["selling_price"]) env.step(None) @@ -356,13 +375,14 @@ def test_distribution_state_only(self) -> None: distribution_unit.place_order(order) self.assertEqual(1, len(distribution_unit._order_queues["train"])) self.assertEqual(20, sum([order.required_quantity for order in distribution_unit._order_queues["train"]])) - supplier_3_id, warehouse_1_id, retailer_1_id = 1, 6, 13 + supplier_3_id, warehouse_1_id, retailer_1_id = 1, 12, 19 env.step(None) # vlt is greater than len(pending_order_len), which will cause the pending order to increase self.assertEqual( - [0, 0, 20, 0], list(env.metrics["products"][warehouse_1.products[SKU3_ID].id]["pending_order_daily"]), + [0, 0, 20, 0], + list(env.metrics["products"][warehouse_1.products[SKU3_ID].id]["pending_order_daily"]), ) self.assertEqual(20, env.metrics["facilities"][warehouse_1_id]["in_transit_orders"][SKU3_ID]) @@ -406,7 +426,8 @@ def test_distribution_state_only(self) -> None: env.step(None) self.assertEqual( - [0, 20, 25, 0], list(env.metrics["products"][warehouse_1.products[SKU3_ID].id]["pending_order_daily"]), + [0, 20, 25, 0], + list(env.metrics["products"][warehouse_1.products[SKU3_ID].id]["pending_order_daily"]), ) self.assertEqual(20 + 25 + 30, env.metrics["facilities"][warehouse_1_id]["in_transit_orders"][SKU3_ID]) @@ -414,13 +435,15 @@ def test_distribution_state_only(self) -> None: env.step(None) self.assertEqual( - [20, 25, 0, 0], list(env.metrics["products"][warehouse_1.products[SKU3_ID].id]["pending_order_daily"]), + [20, 25, 0, 0], + list(env.metrics["products"][warehouse_1.products[SKU3_ID].id]["pending_order_daily"]), ) self.assertEqual(20 + 25 + 30, env.metrics["facilities"][warehouse_1_id]["in_transit_orders"][SKU3_ID]) env.step(None) self.assertEqual( - [25, 0, 0, 30], list(env.metrics["products"][warehouse_1.products[SKU3_ID].id]["pending_order_daily"]), + [25, 0, 0, 30], + list(env.metrics["products"][warehouse_1.products[SKU3_ID].id]["pending_order_daily"]), ) self.assertEqual(25 + 30, env.metrics["facilities"][warehouse_1_id]["in_transit_orders"][SKU3_ID]) diff --git a/tests/supply_chain/test_storage_unit.py b/tests/supply_chain/test_storage_unit.py index 141b9cf60..35334ef37 100644 --- a/tests/supply_chain/test_storage_unit.py +++ b/tests/supply_chain/test_storage_unit.py @@ -2,15 +2,22 @@ # Licensed under the MIT license. import unittest + import numpy as np -from maro.simulator.scenarios.supply_chain import FacilityBase, StorageUnit, ManufactureAction +from maro.simulator.scenarios.supply_chain import FacilityBase, ManufactureAction, StorageUnit from maro.simulator.scenarios.supply_chain.business_engine import SupplyChainBusinessEngine from maro.simulator.scenarios.supply_chain.sku_dynamics_sampler import OneTimeSkuPriceDemandSampler from maro.simulator.scenarios.supply_chain.units.storage import AddStrategy from tests.supply_chain.common import ( - build_env, get_product_dict_from_storage, SKU1_ID, SKU2_ID, SKU3_ID, FOOD_1_ID, HOBBY_1_ID + FOOD_1_ID, + HOBBY_1_ID, + SKU1_ID, + SKU2_ID, + SKU3_ID, + build_env, + get_product_dict_from_storage, ) @@ -59,14 +66,14 @@ def test_storage_get_product_quantity_and_capacity_and_remaining_space(self) -> self.assertEqual(100, init_product_dict[SKU3_ID]) # ######################### Capacity ########################### - capacities = storage_nodes[env.frame_index:storage_node_index:"capacity"].flatten().astype(np.int) + capacities = storage_nodes[env.frame_index : storage_node_index : "capacity"].flatten().astype(np.int) self.assertEqual(200, storage_unit.capacity) self.assertEqual(200, capacities.sum()) # ######################### Remaining Space ########################### - init_remaining_spaces = storage_nodes[ - env.frame_index:storage_node_index:"remaining_space" - ].flatten().astype(np.int) + init_remaining_spaces = ( + storage_nodes[env.frame_index : storage_node_index : "remaining_space"].flatten().astype(np.int) + ) self.assertEqual(200 - 96 - 100, storage_unit.remaining_space) self.assertEqual(200 - 96 - 100, init_remaining_spaces.sum()) @@ -86,14 +93,14 @@ def test_storage_get_product_quantity_and_capacity_and_remaining_space(self) -> self.assertEqual(100, init_product_dict[SKU3_ID]) # ######################### Capacity ########################### - capacities = storage_nodes[env.frame_index:storage_node_index:"capacity"].flatten().astype(np.int) + capacities = storage_nodes[env.frame_index : storage_node_index : "capacity"].flatten().astype(np.int) self.assertEqual(200, storage_unit.capacity) self.assertEqual(200, capacities.sum()) # ######################### Remaining Space ########################### - init_remaining_spaces = storage_nodes[ - env.frame_index:storage_node_index:"remaining_space" - ].flatten().astype(np.int) + init_remaining_spaces = ( + storage_nodes[env.frame_index : storage_node_index : "remaining_space"].flatten().astype(np.int) + ) self.assertEqual(200 - 96 - 100, storage_unit.remaining_space) self.assertEqual(200 - 96 - 100, init_remaining_spaces.sum()) @@ -160,11 +167,11 @@ def test_storage_try_add_products(self) -> None: storage_nodes = env.snapshot_list["storage"] - capacities = storage_nodes[env.frame_index:storage_node_index:"capacity"].flatten().astype(np.int) + capacities = storage_nodes[env.frame_index : storage_node_index : "capacity"].flatten().astype(np.int) - init_remaining_spaces = storage_nodes[ - env.frame_index:storage_node_index:"remaining_space" - ].flatten().astype(np.int) + init_remaining_spaces = ( + storage_nodes[env.frame_index : storage_node_index : "remaining_space"].flatten().astype(np.int) + ) init_product_dict = get_product_dict_from_storage(env, env.frame_index, storage_node_index) @@ -208,7 +215,9 @@ def test_storage_try_add_products(self) -> None: env.step(None) # remaining space in snapshot should be 0 - remaining_spaces = storage_nodes[env.frame_index:storage_node_index:"remaining_space"].flatten().astype(np.int) + remaining_spaces = ( + storage_nodes[env.frame_index : storage_node_index : "remaining_space"].flatten().astype(np.int) + ) self.assertEqual(0, remaining_spaces.sum()) product_dict = get_product_dict_from_storage(env, env.frame_index, storage_node_index) @@ -249,7 +258,9 @@ def test_storage_try_add_products(self) -> None: env.step(None) # remaining space in snapshot should be 0 - remaining_spaces = storage_nodes[env.frame_index:storage_node_index:"remaining_space"].flatten().astype(np.int) + remaining_spaces = ( + storage_nodes[env.frame_index : storage_node_index : "remaining_space"].flatten().astype(np.int) + ) self.assertEqual(0, remaining_spaces.sum()) product_dict = get_product_dict_from_storage(env, env.frame_index, storage_node_index) @@ -287,7 +298,9 @@ def test_storage_try_add_products(self) -> None: # take snapshot env.step(None) - remaining_spaces = storage_nodes[env.frame_index:storage_node_index:"remaining_space"].flatten().astype(np.int) + remaining_spaces = ( + storage_nodes[env.frame_index : storage_node_index : "remaining_space"].flatten().astype(np.int) + ) self.assertEqual(10, remaining_spaces.sum()) product_dict = get_product_dict_from_storage(env, env.frame_index, storage_node_index) @@ -332,8 +345,10 @@ def test_storage_try_take_products(self) -> None: # take snapshot env.step(None) - capacities = storage_nodes[env.frame_index:storage_node_index:"capacity"].flatten().astype(np.int) - remaining_spaces = storage_nodes[env.frame_index:storage_node_index:"remaining_space"].flatten().astype(np.int) + capacities = storage_nodes[env.frame_index : storage_node_index : "capacity"].flatten().astype(np.int) + remaining_spaces = ( + storage_nodes[env.frame_index : storage_node_index : "remaining_space"].flatten().astype(np.int) + ) # remaining space should be same as capacity in snapshot self.assertEqual(capacities.sum(), remaining_spaces.sum()) @@ -353,8 +368,10 @@ def test_storage_upper_bound(self) -> None: storage_nodes = env.snapshot_list["storage"] # ######################### Check the storage upper bound for each sku ############################## - capacities = storage_nodes[env.frame_index:storage_node_index:"capacity"].flatten().astype(np.int) - remaining_spaces = storage_nodes[env.frame_index:storage_node_index:"remaining_space"].flatten().astype(np.int) + capacities = storage_nodes[env.frame_index : storage_node_index : "capacity"].flatten().astype(np.int) + remaining_spaces = ( + storage_nodes[env.frame_index : storage_node_index : "remaining_space"].flatten().astype(np.int) + ) # The capacity should be same as the config self.assertEqual(100, capacities[0]) @@ -376,12 +393,14 @@ def test_storage_upper_bound(self) -> None: self.assertEqual(0, storage_unit.get_product_max_remaining_space(SKU1_ID)) env.step(None) - remaining_spaces = storage_nodes[env.frame_index:storage_node_index:"remaining_space"].flatten().astype(np.int) + remaining_spaces = ( + storage_nodes[env.frame_index : storage_node_index : "remaining_space"].flatten().astype(np.int) + ) self.assertEqual(100 - 40 - 10 - 10, remaining_spaces[0]) def test_storage_unit_dynamics_one_time_sku_price_demand_sampler(self): """Under the OneTimeSkuPriceDemandSampler class,test the store between the storage unit and the dynamics CSV - data interaction. The data file of this test is test_case_04.csv """ + data interaction. The data file of this test is test_case_04.csv""" env = build_env("case_04", 600) be = env.business_engine assert isinstance(be, SupplyChainBusinessEngine) @@ -414,13 +433,18 @@ def test_storage_unit_dynamics_one_time_sku_price_demand_sampler(self): self.assertEqual(4900, 5000 - sku_onetime.sample_demand(0, HOBBY_1_ID)) # ######################### Capacity ########################### - capacities = storage_nodes[env.frame_index:storage_node_index:"capacity"].flatten().astype(np.int) + capacities = storage_nodes[env.frame_index : storage_node_index : "capacity"].flatten().astype(np.int) self.assertEqual(80000, storage_unit.capacity) self.assertEqual(80000, capacities.sum()) # ######################### Remaining Space ########################### - init_remaining_spaces = storage_nodes[env.frame_index:storage_node_index:"remaining_space"].flatten().astype( - np.int) + init_remaining_spaces = ( + storage_nodes[env.frame_index : storage_node_index : "remaining_space"] + .flatten() + .astype( + np.int, + ) + ) self.assertEqual(80000 - (10000 - 10) - (5000 - 100), init_remaining_spaces.sum()) env.step(None) @@ -446,14 +470,14 @@ def test_storage_unit_dynamics_one_time_sku_price_demand_sampler(self): self.assertEqual(5000 - 1000, product_dict[HOBBY_1_ID]) # ######################### Capacity ########################### - capacities = storage_nodes[env.frame_index:storage_node_index:"capacity"].flatten().astype(np.int) + capacities = storage_nodes[env.frame_index : storage_node_index : "capacity"].flatten().astype(np.int) self.assertEqual(80000, storage_unit.capacity) self.assertEqual(80000, capacities.sum()) # ######################### Remaining Space ########################### - init_remaining_spaces = storage_nodes[ - env.frame_index:storage_node_index:"remaining_space" - ].flatten().astype(np.int) + init_remaining_spaces = ( + storage_nodes[env.frame_index : storage_node_index : "remaining_space"].flatten().astype(np.int) + ) self.assertEqual(80000 - 9900 - 4000, storage_unit.remaining_space) self.assertEqual(80000 - 9900 - 4000, init_remaining_spaces.sum()) @@ -475,17 +499,17 @@ def test_storage_unit_dynamics_one_time_sku_price_demand_sampler(self): self.assertEqual(4900, 5000 - sku_onetime.sample_demand(0, HOBBY_1_ID)) # ######################### Capacity ########################### - capacities = storage_nodes[env.frame_index:storage_node_index:"capacity"].flatten().astype(np.int) + capacities = storage_nodes[env.frame_index : storage_node_index : "capacity"].flatten().astype(np.int) self.assertEqual(80000, storage_unit.capacity) self.assertEqual(80000, capacities.sum()) # ######################### Remaining Space ########################### - init_remaining_spaces = storage_nodes[ - env.frame_index:storage_node_index:"remaining_space" - ].flatten().astype(np.int) + init_remaining_spaces = ( + storage_nodes[env.frame_index : storage_node_index : "remaining_space"].flatten().astype(np.int) + ) self.assertEqual(80000 - (10000 - 10) - (5000 - 100), storage_unit.remaining_space) self.assertEqual(80000 - (10000 - 10) - (5000 - 100), init_remaining_spaces.sum()) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/tests/supply_chain/test_units_interaction.py b/tests/supply_chain/test_units_interaction.py index 2852f5a41..17454b810 100644 --- a/tests/supply_chain/test_units_interaction.py +++ b/tests/supply_chain/test_units_interaction.py @@ -2,14 +2,13 @@ # Licensed under the MIT license. import unittest + import numpy as np -from maro.simulator.scenarios.supply_chain import ( - ConsumerAction, FacilityBase -) +from maro.simulator.scenarios.supply_chain import ConsumerAction, FacilityBase from maro.simulator.scenarios.supply_chain.business_engine import SupplyChainBusinessEngine -from tests.supply_chain.common import build_env, SKU3_ID +from tests.supply_chain.common import SKU3_ID, build_env class MyTestCase(unittest.TestCase): @@ -64,7 +63,7 @@ def test_consumer_receive_products_after_vlt_days(self) -> None: expected_frame = env.business_engine.frame_index(expected_tick_1) # Not received yet. - states = consumer_nodes[expected_frame - 1:consumer_node_index:features].flatten().astype(np.int) + states = consumer_nodes[expected_frame - 1 : consumer_node_index : features].flatten().astype(np.int) self.assertEqual(0, states[IDX_RECEIVED]) # received. @@ -91,7 +90,7 @@ def test_consumer_receive_products_after_vlt_days(self) -> None: expected_frame = env.business_engine.frame_index(expected_tick_2) # Not received yet. - states = consumer_nodes[expected_frame - 1:consumer_node_index:features].flatten().astype(np.int) + states = consumer_nodes[expected_frame - 1 : consumer_node_index : features].flatten().astype(np.int) self.assertEqual(0, states[IDX_RECEIVED]) # received. @@ -99,5 +98,5 @@ def test_consumer_receive_products_after_vlt_days(self) -> None: self.assertEqual(required_quantity_2, states[IDX_RECEIVED]) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main()