-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Added Basic Visualization tests #2767
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 5 commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
7650030
added basic viz tests
Sahil-Chhoker a57a1c8
added library requirements
Sahil-Chhoker 92dce61
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 3660b5a
Merge branch 'main' into vis-tests
Sahil-Chhoker f8a7a4f
added pre-commit suggestions
Sahil-Chhoker 372c523
enable playwright
Sahil-Chhoker ca05a8c
replace time.sleep with playwright wait
Sahil-Chhoker f553ae9
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 937207a
Merge branch 'main' into vis-tests
Sahil-Chhoker 1158925
Merge branch 'main' into vis-tests
Sahil-Chhoker 788b575
Merge branch 'main' into vis-tests
Sahil-Chhoker 1d45245
Merge branch 'main' into vis-tests
Sahil-Chhoker e0bb7ec
empty commit
Sahil-Chhoker File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,343 @@ | ||
| # noqa: D100 | ||
| import base64 | ||
| import time | ||
|
|
||
| import playwright.sync_api | ||
| import pytest | ||
| from IPython.display import display | ||
|
|
||
| from mesa.examples import ( | ||
| BoidFlockers, | ||
| BoltzmannWealth, | ||
| ConwaysGameOfLife, | ||
| EpsteinCivilViolence, | ||
| PdGrid, | ||
| Schelling, | ||
| SugarscapeG1mt, | ||
| VirusOnNetwork, | ||
| WolfSheep, | ||
| ) | ||
| from mesa.visualization.components.matplotlib_components import ( | ||
| PlotMatplotlib, | ||
| SpaceMatplotlib, | ||
| ) | ||
|
|
||
|
|
||
| def run_model_test( | ||
| model, | ||
| agent_portrayal, | ||
| solara_test, | ||
| page_session: playwright.sync_api.Page, | ||
| measure_config=None, | ||
| steps=5, | ||
| ): | ||
| """Generic test for agent-based models to verify visual changes after steps. | ||
|
|
||
| For more details, see the documentation: https://solara.dev/documentation/advanced/howto/testing#testing-widgets-using-solara-server | ||
| """ | ||
| try: | ||
| # Create visualizations for the initial model state | ||
| space_viz = SpaceMatplotlib( | ||
| model=model, agent_portrayal=agent_portrayal, propertylayer_portrayal=None | ||
| ) | ||
| initial_graph = None | ||
|
|
||
| if measure_config: | ||
| graph_viz = PlotMatplotlib(model=model, measure=measure_config) | ||
|
|
||
| # Display and capture the initial visualizations | ||
| display(space_viz) | ||
| time.sleep(0.2) # buffer for rendering | ||
| initial_space = page_session.locator("img").screenshot() | ||
|
|
||
| if measure_config: | ||
| display(graph_viz) | ||
| time.sleep(0.2) | ||
| initial_graph = page_session.locator("img").screenshot() | ||
|
|
||
Sahil-Chhoker marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| # Run the model for specified number of steps | ||
| for _ in range(steps): | ||
| model.step() | ||
|
|
||
| # Create new visualizations for the updated model state | ||
| space_viz = SpaceMatplotlib( | ||
| model=model, agent_portrayal=agent_portrayal, propertylayer_portrayal=None | ||
| ) | ||
| changed_graph = None | ||
|
|
||
| if measure_config: | ||
| graph_viz = PlotMatplotlib(model=model, measure=measure_config) | ||
|
|
||
| # Display and capture the updated visualizations | ||
| display(space_viz) | ||
| time.sleep(0.2) | ||
| changed_space = page_session.locator("img").screenshot() | ||
|
|
||
| if measure_config: | ||
| display(graph_viz) | ||
| time.sleep(0.2) | ||
| changed_graph = page_session.locator("img").screenshot() | ||
|
|
||
| # Convert screenshots to base64 for comparison | ||
| initial_space_encoding = base64.b64encode(initial_space).decode() | ||
| changed_space_encoding = base64.b64encode(changed_space).decode() | ||
|
|
||
| if measure_config and initial_graph is not None and changed_graph is not None: | ||
| initial_graph_encoding = base64.b64encode(initial_graph).decode() | ||
| changed_graph_encoding = base64.b64encode(changed_graph).decode() | ||
|
|
||
| # Assert that visualizations changed after running steps | ||
| assert initial_space_encoding != changed_space_encoding, ( | ||
| "The space visualization did not change after steps." | ||
| ) | ||
|
|
||
| if measure_config and initial_graph is not None and changed_graph is not None: | ||
| assert initial_graph_encoding != changed_graph_encoding, ( | ||
| "The graph visualization did not change after steps." | ||
| ) | ||
| except MemoryError: | ||
| pytest.skip("Skipping test due to memory shortage.") | ||
| except Exception as e: | ||
| pytest.fail(f"Test failed with an unexpected exception: {e}") | ||
|
|
||
Sahil-Chhoker marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| @pytest.mark.filterwarnings("ignore::DeprecationWarning") | ||
| def test_schelling_model(solara_test, page_session: playwright.sync_api.Page): | ||
| """Test schelling model behavior and visualization.""" | ||
| model = Schelling(seed=42) | ||
|
|
||
| def agent_portrayal(agent): | ||
| return {"color": "tab:orange" if agent.type == 0 else "tab:blue"} | ||
|
|
||
| measure_config = {"happy": "tab:green"} | ||
|
|
||
| run_model_test( | ||
| model=model, | ||
| agent_portrayal=agent_portrayal, | ||
| measure_config=measure_config, | ||
| solara_test=solara_test, | ||
| page_session=page_session, | ||
| ) | ||
|
|
||
|
|
||
| @pytest.mark.filterwarnings("ignore::DeprecationWarning") | ||
| def test_wolf_sheep_model(solara_test, page_session: playwright.sync_api.Page): | ||
| """Test wolf-sheep model behavior and visualization.""" | ||
| from mesa.examples.advanced.wolf_sheep.agents import GrassPatch, Sheep, Wolf | ||
| from mesa.experimental.devs import ABMSimulator | ||
|
|
||
| model = WolfSheep(simulator=ABMSimulator(), seed=42) | ||
|
|
||
| def agent_portrayal(agent): | ||
| if agent is None: | ||
| return | ||
|
|
||
| portrayal = { | ||
| "size": 25, | ||
| } | ||
|
|
||
| if isinstance(agent, Wolf): | ||
| portrayal["color"] = "tab:red" | ||
| portrayal["marker"] = "o" | ||
| portrayal["zorder"] = 2 | ||
| elif isinstance(agent, Sheep): | ||
| portrayal["color"] = "tab:cyan" | ||
| portrayal["marker"] = "o" | ||
| portrayal["zorder"] = 2 | ||
| elif isinstance(agent, GrassPatch): | ||
| if agent.fully_grown: | ||
| portrayal["color"] = "tab:green" | ||
| else: | ||
| portrayal["color"] = "tab:brown" | ||
| portrayal["marker"] = "s" | ||
| portrayal["size"] = 75 | ||
|
|
||
| return portrayal | ||
|
|
||
| measure_config = {"Wolves": "tab:orange", "Sheep": "tab:cyan", "Grass": "tab:green"} | ||
|
|
||
| run_model_test( | ||
| model=model, | ||
| agent_portrayal=agent_portrayal, | ||
| measure_config=measure_config, | ||
| solara_test=solara_test, | ||
| page_session=page_session, | ||
| ) | ||
|
|
||
|
|
||
| @pytest.mark.filterwarnings("ignore::DeprecationWarning") | ||
| def test_boid_flockers_model(solara_test, page_session: playwright.sync_api.Page): | ||
| """Test boid flockers model behavior and visualization.""" | ||
| model = BoidFlockers(seed=42) | ||
|
|
||
| def agent_portrayal(agent): | ||
| return {"color": "tab:blue"} | ||
|
|
||
| run_model_test( | ||
| model=model, | ||
| agent_portrayal=agent_portrayal, | ||
| measure_config=None, | ||
| solara_test=solara_test, | ||
| page_session=page_session, | ||
| ) | ||
|
|
||
|
|
||
| @pytest.mark.filterwarnings("ignore::DeprecationWarning") | ||
| def test_boltzmann_wealth_model(solara_test, page_session: playwright.sync_api.Page): | ||
| """Test Boltzmann wealth model behavior and visualization.""" | ||
| model = BoltzmannWealth(seed=42) | ||
|
|
||
| def agent_portrayal(agent): | ||
| color = agent.wealth # we are using a colormap to translate wealth to color | ||
| return {"color": color} | ||
|
|
||
| measure_config = "Gini" | ||
|
|
||
| run_model_test( | ||
| model=model, | ||
| agent_portrayal=agent_portrayal, | ||
| measure_config=measure_config, | ||
| solara_test=solara_test, | ||
| page_session=page_session, | ||
| ) | ||
|
|
||
|
|
||
| @pytest.mark.filterwarnings("ignore::DeprecationWarning") | ||
| def test_virus_on_network_model(solara_test, page_session: playwright.sync_api.Page): | ||
| """Test virus on network model behavior and visualization.""" | ||
| from mesa.examples.basic.virus_on_network.model import State | ||
|
|
||
| model = VirusOnNetwork(seed=42) | ||
|
|
||
| def agent_portrayal(agent): | ||
| node_color_dict = { | ||
| State.INFECTED: "tab:red", | ||
| State.SUSCEPTIBLE: "tab:green", | ||
| State.RESISTANT: "tab:gray", | ||
| } | ||
| return {"color": node_color_dict[agent.state], "size": 10} | ||
|
|
||
| measure_config = { | ||
| "Infected": "tab:red", | ||
| "Susceptible": "tab:green", | ||
| "Resistant": "tab:gray", | ||
| } | ||
|
|
||
| run_model_test( | ||
| model=model, | ||
| agent_portrayal=agent_portrayal, | ||
| measure_config=measure_config, | ||
| solara_test=solara_test, | ||
| page_session=page_session, | ||
| ) | ||
|
|
||
|
|
||
| @pytest.mark.filterwarnings("ignore::DeprecationWarning") | ||
| @pytest.mark.filterwarnings("ignore::UserWarning") | ||
| def test_conways_game_of_life_model( | ||
| solara_test, page_session: playwright.sync_api.Page | ||
| ): | ||
| """Test Conway's Game of Life model behavior and visualization.""" | ||
| model = ConwaysGameOfLife(seed=42) | ||
|
|
||
| def agent_portrayal(agent): | ||
| return { | ||
| "color": "white" if agent.state == 0 else "black", | ||
| "marker": "s", | ||
| "size": 25, | ||
| } | ||
|
|
||
| measure_config = None | ||
|
|
||
| run_model_test( | ||
| model=model, | ||
| agent_portrayal=agent_portrayal, | ||
| measure_config=measure_config, | ||
| solara_test=solara_test, | ||
| page_session=page_session, | ||
| ) | ||
|
|
||
|
|
||
| @pytest.mark.filterwarnings("ignore::DeprecationWarning") | ||
| def test_epstein_civil_violence_model( | ||
| solara_test, page_session: playwright.sync_api.Page | ||
| ): | ||
| """Test Epstein civil violence model behavior and visualization.""" | ||
| from mesa.examples.advanced.epstein_civil_violence.agents import ( | ||
| Citizen, | ||
| CitizenState, | ||
| Cop, | ||
| ) | ||
| from mesa.examples.advanced.epstein_civil_violence.app import ( | ||
| COP_COLOR, | ||
| agent_colors, | ||
| ) | ||
|
|
||
| model = EpsteinCivilViolence(seed=42) | ||
|
|
||
| def agent_portrayal(agent): | ||
| if agent is None: | ||
| return | ||
|
|
||
| portrayal = { | ||
| "size": 50, | ||
| } | ||
|
|
||
| if isinstance(agent, Citizen): | ||
| portrayal["color"] = agent_colors[agent.state] | ||
| elif isinstance(agent, Cop): | ||
| portrayal["color"] = COP_COLOR | ||
|
|
||
| return portrayal | ||
|
|
||
| measure_config = {state.name.lower(): agent_colors[state] for state in CitizenState} | ||
|
|
||
| run_model_test( | ||
| model=model, | ||
| agent_portrayal=agent_portrayal, | ||
| measure_config=measure_config, | ||
| solara_test=solara_test, | ||
| page_session=page_session, | ||
| ) | ||
|
|
||
|
|
||
| @pytest.mark.filterwarnings("ignore::DeprecationWarning") | ||
| def test_sugarscape_g1mt_model(solara_test, page_session: playwright.sync_api.Page): | ||
| """Test Sugarscape G1mt model behavior and visualization.""" | ||
| model = SugarscapeG1mt(seed=42) | ||
|
|
||
| def agent_portrayal(agent): | ||
| return {"marker": "o", "color": "red", "size": 10} | ||
|
|
||
| measure_config = "Price" | ||
|
|
||
| run_model_test( | ||
| model=model, | ||
| agent_portrayal=agent_portrayal, | ||
| measure_config=measure_config, | ||
| solara_test=solara_test, | ||
| page_session=page_session, | ||
| ) | ||
|
|
||
|
|
||
| @pytest.mark.filterwarnings("ignore::DeprecationWarning") | ||
| def test_pd_grid_model(solara_test, page_session: playwright.sync_api.Page): | ||
| """Test Prisoner's Dilemma model behavior and visualization.""" | ||
| model = PdGrid(seed=42) | ||
|
|
||
| def agent_portrayal(agent): | ||
| return { | ||
| "color": "blue" if agent.move == "C" else "red", | ||
| "marker": "s", # square marker | ||
| "size": 25, | ||
| } | ||
|
|
||
| measure_config = "Cooperating_Agents" | ||
|
|
||
| run_model_test( | ||
| model=model, | ||
| agent_portrayal=agent_portrayal, | ||
| measure_config=measure_config, | ||
| solara_test=solara_test, | ||
| page_session=page_session, | ||
| ) | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.