Add basic unit tests (#7)

* Unit tests for components, state, and utils
This commit is contained in:
Nikhil Rao 2022-11-20 14:34:25 -08:00 committed by GitHub
parent 7d91a9db68
commit 29e37350e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 640 additions and 123 deletions

View File

@ -1,5 +1,6 @@
"""Welcome to Pynecone! This file outlines the steps to create a basic app."""
import pcconfig
import pynecone as pc
docs_url = "https://pynecone.io/docs/getting-started/introduction"
@ -8,6 +9,7 @@ filename = f"{pcconfig.APP_NAME}/{pcconfig.APP_NAME}.py"
class State(pc.State):
"""The app state."""
pass
@ -21,10 +23,10 @@ def index():
href=docs_url,
border="0.1em solid",
padding="0.5em",
border_radius="0.5em"
border_radius="0.5em",
),
),
padding="5em"
padding="5em",
)
@ -32,4 +34,3 @@ def index():
app = pc.App(state=State)
app.add_page(index)
app.compile()

View File

@ -1,5 +1,6 @@
"""The main Pynecone app."""
import os
import re
from typing import Any, Callable, Coroutine, Dict, List, Optional, Tuple, Type, Union
@ -12,7 +13,7 @@ from pynecone.compiler import compiler
from pynecone.compiler import utils as compiler_utils
from pynecone.components.component import Component, ComponentStyle
from pynecone.event import Event
from pynecone.middleware import HydrateMiddleware, LoggingMiddleware, Middleware
from pynecone.middleware import HydrateMiddleware, Middleware
from pynecone.model import Model
from pynecone.state import DefaultState, Delta, State, StateManager, StateUpdate
@ -56,7 +57,6 @@ class App(Base):
# Add middleware.
self.middleware.append(HydrateMiddleware())
self.middleware.append(LoggingMiddleware())
# Set up the state manager.
self.state_manager.set(state=self.state)
@ -187,7 +187,7 @@ class App(Base):
from pynecone.var import BaseVar
parts = path.split("/")
parts = os.path.split(path)
check = re.compile(r"^\[(.+)\]$")
args = []
for part in parts:

View File

@ -1,8 +1,8 @@
"""Templates to use in the pynecone compiler."""
from typing import Callable, Optional, Set
from typing import Optional, Set
from pynecone import constants, utils
from pynecone import constants
from pynecone.utils import join
# Template for the Pynecone config file.

View File

@ -327,7 +327,7 @@ class Component(Base, ABC):
for child in self.children:
child_code = child.get_custom_code()
if child_code != "" and child_code not in code:
code += child_code
code += "\n" + child_code
return code
def _get_imports(self) -> ImportDict:

View File

