mirror of
https://github.com/wassname/optuna-dashboard.git
synced 2026-09-10 12:23:22 +08:00
Rename tests to python_tests
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
import json
|
||||
from unittest import TestCase
|
||||
|
||||
import optuna
|
||||
|
||||
from optuna_dashboard.app import create_app
|
||||
|
||||
from .wsgi_client import send_request
|
||||
|
||||
|
||||
def objective(trial: optuna.trial.Trial) -> float:
|
||||
x = trial.suggest_float("x", -1, 1)
|
||||
return x
|
||||
|
||||
|
||||
class APITestCase(TestCase):
|
||||
def test_get_study_summaries(self) -> None:
|
||||
storage = optuna.storages.InMemoryStorage()
|
||||
storage.create_new_study("foo1")
|
||||
storage.create_new_study("foo2")
|
||||
|
||||
app = create_app(storage)
|
||||
status, _, body = send_request(
|
||||
app,
|
||||
"/api/studies/",
|
||||
"GET",
|
||||
content_type="application/json",
|
||||
)
|
||||
self.assertEqual(status, 200)
|
||||
study_summaries = json.loads(body)["study_summaries"]
|
||||
self.assertEqual(len(study_summaries), 2)
|
||||
|
||||
def test_get_study_details_without_after_param(self) -> None:
|
||||
study = optuna.create_study()
|
||||
study_id = study._study_id
|
||||
study.optimize(objective, n_trials=2)
|
||||
app = create_app(study._storage)
|
||||
|
||||
status, _, body = send_request(
|
||||
app,
|
||||
f"/api/studies/{study_id}",
|
||||
"GET",
|
||||
content_type="application/json",
|
||||
)
|
||||
self.assertEqual(status, 200)
|
||||
all_trials = json.loads(body)["trials"]
|
||||
self.assertEqual(len(all_trials), 2)
|
||||
|
||||
def test_get_study_details_with_after_param_partial(self) -> None:
|
||||
study = optuna.create_study()
|
||||
study_id = study._study_id
|
||||
study.optimize(objective, n_trials=2)
|
||||
app = create_app(study._storage)
|
||||
|
||||
status, _, body = send_request(
|
||||
app,
|
||||
f"/api/studies/{study_id}",
|
||||
"GET",
|
||||
queries={"after": "1"},
|
||||
content_type="application/json",
|
||||
)
|
||||
self.assertEqual(status, 200)
|
||||
all_trials = json.loads(body)["trials"]
|
||||
self.assertEqual(len(all_trials), 1)
|
||||
|
||||
def test_get_study_details_with_after_param_full(self) -> None:
|
||||
study = optuna.create_study()
|
||||
study_id = study._study_id
|
||||
study.optimize(objective, n_trials=2)
|
||||
app = create_app(study._storage)
|
||||
|
||||
status, _, body = send_request(
|
||||
app,
|
||||
f"/api/studies/{study_id}",
|
||||
"GET",
|
||||
queries={"after": "2"},
|
||||
content_type="application/json",
|
||||
)
|
||||
self.assertEqual(status, 200)
|
||||
all_trials = json.loads(body)["trials"]
|
||||
self.assertEqual(len(all_trials), 0)
|
||||
|
||||
def test_get_study_details_with_after_param_illegal(self) -> None:
|
||||
study = optuna.create_study()
|
||||
study_id = study._study_id
|
||||
study.optimize(objective, n_trials=2)
|
||||
app = create_app(study._storage)
|
||||
|
||||
status, _, body = send_request(
|
||||
app,
|
||||
f"/api/studies/{study_id}",
|
||||
"GET",
|
||||
queries={"after": "-1"},
|
||||
content_type="application/json",
|
||||
)
|
||||
self.assertEqual(status, 400)
|
||||
|
||||
def test_create_study(self) -> None:
|
||||
for name, directions, expected_status in [
|
||||
("single-objective success", ["minimize"], 201),
|
||||
("multi-objective success", ["minimize", "maximize"], 201),
|
||||
("invalid direction name", ["invalid-direction", "maximize"], 400),
|
||||
]:
|
||||
with self.subTest(name):
|
||||
storage = optuna.storages.InMemoryStorage()
|
||||
self.assertEqual(len(storage.get_all_study_summaries()), 0)
|
||||
|
||||
app = create_app(storage)
|
||||
request_body = {
|
||||
"study_name": "foo",
|
||||
"directions": directions,
|
||||
}
|
||||
status, _, _ = send_request(
|
||||
app,
|
||||
"/api/studies",
|
||||
"POST",
|
||||
content_type="application/json",
|
||||
body=json.dumps(request_body),
|
||||
)
|
||||
self.assertEqual(status, expected_status)
|
||||
|
||||
if expected_status == 201:
|
||||
self.assertEqual(len(storage.get_all_study_summaries()), 1)
|
||||
else:
|
||||
self.assertEqual(len(storage.get_all_study_summaries()), 0)
|
||||
|
||||
def test_create_study_duplicated(self) -> None:
|
||||
storage = optuna.storages.InMemoryStorage()
|
||||
storage.create_new_study("foo")
|
||||
self.assertEqual(len(storage.get_all_study_summaries()), 1)
|
||||
|
||||
app = create_app(storage)
|
||||
request_body = {
|
||||
"study_name": "foo",
|
||||
"direction": "minimize",
|
||||
}
|
||||
status, _, _ = send_request(
|
||||
app,
|
||||
"/api/studies",
|
||||
"POST",
|
||||
content_type="application/json",
|
||||
body=json.dumps(request_body),
|
||||
)
|
||||
self.assertEqual(status, 400)
|
||||
self.assertEqual(len(storage.get_all_study_summaries()), 1)
|
||||
|
||||
def test_delete_study(self) -> None:
|
||||
storage = optuna.storages.InMemoryStorage()
|
||||
storage.create_new_study("foo1")
|
||||
storage.create_new_study("foo2")
|
||||
self.assertEqual(len(storage.get_all_study_summaries()), 2)
|
||||
|
||||
app = create_app(storage)
|
||||
status, _, _ = send_request(
|
||||
app,
|
||||
"/api/studies/1",
|
||||
"DELETE",
|
||||
content_type="application/json",
|
||||
)
|
||||
self.assertEqual(status, 204)
|
||||
self.assertEqual(len(storage.get_all_study_summaries()), 1)
|
||||
|
||||
def test_delete_study_not_found(self) -> None:
|
||||
storage = optuna.storages.InMemoryStorage()
|
||||
app = create_app(storage)
|
||||
status, _, _ = send_request(
|
||||
app,
|
||||
"/api/studies/1",
|
||||
"DELETE",
|
||||
content_type="application/json",
|
||||
)
|
||||
self.assertEqual(status, 404)
|
||||
|
||||
|
||||
class BottleRequestHookTestCase(TestCase):
|
||||
def test_ignore_trailing_slashes(self) -> None:
|
||||
storage = optuna.storages.InMemoryStorage()
|
||||
app = create_app(storage)
|
||||
|
||||
endpoints = ["/api/studies", "/api/studies/"]
|
||||
for endpoint in endpoints:
|
||||
with self.subTest(msg=endpoint):
|
||||
status, _, body = send_request(
|
||||
app,
|
||||
endpoint,
|
||||
"GET",
|
||||
content_type="application/json",
|
||||
)
|
||||
self.assertEqual(status, 200)
|
||||
@@ -0,0 +1,115 @@
|
||||
from unittest import TestCase
|
||||
import warnings
|
||||
|
||||
import optuna
|
||||
from optuna import create_trial
|
||||
from optuna.distributions import UniformDistribution
|
||||
from optuna.exceptions import ExperimentalWarning
|
||||
from optuna.trial import TrialState
|
||||
|
||||
from optuna_dashboard.search_space import _SearchSpace
|
||||
|
||||
|
||||
class SearchSpaceTestCase(TestCase):
|
||||
def setUp(self) -> None:
|
||||
optuna.logging.set_verbosity(optuna.logging.ERROR)
|
||||
warnings.simplefilter("ignore", category=ExperimentalWarning)
|
||||
|
||||
def test_same_distributions(self) -> None:
|
||||
distributions = [
|
||||
{
|
||||
"x0": UniformDistribution(low=0, high=10),
|
||||
"x1": UniformDistribution(low=0, high=10),
|
||||
},
|
||||
{
|
||||
"x0": UniformDistribution(low=0, high=10),
|
||||
"x1": UniformDistribution(low=0, high=10),
|
||||
},
|
||||
]
|
||||
params = [
|
||||
{
|
||||
"x0": 0.5,
|
||||
"x1": 0.5,
|
||||
},
|
||||
{
|
||||
"x0": 0.5,
|
||||
"x1": 0.5,
|
||||
},
|
||||
]
|
||||
trials = [
|
||||
create_trial(state=TrialState.COMPLETE, value=0, distributions=d, params=p)
|
||||
for d, p in zip(distributions, params)
|
||||
]
|
||||
search_space = _SearchSpace()
|
||||
search_space.update(trials)
|
||||
|
||||
self.assertEqual(len(search_space.intersection), 2)
|
||||
self.assertEqual(len(search_space.union), 2)
|
||||
|
||||
def test_different_distributions(self) -> None:
|
||||
distributions = [
|
||||
{
|
||||
"x0": UniformDistribution(low=0, high=10),
|
||||
"x1": UniformDistribution(low=0, high=10),
|
||||
},
|
||||
{
|
||||
"x0": UniformDistribution(low=0, high=5),
|
||||
"x1": UniformDistribution(low=0, high=10),
|
||||
},
|
||||
]
|
||||
params = [
|
||||
{
|
||||
"x0": 0.5,
|
||||
"x1": 0.5,
|
||||
},
|
||||
{
|
||||
"x0": 0.5,
|
||||
"x1": 0.5,
|
||||
},
|
||||
]
|
||||
trials = [
|
||||
create_trial(state=TrialState.COMPLETE, value=0, distributions=d, params=p)
|
||||
for d, p in zip(distributions, params)
|
||||
]
|
||||
search_space = _SearchSpace()
|
||||
search_space.update(trials)
|
||||
|
||||
self.assertEqual(len(search_space.intersection), 1)
|
||||
self.assertEqual(len(search_space.union), 3)
|
||||
|
||||
def test_dynamic_search_space(self) -> None:
|
||||
distributions = [
|
||||
{
|
||||
"x0": UniformDistribution(low=0, high=10),
|
||||
"x1": UniformDistribution(low=0, high=10),
|
||||
},
|
||||
{
|
||||
"x0": UniformDistribution(low=0, high=5),
|
||||
},
|
||||
{
|
||||
"x0": UniformDistribution(low=0, high=10),
|
||||
"x1": UniformDistribution(low=0, high=10),
|
||||
},
|
||||
]
|
||||
params = [
|
||||
{
|
||||
"x0": 0.5,
|
||||
"x1": 0.5,
|
||||
},
|
||||
{
|
||||
"x0": 0.5,
|
||||
},
|
||||
{
|
||||
"x0": 0.5,
|
||||
"x1": 0.5,
|
||||
},
|
||||
]
|
||||
trials = [
|
||||
create_trial(state=TrialState.COMPLETE, value=0, distributions=d, params=p)
|
||||
for d, p in zip(distributions, params)
|
||||
]
|
||||
search_space = _SearchSpace()
|
||||
search_space.update(trials)
|
||||
|
||||
self.assertEqual(len(search_space.intersection), 0)
|
||||
self.assertEqual(len(search_space.union), 3)
|
||||
@@ -0,0 +1,28 @@
|
||||
from unittest import TestCase
|
||||
|
||||
from optuna_dashboard.serializer import serialize_attrs
|
||||
|
||||
|
||||
class SerializeAttrsTestCase(TestCase):
|
||||
def test_serialize_bytes(self) -> None:
|
||||
serialized = serialize_attrs({"bytes": b"This is a bytes object."})
|
||||
self.assertEqual(serialized[0]["value"], "<binary object>")
|
||||
|
||||
def test_serialize_string(self) -> None:
|
||||
for length in [1000, 1024, 1100]:
|
||||
with self.subTest(f"length: {length}"):
|
||||
value = "a" * length
|
||||
serialized = serialize_attrs(
|
||||
{
|
||||
"key": value,
|
||||
}
|
||||
)
|
||||
self.assertLessEqual(len(serialized[0]["value"]), 1024)
|
||||
|
||||
def test_serialize_dict(self) -> None:
|
||||
serialized = serialize_attrs(
|
||||
{
|
||||
"key": {"foo": "bar"},
|
||||
}
|
||||
)
|
||||
self.assertLessEqual(len(serialized), 1)
|
||||
@@ -0,0 +1,77 @@
|
||||
import io
|
||||
from typing import Any
|
||||
from typing import Callable
|
||||
from typing import Dict
|
||||
from typing import Iterable
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Tuple
|
||||
from typing import Union
|
||||
|
||||
|
||||
WSGIEnv = Dict[str, Any] # Cannot use TypedDict because of 'HTTP_' variables
|
||||
StartResponse = Callable[[str, List[Tuple[str, str]]], None]
|
||||
WSGIApp = Callable[[WSGIEnv, StartResponse], Iterable[bytes]]
|
||||
|
||||
|
||||
def create_wsgi_env(
|
||||
path: str,
|
||||
method: str,
|
||||
content_type: str,
|
||||
body: bytes,
|
||||
queries: Dict[str, str],
|
||||
headers: Dict[str, str],
|
||||
) -> WSGIEnv:
|
||||
# 'key1=value1&key2=value2'
|
||||
query_string = "&".join([f"{k}={v}" for k, v in queries.items()])
|
||||
|
||||
# See https://www.python.org/dev/peps/pep-3333/#environ-variables
|
||||
env = {
|
||||
"PATH_INFO": path,
|
||||
"REQUEST_METHOD": method.upper(),
|
||||
"SCRIPT_NAME": "",
|
||||
"QUERY_STRING": query_string,
|
||||
"CONTENT_TYPE": content_type,
|
||||
"CONTENT_LENGTH": len(body),
|
||||
"SERVER_PROTOCOL": "http",
|
||||
"SERVER_NAME": "localhost",
|
||||
"wsgi.input": io.BytesIO(body),
|
||||
"wsgi.version": (1, 0),
|
||||
"wsgi.errors": io.StringIO(""),
|
||||
"wsgi.multithread": True,
|
||||
"wsgi.multitprocess": True,
|
||||
"wsgi.run_once": False,
|
||||
}
|
||||
for k, v in headers.items():
|
||||
env[f"HTTP_{k.upper()}"] = v
|
||||
return env
|
||||
|
||||
|
||||
def send_request(
|
||||
app: WSGIApp,
|
||||
path: str,
|
||||
method: str,
|
||||
body: Union[str, bytes] = b"",
|
||||
queries: Optional[Dict[str, str]] = None,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
content_type: str = "text/plain; charset=utf-8",
|
||||
) -> Tuple[int, List[Tuple[str, str]], bytes]:
|
||||
status: str = ""
|
||||
response_headers: List[Tuple[str, str]] = []
|
||||
|
||||
def start_response(status_: str, headers_: List[Tuple[str, str]]) -> None:
|
||||
nonlocal status, response_headers
|
||||
status = status_
|
||||
response_headers = headers_
|
||||
|
||||
bytes_body = body if isinstance(body, bytes) else body.encode("utf-8")
|
||||
headers = headers or {}
|
||||
queries = queries or {}
|
||||
env = create_wsgi_env(path, method, content_type, bytes_body, queries, headers)
|
||||
body = b""
|
||||
iterable_body = app(env, start_response)
|
||||
for b in iterable_body:
|
||||
body += b
|
||||
|
||||
status_code = int(status.split()[0])
|
||||
return status_code, response_headers, body
|
||||
Reference in New Issue
Block a user