/ tests / AVToolTest.php
<?php
/**
 * SeekQuarry/Yioop --
 * Open Source Pure PHP Search Engine, Crawler, and Indexer
 *
 * Copyright (C) 2009 - 2026  Chris Pollett chris@pollett.org
 *
 * LICENSE:
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * END LICENSE
 *
 * @author Chris Pollett chris@pollett.org
 * @license https://www.gnu.org/licenses/ GPL3
 * @link https://www.seekquarry.com/
 * @copyright 2009 - 2026
 * @filesource
 */
namespace seekquarry\yioop\tests;

use seekquarry\yioop\configs as C;
use seekquarry\yioop\library\UnitTest;

/**
 * Tests the command line tool for audio and video: that it writes a
 * thumbnail where it is told to, at the width asked for, that it can
 * describe a file without decoding it, and that it says what is wrong
 * rather than stopping without a word.
 *
 * The tool is run as a person runs it, in its own process, since that is
 * the only way its reading of a command line and what it prints are both
 * checked.
 *
 * @author Chris Pollett
 */
class AVToolTest extends UnitTest
{
    /**
     * The video file the cases run the tool over.
     * @var string
     */
    public $video = "";
    /**
     * Where the tool is asked to write its picture.
     * @var string
     */
    public $written = "";
    /**
     * Writes the video out of the base64 it is kept in.
     */
    public function setUp()
    {
        $held = file_get_contents(C\PARENT_DIR .
            "/tests/test_files/video_white_mp4.txt");
        $this->video = C\WORK_DIRECTORY . "/temp/av_tool" . getmypid() .
            ".mp4";
        file_put_contents($this->video, base64_decode($held));
        $this->written = C\WORK_DIRECTORY . "/temp/av_tool" . getmypid() .
            ".webp";
    }
    /**
     * Removes what the cases wrote.
     */
    public function tearDown()
    {
        foreach ([$this->video, $this->written] as $path) {
            if ($path !== "" && file_exists($path)) {
                unlink($path);
            }
        }
    }
    /**
     * Runs the tool and gives back what it printed.
     *
     * @param string $arguments what to put after the tool's own name
     * @return string everything the tool printed, output and errors both
     */
    public function runTool($arguments)
    {
        $tool = C\BASE_DIR . "/executables/AVTool.php";
        return shell_exec("php " . escapeshellarg($tool) . " " .
            $arguments . " 2>&1");
    }
    /**
     * Asked for help, the tool says what it does and what its options
     * are, so somebody who runs it with nothing is not left guessing.
     */
    public function askedForHelpItSaysWhatItDoesTestCase()
    {
        $said = $this->runTool("--help");
        $this->assertTrue(strpos($said, "picture from a video") !== false,
            "the help says the tool makes a picture from a video");
        $this->assertTrue(strpos($said, "convert a recording") !== false,
            "and that it converts a recording");
        $this->assertTrue(strpos($said, "--probe") !== false,
            "and lists the option that describes a file");
        $this->assertTrue(strpos($said, "--width") !== false,
            "and the option that sets the width");
    }
    /**
     * The tool writes a picture where it is told to, at the width asked
     * for, and says what it wrote.
     */
    public function itWritesAThumbnailWhereToldTestCase()
    {
        $said = $this->runTool(escapeshellarg($this->video) . " -o " .
            escapeshellarg($this->written) . " -w 200");
        $this->assertTrue(file_exists($this->written),
            "a picture is written where the tool was told to write it");
        $size = getimagesize($this->written);
        $this->assertEqual(200, $size[0] ?? 0,
            "it is as wide as was asked for");
        $this->assertEqual(150, $size[1] ?? 0,
            "and its height keeps the shape of the frame");
        $this->assertTrue(strpos($said, "200x150") !== false,
            "and the tool says what it wrote");
    }
    /**
     * Asked to describe a file, the tool says what it holds without
     * decoding a frame, which is what a site wants when it only needs to
     * know how long a video runs.
     */
    public function itDescribesAFileWithoutDecodingTestCase()
    {
        $said = $this->runTool(escapeshellarg($this->video) . " --probe");
        $held = json_decode($said, true);
        $this->assertTrue(is_array($held),
            "what it prints reads back as a description");
        $this->assertEqual("MP4", $held['container'] ?? "",
            "it names the kind of file");
        $this->assertEqual(320, $held['width'] ?? 0,
            "how wide the video is");
        $this->assertEqual(10, round($held['duration'] ?? 0),
            "and how long it runs");
    }
    /**
     * A still picture is what the tool writes unless a moving one is
     * asked for. It used to look at how long the video ran and write a
     * moving picture for anything over a minute, which surprised
     * somebody who asked for one frame at one moment.
     */
    public function itWritesAStillPictureUnlessAskedOtherwiseTestCase()
    {
        $this->runTool(escapeshellarg($this->video) . " -t 2.5 -o " .
            escapeshellarg($this->written) . " -w 200");
        $written = file_get_contents($this->written);
        $this->assertTrue(strpos($written, "ANIM") === false,
            "what it wrote carries no moving picture mark");
        $size = getimagesize($this->written);
        $this->assertEqual(200, $size[0] ?? 0,
            "and it is as wide as was asked for");
        $said = $this->runTool(escapeshellarg($this->video) .
            " --animated -n 3 -o " . escapeshellarg($this->written) .
            " -w 200");
        $written = file_get_contents($this->written);
        $this->assertTrue(strpos($written, "ANIM") !== false,
            "asked for a moving picture, it writes one");
        $this->assertTrue(strpos($said, "animated") !== false,
            "and says so");
    }
    /**
     * Handed a file that is not a video, the tool says so and stops,
     * rather than writing a broken picture or saying nothing.
     */
    public function handedSomethingElseItSaysSoTestCase()
    {
        $not_a_video = C\WORK_DIRECTORY . "/temp/av_tool_words" .
            getmypid() . ".txt";
        file_put_contents($not_a_video, "these are only words");
        $said = $this->runTool(escapeshellarg($not_a_video));
        $this->assertTrue(strpos($said, "unrecognized container") !== false,
            "it says the file is not a kind it knows");
        $this->assertTrue(!file_exists($this->written),
            "and writes no picture");
        if (file_exists($not_a_video)) {
            unlink($not_a_video);
        }
    }
}
X