@ -368,13 +368,16 @@ class State(Base, ABC):
Returns:
The delta for the state.
"""
delta = {}
# Return the dirty vars, as well as all computed vars.
delta = {
self.get_full_name(): {
prop: getattr(self, prop)
for prop in self.dirty_vars | set(self.computed_vars.keys())
}
subdelta = {
prop: getattr(self, prop)
for prop in self.dirty_vars | set(self.computed_vars.keys())
}
if len(subdelta) > 0:
delta[self.get_full_name()] = subdelta
# Recursively find the substate deltas.
for substate in self.dirty_substates:
delta.update(self.substates[substate].get_delta())

View File

@ -4,7 +4,7 @@ from __future__ import annotations
import json
from abc import ABC
from typing import _GenericAlias # type: ignore
from typing import TYPE_CHECKING, Any, Callable, List, Optional, Type, Union
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Type, Union
from plotly.graph_objects import Figure
from plotly.io import to_json
@ -153,7 +153,7 @@ class Var(ABC):
type_ = utils.get_args(self.type_)[0]
else:
type_ = Any
elif utils.is_dataframe(self.type_):
elif utils._issubclass(self.type_, Dict) or utils.is_dataframe(self.type_):
if isinstance(i, str):
i = utils.wrap(i, '"')
if isinstance(self.type_, _GenericAlias):

View File

@ -1,6 +1,6 @@
[tool.poetry]
name = "pynecone-io"
version = "0.1.2"
version = "0.1.3"
description = ""
authors = [
"Nikhil Rao <nikhil@pynecone.io>",

1
tests/__init__.py Normal file
View File

@ -0,0 +1 @@
"""Root directory for tests."""

View File

@ -0,0 +1 @@
"""Compiler tests."""

View File

@ -0,0 +1,72 @@
from typing import Set
import pytest
from pynecone.compiler import utils
@pytest.mark.parametrize(
"lib,fields,output",
[
("axios", {"axios"}, 'import axios from "axios"'),
("axios", {"foo", "bar"}, 'import {bar, foo} from "axios"'),
("axios", {"axios", "foo", "bar"}, 'import axios, {bar, foo} from "axios"'),
],
)
def test_compile_import_statement(lib: str, fields: Set[str], output: str):
"""Test the compile_import_statement function.
Args:
lib: The library name.
fields: The fields to import.
output: The expected output.
"""
assert utils.compile_import_statement(lib, fields) == output
@pytest.mark.parametrize(
"import_dict,output",
[
({}, ""),
({"axios": {"axios"}}, 'import axios from "axios"'),
({"axios": {"foo", "bar"}}, 'import {bar, foo} from "axios"'),
(
{"axios": {"axios", "foo", "bar"}, "react": {"react"}},
'import axios, {bar, foo} from "axios"\nimport react from "react"',
),
({"": {"lib1.js", "lib2.js"}}, 'import "lib1.js"\nimport "lib2.js"'),
(
{"": {"lib1.js", "lib2.js"}, "axios": {"axios"}},
'import "lib1.js"\nimport "lib2.js"\nimport axios from "axios"',
),
],
)
def test_compile_imports(import_dict: utils.ImportDict, output: str):
"""Test the compile_imports function.
Args:
import_dict: The import dictionary.
output: The expected output.
"""
assert utils.compile_imports(import_dict) == output
@pytest.mark.parametrize(
"name,value,output",
[
("foo", "bar", 'const foo = "bar"'),
("num", 1, "const num = 1"),
("check", False, "const check = false"),
("arr", [1, 2, 3], "const arr = [1, 2, 3]"),
("obj", {"foo": "bar"}, 'const obj = {"foo": "bar"}'),
],
)
def test_compile_constant_declaration(name: str, value: str, output: str):
"""Test the compile_constant_declaration function.
Args:
name: The name of the constant.
value: The value of the constant.
output: The expected output.
"""
assert utils.compile_constant_declaration(name, value) == output

View File

@ -0,0 +1 @@
"""Component tests."""

View File

@ -0,0 +1,152 @@
from typing import Type
import pytest
from pynecone.components.component import Component, ImportDict
from pynecone.event import EventHandler
from pynecone.style import Style
@pytest.fixture
def component1() -> Type[Component]:
"""A test component.
Returns:
A test component.
"""
class TestComponent1(Component):
def _get_imports(self) -> ImportDict:
return {"react": {"Component"}}
def _get_custom_code(self) -> str:
return "console.log('component1')"
return TestComponent1
@pytest.fixture
def component2() -> Type[Component]:
"""A test component.
Returns:
A test component.
"""
class TestComponent2(Component):
def _get_imports(self) -> ImportDict:
return {"react-redux": {"connect"}}
def _get_custom_code(self) -> str:
return "console.log('component2')"
return TestComponent2
@pytest.fixture
def on_click1() -> EventHandler:
"""A sample on click function.
Returns:
A sample on click function.
"""
def on_click1():
pass
return EventHandler(fn=on_click1)
@pytest.fixture
def on_click2() -> EventHandler:
"""A sample on click function.
Returns:
A sample on click function.
"""
def on_click2():
pass
return EventHandler(fn=on_click2)
def test_set_style_attrs(component1: Type[Component]):
"""Test that style attributes are set in the dict.
Args:
component1: A test component.
"""
component = component1(color="white", text_align="center")
assert component.style["color"] == "white"
assert component.style["textAlign"] == "center"
def test_create_component(component1: Type[Component]):
"""Test that the component is created correctly.
Args:
component1: A test component.
"""
children = [component1() for _ in range(3)]
attrs = {"color": "white", "text_align": "center"}
c = component1.create(*children, **attrs)
assert isinstance(c, component1)
assert c.children == children
assert c.style == {"color": "white", "textAlign": "center"}
def test_add_style(component1: Type[Component], component2: Type[Component]):
"""Test adding a style to a component.
Args:
component1: A test component.
component2: A test component.
"""
style = {
component1: Style({"color": "white"}),
component2: Style({"color": "black"}),
}
c1 = component1().add_style(style) # type: ignore
c2 = component2().add_style(style) # type: ignore
assert c1.style["color"] == "white"
assert c2.style["color"] == "black"
def test_get_imports(component1: Type[Component], component2: Type[Component]):
"""Test getting the imports of a component.
Args:
component1: A test component.
component2: A test component.
"""
c1 = component1.create()
c2 = component2.create(c1)
assert c1.get_imports() == {"react": {"Component"}}
assert c2.get_imports() == {"react-redux": {"connect"}, "react": {"Component"}}
def test_get_custom_code(component1: Type[Component], component2: Type[Component]):
"""Test getting the custom code of a component.
Args:
component1: A test component.
component2: A test component.
"""
# Check that the code gets compiled correctly.
c1 = component1.create()
c2 = component2.create()
assert c1.get_custom_code() == "console.log('component1')"
assert c2.get_custom_code() == "console.log('component2')"
# Check that nesting components compiles both codes.
c1 = component1.create(c2)
assert (
c1.get_custom_code() == "console.log('component1')\nconsole.log('component2')"
)
# Check that code is not duplicated.
c1 = component1.create(c2, c2, c1, c1)
assert (
c1.get_custom_code() == "console.log('component1')\nconsole.log('component2')"
)

View File

@ -1,9 +1,9 @@
from typing import Dict
import pydantic
import pytest
from pynecone.components.tags import CondTag, Tag
from pynecone.components import Box
from pynecone.components.tags import CondTag, IterTag, Tag
from pynecone.event import EventHandler, EventSpec, EventChain
from pynecone.var import BaseVar, Var
@ -13,29 +13,7 @@ def mock_event(arg):
@pytest.mark.parametrize(
"cond,valid",
[
(BaseVar(name="p", type_=bool), True),
(BaseVar(name="p", type_=int), False),
(BaseVar(name="p", type_=str), False),
],
)
def test_validate_cond(cond: BaseVar, valid: bool):
"""Test that the cond is a boolean.
Args:
cond: The cond to test.
valid: The expected validity of the cond.
"""
if not valid:
with pytest.raises(pydantic.ValidationError):
Tag(cond=cond)
else:
assert cond == Tag(cond=cond).cond
@pytest.mark.parametrize(
"attr,formatted",
"prop,formatted",
[
("string", '"string"'),
("{wrapped_string}", "{wrapped_string}"),
@ -47,31 +25,35 @@ def test_validate_cond(cond: BaseVar, valid: bool):
(["a", "b", "c"], '{["a", "b", "c"]}'),
({"a": 1, "b": 2, "c": 3}, '{{"a": 1, "b": 2, "c": 3}}'),
(
EventSpec(handler=EventHandler(fn=mock_event)),
EventChain(events=[EventSpec(handler=EventHandler(fn=mock_event))]),
'{() => Event([E("mock_event", {})])}',
),
(
EventSpec(
handler=EventHandler(fn=mock_event),
local_args=("e",),
args=(("arg", "e.target.value"),),
EventChain(
events=[
EventSpec(
handler=EventHandler(fn=mock_event),
local_args=("e",),
args=(("arg", "e.target.value"),),
)
]
),
'{(e) => Event([E("mock_event", {arg:e.target.value})])}',
),
],
)
def test_format_value(attr: Var, formatted: str):
"""Test that the formatted value of an attribute is correct.
def test_format_value(prop: Var, formatted: str):
"""Test that the formatted value of an prop is correct.
Args:
attr: The attribute to test.
prop: The prop to test.
formatted: The expected formatted value.
"""
assert Tag.format_attr_value(attr) == formatted
assert Tag.format_prop(prop) == formatted
@pytest.mark.parametrize(
"attrs,formatted",
"props,formatted",
[
({}, ""),
({"key": 1}, "key={1}"),
@ -79,18 +61,18 @@ def test_format_value(attr: Var, formatted: str):
({"key": True, "key2": "value2"}, 'key={true}\nkey2="value2"'),
],
)
def test_format_attrs(attrs: Dict[str, Var], formatted: str):
"""Test that the formatted attributes are correct.
def test_format_props(props: Dict[str, Var], formatted: str):
"""Test that the formatted props are correct.
Args:
attrs: The attributes to test.
formatted: The expected formatted attributes.
props: The props to test.
formatted: The expected formatted props.
"""
assert Tag(attrs=attrs).format_attrs() == formatted
assert Tag(props=props).format_props() == formatted
@pytest.mark.parametrize(
"attr,valid",
"prop,valid",
[
(1, True),
(3.14, True),
@ -101,23 +83,23 @@ def test_format_attrs(attrs: Dict[str, Var], formatted: str):
(None, False),
],
)
def test_is_valid_attr(attr: Var, valid: bool):
"""Test that the attribute is valid.
def test_is_valid_prop(prop: Var, valid: bool):
"""Test that the prop is valid.
Args:
attr: The attribute to test.
valid: The expected validity of the attribute.
prop: The prop to test.
valid: The expected validity of the prop.
"""
assert Tag.is_valid_attr(attr) == valid
assert Tag.is_valid_prop(prop) == valid
def test_add_props():
"""Test that the attributes are added."""
"""Test that the props are added."""
tag = Tag().add_props(key="value", key2=42, invalid=None, invalid2={})
assert tag.attrs["key"] == Var.create("value")
assert tag.attrs["key2"] == Var.create(42)
assert "invalid" not in tag.attrs
assert "invalid2" not in tag.attrs
assert tag.props["key"] == Var.create("value")
assert tag.props["key2"] == Var.create(42)
assert "invalid" not in tag.props
assert "invalid2" not in tag.props
@pytest.mark.parametrize(
@ -128,25 +110,17 @@ def test_add_props():
(Tag(contents="hello"), "<>hello</>"),
(Tag(name="h1", contents="hello"), "<h1>hello</h1>"),
(
Tag(name="box", attrs={"color": "red", "textAlign": "center"}),
Tag(name="box", props={"color": "red", "textAlign": "center"}),
'<box color="red"\ntextAlign="center"/>',
),
(
Tag(
name="box",
attrs={"color": "red", "textAlign": "center"},
props={"color": "red", "textAlign": "center"},
contents="text",
),
'<box color="red"\ntextAlign="center">text</box>',
),
(
Tag(
name="h1",
contents="hello",
cond=BaseVar(name="logged_in", type_=bool),
),
'{logged_in ? <h1>hello</h1> : ""}',
),
],
)
def test_format_tag(tag: Tag, expected: str):
@ -167,15 +141,3 @@ def test_format_cond_tag():
cond=BaseVar(name="logged_in", type_=bool),
)
assert str(tag) == "{logged_in ? <h1>True content</h1> : <h2>False content</h2>}"
def test_format_iter_tag():
"""Test that the formatted iter tag is correct."""
# def render_todo(todo: str):
# return Tag(name="Text", contents=todo)
# tag = IterTag(
# iterable=BaseVar(name="todos", type_=list),
# render_fn=render_todo
# )
# assert str(tag) == '{state.todos.map(render_todo)}'

