majordome_test.py 8.64 KB
#!/usr/bin/env python

import os
import sys
import django
import subprocess
import fileinput
import time
from django.shortcuts import get_object_or_404
from django.conf import settings as djangosettings
import datetime

# DONE LATER:
# sys.path.append('..')
# from common.models import Config, PlcDeviceStatus


DEBUG = True

agent = "majordome"

fixture = "tests/majordome_test"

# venv_bin = '../../private/venv_py3_pyros/bin/python'
venv_bin = "../private/venv_py3_pyros/bin/python"
venv_bin2 = "../../private/venv_py3_pyros/bin/python"


def tearDown(self):
    os.chdir("..")
    self.replacePatternInFile(
        "MAJORDOME_TEST = True", "MAJORDOME_TEST = False", "pyros/settings.py"
    )
    os.chdir("majordome")


def replacePatternInFileOrError(pattern, replace, file_path):
    with fileinput.FileInput(file_path, inplace=True, backup=".bak") as file:
        for line in file:
            print(line.replace(pattern, replace), end="")
    # Now, check that replacement has been done or ERROR !!!
    FOUND = False
    # with fileinput.FileInput(file_path) as file:
    with open(file_path) as file:
        for line in file:
            # if replace in line:
            if line.startswith(replace):
                FOUND = True
                break
    # if FOUND: print("pattern "+replace+" found in file "+file_path)
    if not FOUND:
        raise (Exception("pattern " + replace + " not found in file " + file_path))


def execProcessAsync(command):
    # self.printFullTerm(Colors.BLUE, "Executing command [" + command + "]")
    p = subprocess.Popen(command, shell=True)
    """
    self.subproc.append((p, command))
    self.printFullTerm(Colors.GREEN, "Process launched successfully")
    self.addExecuted(self.current_command, command)
    """
    return p


def execProcessFromVenvSync(command: str):
    print(f"Execute command from venv as a process: {command}")
    command = venv_bin + " " + command
    args = command.split()
    process = subprocess.Popen(args)
    process.wait()
    if process.returncode == 0:
        print("Process executed successfully")
    else:
        print("Process execution failed")
    return process.returncode


def execProcessFromVenvAsync(command: str):
    command = venv_bin2 + " " + command
    args = command.split()
    # self.printFullTerm(Colors.BLUE, "Executing command from venv [" + str(' '.join(args[1:])) + "]")
    p = subprocess.Popen(args)
    # self.subproc.append((p, ' '.join(args[1:])))
    # self.printFullTerm(Colors.GREEN, "Process launched successfully")
    # self.addExecuted(self.current_command, str(' '.join(args[1:])))
    return p


def init_database():
    # Go into src/
    # os.chdir('..')
    print("Current directory : " + str(os.getcwd()))
    # If executed the very first time, it raises errors, so executes it later
    # execProcessFromVenvSync("manage.py flush  --noinput")
    execProcessFromVenvSync("manage.py makemigrations")
    execProcessFromVenvSync("manage.py migrate")
    # Executed here, it is ok
    execProcessFromVenvSync("manage.py flush  --noinput")
    # Do we really need now to make migrations and to migrate again (a second time) ? (not sure)
    execProcessFromVenvSync("manage.py makemigrations")
    execProcessFromVenvSync("manage.py migrate")
    # Load fixture:
    execProcessFromVenvSync(
        "manage.py loaddata misc" + os.sep + "fixtures" + os.sep + fixture
    )


def getConfigFromDB():
    return get_object_or_404(Config, id=1)


def changeDirectory(path):
    # if DEBUG: print("Moving to : " + path)
    os.chdir(path)
    if DEBUG:
        print("NEW Current directory : " + str(os.getcwd()))


def assert_majordome_has_now_state(state: str) -> bool:
    config = getConfigFromDB()
    print()
    # print("Majordome state should now be", config.pyros_state)
    print("=> Majordome state should now be", state)
    assert config.pyros_state == state
    print("=> CHECKED")
    print()


def print_step_message(step_num: int, message: str):
    print()
    print("--------------")
    print(f"{step_num}) {message}")
    print("--------------")


def go_sleeping(nbsec: int):
    print(f"GO TO SLEEP {nbsec} seconds...")
    time.sleep(nbsec)
    print("...STOP SLEEPING")


i = 0
print("Current directory : " + str(os.getcwd()))


i += 1
print_step_message(
    i, "Change pyros/settings just for this test, so that it uses test database"
)
# Go into src/
os.chdir("..")
replacePatternInFileOrError(
    "MAJORDOME_TEST = False", "MAJORDOME_TEST = True", "pyros/settings.py"
)
# os.chdir('majordome')


i += 1
print_step_message(i, "Init test database")
init_database()


