Merge branch 'main' of github.com:Chroma-Case/Chromacase into front/play-page-connection
This commit is contained in:
9
.github/workflows/CI.yml
vendored
9
.github/workflows/CI.yml
vendored
@@ -96,6 +96,11 @@ jobs:
|
||||
docker-compose ps -a
|
||||
wget --retry-connrefused http://localhost:3000 # /healthcheck
|
||||
|
||||
- name: Run scorometer tests
|
||||
run: |
|
||||
pip install -r scorometer/requirements.txt
|
||||
cd scorometer/tests && ./runner.sh
|
||||
|
||||
- name: Run robot tests
|
||||
run: |
|
||||
pip install -r back/test/robot/requirements.txt
|
||||
@@ -106,7 +111,7 @@ jobs:
|
||||
name: results
|
||||
path: out
|
||||
|
||||
- name: Write results to Pull Request and Summarry
|
||||
- name: Write results to Pull Request and Summary
|
||||
if: always() && github.event_name == 'pull_request'
|
||||
uses: joonvena/robotframework-reporter-action@v2.1
|
||||
with:
|
||||
@@ -114,7 +119,7 @@ jobs:
|
||||
gh_access_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
only_summary: false
|
||||
|
||||
- name: Write results to Summarry
|
||||
- name: Write results to Summary
|
||||
if: always() && github.event_name != 'pull_request'
|
||||
uses: joonvena/robotframework-reporter-action@v2.1
|
||||
with:
|
||||
|
||||
21
musics/SCORO_TEST/SCORO_TEST.ini
Normal file
21
musics/SCORO_TEST/SCORO_TEST.ini
Normal file
@@ -0,0 +1,21 @@
|
||||
[Metadata]
|
||||
Name=Symphony No 9 in D Minor
|
||||
Artist=Beethoven
|
||||
Genre=Classical
|
||||
Album=Symphony No 9
|
||||
|
||||
[Difficulties]
|
||||
TwoHands=0
|
||||
Rhythm=4
|
||||
NoteCombo=0
|
||||
Arpeggio=6
|
||||
Distance=0
|
||||
LeftHand=2
|
||||
RightHand=1
|
||||
LeadHandChange=0
|
||||
ChordComplexity=0
|
||||
ChordTiming=0
|
||||
Length=1
|
||||
PedalPoint=0
|
||||
Precision=10
|
||||
|
||||
BIN
musics/SCORO_TEST/SCORO_TEST.midi
Normal file
BIN
musics/SCORO_TEST/SCORO_TEST.midi
Normal file
Binary file not shown.
BIN
musics/SCORO_TEST/SCORO_TEST.midi.bak
Normal file
BIN
musics/SCORO_TEST/SCORO_TEST.midi.bak
Normal file
Binary file not shown.
BIN
musics/SCORO_TEST/SCORO_TEST.mxl
Normal file
BIN
musics/SCORO_TEST/SCORO_TEST.mxl
Normal file
Binary file not shown.
@@ -1,11 +1,9 @@
|
||||
class Key:
|
||||
def __init__(self, key: int, start: int, duration: int):
|
||||
self.key = key
|
||||
self.start = start
|
||||
self.duration = duration
|
||||
self.done = False
|
||||
|
||||
def __repr__(self):
|
||||
return f"{self.key} ({self.start} - {self.duration})"
|
||||
|
||||
def __init__(self, key: int, start: int, duration: int):
|
||||
self.key = key
|
||||
self.start = start
|
||||
self.duration = duration
|
||||
self.done = False
|
||||
|
||||
def __repr__(self):
|
||||
return f"{self.key} ({self.start} - {self.duration})"
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
class Note:
|
||||
def __init__(self, start_time, data) -> None:
|
||||
def __init__(self, start_time, data) -> None:
|
||||
|
||||
self.__start_time = start_time
|
||||
self.__data = data
|
||||
self.__start_time = start_time
|
||||
self.__data = data
|
||||
|
||||
def get_start_time(self):
|
||||
return self.__start_time
|
||||
|
||||
def get_data(self):
|
||||
return self.__data
|
||||
def get_start_time(self):
|
||||
return self.__start_time
|
||||
|
||||
def get_data(self):
|
||||
return self.__data
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from .Key import Key
|
||||
|
||||
class Partition:
|
||||
|
||||
def __init__(self, name:str, notes:list[Key]) -> None:
|
||||
class Partition:
|
||||
def __init__(self, name: str, notes: list[Key]) -> None:
|
||||
|
||||
self.__name = name
|
||||
self.notes = notes
|
||||
@@ -12,4 +12,3 @@ class Partition:
|
||||
for i in self.notes:
|
||||
r += f"{i.__repr__()}\n"
|
||||
return r
|
||||
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
#!/usr/bin/python3
|
||||
from chroma_case.Partition import Partition
|
||||
from chroma_case.Key import Key
|
||||
import sys
|
||||
import select
|
||||
import os
|
||||
import itertools
|
||||
import requests
|
||||
import operator
|
||||
import json
|
||||
import logging
|
||||
import operator
|
||||
import os
|
||||
import select
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from typing import Literal, Tuple
|
||||
|
||||
import requests
|
||||
from chroma_case.Key import Key
|
||||
from chroma_case.Partition import Partition
|
||||
from mido import MidiFile
|
||||
from validated_dc import ValidatedDC, get_errors, is_valid
|
||||
|
||||
BACK_URL = os.environ.get("BACK_URL") or "http://back:3000"
|
||||
MUSICS_FOLDER = os.environ.get("MUSICS_FOLDER") or "/musics/"
|
||||
|
||||
RATIO = float(sys.argv[2] if len(sys.argv) > 2 else 1)
|
||||
OCTAVE = 5
|
||||
@@ -19,33 +27,104 @@ NORMAL = 0
|
||||
PRACTICE = 1
|
||||
|
||||
|
||||
@dataclass
|
||||
class InvalidMessage:
|
||||
message: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class StartMessage(ValidatedDC):
|
||||
id: int
|
||||
bearer: str
|
||||
mode: Literal["normal", "practice"]
|
||||
type: Literal["start"] = "start"
|
||||
|
||||
|
||||
@dataclass
|
||||
class EndMessage(ValidatedDC):
|
||||
type: Literal["end"] = "end"
|
||||
|
||||
|
||||
@dataclass
|
||||
class NoteOnMessage(ValidatedDC):
|
||||
time: int
|
||||
note: int
|
||||
id: int
|
||||
type: Literal["note_on"] = "note_on"
|
||||
|
||||
|
||||
@dataclass
|
||||
class NoteOffMessage(ValidatedDC):
|
||||
time: int
|
||||
note: int
|
||||
id: int
|
||||
type: Literal["note_off"] = "note_off"
|
||||
|
||||
|
||||
@dataclass
|
||||
class PauseMessage(ValidatedDC):
|
||||
paused: bool
|
||||
time: int
|
||||
type: Literal["pause"] = "pause"
|
||||
|
||||
|
||||
message_map = {
|
||||
"start": StartMessage,
|
||||
"end": EndMessage,
|
||||
"note_on": NoteOnMessage,
|
||||
"note_off": NoteOffMessage,
|
||||
"pause": PauseMessage,
|
||||
}
|
||||
|
||||
|
||||
def getMessage() -> (
|
||||
Tuple[
|
||||
StartMessage
|
||||
| EndMessage
|
||||
| NoteOnMessage
|
||||
| NoteOffMessage
|
||||
| PauseMessage
|
||||
| InvalidMessage,
|
||||
str,
|
||||
]
|
||||
):
|
||||
try:
|
||||
msg = input()
|
||||
obj = json.loads(msg)
|
||||
res = message_map[obj["type"]](**obj)
|
||||
if is_valid(res):
|
||||
return res, msg
|
||||
else:
|
||||
return InvalidMessage(str(get_errors(res))), msg
|
||||
except Exception as e:
|
||||
return InvalidMessage(str(e)), ""
|
||||
|
||||
|
||||
def send(o):
|
||||
print(json.dumps(o), flush=True)
|
||||
|
||||
def log(level, message):
|
||||
send({"type": "log", "level": level, "message": message})
|
||||
|
||||
def debug(message):
|
||||
log("DEBUG", message)
|
||||
|
||||
def info(message):
|
||||
log("INFO", message)
|
||||
|
||||
def warn(message):
|
||||
log("WARN", message)
|
||||
|
||||
def fatal(message):
|
||||
log("FATAL", message)
|
||||
|
||||
class Scorometer():
|
||||
def __init__(self, mode, midiFile) -> None:
|
||||
class Scorometer:
|
||||
def __init__(self, mode, midiFile, song_id, user_id) -> None:
|
||||
self.partition = self.getPartition(midiFile)
|
||||
self.keys_down = []
|
||||
self.score = 0
|
||||
self.mode = mode
|
||||
self.song_id = song_id
|
||||
self.user_id = user_id
|
||||
self.score = 0
|
||||
self.missed = 0
|
||||
self.perfect = 0
|
||||
self.great = 0
|
||||
self.good = 0
|
||||
self.difficulties = {}
|
||||
if mode == PRACTICE:
|
||||
get_start = operator.attrgetter("start")
|
||||
self.practice_partition = [list(g) for _, g in itertools.groupby(sorted(self.partition.notes, key=get_start), get_start)]
|
||||
self.practice_partition = [
|
||||
list(g)
|
||||
for _, g in itertools.groupby(
|
||||
sorted(self.partition.notes, key=get_start), get_start
|
||||
)
|
||||
]
|
||||
else:
|
||||
self.practice_partition: list[list[Key]] = []
|
||||
|
||||
@@ -56,165 +135,268 @@ class Scorometer():
|
||||
prev_note_on = {}
|
||||
for msg in MidiFile(midiFile):
|
||||
d = msg.dict()
|
||||
s += d['time'] * 1000 * RATIO
|
||||
s += d["time"] * 1000 * RATIO
|
||||
|
||||
if d["type"] == "note_on":
|
||||
prev_note_on[d["note"]] = 0
|
||||
if d["note"] in notes_on:
|
||||
prev_note_on[d["note"]] = notes_on[d["note"]] # 500
|
||||
notes_on[d["note"]] = s # 0
|
||||
notes_on[d["note"]] = s # 0
|
||||
|
||||
if d["type"] == "note_off":
|
||||
duration = s - notes_on[d["note"]]
|
||||
note_start = notes_on[d["note"]]
|
||||
notes.append(Key(d["note"], note_start, duration - 10))
|
||||
notes_on[d["note"]] = s # 500
|
||||
notes_on[d["note"]] = s # 500
|
||||
return Partition(midiFile, notes)
|
||||
|
||||
def handleNote(self, obj):
|
||||
_key = obj["note"]
|
||||
status = obj["type"]
|
||||
timestamp = obj["time"]
|
||||
def handleNoteOn(self, message: NoteOnMessage):
|
||||
_key = message.note
|
||||
timestamp = message.time
|
||||
is_down = any(x[0] == _key for x in self.keys_down)
|
||||
key = None
|
||||
if status == "note_on" and not is_down:
|
||||
if not is_down:
|
||||
self.keys_down.append((_key, timestamp))
|
||||
debug({"note": _key})
|
||||
elif status == "note_off" or is_down:
|
||||
down_since = next(since for (h_key, since) in self.keys_down if h_key == _key)
|
||||
self.keys_down.remove((_key, down_since))
|
||||
key = Key(_key, down_since, (timestamp - down_since))
|
||||
#debug({key: key})
|
||||
if key is None:
|
||||
return
|
||||
to_play = next((i for i in self.partition.notes if i.key == key.key and self.is_timing_close(key, i) and i.done == False), None)
|
||||
if to_play == None:
|
||||
logging.debug({"note": _key})
|
||||
|
||||
def handleNoteOff(self, message: NoteOffMessage):
|
||||
_key = message.note
|
||||
timestamp = message.time
|
||||
down_since = next(since for (h_key, since) in self.keys_down if h_key == _key)
|
||||
self.keys_down.remove((_key, down_since))
|
||||
key = Key(_key, down_since, (timestamp - down_since))
|
||||
# debug({key: key})
|
||||
to_play = next(
|
||||
(
|
||||
i
|
||||
for i in self.partition.notes
|
||||
if i.key == key.key and self.is_timing_close(key, i) and i.done is False
|
||||
),
|
||||
None,
|
||||
)
|
||||
if to_play is None:
|
||||
self.score -= 50
|
||||
debug(f"Invalid key.")
|
||||
logging.info("Invalid key.")
|
||||
else:
|
||||
timingScore, timingInformation = self.getTiming(key, to_play)
|
||||
self.score += 100 if timingScore == "perfect" else 75 if timingScore == "great" else 50
|
||||
self.score += (
|
||||
100
|
||||
if timingScore == "perfect"
|
||||
else 75
|
||||
if timingScore == "great"
|
||||
else 50
|
||||
)
|
||||
to_play.done = True
|
||||
self.sendScore(obj["id"], timingScore, timingInformation)
|
||||
self.sendScore(message.id, timingScore, timingInformation)
|
||||
|
||||
def handleNotePractice(self, obj):
|
||||
_key = obj["note"]
|
||||
status = obj["type"]
|
||||
timestamp = obj["time"]
|
||||
def handleNoteOnPractice(self, message: NoteOnMessage):
|
||||
_key = message.note
|
||||
timestamp = message.time
|
||||
is_down = any(x[0] == _key for x in self.keys_down)
|
||||
key = None
|
||||
if status == "note_on" and not is_down:
|
||||
if not is_down:
|
||||
self.keys_down.append((_key, timestamp))
|
||||
debug({"note": _key})
|
||||
elif status == "note_off" or is_down:
|
||||
down_since = next(since for (h_key, since) in self.keys_down if h_key == _key)
|
||||
self.keys_down.remove((_key, down_since))
|
||||
key = Key(_key, down_since, (timestamp - down_since))
|
||||
#debug({key: key})
|
||||
if key is None:
|
||||
return
|
||||
keys_to_play = next((i for i in self.practice_partition if any(x.done != True for x in i)), None)
|
||||
logging.debug({"note": _key})
|
||||
|
||||
def handleNoteOffPractice(self, message: NoteOffMessage):
|
||||
_key = message.note
|
||||
timestamp = message.time
|
||||
# is_down = any(x[0] == _key for x in self.keys_down)
|
||||
down_since = next(since for (h_key, since) in self.keys_down if h_key == _key)
|
||||
self.keys_down.remove((_key, down_since))
|
||||
key = Key(_key, down_since, (timestamp - down_since))
|
||||
keys_to_play = next(
|
||||
(i for i in self.practice_partition if any(x.done is not True for x in i)),
|
||||
None,
|
||||
)
|
||||
if keys_to_play is None:
|
||||
warn("Key sent but there is no keys to play")
|
||||
logging.info("Key sent but there is no keys to play")
|
||||
self.score -= 50
|
||||
return
|
||||
to_play = next((i for i in keys_to_play if i.key == key.key and i.done != True), None)
|
||||
if to_play == None:
|
||||
to_play = next(
|
||||
(i for i in keys_to_play if i.key == key.key and i.done is not True), None
|
||||
)
|
||||
if to_play is None:
|
||||
self.score -= 50
|
||||
debug(f"Invalid key.")
|
||||
logging.info("Invalid key.")
|
||||
else:
|
||||
timingScore, _ = self.getTiming(key, to_play)
|
||||
self.score += 100 if timingScore == "perfect" else 75 if timingScore == "great" else 50
|
||||
self.score += (
|
||||
100
|
||||
if timingScore == "perfect"
|
||||
else 75
|
||||
if timingScore == "great"
|
||||
else 50
|
||||
)
|
||||
to_play.done = True
|
||||
self.sendScore(obj["id"], timingScore, "practice")
|
||||
self.sendScore(message.id, timingScore, "practice")
|
||||
|
||||
def getTiming(self, key: Key, to_play: Key):
|
||||
return self.getTimingScore(key, to_play), self.getTimingInfo(key, to_play)
|
||||
|
||||
def getTimingScore(self, key: Key, to_play: Key):
|
||||
tempo_percent = abs((key.duration / to_play.duration) - 1)
|
||||
if tempo_percent < .3:
|
||||
if tempo_percent < 0.3:
|
||||
timingScore = "perfect"
|
||||
elif tempo_percent < .5:
|
||||
timingScore = f"great"
|
||||
elif tempo_percent < 0.5:
|
||||
timingScore = "great"
|
||||
else:
|
||||
timingScore = "good"
|
||||
return timingScore
|
||||
|
||||
def getTimingInfo(self, key: Key, to_play: Key):
|
||||
return "perfect" if abs(key.start - to_play.start) < 200 else "fast" if key.start < to_play.start else "late"
|
||||
return (
|
||||
"perfect"
|
||||
if abs(key.start - to_play.start) < 200
|
||||
else "fast"
|
||||
if key.start < to_play.start
|
||||
else "late"
|
||||
)
|
||||
|
||||
# is it in the 500 ms range
|
||||
def is_timing_close(self, key: Key, i: Key):
|
||||
return abs(i.start - key.start) < 500
|
||||
|
||||
def handleMessage(self, message: str):
|
||||
obj = json.loads(message)
|
||||
if "type" not in obj.keys():
|
||||
warn(f"Could not handle message {message}")
|
||||
return
|
||||
if obj["type"] == "note_on" or obj["type"] == "note_off":
|
||||
if self.mode == NORMAL:
|
||||
self.handleNote(obj)
|
||||
elif self.mode == PRACTICE:
|
||||
self.handleNotePractice(obj)
|
||||
if obj["type"] == "pause":
|
||||
pass
|
||||
def handleMessage(
|
||||
self,
|
||||
message: StartMessage
|
||||
| EndMessage
|
||||
| NoteOnMessage
|
||||
| NoteOffMessage
|
||||
| PauseMessage
|
||||
| InvalidMessage,
|
||||
line: str,
|
||||
):
|
||||
match message:
|
||||
case InvalidMessage(error):
|
||||
logging.warning(f"Invalid message {line} with error: {error}")
|
||||
send({"error": f"Invalid message {line} with error: {error}"})
|
||||
case NoteOnMessage():
|
||||
if self.mode == NORMAL:
|
||||
self.handleNoteOn(message)
|
||||
elif self.mode == PRACTICE:
|
||||
self.handleNoteOnPractice(message)
|
||||
case NoteOffMessage():
|
||||
if self.mode == NORMAL:
|
||||
self.handleNoteOff(message)
|
||||
elif self.mode == PRACTICE:
|
||||
self.handleNoteOffPractice(message)
|
||||
case PauseMessage():
|
||||
pass
|
||||
case EndMessage():
|
||||
self.endGame()
|
||||
case _:
|
||||
logging.warning(
|
||||
f"Expected note_on note_off or pause message but got {message.type} instead"
|
||||
)
|
||||
|
||||
def sendScore(self, id, timingScore, timingInformation):
|
||||
send({"id": id, "timingScore": timingScore, "timingInformation": timingInformation})
|
||||
send(
|
||||
{
|
||||
"id": id,
|
||||
"timingScore": timingScore,
|
||||
"timingInformation": timingInformation,
|
||||
}
|
||||
)
|
||||
|
||||
def gameLoop(self):
|
||||
while True:
|
||||
if select.select([sys.stdin, ], [], [], 0.0)[0]:
|
||||
line = input()
|
||||
if not line:
|
||||
break
|
||||
info(f"handling message {line}")
|
||||
self.handleMessage(line.rstrip())
|
||||
if select.select(
|
||||
[
|
||||
sys.stdin,
|
||||
],
|
||||
[],
|
||||
[],
|
||||
0.0,
|
||||
)[0]:
|
||||
message, line = getMessage()
|
||||
logging.info(f"handling message {line}")
|
||||
self.handleMessage(message, line)
|
||||
else:
|
||||
pass
|
||||
return self.score, {}
|
||||
|
||||
def handleStartMessage(start_message):
|
||||
if "type" not in start_message.keys():
|
||||
raise Exception("type of start message not specified")
|
||||
if start_message["type"] != "start":
|
||||
raise Exception("start message is not of type start")
|
||||
if "id" not in start_message.keys():
|
||||
raise Exception("id of song not specified in start message")
|
||||
if "mode" not in start_message.keys():
|
||||
raise Exception("mode of song not specified in start message")
|
||||
if "user_id" not in start_message.keys():
|
||||
raise Exception("user_id not specified in start message")
|
||||
mode = PRACTICE if start_message["mode"] == "practice" else NORMAL
|
||||
# TODO get song path from the API
|
||||
song_id = start_message["id"]
|
||||
# TODO: use something secure here but I don't find sending a jwt something elegant.
|
||||
user_id = start_message["user_id"]
|
||||
song_path = requests.get(f"http://back:3000/song/{song_id}").json()["midiPath"]
|
||||
def endGame(self):
|
||||
for i in self.partition.notes:
|
||||
if i.done is False:
|
||||
self.score -= 50
|
||||
send(
|
||||
{
|
||||
"overallScore": self.score,
|
||||
"score": {
|
||||
"missed": self.missed,
|
||||
"good": self.good,
|
||||
"great": self.great,
|
||||
"perfect": self.perfect,
|
||||
"maxScore": len(self.partition.notes) * 100,
|
||||
},
|
||||
}
|
||||
)
|
||||
if self.user_id != -1:
|
||||
requests.post(
|
||||
f"{BACK_URL}/history",
|
||||
json={
|
||||
"songID": self.song_id,
|
||||
"userID": self.user_id,
|
||||
"score": self.score,
|
||||
"difficulties": self.difficulties,
|
||||
},
|
||||
)
|
||||
exit()
|
||||
|
||||
|
||||
def handleStartMessage(start_message: StartMessage):
|
||||
mode = PRACTICE if start_message.mode == "practice" else NORMAL
|
||||
song_id = start_message.id
|
||||
user_id = -1
|
||||
try:
|
||||
if start_message.bearer != "":
|
||||
r = requests.get(
|
||||
f"{BACK_URL}/auth/me",
|
||||
headers={"Authorization": f"Bearer {start_message.bearer}"},
|
||||
)
|
||||
r.raise_for_status()
|
||||
user_id = r.json()["id"]
|
||||
except Exception as e:
|
||||
logging.fatal("Could not get user id with given bearer", exc_info=e)
|
||||
send({"error": "Could not get user id with given bearer"})
|
||||
exit()
|
||||
|
||||
try:
|
||||
r = requests.get(f"{BACK_URL}/song/{song_id}")
|
||||
r.raise_for_status()
|
||||
song_path = r.json()["midiPath"]
|
||||
song_path = song_path.replace("/musics/", MUSICS_FOLDER)
|
||||
except Exception as e:
|
||||
logging.fatal("Invalid song id", exc_info=e)
|
||||
send({"error": "Invalid song id, song does not exist"})
|
||||
exit()
|
||||
return mode, song_path, song_id, user_id
|
||||
|
||||
|
||||
def sendScore(score, difficulties, song_id, user_id):
|
||||
send({"overallScore": score, "score": difficulties})
|
||||
requests.post(f"http://back:3000/history", json={
|
||||
"songID": song_id,
|
||||
"userID": user_id,
|
||||
"score": score,
|
||||
"difficulties": difficulties,
|
||||
})
|
||||
def startGame(start_message: StartMessage):
|
||||
mode, song_path, song_id, user_id = handleStartMessage(start_message)
|
||||
sc = Scorometer(mode, song_path, song_id, user_id)
|
||||
sc.gameLoop()
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
start_message = json.loads(input())
|
||||
mode, song_path, song_id, user_id = handleStartMessage(start_message)
|
||||
sc = Scorometer(mode, song_path)
|
||||
score, difficulties = sc.gameLoop()
|
||||
sendScore(score, difficulties, song_id, user_id)
|
||||
except Exception as error:
|
||||
send({ "error": error })
|
||||
msg, _ = getMessage()
|
||||
match msg:
|
||||
case StartMessage():
|
||||
startGame(msg)
|
||||
case EndMessage():
|
||||
logging.info("scorometer ended before a start message")
|
||||
send({"error": "Did not receive a start message"})
|
||||
exit()
|
||||
case InvalidMessage(error):
|
||||
logging.warning(f"invalid message with error: {error}")
|
||||
send({"error": "Invalid input, expected a start message"})
|
||||
case _:
|
||||
logging.warning(f"invalid message with type: {msg.type}")
|
||||
send({"error": "Invalid input, expected a start message"})
|
||||
except Exception as e:
|
||||
logging.fatal("error", exc_info=e)
|
||||
send({"error": "a fatal error occured"})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
mido
|
||||
requests
|
||||
black-with-tabs
|
||||
validated-dc
|
||||
|
||||
21
scorometer/tests/almost_perfect_play/input
Normal file
21
scorometer/tests/almost_perfect_play/input
Normal file
@@ -0,0 +1,21 @@
|
||||
{"type":"start", "id": 1, "mode": "normal", "bearer": ""}
|
||||
{"type": "note_on", "id": 2, "time": 3750, "note": 67}
|
||||
{"type": "note_off", "id": 2, "time": 3980, "note": 67}
|
||||
{"type": "note_on", "id": 3, "time": 4000, "note": 62}
|
||||
{"type": "note_off", "id": 3, "time": 4240, "note": 62}
|
||||
{"type": "note_on", "id": 4, "time": 4000, "note": 64}
|
||||
{"type": "note_off", "id": 4, "time": 4240, "note": 64}
|
||||
{"type": "note_on", "id": 5, "time": 4000, "note": 60}
|
||||
{"type": "note_off", "id": 5, "time": 4240, "note": 60}
|
||||
{"type": "note_on", "id": 6, "time": 4500, "note": 63}
|
||||
{"type": "note_off", "id": 6, "time": 4740, "note": 63}
|
||||
{"type": "note_on", "id": 7, "time": 4750, "note": 63}
|
||||
{"type": "note_off", "id": 7, "time": 4980, "note": 63}
|
||||
{"type": "note_on", "id": 8, "time": 5000, "note": 63}
|
||||
{"type": "note_off", "id": 8, "time": 5990, "note": 63}
|
||||
{"type": "note_on", "id": 9, "time": 6500, "note": 62}
|
||||
{"type": "note_off", "id": 9, "time": 6990, "note": 62}
|
||||
{"type": "note_on", "id": 10, "time": 6750, "note": 60}
|
||||
{"type": "note_off", "id": 10, "time": 7240, "note": 60}
|
||||
{"type": "end"}
|
||||
|
||||
10
scorometer/tests/almost_perfect_play/output
Normal file
10
scorometer/tests/almost_perfect_play/output
Normal file
@@ -0,0 +1,10 @@
|
||||
{"id": 2, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"id": 3, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"id": 4, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"id": 5, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"id": 6, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"id": 7, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"id": 8, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"id": 9, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"id": 10, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"overallScore": 850, "score": {"missed": 0, "good": 0, "great": 0, "perfect": 0, "maxScore": 1000}}
|
||||
23
scorometer/tests/early/input
Normal file
23
scorometer/tests/early/input
Normal file
@@ -0,0 +1,23 @@
|
||||
{"type":"start", "id": 1, "mode": "normal", "bearer": ""}
|
||||
{"type": "note_on", "id": 1, "time": 3250, "note": 68}
|
||||
{"type": "note_off", "id": 1, "time": 3490, "note": 68}
|
||||
{"type": "note_on", "id": 2, "time": 3500, "note": 67}
|
||||
{"type": "note_off", "id": 2, "time": 3730, "note": 67}
|
||||
{"type": "note_on", "id": 3, "time": 3750, "note": 62}
|
||||
{"type": "note_off", "id": 3, "time": 3990, "note": 62}
|
||||
{"type": "note_on", "id": 4, "time": 3750, "note": 64}
|
||||
{"type": "note_off", "id": 4, "time": 3990, "note": 64}
|
||||
{"type": "note_on", "id": 5, "time": 3750, "note": 60}
|
||||
{"type": "note_off", "id": 5, "time": 3990, "note": 60}
|
||||
{"type": "note_on", "id": 6, "time": 4250, "note": 63}
|
||||
{"type": "note_off", "id": 6, "time": 4490, "note": 63}
|
||||
{"type": "note_on", "id": 7, "time": 4500, "note": 63}
|
||||
{"type": "note_off", "id": 7, "time": 4730, "note": 63}
|
||||
{"type": "note_on", "id": 8, "time": 4750, "note": 63}
|
||||
{"type": "note_off", "id": 8, "time": 5740, "note": 63}
|
||||
{"type": "note_on", "id": 9, "time": 6250, "note": 62}
|
||||
{"type": "note_off", "id": 9, "time": 6740, "note": 62}
|
||||
{"type": "note_on", "id": 10, "time": 6500, "note": 60}
|
||||
{"type": "note_off", "id": 10, "time": 6990, "note": 60}
|
||||
{"type": "end"}
|
||||
|
||||
11
scorometer/tests/early/output
Normal file
11
scorometer/tests/early/output
Normal file
@@ -0,0 +1,11 @@
|
||||
{"id": 1, "timingScore": "perfect", "timingInformation": "fast"}
|
||||
{"id": 2, "timingScore": "perfect", "timingInformation": "fast"}
|
||||
{"id": 3, "timingScore": "perfect", "timingInformation": "fast"}
|
||||
{"id": 4, "timingScore": "perfect", "timingInformation": "fast"}
|
||||
{"id": 5, "timingScore": "perfect", "timingInformation": "fast"}
|
||||
{"id": 6, "timingScore": "perfect", "timingInformation": "fast"}
|
||||
{"id": 7, "timingScore": "perfect", "timingInformation": "fast"}
|
||||
{"id": 8, "timingScore": "perfect", "timingInformation": "fast"}
|
||||
{"id": 9, "timingScore": "perfect", "timingInformation": "fast"}
|
||||
{"id": 10, "timingScore": "perfect", "timingInformation": "fast"}
|
||||
{"overallScore": 1000, "score": {"missed": 0, "good": 0, "great": 0, "perfect": 0, "maxScore": 1000}}
|
||||
19
scorometer/tests/end_miss/input
Normal file
19
scorometer/tests/end_miss/input
Normal file
@@ -0,0 +1,19 @@
|
||||
{"type":"start", "id": 1, "mode": "normal", "bearer": ""}
|
||||
{"type": "note_on", "id": 1, "time": 3500, "note": 68}
|
||||
{"type": "note_off", "id": 1, "time": 3740, "note": 68}
|
||||
{"type": "note_on", "id": 2, "time": 3750, "note": 67}
|
||||
{"type": "note_off", "id": 2, "time": 3980, "note": 67}
|
||||
{"type": "note_on", "id": 3, "time": 4000, "note": 62}
|
||||
{"type": "note_off", "id": 3, "time": 4240, "note": 62}
|
||||
{"type": "note_on", "id": 4, "time": 4000, "note": 64}
|
||||
{"type": "note_off", "id": 4, "time": 4240, "note": 64}
|
||||
{"type": "note_on", "id": 5, "time": 4000, "note": 60}
|
||||
{"type": "note_off", "id": 5, "time": 4240, "note": 60}
|
||||
{"type": "note_on", "id": 6, "time": 4500, "note": 63}
|
||||
{"type": "note_off", "id": 6, "time": 4740, "note": 63}
|
||||
{"type": "note_on", "id": 7, "time": 4750, "note": 63}
|
||||
{"type": "note_off", "id": 7, "time": 4980, "note": 63}
|
||||
{"type": "note_on", "id": 8, "time": 5000, "note": 63}
|
||||
{"type": "note_off", "id": 8, "time": 5990, "note": 63}
|
||||
{"type": "end"}
|
||||
|
||||
9
scorometer/tests/end_miss/output
Normal file
9
scorometer/tests/end_miss/output
Normal file
@@ -0,0 +1,9 @@
|
||||
{"id": 1, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"id": 2, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"id": 3, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"id": 4, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"id": 5, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"id": 6, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"id": 7, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"id": 8, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"overallScore": 700, "score": {"missed": 0, "good": 0, "great": 0, "perfect": 0, "maxScore": 1000}}
|
||||
23
scorometer/tests/hold_not_enough/input
Normal file
23
scorometer/tests/hold_not_enough/input
Normal file
@@ -0,0 +1,23 @@
|
||||
{"type":"start", "id": 1, "mode": "normal", "bearer": ""}
|
||||
{"type": "note_on", "id": 1, "time": 3500, "note": 68}
|
||||
{"type": "note_off", "id": 1, "time": 3540, "note": 68}
|
||||
{"type": "note_on", "id": 2, "time": 3750, "note": 67}
|
||||
{"type": "note_off", "id": 2, "time": 3780, "note": 67}
|
||||
{"type": "note_on", "id": 3, "time": 4000, "note": 62}
|
||||
{"type": "note_off", "id": 3, "time": 4040, "note": 62}
|
||||
{"type": "note_on", "id": 4, "time": 4000, "note": 64}
|
||||
{"type": "note_off", "id": 4, "time": 4040, "note": 64}
|
||||
{"type": "note_on", "id": 5, "time": 4000, "note": 60}
|
||||
{"type": "note_off", "id": 5, "time": 4040, "note": 60}
|
||||
{"type": "note_on", "id": 6, "time": 4500, "note": 63}
|
||||
{"type": "note_off", "id": 6, "time": 4540, "note": 63}
|
||||
{"type": "note_on", "id": 7, "time": 4750, "note": 63}
|
||||
{"type": "note_off", "id": 7, "time": 4780, "note": 63}
|
||||
{"type": "note_on", "id": 8, "time": 5000, "note": 63}
|
||||
{"type": "note_off", "id": 8, "time": 5290, "note": 63}
|
||||
{"type": "note_on", "id": 9, "time": 6500, "note": 62}
|
||||
{"type": "note_off", "id": 9, "time": 6690, "note": 62}
|
||||
{"type": "note_on", "id": 10, "time": 6750, "note": 60}
|
||||
{"type": "note_off", "id": 10, "time": 6840, "note": 60}
|
||||
{"type": "end"}
|
||||
|
||||
11
scorometer/tests/hold_not_enough/output
Normal file
11
scorometer/tests/hold_not_enough/output
Normal file
@@ -0,0 +1,11 @@
|
||||
{"id": 1, "timingScore": "good", "timingInformation": "perfect"}
|
||||
{"id": 2, "timingScore": "good", "timingInformation": "perfect"}
|
||||
{"id": 3, "timingScore": "good", "timingInformation": "perfect"}
|
||||
{"id": 4, "timingScore": "good", "timingInformation": "perfect"}
|
||||
{"id": 5, "timingScore": "good", "timingInformation": "perfect"}
|
||||
{"id": 6, "timingScore": "good", "timingInformation": "perfect"}
|
||||
{"id": 7, "timingScore": "good", "timingInformation": "perfect"}
|
||||
{"id": 8, "timingScore": "good", "timingInformation": "perfect"}
|
||||
{"id": 9, "timingScore": "good", "timingInformation": "perfect"}
|
||||
{"id": 10, "timingScore": "good", "timingInformation": "perfect"}
|
||||
{"overallScore": 500, "score": {"missed": 0, "good": 0, "great": 0, "perfect": 0, "maxScore": 1000}}
|
||||
23
scorometer/tests/hold_too_long/input
Normal file
23
scorometer/tests/hold_too_long/input
Normal file
@@ -0,0 +1,23 @@
|
||||
{"type":"start", "id": 1, "mode": "normal", "bearer": ""}
|
||||
{"type": "note_on", "id": 1, "time": 3500, "note": 68}
|
||||
{"type": "note_off", "id": 1, "time": 3990, "note": 68}
|
||||
{"type": "note_on", "id": 2, "time": 3750, "note": 67}
|
||||
{"type": "note_off", "id": 2, "time": 4230, "note": 67}
|
||||
{"type": "note_on", "id": 3, "time": 4000, "note": 62}
|
||||
{"type": "note_off", "id": 3, "time": 4490, "note": 62}
|
||||
{"type": "note_on", "id": 4, "time": 4000, "note": 64}
|
||||
{"type": "note_off", "id": 4, "time": 4490, "note": 64}
|
||||
{"type": "note_on", "id": 5, "time": 4000, "note": 60}
|
||||
{"type": "note_off", "id": 5, "time": 4490, "note": 60}
|
||||
{"type": "note_on", "id": 6, "time": 4500, "note": 63}
|
||||
{"type": "note_off", "id": 6, "time": 4990, "note": 63}
|
||||
{"type": "note_on", "id": 7, "time": 4750, "note": 63}
|
||||
{"type": "note_off", "id": 7, "time": 5230, "note": 63}
|
||||
{"type": "note_on", "id": 8, "time": 5000, "note": 63}
|
||||
{"type": "note_off", "id": 8, "time": 6490, "note": 63}
|
||||
{"type": "note_on", "id": 9, "time": 6500, "note": 62}
|
||||
{"type": "note_off", "id": 9, "time": 7240, "note": 62}
|
||||
{"type": "note_on", "id": 10, "time": 6750, "note": 60}
|
||||
{"type": "note_off", "id": 10, "time": 7490, "note": 60}
|
||||
{"type": "end"}
|
||||
|
||||
11
scorometer/tests/hold_too_long/output
Normal file
11
scorometer/tests/hold_too_long/output
Normal file
@@ -0,0 +1,11 @@
|
||||
{"id": 1, "timingScore": "good", "timingInformation": "perfect"}
|
||||
{"id": 2, "timingScore": "good", "timingInformation": "perfect"}
|
||||
{"id": 3, "timingScore": "good", "timingInformation": "perfect"}
|
||||
{"id": 4, "timingScore": "good", "timingInformation": "perfect"}
|
||||
{"id": 5, "timingScore": "good", "timingInformation": "perfect"}
|
||||
{"id": 6, "timingScore": "good", "timingInformation": "perfect"}
|
||||
{"id": 7, "timingScore": "good", "timingInformation": "perfect"}
|
||||
{"id": 8, "timingScore": "good", "timingInformation": "perfect"}
|
||||
{"id": 9, "timingScore": "good", "timingInformation": "perfect"}
|
||||
{"id": 10, "timingScore": "good", "timingInformation": "perfect"}
|
||||
{"overallScore": 500, "score": {"missed": 0, "good": 0, "great": 0, "perfect": 0, "maxScore": 1000}}
|
||||
2
scorometer/tests/invalid_song/input
Normal file
2
scorometer/tests/invalid_song/input
Normal file
@@ -0,0 +1,2 @@
|
||||
{"type":"start", "id": 0, "mode": "normal", "bearer": ""}
|
||||
|
||||
1
scorometer/tests/invalid_song/output
Normal file
1
scorometer/tests/invalid_song/output
Normal file
@@ -0,0 +1 @@
|
||||
{"error": "Invalid song id, song does not exist"}
|
||||
23
scorometer/tests/late/input
Normal file
23
scorometer/tests/late/input
Normal file
@@ -0,0 +1,23 @@
|
||||
{"type":"start", "id": 1, "mode": "normal", "bearer": ""}
|
||||
{"type": "note_on", "id": 1, "time": 3750, "note": 68}
|
||||
{"type": "note_off", "id": 1, "time": 3990, "note": 68}
|
||||
{"type": "note_on", "id": 2, "time": 4000, "note": 67}
|
||||
{"type": "note_off", "id": 2, "time": 4230, "note": 67}
|
||||
{"type": "note_on", "id": 3, "time": 4250, "note": 62}
|
||||
{"type": "note_off", "id": 3, "time": 4490, "note": 62}
|
||||
{"type": "note_on", "id": 4, "time": 4250, "note": 64}
|
||||
{"type": "note_off", "id": 4, "time": 4490, "note": 64}
|
||||
{"type": "note_on", "id": 5, "time": 4250, "note": 60}
|
||||
{"type": "note_off", "id": 5, "time": 4490, "note": 60}
|
||||
{"type": "note_on", "id": 6, "time": 4750, "note": 63}
|
||||
{"type": "note_off", "id": 6, "time": 4990, "note": 63}
|
||||
{"type": "note_on", "id": 7, "time": 5000, "note": 63}
|
||||
{"type": "note_off", "id": 7, "time": 5230, "note": 63}
|
||||
{"type": "note_on", "id": 8, "time": 5250, "note": 63}
|
||||
{"type": "note_off", "id": 8, "time": 6240, "note": 63}
|
||||
{"type": "note_on", "id": 9, "time": 6750, "note": 62}
|
||||
{"type": "note_off", "id": 9, "time": 7240, "note": 62}
|
||||
{"type": "note_on", "id": 10, "time": 7000, "note": 60}
|
||||
{"type": "note_off", "id": 10, "time": 7490, "note": 60}
|
||||
{"type": "end"}
|
||||
|
||||
11
scorometer/tests/late/output
Normal file
11
scorometer/tests/late/output
Normal file
@@ -0,0 +1,11 @@
|
||||
{"id": 1, "timingScore": "perfect", "timingInformation": "late"}
|
||||
{"id": 2, "timingScore": "perfect", "timingInformation": "late"}
|
||||
{"id": 3, "timingScore": "perfect", "timingInformation": "late"}
|
||||
{"id": 4, "timingScore": "perfect", "timingInformation": "late"}
|
||||
{"id": 5, "timingScore": "perfect", "timingInformation": "late"}
|
||||
{"id": 6, "timingScore": "perfect", "timingInformation": "late"}
|
||||
{"id": 7, "timingScore": "perfect", "timingInformation": "late"}
|
||||
{"id": 8, "timingScore": "perfect", "timingInformation": "late"}
|
||||
{"id": 9, "timingScore": "perfect", "timingInformation": "late"}
|
||||
{"id": 10, "timingScore": "perfect", "timingInformation": "late"}
|
||||
{"overallScore": 1000, "score": {"missed": 0, "good": 0, "great": 0, "perfect": 0, "maxScore": 1000}}
|
||||
23
scorometer/tests/perfect_play/input
Normal file
23
scorometer/tests/perfect_play/input
Normal file
@@ -0,0 +1,23 @@
|
||||
{"type":"start", "id": 1, "mode": "normal", "bearer": ""}
|
||||
{"type": "note_on", "id": 1, "time": 3500, "note": 68}
|
||||
{"type": "note_off", "id": 1, "time": 3740, "note": 68}
|
||||
{"type": "note_on", "id": 2, "time": 3750, "note": 67}
|
||||
{"type": "note_off", "id": 2, "time": 3980, "note": 67}
|
||||
{"type": "note_on", "id": 3, "time": 4000, "note": 62}
|
||||
{"type": "note_off", "id": 3, "time": 4240, "note": 62}
|
||||
{"type": "note_on", "id": 4, "time": 4000, "note": 64}
|
||||
{"type": "note_off", "id": 4, "time": 4240, "note": 64}
|
||||
{"type": "note_on", "id": 5, "time": 4000, "note": 60}
|
||||
{"type": "note_off", "id": 5, "time": 4240, "note": 60}
|
||||
{"type": "note_on", "id": 6, "time": 4500, "note": 63}
|
||||
{"type": "note_off", "id": 6, "time": 4740, "note": 63}
|
||||
{"type": "note_on", "id": 7, "time": 4750, "note": 63}
|
||||
{"type": "note_off", "id": 7, "time": 4980, "note": 63}
|
||||
{"type": "note_on", "id": 8, "time": 5000, "note": 63}
|
||||
{"type": "note_off", "id": 8, "time": 5990, "note": 63}
|
||||
{"type": "note_on", "id": 9, "time": 6500, "note": 62}
|
||||
{"type": "note_off", "id": 9, "time": 6990, "note": 62}
|
||||
{"type": "note_on", "id": 10, "time": 6750, "note": 60}
|
||||
{"type": "note_off", "id": 10, "time": 7240, "note": 60}
|
||||
{"type": "end"}
|
||||
|
||||
11
scorometer/tests/perfect_play/output
Normal file
11
scorometer/tests/perfect_play/output
Normal file
@@ -0,0 +1,11 @@
|
||||
{"id": 1, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"id": 2, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"id": 3, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"id": 4, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"id": 5, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"id": 6, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"id": 7, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"id": 8, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"id": 9, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"id": 10, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"overallScore": 1000, "score": {"missed": 0, "good": 0, "great": 0, "perfect": 0, "maxScore": 1000}}
|
||||
19
scorometer/tests/random_miss/input
Normal file
19
scorometer/tests/random_miss/input
Normal file
@@ -0,0 +1,19 @@
|
||||
{"type":"start", "id": 1, "mode": "normal", "bearer": ""}
|
||||
{"type": "note_on", "id": 1, "time": 3500, "note": 68}
|
||||
{"type": "note_off", "id": 1, "time": 3740, "note": 68}
|
||||
{"type": "note_on", "id": 3, "time": 4000, "note": 62}
|
||||
{"type": "note_off", "id": 3, "time": 4240, "note": 62}
|
||||
{"type": "note_on", "id": 4, "time": 4000, "note": 64}
|
||||
{"type": "note_off", "id": 4, "time": 4240, "note": 64}
|
||||
{"type": "note_on", "id": 5, "time": 4000, "note": 60}
|
||||
{"type": "note_off", "id": 5, "time": 4240, "note": 60}
|
||||
{"type": "note_on", "id": 6, "time": 4500, "note": 63}
|
||||
{"type": "note_off", "id": 6, "time": 4740, "note": 63}
|
||||
{"type": "note_on", "id": 8, "time": 5000, "note": 63}
|
||||
{"type": "note_off", "id": 8, "time": 5990, "note": 63}
|
||||
{"type": "note_on", "id": 9, "time": 6500, "note": 62}
|
||||
{"type": "note_off", "id": 9, "time": 6990, "note": 62}
|
||||
{"type": "note_on", "id": 10, "time": 6750, "note": 60}
|
||||
{"type": "note_off", "id": 10, "time": 7240, "note": 60}
|
||||
{"type": "end"}
|
||||
|
||||
9
scorometer/tests/random_miss/output
Normal file
9
scorometer/tests/random_miss/output
Normal file
@@ -0,0 +1,9 @@
|
||||
{"id": 1, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"id": 3, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"id": 4, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"id": 5, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"id": 6, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"id": 8, "timingScore": "good", "timingInformation": "late"}
|
||||
{"id": 9, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"id": 10, "timingScore": "perfect", "timingInformation": "perfect"}
|
||||
{"overallScore": 650, "score": {"missed": 0, "good": 0, "great": 0, "perfect": 0, "maxScore": 1000}}
|
||||
43
scorometer/tests/runner.sh
Executable file
43
scorometer/tests/runner.sh
Executable file
@@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
|
||||
EMPTY_DB=$(curl localhost:3000/song/1 -s | jq '.statusCode == 404')
|
||||
if [[ $EMPTY_DB == "true" ]]; then
|
||||
curl localhost:3000/song -X POST --data '{"name": "SCORO_TEST", "difficulties": {}, "midiPath": "/musics/SCORO_TEST/SCORO_TEST.midi", "musicXmlPath": "/musics/SCORO_TEST/SCORO_TEST.mxl"}' -H "Content-Type: application/json" &> /dev/null
|
||||
fi
|
||||
|
||||
TESTS_DONE=0
|
||||
TESTS_SUCCESS=0
|
||||
TESTS_FAILED=0
|
||||
|
||||
function test {
|
||||
cat $1/input | BACK_URL="http://localhost:3000" MUSICS_FOLDER="../../musics/" python3 ../main.py 1> /tmp/scorometer_res 2> /tmp/scorometer_log
|
||||
TESTS_DONE=$((TESTS_DONE + 1))
|
||||
if ! diff $1/output /tmp/scorometer_res &>/dev/null; then
|
||||
echo "$t failed, do runner.sh $t for more info"
|
||||
TESTS_FAILED=$((TESTS_FAILED + 1))
|
||||
else
|
||||
TESTS_SUCCESS=$((TESTS_SUCCESS + 1))
|
||||
fi
|
||||
}
|
||||
|
||||
if [ -z "$1" ];
|
||||
then
|
||||
for t in */; do
|
||||
test $t
|
||||
done
|
||||
exit $TESTS_FAILED
|
||||
else
|
||||
cat $1/input | BACK_URL="http://localhost:3000" MUSICS_FOLDER="../../musics/" python3 ../main.py 1> /tmp/scorometer_res 2> /tmp/scorometer_log
|
||||
echo "=========== CURRENT OUTPUT ==========="
|
||||
cat /tmp/scorometer_res
|
||||
echo "======================================"
|
||||
echo "=========== EXPECTED OUTPUT =========="
|
||||
cat $1/output
|
||||
echo "======================================"
|
||||
echo "=============== DIFF ================="
|
||||
diff --side-by-side -q /tmp/scorometer_res $1/output
|
||||
RET=$?
|
||||
echo "======================================"
|
||||
exit $RET
|
||||
fi;
|
||||
|
||||
BIN
scorometer/tests/test_midi.png
Normal file
BIN
scorometer/tests/test_midi.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 83 KiB |
Reference in New Issue
Block a user