View File

@ -1,9 +1,12 @@
from typing import Type
import pytest
from pynecone.base import Base
from pynecone.app import App, DefaultState
from pynecone.middleware import HydrateMiddleware
from pynecone.components import Box
from pynecone.state import State
from pynecone.style import Style
@pytest.fixture
@ -36,25 +39,32 @@ def about_page():
return about
def test_default_state(app: App) -> None:
"""Test creating an app with no state.
@pytest.fixture()
def TestState() -> Type[State]:
"""A default state.
Returns:
A default state.
"""
class TestState(State):
var: int
return TestState
def test_default_app(app: App):
"""Test creating an app with no args.
Args:
app: The app to test.
"""
assert app.state() == DefaultState()
def test_default_middleware(app: App) -> None:
"""Test creating an app with no middleware.
Args:
app: The app to test.
"""
assert app.middleware == [HydrateMiddleware()]
assert app.style == Style()
def test_add_page_default_route(app: App, index_page, about_page) -> None:
def test_add_page_default_route(app: App, index_page, about_page):
"""Test adding a page to an app.
Args:
@ -69,7 +79,7 @@ def test_add_page_default_route(app: App, index_page, about_page) -> None:
assert set(app.pages.keys()) == {"index", "about"}
def test_add_page_set_route(app: App, index_page) -> None:
def test_add_page_set_route(app: App, index_page):
"""Test adding a page to an app.
Args:
@ -79,3 +89,62 @@ def test_add_page_set_route(app: App, index_page) -> None:
assert app.pages == {}
app.add_page(index_page, path="/test")
assert set(app.pages.keys()) == {"test"}
def test_add_page_set_route_nested(app: App, index_page):
"""Test adding a page to an app.
Args:
app: The app to test.
index_page: The index page.
"""
assert app.pages == {}
app.add_page(index_page, path="/test/nested")
assert set(app.pages.keys()) == {"test/nested"}
def test_initialize_with_state(TestState: Type[State]):
"""Test setting the state of an app.
Args:
DefaultState: The default state.
"""
app = App(state=TestState)
assert app.state == TestState
# Get a state for a given token.
token = "token"
state = app.get_state(token)
assert isinstance(state, TestState)
assert state.var == 0
def test_set_and_get_state(TestState: Type[State]):
"""Test setting and getting the state of an app with different tokens.
Args:
DefaultState: The default state.
"""
app = App(state=TestState)
# Create two tokens.
token1 = "token1"
token2 = "token2"
# Get the default state for each token.
state1 = app.get_state(token1)
state2 = app.get_state(token2)
assert state1.var == 0
assert state2.var == 0
# Set the vars to different values.
state1.var = 1
state2.var = 2
app.set_state(token1, state1)
app.set_state(token2, state2)
# Get the states again and check the values.
state1 = app.get_state(token1)
state2 = app.get_state(token2)
assert state1.var == 1
assert state2.var == 2

47
tests/test_base.py Normal file
View File

@ -0,0 +1,47 @@
import pytest
from pynecone.base import Base
@pytest.fixture
def child() -> Base:
"""A child class.
Returns:
A child class.
"""
class Child(Base):
num: float
key: str
return Child(num=3.14, key="pi")
def test_get_fields(child):
"""Test that the fields are set correctly.
Args:
child: A child class.
"""
assert child.get_fields().keys() == {"num", "key"}
def test_set(child):
"""Test setting fields.
Args:
child: A child class.
"""
child.set(num=1, key="a")
assert child.num == 1
assert child.key == "a"
def test_json(child):
"""Test converting to json.
Args:
child: A child class.
"""
assert child.json().replace(" ", "") == '{"num":3.14,"key":"pi"}'

View File

@ -1,15 +1,35 @@
from datetime import datetime
import pytest
from pynecone.event import Event
from pynecone.event import Event, EventHandler, EventSpec
# def test_event_default_date():
# """Test that that the default date is set."""
# t1 = datetime.now()
def test_create_event():
"""Test creating an event."""
event = Event(token="token", name="state.do_thing", payload={"arg": "value"})
assert event.token == "token"
assert event.name == "state.do_thing"
assert event.payload == {"arg": "value"}
# e1 = Event(token="t", name="e1")
# e2 = Event(token="t", name="e2")
# t2 = datetime.now()
def test_call_event_handler():
"""Test that calling an event handler creates an event spec."""
# assert t1 < e1.date < e2.date < t2
def test_fn():
pass
def test_fn_with_args(_, arg1, arg2):
pass
handler = EventHandler(fn=test_fn)
event_spec = handler()
assert event_spec.handler == handler
assert event_spec.local_args == ()
assert event_spec.args == ()
handler = EventHandler(fn=test_fn_with_args)
event_spec = handler("first", "second")
assert event_spec.handler == handler
assert event_spec.local_args == ()
assert event_spec.args == (("arg1", "first"), ("arg2", "second"))

View File

View File

@ -643,14 +643,15 @@ async def test_process_event_simple(TestState):
test_state = TestState()
assert test_state.num1 == 0
event = Event(token="t", name="set_num1", payload={"num1": 69})
delta = await test_state.process(event)
event = Event(token="t", name="set_num1", payload={"value": 69})
update = await test_state.process(event)
# The event should update the value.
assert test_state.num1 == 69
# The delta should contain the changes, including computed vars.
assert delta == {"test_state": {"num1": 69, "sum": 72.14, "upper": ""}}
assert update.delta == {"test_state": {"num1": 69, "sum": 72.14, "upper": ""}}
assert update.events == []
@pytest.mark.asyncio
@ -672,10 +673,13 @@ async def test_process_event_substate(TestState, ChildState, GrandchildState):
event = Event(
token="t", name="child_state.change_both", payload={"value": "hi", "count": 12}
)
delta = await test_state.process(event)
update = await test_state.process(event)
assert child_state.value == "HI"
assert child_state.count == 24
assert delta == {"test_state.child_state": {"value": "HI", "count": 24}}
assert update.delta == {
"test_state.child_state": {"value": "HI", "count": 24},
"test_state": {"sum": 3.14, "upper": ""},
}
test_state.clean()
# Test with the granchild state.
@ -683,11 +687,14 @@ async def test_process_event_substate(TestState, ChildState, GrandchildState):
event = Event(
token="t",
name="child_state.grandchild_state.set_value2",
payload={"value2": "new"},
payload={"value": "new"},
)
delta = await test_state.process(event)
update = await test_state.process(event)
assert grandchild_state.value2 == "new"
assert delta == {"test_state.child_state.grandchild_state": {"value2": "new"}}
assert update.delta == {
"test_state.child_state.grandchild_state": {"value2": "new"},
"test_state": {"sum": 3.14, "upper": ""},
}
@pytest.mark.asyncio
@ -699,7 +706,7 @@ async def test_process_event_substate_set_parent_state(TestState, ChildState):
ChildState: The child state class.
"""
test_state = TestState()
event = Event(token="t", name="child_state.set_num1", payload={"num1": 69})
delta = await test_state.process(event)
event = Event(token="t", name="child_state.set_num1", payload={"value": 69})
update = await test_state.process(event)
assert test_state.num1 == 69
assert delta == {"test_state": {"num1": 69, "sum": 72.14, "upper": ""}}
assert update.delta == {"test_state": {"num1": 69, "sum": 72.14, "upper": ""}}