i += 1
print_step_message(i, "Django setup")
sys.path.append("..")
# print(sys.path)
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "src.pyros.settings")
django.setup()
assert djangosettings.DATABASES["default"]["NAME"] == "pyros_test"


i += 1
print_step_message(i, "Get config")
# Needs sys.path.append('..')
from common.models import Config, PlcDeviceStatus

config = get_object_or_404(Config, id=1)
print("config id is", config.id)
print("config latitude is", config.latitude)
print("config global mode is", config.global_mode)
print("config row_data_save_frequency is", config.row_data_save_frequency)

# with print_lock:
print("global mode is", getConfigFromDB().global_mode)
# sys.stdout.write("global mode")
# sys.stdout.flush()
print("global mode is", getConfigFromDB().global_mode)
print("DB1 used is:", djangosettings.DATABASES["default"]["NAME"])
go_sleeping(2)
# ...


"""
# Launch the Majordome agent
"""
i += 1
print_step_message(i, "Launch Majordome agent")
os.chdir(agent)
# p = execProcessFromVenvAsync(venv_bin + ' start_agent_'+agent+'_from_test.py')
# p = execProcessFromVenvAsync(venv_bin + ' start_agent_'+agent+'.py')
majordome = execProcessFromVenvAsync("start_agent_" + agent + ".py" + " test")
# p = self.execProcessFromVenvAsync('./start_agent_'+agent+'.py')
# p.wait()
go_sleeping(10)
# ...


"""
# START TESTING Majordome
"""
i += 1
print_step_message(i, "Start testing Majordome states")
plcstatus = get_object_or_404(PlcDeviceStatus, id=1)

# Now, Majordome should be in state "PASSIVE no PLC" (waiting for PLC)
assert_majordome_has_now_state("PASSIVE_NO_PLC")

print("\n- I now simulate that PLC is OK:")
# Let's pretend that PLC is now OK (via DB)
# ==> check that it goes to state "PASSIVE" (waiting for PLC "AUTO")
# plcstatus.created = "2018-07-24 11:49:49"
# ex: '2018-07-27T10:12:38.875112'
plcstatus.created = datetime.datetime.now().isoformat()
plcstatus.save()
go_sleeping(5)
# ...
assert_majordome_has_now_state("PASSIVE")

print("\n- I now simulate that PLC is AUTO:")
# Let's pretend that PLC is now AUTO (via DB)
# ==> check that it goes to state "STANDBY"
plcstatus.plc_mode = "AUTO"
plcstatus.save()
go_sleeping(15)
# ...
assert_majordome_has_now_state("Standby")

print("\n- I now simulate that Majordome is going REMOTE mode:")
# Let's pretend that we are going REMOTE mode
# ==> check that it goes to state "REMOTE"
config.global_mode = False
config.lock = False
config.save()
go_sleeping(10)
# ...
assert_majordome_has_now_state("Remote")

# Let's pretend that PLC is now UNSAFE
# ==> check that shutter is closing
# TODO:
# plc_status.is_safe

# Let's pretend that PLC is now SAFE again
# ==> check that shutter is opening
# TODO:

print("\n- I now simulate that Majordome is going SCHEDULER mode:")
# Let's pretend that we are going SCHEDULER mode
# ==> check that it goes to state "STANDBY"
config.global_mode = True
config.lock = False
config.save()
go_sleeping(10)
# ...
assert_majordome_has_now_state("Standby")

# Let's set conditions for going to SCHEDULER mode
# ==> check that it goes to state "SCHEDULER"
# TODO:
# self.is_night() and self.plc_is_safe() and self.config.ack and not self.config.lock

# Let's set conditions for leaving the SCHEDULER mode
# ==> check that it goes to state "STANDBY"
# TODO:


"""
# KILL agent Majordome (using DB config table)
"""
i += 1
print_step_message(i, "Stop (kill) Majordome agent process")
# Let's set conditions for stopping the Majordome
# ==> check that it goes to state config.majordome_state = "RUNNING"
config.majordome_state = "STOP"
config.save()
go_sleeping(5)
# ...
config = getConfigFromDB()
print("config is", config.majordome_state)
assert config.majordome_state == "RUNNING"

##majordome.kill()

# self.execProcessAsync("ps aux | grep \"start_agent_majordome.py\" | awk '{ print $2 }' | xargs kill")
# self.execProcessAsync("ps aux | grep start_agent_majordome.py | awk '{ print $2 }' | xargs kill")
#'''
go_sleeping(5)
# ...


# thread_majordome.join()
# thread_majordome.killExecutingSequence()

i += 1
print_step_message(i, "Restore pyros/settings as before")
os.chdir("..")
replacePatternInFileOrError(
    "MAJORDOME_TEST = True", "MAJORDOME_TEST = False", "pyros/settings.py"
)
# os.chdir('majordome')