1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4#
5# Copyright 2017, Data61
6# Commonwealth Scientific and Industrial Research Organisation (CSIRO)
7# ABN 41 687 119 230.
8#
9# This software may be distributed and modified according to the terms of
10# the BSD 2-Clause license. Note that NO WARRANTY is provided.
11# See "LICENSE_BSD2.txt" for details.
12#
13# @TAG(DATA61_BSD)
14#
15
16from __future__ import absolute_import, division, print_function, \
17    unicode_literals
18
19import collections, os, shutil, subprocess, tempfile, unittest
20
21def which(command):
22    with open(os.devnull, 'wt') as f:
23        try:
24            return subprocess.check_output(['which', command], stderr=f,
25                universal_newlines=True).strip()
26        except subprocess.CalledProcessError:
27            return None
28
29_cpp_available = None
30def cpp_available():
31    global _cpp_available
32    if _cpp_available is None:
33        _cpp_available = which('cpp')
34    return _cpp_available
35
36_spin_available = None
37def spin_available():
38    global _spin_available
39    if _spin_available is None:
40        _spin_available = which('spin')
41    return _spin_available
42
43_plyplus_introspectible = None
44def plyplus_introspectible():
45    '''
46    Returns True if plyplus' internals look like we expect.
47
48    This is irrelevant except for tests that need to reach into plyplus and
49    poke at its beating heart. If this function returns False, your only option
50    to re-enable tests that depend on it is to inspect the plyplus sources and
51    figure out what needs to be updated on our side.
52    '''
53    global _plyplus_introspectible
54    if _plyplus_introspectible is None:
55        try:
56            from plyplus.grammar_parser import parse
57            _plyplus_introspectible = True
58        except ImportError:
59            _plyplus_introspectible = False
60    return _plyplus_introspectible
61
62_c_compiler = None
63def c_compiler():
64    '''
65    Find a C compiler or return `None` if we don't have one.
66    '''
67    global _c_compiler
68    if _c_compiler is None:
69        for cc in ('ccomp', # CompCert
70                   'gcc', # GCC
71                   'clang', # Clang
72                   ):
73            _c_compiler = which(cc)
74            if _c_compiler is not None:
75                break
76    return _c_compiler
77
78_python_available = None
79def python_available():
80    '''
81    Test whether we can call out to the Python binary.
82    '''
83    global _python_available
84    if _python_available is None:
85        _python_available = which('python')
86    return _python_available
87
88_sha256sum_available = None
89def sha256sum_available():
90    '''
91    Test whether we can call out to sha256sum.
92    '''
93    global _sha256sum_available
94    if _sha256sum_available is None:
95        _sha256sum_available = which('sha256sum')
96    return _sha256sum_available
97
98class CAmkESTest(unittest.TestCase):
99    '''
100    A `unittest` test case with a few more bells and whistles.
101    '''
102    def setUp(self):
103        self.tempdirs = []
104        self.tempfiles = []
105
106    def mkdtemp(self):
107        tmp = tempfile.mkdtemp()
108        self.tempdirs.append(tmp)
109        return tmp
110
111    def mkstemp(self):
112        _, tmp = tempfile.mkstemp()
113        self.tempfiles.append(tmp)
114        return tmp
115
116    def execute(self, argv, cwd=os.getcwd(), env=os.environ):
117        p = subprocess.Popen(argv, stdout=subprocess.PIPE,
118            stderr=subprocess.PIPE, cwd=cwd, env=env, universal_newlines=True)
119        stdout, stderr = p.communicate()
120        return p.returncode, stdout, stderr
121
122    def tearDown(self):
123        [shutil.rmtree(t) for t in self.tempdirs]
124        [os.remove(t) for t in self.tempfiles]
125
126    def assertLen(self, container, *args):
127        '''
128        Assert the length of a variable.
129
130        I find this is a common operation I want to do in the CAmkES test suite
131        but there seems to be no `unittest` builtin for it.
132        '''
133        assert isinstance(container, collections.Iterable)
134        return self.assertEqual(len(container), *args)
135