1#!/usr/bin/env python3
2#
3# Copyright 2018, The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""Unittests for robolectric_test_runner."""
18
19# pylint: disable=line-too-long
20
21import json
22import platform
23import subprocess
24import tempfile
25import unittest
26
27from unittest import mock
28
29from test_finders import test_info
30from test_runners import event_handler
31from test_runners import robolectric_test_runner
32
33# pylint: disable=protected-access
34class RobolectricTestRunnerUnittests(unittest.TestCase):
35    """Unit tests for robolectric_test_runner.py"""
36
37    def setUp(self):
38        self.polling_time = robolectric_test_runner.POLL_FREQ_SECS
39        self.suite_tr = robolectric_test_runner.RobolectricTestRunner(results_dir='')
40
41    def tearDown(self):
42        mock.patch.stopall()
43
44    @mock.patch.object(robolectric_test_runner.RobolectricTestRunner, 'run')
45    def test_run_tests_raw(self, mock_run):
46        """Test run_tests_raw method."""
47        test_infos = [test_info.TestInfo("Robo1",
48                                         "RobolectricTestRunner",
49                                         ["RoboTest"])]
50        extra_args = []
51        mock_subproc = mock.Mock()
52        mock_run.return_value = mock_subproc
53        mock_subproc.returncode = 0
54        mock_reporter = mock.Mock()
55        # Test Build Pass
56        self.assertEqual(
57            0,
58            self.suite_tr.run_tests_raw(test_infos, extra_args, mock_reporter))
59        # Test Build Fail
60        mock_subproc.returncode = 1
61        self.assertNotEqual(
62            0,
63            self.suite_tr.run_tests_raw(test_infos, extra_args, mock_reporter))
64
65    @mock.patch.object(event_handler.EventHandler, 'process_event')
66    def test_exec_with_robo_polling_complete_information(self, mock_pe):
67        """Test _exec_with_robo_polling method."""
68        event_name = 'TEST_STARTED'
69        event_data = {'className':'SomeClass', 'testName':'SomeTestName'}
70
71        json_event_data = json.dumps(event_data)
72        data = '%s %s\n\n' %(event_name, json_event_data)
73        event_file = tempfile.NamedTemporaryFile(delete=True)
74        subprocess.call("echo '%s' -n >> %s" %(data, event_file.name), shell=True)
75        robo_proc = subprocess.Popen("sleep %s" %str(self.polling_time * 2), shell=True)
76        self.suite_tr. _exec_with_robo_polling(event_file, robo_proc, mock_pe)
77        calls = [mock.call.process_event(event_name, event_data)]
78        mock_pe.assert_has_calls(calls)
79
80    @mock.patch.object(event_handler.EventHandler, 'process_event')
81    def test_exec_with_robo_polling_with_partial_info(self, mock_pe):
82        """Test _exec_with_robo_polling method."""
83        event_name = 'TEST_STARTED'
84        event1 = '{"className":"SomeClass","test'
85        event2 = 'Name":"SomeTestName"}\n\n'
86        data1 = '%s %s'%(event_name, event1)
87        data2 = event2
88        event_file = tempfile.NamedTemporaryFile(delete=True)
89        subprocess.Popen("echo -n '%s' >> %s" %(data1, event_file.name), shell=True)
90        robo_proc = subprocess.Popen("echo '%s' >> %s && sleep %s"
91                                     %(data2,
92                                       event_file.name,
93                                       str(self.polling_time*5)),
94                                     shell=True)
95        self.suite_tr. _exec_with_robo_polling(event_file, robo_proc, mock_pe)
96        calls = [mock.call.process_event(event_name,
97                                         json.loads(event1 + event2))]
98        # (b/147569951) subprocessing 'echo'  behaves differently between
99        # linux/darwin. Ensure it is not called in MacOS.
100        if platform.system() == 'Linux':
101            mock_pe.assert_has_calls(calls)
102
103    @mock.patch.object(event_handler.EventHandler, 'process_event')
104    def test_exec_with_robo_polling_with_fail_stacktrace(self, mock_pe):
105        """Test _exec_with_robo_polling method."""
106        event_name = 'TEST_FAILED'
107        event_data = {'className':'SomeClass', 'testName':'SomeTestName',
108                      'trace':'{"trace":"AssertionError: <true> is equal to <false>\n'
109                              'at FailureStrategy.fail(FailureStrategy.java:24)\n'
110                              'at FailureStrategy.fail(FailureStrategy.java:20)\n'}
111        data = '%s %s\n\n'%(event_name, json.dumps(event_data))
112        event_file = tempfile.NamedTemporaryFile(delete=True)
113        subprocess.call("echo '%s' -n >> %s" %(data, event_file.name), shell=True)
114        robo_proc = subprocess.Popen("sleep %s" %str(self.polling_time * 2), shell=True)
115        self.suite_tr. _exec_with_robo_polling(event_file, robo_proc, mock_pe)
116        calls = [mock.call.process_event(event_name, event_data)]
117        mock_pe.assert_has_calls(calls)
118
119    @mock.patch.object(event_handler.EventHandler, 'process_event')
120    def test_exec_with_robo_polling_with_multi_event(self, mock_pe):
121        """Test _exec_with_robo_polling method."""
122        event_file = tempfile.NamedTemporaryFile(delete=True)
123        events = [
124            ('TEST_MODULE_STARTED', {
125                'moduleContextFileName':'serial-util1146216{974}2772610436.ser',
126                'moduleName':'someTestModule'}),
127            ('TEST_RUN_STARTED', {'testCount': 2}),
128            ('TEST_STARTED', {'start_time':52, 'className':'someClassName',
129                              'testName':'someTestName'}),
130            ('TEST_ENDED', {'end_time':1048, 'className':'someClassName',
131                            'testName':'someTestName'}),
132            ('TEST_STARTED', {'start_time':48, 'className':'someClassName2',
133                              'testName':'someTestName2'}),
134            ('TEST_FAILED', {'className':'someClassName2', 'testName':'someTestName2',
135                             'trace': 'someTrace'}),
136            ('TEST_ENDED', {'end_time':9876450, 'className':'someClassName2',
137                            'testName':'someTestName2'}),
138            ('TEST_RUN_ENDED', {}),
139            ('TEST_MODULE_ENDED', {'foo': 'bar'}),]
140        data = ''
141        for event in events:
142            data += '%s %s\n\n'%(event[0], json.dumps(event[1]))
143
144        subprocess.call("echo '%s' -n >> %s" %(data, event_file.name), shell=True)
145        robo_proc = subprocess.Popen("sleep %s" %str(self.polling_time * 2), shell=True)
146        self.suite_tr. _exec_with_robo_polling(event_file, robo_proc, mock_pe)
147        calls = [mock.call.process_event(name, data) for name, data in events]
148        mock_pe.assert_has_calls(calls)
149
150if __name__ == '__main__':
151    unittest.main()
152