From 71f74dd16568210697f7c47d8375bcc6ceb94f5d Mon Sep 17 00:00:00 2001 From: Ruslan Kabalin Date: Mon, 22 Feb 2016 16:09:33 +0000 Subject: [PATCH] MDL-50887 antivirus_clamav: Add unit tests for scanner class. We are using "stubbing" method to replace real component (clamav) control points with doubles in order to avoid direct calls to clamav. For more details, please refer to https://phpunit.de/manual/4.8/en/test-doubles.html Notice, that while it is possible to use setExpectedException in the tests, this does not seem play correctly with consecutive assertions in the same test after exception is thrown (they seem omitted). Explicit try/catch construction has been used in testing instead. To run just this test, use: vendor/bin/phpunit antivirus_clamav_scanner_testcase lib/antivirus/clamav/tests/scanner_test.php --- lib/antivirus/clamav/tests/scanner_test.php | 174 ++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 lib/antivirus/clamav/tests/scanner_test.php diff --git a/lib/antivirus/clamav/tests/scanner_test.php b/lib/antivirus/clamav/tests/scanner_test.php new file mode 100644 index 00000000000..0a81d42efbb --- /dev/null +++ b/lib/antivirus/clamav/tests/scanner_test.php @@ -0,0 +1,174 @@ +. + +/** + * Tests for ClamAV antivirus scanner class. + * + * @package antivirus_clamav + * @category phpunit + * @copyright 2016 Ruslan Kabalin, Lancaster University. + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +class antivirus_clamav_scanner_testcase extends advanced_testcase { + protected $tempfile; + + protected function setUp() { + $this->resetAfterTest(); + + // Create tempfile. + $tempfolder = make_request_directory(false); + $this->tempfile = $tempfolder . '/' . rand(); + touch($this->tempfile); + } + + protected function tearDown() { + @unlink($this->tempfile); + } + + public function test_scan_file_not_exists() { + $antivirus = $this->getMockBuilder('\antivirus_clamav\scanner') + ->setMethods(array('scan_file_execute_commandline', 'message_admins')) + ->getMock(); + + // Test specifying file that does not exist. + $nonexistingfile = $this->tempfile . '_'; + $this->assertFileNotExists($nonexistingfile); + // Run mock scanning with deleting infected file. + $antivirus->scan_file($nonexistingfile, '', true); + $this->assertDebuggingCalled(); + } + + public function test_scan_file_no_virus() { + $antivirus = $this->getMockBuilder('\antivirus_clamav\scanner') + ->setMethods(array('scan_file_execute_commandline', 'message_admins')) + ->getMock(); + + // Configure scan_file_execute_commandline method stub to behave + // as if no virus has been found. + $antivirus->method('scan_file_execute_commandline')->willReturn(array(0, '')); + + // Set expectation that message_admins is NOT called. + $antivirus->expects($this->never())->method('message_admins'); + + // Run mock scanning with deleting infected file. + $this->assertFileExists($this->tempfile); + try { + $antivirus->scan_file($this->tempfile, '', true); + } catch (\core\antivirus\scanner_exception $e) { + $this->fail('Exception scanner_exception is not expected in clean file scanning.'); + } + // File expected to remain in place. + $this->assertFileExists($this->tempfile); + } + + public function test_scan_file_virus() { + $antivirus = $this->getMockBuilder('\antivirus_clamav\scanner') + ->setMethods(array('scan_file_execute_commandline', 'message_admins')) + ->getMock(); + + // Configure scan_file_execute_commandline method stub to behave + // as if virus has been found. + $antivirus->method('scan_file_execute_commandline')->willReturn(array(1, '')); + + // Set expectation that message_admins is NOT called. + $antivirus->expects($this->never())->method('message_admins'); + + // Run mock scanning without deleting infected file. + $this->assertFileExists($this->tempfile); + try { + $antivirus->scan_file($this->tempfile, '', false); + } catch (\moodle_exception $e) { + $this->assertInstanceOf('\core\antivirus\scanner_exception', $e); + } + // File expected to remain in place. + $this->assertFileExists($this->tempfile); + + // Run mock scanning with deleting infected file. + try { + $antivirus->scan_file($this->tempfile, '', true); + } catch (\moodle_exception $e) { + $this->assertInstanceOf('\core\antivirus\scanner_exception', $e); + } + // File expected to be deleted. + $this->assertFileNotExists($this->tempfile); + } + + public function test_scan_file_error_donothing() { + $antivirus = $this->getMockBuilder('\antivirus_clamav\scanner') + ->setMethods(array('scan_file_execute_commandline', 'message_admins', 'get_config')) + ->getMock(); + + // Configure scan_file_execute_commandline method stub to behave + // as if there is a scanning error. + $antivirus->method('scan_file_execute_commandline')->willReturn(array(2, 'someerror')); + + // Set expectation that message_admins is called. + $antivirus->expects($this->atLeastOnce())->method('message_admins')->with($this->equalTo('someerror')); + + // Initiate mock scanning with configuration setting to do nothing on scanning error. + $configmap = array(array('clamfailureonupload', 'donothing')); + $antivirus->method('get_config')->will($this->returnValueMap($configmap)); + + // Run mock scanning with deleting infected file. + $this->assertFileExists($this->tempfile); + try { + $antivirus->scan_file($this->tempfile, '', true); + } catch (\core\antivirus\scanner_exception $e) { + $this->fail('Exception scanner_exception is not expected with config setting to do nothing on error.'); + } + // File expected to remain in place. + $this->assertFileExists($this->tempfile); + } + + public function test_scan_file_error_actlikevirus() { + $antivirus = $this->getMockBuilder('\antivirus_clamav\scanner') + ->setMethods(array('scan_file_execute_commandline', 'message_admins', 'get_config')) + ->getMock(); + + // Configure scan_file_execute_commandline method stub to behave + // as if there is a scanning error. + $antivirus->method('scan_file_execute_commandline')->willReturn(array(2, 'someerror')); + + // Set expectation that message_admins is called. + $antivirus->expects($this->atLeastOnce())->method('message_admins')->with($this->equalTo('someerror')); + + // Initiate mock scanning with configuration setting to act like virus on scanning error. + $configmap = array(array('clamfailureonupload', 'actlikevirus')); + $antivirus->method('get_config')->will($this->returnValueMap($configmap)); + + // Run mock scanning without deleting infected file. + $this->assertFileExists($this->tempfile); + try { + $antivirus->scan_file($this->tempfile, '', false); + } catch (\moodle_exception $e) { + $this->assertInstanceOf('\core\antivirus\scanner_exception', $e); + } + // File expected to remain in place. + $this->assertFileExists($this->tempfile); + + // Run mock scanning with deleting infected file. + try { + $antivirus->scan_file($this->tempfile, '', true); + } catch (\moodle_exception $e) { + $this->assertInstanceOf('\core\antivirus\scanner_exception', $e); + } + // File expected to be deleted. + $this->assertFileNotExists($this->tempfile); + } +}