View File

@ -11,6 +11,8 @@ from pynecone import utils
("Hello", "hello"),
("camelCase", "camel_case"),
("camelTwoHumps", "camel_two_humps"),
("_start_with_underscore", "_start_with_underscore"),
("__start_with_double_underscore", "__start_with_double_underscore"),
],
)
def test_to_snake_case(input: str, output: str):
@ -169,3 +171,13 @@ def test_format_cond(condition: str, true_value: str, false_value: str, expected
expected: The expected output string.
"""
assert utils.format_cond(condition, true_value, false_value) == expected
def test_merge_imports():
"""Test that imports are merged correctly."""
d1 = {"react": {"Component"}}
d2 = {"react": {"Component"}, "react-dom": {"render"}}
d = utils.merge_imports(d1, d2)
assert set(d.keys()) == {"react", "react-dom"}
assert set(d["react"]) == {"Component"}
assert set(d["react-dom"]) == {"render"}

169
tests/test_var.py Normal file
View File

@ -0,0 +1,169 @@
import pytest
from pynecone.base import Base
from pynecone.var import BaseVar, Var
test_vars = [
BaseVar(name="prop1", type_=int),
BaseVar(name="key", type_=str),
BaseVar(name="value", type_=str, state="state"),
BaseVar(name="local", type_=str, state="state", is_local=True),
BaseVar(name="local2", type_=str, is_local=True),
]
@pytest.fixture
def TestObj():
class TestObj(Base):
foo: int
bar: str
return TestObj
@pytest.mark.parametrize(
"prop,expected",
zip(
test_vars,
[
"prop1",
"key",
"state.value",
"state.local",
"local2",
],
),
)
def test_full_name(prop, expected):
"""Test that the full name of a var is correct.
Args:
prop: The var to test.
expected: The expected full name.
"""
assert prop.full_name == expected
@pytest.mark.parametrize(
"prop,expected",
zip(
test_vars,
["{prop1}", "{key}", "{state.value}", "state.local", "local2"],
),
)
def test_str(prop, expected):
"""Test that the string representation of a var is correct.
Args:
prop: The var to test.
expected: The expected string representation.
"""
assert str(prop) == expected
@pytest.mark.parametrize(
"prop,expected",
[
(BaseVar(name="p", type_=int), 0),
(BaseVar(name="p", type_=float), 0.0),
(BaseVar(name="p", type_=str), ""),
(BaseVar(name="p", type_=bool), False),
(BaseVar(name="p", type_=list), []),
(BaseVar(name="p", type_=dict), {}),
(BaseVar(name="p", type_=tuple), ()),
(BaseVar(name="p", type_=set), set()),
],
)
def test_default_value(prop, expected):
"""Test that the default value of a var is correct.
Args:
prop: The var to test.
expected: The expected default value.
"""
assert prop.get_default_value() == expected
@pytest.mark.parametrize(
"prop,expected",
zip(
test_vars,
[
"set_prop1",
"set_key",
"state.set_value",
"state.set_local",
"set_local2",
],
),
)
def test_get_setter(prop, expected):
"""Test that the name of the setter function of a var is correct.
Args:
prop: The var to test.
expected: The expected name of the setter function.
"""
assert prop.get_setter_name() == expected
@pytest.mark.parametrize(
"value,expected",
[
(None, None),
(1, BaseVar(name="1", type_=int, is_local=True)),
("key", BaseVar(name="key", type_=str, is_local=True)),
(3.14, BaseVar(name="3.14", type_=float, is_local=True)),
([1, 2, 3], BaseVar(name="[1, 2, 3]", type_=list, is_local=True)),
(
{"a": 1, "b": 2},
BaseVar(name='{"a": 1, "b": 2}', type_=dict, is_local=True),
),
],
)
def test_create(value, expected):
"""Test the var create function.
Args:
value: The value to create a var from.
expected: The expected name of the setter function.
"""
prop = Var.create(value)
if value is None:
assert prop == expected
else:
assert prop.equals(expected) # type: ignore
def test_basic_operations(TestObj):
"""Test the var operations.
Args:
TestObj: The test object.
"""
def v(value) -> Var:
val = Var.create(value)
assert val is not None
return val
assert str(v(1) == v(2)) == "{(1 == 2)}"
assert str(v(1) != v(2)) == "{(1 != 2)}"
assert str(v(1) < v(2)) == "{(1 < 2)}"
assert str(v(1) <= v(2)) == "{(1 <= 2)}"
assert str(v(1) > v(2)) == "{(1 > 2)}"
assert str(v(1) >= v(2)) == "{(1 >= 2)}"
assert str(v(1) + v(2)) == "{(1 + 2)}"
assert str(v(1) - v(2)) == "{(1 - 2)}"
assert str(v(1) * v(2)) == "{(1 * 2)}"
assert str(v(1) / v(2)) == "{(1 / 2)}"
assert str(v(1) // v(2)) == "{Math.floor(1 / 2)}"
assert str(v(1) % v(2)) == "{(1 % 2)}"
assert str(v(1) ** v(2)) == "{Math.pow(1 , 2)}"
assert str(v(1) & v(2)) == "{(1 && 2)}"
assert str(v(1) | v(2)) == "{(1 || 2)}"
assert str(v([1, 2, 3])[v(0)]) == "{[1, 2, 3][0]}"
assert str(v({"a": 1, "b": 2})["a"]) == '{{"a": 1, "b": 2}["a"]}'
assert (
str(BaseVar(name="foo", state="state", type_=TestObj).bar) == "{state.foo.bar}"
)