Skip to content
Snippets Groups Projects
renderer.py 6.84 KiB
Newer Older
#!/usr/bin/env python3
###################################################################################
# Test case renderer.
# Create a HTML page from a YAML test case file.
#
# Copyright (C) 2018
# Luis Araujo <luis.araujo@collabora.co.uk>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.

# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.

# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  US
###################################################################################

import os
import yaml
from jinja2 import Environment, FileSystemLoader

from parser import parse_format
from exceptions import ParserTypeError, ParserMissingFieldError

TEMPLATE_DIR="."

# This command parses each line of a list and returns a structure of the form
# (comment, command, output) to apply the following formatting for the respective
# sections:
#
# pre-conditions:
# notes:
# expected:
#  - Start line with '$' for commands.
#  - Start line with '>' for command output.
#  - Start line with '@' to attach image.
#  - Start line with '~' to add web link.
#  - Everything else is a comment.
#
# run: steps: (only for automated tests)
#  - Start line with '#' for comments , everything else is a command.
#
def parse_list(lines, automated_run=False):
    processed_lines = []
    for line in lines:        
        p, c = '', ''
        sline = line.strip()
        if not sline:
            continue

        if automated_run:
            if sline[0] == '#':
                # Remove the '#' with the slice [1:]
                processed_lines.append((sline[1:], '', '', '', ''))
            else:
                # Add the '$ ' in the line for the command
                processed_lines.append(('', '$ '+sline, '', '', ''))
        else:
            if sline[0] == '$':
                processed_lines.append(('', sline, '', '', ''))
            elif sline[0] == '>':
                processed_lines.append(('', '', sline[1:].split('\n'), '', ''))
            elif sline[0] == '@':
                processed_lines.append(('', '', '', sline[1:], ''))
            elif sline[0] == '~':
                processed_lines.append(('', '', '', '', sline[1:]))
            else:
                processed_lines.append((sline, '', '', '', ''))

    return processed_lines

def priority_color(priority):
    return \
        (priority == 'low'      and 'secondary') or \
        (priority == 'medium'   and 'info')      or \
        (priority == 'high'     and 'warning')   or \
        (priority == 'critical' and 'danger')    or 'light'

def get_template_values(testcase_data):
    template_values = {}
    is_automated = False

    # Mandatory fields
    metadata = testcase_data.get('metadata')
    if not metadata:
        print("Error: missing mandatory field metadata")
        exit(1)

    for mv in ['name', 'image-type', 'image-arch', 'type',
               'exec-type', 'priority', 'description', 'expected']:
        value = metadata.get(mv)
        if value:
            if mv == 'exec-type' and value == 'automated':
                is_automated = True
            if mv == 'expected':
                template_values.update({ mv : parse_list(value) })
            else:
                # Set the priority_color.
                if mv == 'priority':
                    template_values.update({ 'priority_color' :
                                             priority_color(value) })
                template_values.update({ mv.replace('-', '_') : value })
        else:
            print("Error: missing mandatory field", mv)
            exit(1)

    run = testcase_data.get('run')
    if not run:
        print("Error: missing mandatory field run")
        exit(1)
    else:
        steps = run.get('steps')
        if not steps:
            print("Error: missing mandatory field steps for run")
            exit(1)
        # 'is_automated' so automated tests steps are parsed differently.
        template_values.update({ 'run_steps' :
                                 parse_list(steps, automated_run=is_automated) })

    # No mandatory fields
    for nm in ['notes', 'format', 'maintainer', 'resources',
               'pre-conditions', 'post-conditions']:
        value = metadata.get(nm)
        if value:
            template_values.update({ nm.replace('-', '_') : parse_list(value)
                                     if nm in ['notes', 'pre-conditions',
                                               'post-conditions']
                                     else value })

    install = testcase_data.get('install')
    if install:
        deps = install.get('deps')
        if not deps:
            print("Error: missing mandatory field deps for install")
            exit(1)
        template_values.update({ 'install_steps' : deps })
        # A install.deps directive will always add a preconditions section
        # to install the required packages using the macro to install packages.
        template_values.update({ 'packages_list' : ' '.join(deps) })

    # Macros variables
    ostree_preconditions = metadata.get('macro_ostree_preconditions')
    if ostree_preconditions:
        template_values.update({ 'pkgname' : ostree_preconditions })

    packages_list = metadata.get('macro_install_packages_preconditions')
    if packages_list:
        template_values.update({ 'packages_list' :  packages_list })

    modules_preconditions = metadata.get('macro_modules_preconditions')
    if modules_preconditions:
        template_values.update({ 'libname' : modules_preconditions })

    return template_values

def generate_test_case(tc_file, directory):
    try:
        with open(tc_file) as testcase:
            tc_data = yaml.safe_load(testcase)
    except yaml.scanner.ScannerError as e:
        print("yaml format error:", e)
        exit(1)

    # Parse file to detect any syntax error.
    print("Parsing file", tc_file)
    try:
        parse_format(tc_data)
    except (ParserTypeError, ParserMissingFieldError) as error:
        print("Error: " + str(error))
        exit(1)

    env = Environment(loader=FileSystemLoader([TEMPLATE_DIR]))
    # Get template from environment and render it.
    data = env.get_template('templates/index.html').render(get_template_values(tc_data))

    filename = os.path.splitext(os.path.basename(tc_file))[0] + ".html"
    print("Generating test case page", filename)
    with open(os.path.join(directory, filename), 'w') as html_file:
        html_file.write(data)