<?php
/**
 * @copyright Copyright (c) 2017, Matias De lellis <mati86dl@gmail.com>
 * @copyright Copyright (c) 2019, Branko Kokanovic <branko@kokanovic.org>
 *
 * @author Branko Kokanovic <branko@kokanovic.org>
 *
 * @license GNU AGPL version 3 or any later version
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
namespace OCA\FaceRecognition\Tests\Integration;

use OC;

use OCP\IConfig;
use OCP\IUser;
use OCP\AppFramework\App;
use OCP\AppFramework\Db\DoesNotExistException;

use OCA\FaceRecognition\BackgroundJob\FaceRecognitionContext;
use OCA\FaceRecognition\BackgroundJob\FaceRecognitionLogger;
use OCA\FaceRecognition\BackgroundJob\Tasks\CreateClustersTask;
use OCA\FaceRecognition\Db\Face;
use OCA\FaceRecognition\Db\Image;
use OCA\FaceRecognition\Db\Person;
use OCA\FaceRecognition\Model\ModelManager;

use Test\TestCase;

class MergeClusterToDatabaseTest extends IntegrationTestCase {

	public function testMergeEmptyClusterToDatabase() {
		$personMapper = $this->container->query('OCA\FaceRecognition\Db\PersonMapper');
		$personMapper->mergeClusterToDatabase($this->user->getUid(), array(), array());

		$personCount = $personMapper->countPersons($this->user->getUID(), ModelManager::DEFAULT_FACE_MODEL_ID);
		$this->assertEquals(0, $personCount);
	}

	/**
	 * Test when [] changes to p1=>[f1]
	 * (test that new person is created)
	 */
	public function testCreatePerson() {
		$personMapper = $this->container->query('OCA\FaceRecognition\Db\PersonMapper');

		$image = $this->createImage();
		$face = $this->createFace($image->getId());

		$personMapper->mergeClusterToDatabase($this->user->getUid(), array(), array(100=>[$face->getId()]));

		$personId = $this->assertOnePerson();
		$this->assertFaces([$personId => [$face->getId()]]);
	}

	/**
	 * Test when p1=>[f1] changes to p1=>[f1]
	 * (test that nothing happens when input and output clusters are the same)
	 */
	public function testSamePerson() {
		$personMapper = $this->container->query('OCA\FaceRecognition\Db\PersonMapper');

		$person = $this->createPerson();
		$image = $this->createImage();
		$face = $this->createFace($image->getId(), $person->getId());
		$personMapper->invalidatePersons($image->getId());

		$personMapper->mergeClusterToDatabase($this->user->getUid(),
			array($person->getId() => [$face->getId()]),
			array($person->getId() => [$face->getId()]));

		$personId = $this->assertOnePerson("foo");
		$this->assertFaces([$personId => [$face->getId()]]);
	}

	/**
	 * Test when p1=>[f1] changes to p2=>[f1]
	 * (test that new person is created and old one deleted when face changes person)
	 */
	public function testChangePerson() {
		$personMapper = $this->container->query('OCA\FaceRecognition\Db\PersonMapper');

		$person = $this->createPerson();
		$image = $this->createImage();
		$face = $this->createFace($image->getId(), $person->getId());
		$personMapper->invalidatePersons($image->getId());

		$personMapper->mergeClusterToDatabase($this->user->getUid(),
			array($person->getId() => [$face->getId()]),
			array($person->getId()+1 => [$face->getId()])
		);

		$this->assertPersonDoNotExist($person->getId());
		$personId = $this->assertOnePerson();
		$this->assertFaces([$personId => [$face->getId()]]);
	}

	/**
	 * Test when p1=>[f1] changes to []
	 * (old person p1 should be deleted and face f1 resets its personId to null)
	 */
	public function testNoPersons() {
		$personMapper = $this->container->query('OCA\FaceRecognition\Db\PersonMapper');
		$faceMapper = $this->container->query('OCA\FaceRecognition\Db\FaceMapper');

		$person = $this->createPerson();
		$image = $this->createImage();
		$face = $this->createFace($image->getId(), $person->getId());

		$personMapper->mergeClusterToDatabase($this->user->getUid(),
			array($person->getId() => [$face->getId()]),
			array());

		$personCount = $personMapper->countPersons($this->user->getUID(), ModelManager::DEFAULT_FACE_MODEL_ID);
		$this->assertEquals(0, $personCount);
		$persons = $personMapper->findAll($this->user->getUID(), ModelManager::DEFAULT_FACE_MODEL_ID);
		$this->assertEquals(0, count($persons));
		$this->assertPersonDoNotExist($person->getId());

		$this->assertFaces([null => [$face->getId()]]);

		$faceCount = $faceMapper->countFaces($this->user->getUID(), ModelManager::DEFAULT_FACE_MODEL_ID);
		$this->assertEquals(1, $faceCount);
		$faces = $faceMapper->getFaces($this->user->getUID(), ModelManager::DEFAULT_FACE_MODEL_ID);
		$this->assertEquals(1, count($faces));
		$this->assertNull($faces[0]->getPerson());
		$faces = $faceMapper->findFromCluster($this->user->getUID(), $person->getId(), ModelManager::DEFAULT_FACE_MODEL_ID);
		$this->assertEquals(0, count($faces));
	}

	/**
	 * Test when p1=>[f1, f2] changes to p2=>[f1], p3=>[f2]
	 * (old person p1 should be deleted, and p2, p3 should be created)
	 */
	public function testSplitToNewPersons() {
		$personMapper = $this->container->query('OCA\FaceRecognition\Db\PersonMapper');

		$person = $this->createPerson();
		$image = $this->createImage();
		$face1 = $this->createFace($image->getId(), $person->getId());
		$face2 = $this->createFace($image->getId(), $person->getId());
		$personMapper->invalidatePersons($image->getId());

		$personMapper->mergeClusterToDatabase($this->user->getUid(),
			array(
				$person->getId() => [$face1->getId(), $face2->getId()]
			),
			array(
				$person->getId()+1 => [$face1->getId()],
				$person->getId()+2 => [$face2->getId()]
			)
		);

		$clusterCount = $personMapper->countClusters($this->user->getUID(), ModelManager::DEFAULT_FACE_MODEL_ID);
		$this->assertEquals(2, $clusterCount);
		$persons = $personMapper->findAll($this->user->getUID(), ModelManager::DEFAULT_FACE_MODEL_ID);
		$this->assertEquals(2, count($persons));
		usort($persons, function($p1, $p2) {
			return $p1->getId() - $p2->getId();
		});
		$this->assertTrue(is_null($persons[0]->getName()));
		$this->assertTrue(is_null($persons[1]->getName()));
		$this->assertTrue($persons[0]->getIsValid());
		$this->assertTrue($persons[1]->getIsValid());
		$this->assertPersonDoNotExist($person->getId());

		$person1Id = $persons[0]->getId();
		$person2Id = $persons[1]->getId();
		$this->assertFaces([$person1Id => [$face1->getId()], $person2Id => [$face2->getId()]]);
	}

	/**
	 * Test when p1=>[f1, f2] changes to p1=>[f1], p2=>[f2]
	 * (new person p2 should be created, f2 should change person)
	 */
	public function testSplitToSamePerson() {
		$personMapper = $this->container->query('OCA\FaceRecognition\Db\PersonMapper');

		$person = $this->createPerson();
		$image = $this->createImage();
		$face1 = $this->createFace($image->getId(), $person->getId());
		$face2 = $this->createFace($image->getId(), $person->getId());
		$personMapper->invalidatePersons($image->getId());

		$personMapper->mergeClusterToDatabase($this->user->getUid(),
			array(
				$person->getId() => [$face1->getId(), $face2->getId()]
			),
			array(
				$person->getId() => [$face1->getId()],
				$person->getId()+1 => [$face2->getId()]
			)
		);

		$clusterCount = $personMapper->countClusters($this->user->getUID(), ModelManager::DEFAULT_FACE_MODEL_ID);
		$this->assertEquals(2, $clusterCount);
		$persons = $personMapper->findAll($this->user->getUID(), ModelManager::DEFAULT_FACE_MODEL_ID);
		$this->assertEquals(2, count($persons));
		usort($persons, function($p1, $p2) {
			return $p1->getId() - $p2->getId();
		});
		$this->assertTrue($persons[0]->getName() === 'foo');
		$this->assertTrue($persons[1]->getName() === null);
		$this->assertTrue($persons[0]->getIsValid());
		$this->assertTrue($persons[1]->getIsValid());
		$person1Id = $persons[0]->getId();
		$person2Id = $persons[1]->getId();
		$this->assertEquals($person1Id, $person->getId());

		$this->assertFaces([$person1Id => [$face1->getId()], $person2Id => [$face2->getId()]]);
	}

	/**
	 * Test when p1=>[f1], p2=>[f2] changes to p1=>[f1, f2], p2=>[]
	 * (old person p2 should be deleted, and p1 should be re-populated with both faces)
	 */
	public function testMergeToSamePersons() {
		$personMapper = $this->container->query('OCA\FaceRecognition\Db\PersonMapper');
		$faceMapper = $this->container->query('OCA\FaceRecognition\Db\FaceMapper');

		$person1 = $this->createPerson('foo');
		$person2 = $this->createPerson('bar');
		$image = $this->createImage();
		$face1 = $this->createFace($image->getId(), $person1->getId());
		$face2 = $this->createFace($image->getId(), $person2->getId());
		$personMapper->invalidatePersons($image->getId());

		$personMapper->mergeClusterToDatabase($this->user->getUid(),
			array(
				$person1->getId() => [$face1->getId()],
				$person2->getId() => [$face2->getId()],
			),
			array(
				$person1->getId() => [$face1->getId(), $face2->getId()]
			)
		);

		$this->assertPersonDoNotExist($person2->getId());
		$personId = $this->assertOnePerson('foo');
		$this->assertEquals($personId, $person1->getId());
		$this->assertFaces([$personId => [$face1->getId(), $face2->getId()]]);
	}

	/**
	 * Test when p1=>[f1], p2=>[f2] changes to p1=>[], p2=[], p3=>[f1, f2]
	 * (old persons p1 and p2 should be deleted, and p3 should be re-populated with both faces)
	 */
	public function testMergeToNewPersons() {
		$personMapper = $this->container->query('OCA\FaceRecognition\Db\PersonMapper');
		$faceMapper = $this->container->query('OCA\FaceRecognition\Db\FaceMapper');

		$person1 = $this->createPerson('foo');
		$person2 = $this->createPerson('bar');
		$image = $this->createImage();
		$face1 = $this->createFace($image->getId(), $person1->getId());
		$face2 = $this->createFace($image->getId(), $person2->getId());
		$personMapper->invalidatePersons($image->getId());

		$personMapper->mergeClusterToDatabase($this->user->getUid(),
			array(
				$person1->getId() => [$face1->getId()],
				$person2->getId() => [$face2->getId()],
			),
			array(
				$person1->getId()+5 => [$face1->getId(), $face2->getId()]
			)
		);

		$this->assertPersonDoNotExist($person1->getId());
		$this->assertPersonDoNotExist($person2->getId());
		$personId = $this->assertOnePerson();
		$this->assertFaces([$personId => [$face1->getId(), $face2->getId()]]);
	}

	/**
	 * Test when p1=>[f1], p2=>[f2] changes to p1=>[f2], p2=>[f1]
	 * (both persons and faces stay, but they change who is who)
	 */
	public function testSwap() {
		$personMapper = $this->container->query('OCA\FaceRecognition\Db\PersonMapper');
		$faceMapper = $this->container->query('OCA\FaceRecognition\Db\FaceMapper');

		$person1 = $this->createPerson('foo');
		$person2 = $this->createPerson('bar');
		$image = $this->createImage();
		$face1 = $this->createFace($image->getId(), $person1->getId());
		$face2 = $this->createFace($image->getId(), $person2->getId());
		$personMapper->invalidatePersons($image->getId());

		$personMapper->mergeClusterToDatabase($this->user->getUid(),
			array(
				$person1->getId() => [$face1->getId()],
				$person2->getId() => [$face2->getId()],
			),
			array(
				$person1->getId() => [$face2->getId()],
				$person2->getId() => [$face1->getId()],
			)
		);

		$personCount = $personMapper->countPersons($this->user->getUID(), ModelManager::DEFAULT_FACE_MODEL_ID);
		$this->assertEquals(2, $personCount);
		$persons = $personMapper->findAll($this->user->getUID(), ModelManager::DEFAULT_FACE_MODEL_ID);
		$this->assertEquals(2, count($persons));
		usort($persons, function($p1, $p2) {
			return $p1->getId() - $p2->getId();
		});
		$this->assertTrue(strpos($persons[0]->getName(), 'foo') !== false);
		$this->assertTrue(strpos($persons[1]->getName(), 'bar') !== false);
		$this->assertTrue($persons[0]->getIsValid());
		$this->assertTrue($persons[1]->getIsValid());
		$this->assertEquals($person1->getId(), $persons[0]->getId());
		$this->assertEquals($person2->getId(), $persons[1]->getId());

		$this->assertFaces([$person1->getId() => [$face2->getId()], $person2->getId() => [$face1->getId()]]);
	}

	/**
	 * Test when p1=>[f1], p2=>[f2] changes to p1=>[f2], p3=>[f1]
	 * (p2 is lost and p3 is created. f2 is swapped to existing person p1)
	 */
	public function testHalfSwap() {
		$personMapper = $this->container->query('OCA\FaceRecognition\Db\PersonMapper');
		$faceMapper = $this->container->query('OCA\FaceRecognition\Db\FaceMapper');

		$person1 = $this->createPerson('foo');
		$person2 = $this->createPerson('bar');
		$image = $this->createImage();
		$face1 = $this->createFace($image->getId(), $person1->getId());
		$face2 = $this->createFace($image->getId(), $person2->getId());
		$personMapper->invalidatePersons($image->getId());

		$personMapper->mergeClusterToDatabase($this->user->getUid(),
			array(
				$person1->getId() => [$face1->getId()],
				$person2->getId() => [$face2->getId()],
			),
			array(
				$person1->getId() => [$face2->getId()],
				$person2->getId()+5 => [$face1->getId()],
			)
		);

		$clusterCount = $personMapper->countClusters($this->user->getUID(), ModelManager::DEFAULT_FACE_MODEL_ID);
		$this->assertEquals(2, $clusterCount);
		$persons = $personMapper->findAll($this->user->getUID(), ModelManager::DEFAULT_FACE_MODEL_ID);
		$this->assertEquals(2, count($persons));
		usort($persons, function($p1, $p2) {
			return $p1->getId() - $p2->getId();
		});
		$this->assertTrue($persons[0]->getName() === 'foo');
		$this->assertTrue($persons[1]->getName() === null);
		$this->assertTrue($persons[0]->getIsValid());
		$this->assertTrue($persons[1]->getIsValid());
		$this->assertEquals($person1->getId(), $persons[0]->getId());
		$person3Id = $persons[1]->getId();

		$this->assertFaces([$person1->getId() => [$face2->getId()], $person3Id => [$face1->getId()]]);
	}

	/**
	 * Same test as MergeClustersTest::testMergeClustersComplex
	 * p1=>[f1,f2,f3,f4], p2=>[f5,f6,f7,f8], p3=>[f9,f10,f11,f12], p4=>[f13,f14,f15,f16]
	 * is changed to
	 * p1=>[f1,f2,f3,f4] (same as before)
	 * p2=>[f5,f6,f7,f8,f17] (added f17)
	 * p3=>[f9,f10,f11] (removed f12)
	 * p4=>[f13,f14,f15] (removed f16)
	 * p5=>[f16,f18] (new person, moved f16 here plus new face f18)
	 * p6=>[f19,f20,f21] (new person, all new faces)
	 */
	public function testComplexToSamePerson() {
		$personMapper = $this->container->query('OCA\FaceRecognition\Db\PersonMapper');
		$faceMapper = $this->container->query('OCA\FaceRecognition\Db\FaceMapper');

		$person1 = $this->createPerson('foo-p1');
		$person2 = $this->createPerson('foo-p2');
		$person3 = $this->createPerson('foo-p3');
		$person4 = $this->createPerson('foo-p4');
		$image = $this->createImage();
		$face1  = $this->createFace($image->getId(), $person1->getId());
		$face2  = $this->createFace($image->getId(), $person1->getId());
		$face3  = $this->createFace($image->getId(), $person1->getId());
		$face4  = $this->createFace($image->getId(), $person1->getId());
		$face5  = $this->createFace($image->getId(), $person2->getId());
		$face6  = $this->createFace($image->getId(), $person2->getId());
		$face7  = $this->createFace($image->getId(), $person2->getId());
		$face8  = $this->createFace($image->getId(), $person2->getId());
		$face9  = $this->createFace($image->getId(), $person3->getId());
		$face10 = $this->createFace($image->getId(), $person3->getId());
		$face11 = $this->createFace($image->getId(), $person3->getId());
		$face12 = $this->createFace($image->getId(), $person3->getId());
		$face13 = $this->createFace($image->getId(), $person4->getId());
		$face14 = $this->createFace($image->getId(), $person4->getId());
		$face15 = $this->createFace($image->getId(), $person4->getId());
		$face16 = $this->createFace($image->getId(), $person4->getId());
		$face17 = $this->createFace($image->getId());
		$face18 = $this->createFace($image->getId());
		$face19 = $this->createFace($image->getId());
		$face20 = $this->createFace($image->getId());
		$face21 = $this->createFace($image->getId());
		$personMapper->invalidatePersons($image->getId());

		// First person is not invalid (it will remain same, so change it back to valid)
		$person1->setIsValid(true);
		$personMapper->update($person1);

		$personMapper->mergeClusterToDatabase($this->user->getUid(),
			array(
				$person1->getId() => [$face1->getId(), $face2->getId(), $face3->getId(), $face4->getId()],
				$person2->getId() => [$face5->getId(), $face6->getId(), $face7->getId(), $face8->getId()],
				$person3->getId() => [$face9->getId(), $face10->getId(), $face11->getId(), $face12->getId()],
				$person4->getId() => [$face13->getId(), $face14->getId(), $face15->getId(), $face16->getId()],
			),
			array(
				$person1->getId() => [$face1->getId(), $face2->getId(), $face3->getId(), $face4->getId()],
				$person2->getId() => [$face5->getId(), $face6->getId(), $face7->getId(), $face8->getId(), $face17->getId()],
				$person3->getId() => [$face9->getId(), $face10->getId(), $face11->getId()],
				$person4->getId() => [$face13->getId(), $face14->getId(), $face15->getId()],
				$person1->getId() + 100 => [$face16->getId(), $face18->getId()],
				$person1->getId() + 101 => [$face19->getId(), $face20->getId(), $face21->getId()],
			)
		);

		$clusterCount = $personMapper->countClusters($this->user->getUID(), ModelManager::DEFAULT_FACE_MODEL_ID);
		$this->assertEquals(6, $clusterCount);
		$persons = $personMapper->findAll($this->user->getUID(), ModelManager::DEFAULT_FACE_MODEL_ID);
		$this->assertEquals(6, count($persons));
		usort($persons, function($p1, $p2) {
			return $p1->getId() - $p2->getId();
		});
		$this->assertTrue($persons[0]->getName() === 'foo-p1');
		$this->assertTrue($persons[1]->getName() === 'foo-p2');
		$this->assertTrue($persons[2]->getName() === 'foo-p3');
		$this->assertTrue($persons[3]->getName() === 'foo-p4');
		$this->assertTrue($persons[4]->getName() === null);
		$this->assertTrue($persons[5]->getName() === null);
		foreach ($persons as $person) {
			$this->assertTrue($person->getIsValid());
		}
		$this->assertEquals($person1->getId(), $persons[0]->getId());
		$this->assertEquals($person2->getId(), $persons[1]->getId());
		$this->assertEquals($person3->getId(), $persons[2]->getId());
		$this->assertEquals($person4->getId(), $persons[3]->getId());
		$person5Id = $persons[4]->getId();
		$person6Id = $persons[5]->getId();

		$this->assertFaces([
			$person1->getId() => [$face1->getId(), $face2->getId(), $face3->getId(), $face4->getId()],
			$person2->getId() => [$face5->getId(), $face6->getId(), $face7->getId(), $face8->getId(), $face17->getId()],
			$person3->getId() => [$face9->getId(), $face10->getId(), $face11->getId()],
			$person4->getId() => [$face13->getId(), $face14->getId(), $face15->getId()],
			$person5Id => [$face16->getId(), $face18->getId()],
			$person6Id => [$face19->getId(), $face20->getId(), $face21->getId()],
			null => [$face12->getId()]
		]);
	}

	/**
	 * Same test as MergeClustersTest::testMergeClustersComplex
	 * and here same as testComplexToSamePerson, but with a twist that all persons are new.
	 * p1=>[f1,f2,f3,f4], p2=>[f5,f6,f7,f8], p3=>[f9,f10,f11,f12], p4=>[f13,f14,f15,f16]
	 * is changed to
	 * p5=>[f1,f2,f3,f4] (same as before)
	 * p6=>[f5,f6,f7,f8,f17] (added f17)
	 * p7=>[f9,f10,f11] (removed f12)
	 * p8=>[f13,f14,f15] (removed f16)
	 * p9=>[f16,f18] (new person, moved f16 here plus new face f18)
	 * p10=>[f19,f20,f21] (new person, all new faces)
	 */
	public function testComplexToNewPerson() {
		$personMapper = $this->container->query('OCA\FaceRecognition\Db\PersonMapper');
		$faceMapper = $this->container->query('OCA\FaceRecognition\Db\FaceMapper');

		$person1 = $this->createPerson();
		$person2 = $this->createPerson();
		$person3 = $this->createPerson();
		$person4 = $this->createPerson();
		$image = $this->createImage();
		$face1  = $this->createFace($image->getId(), $person1->getId());
		$face2  = $this->createFace($image->getId(), $person1->getId());
		$face3  = $this->createFace($image->getId(), $person1->getId());
		$face4  = $this->createFace($image->getId(), $person1->getId());
		$face5  = $this->createFace($image->getId(), $person2->getId());
		$face6  = $this->createFace($image->getId(), $person2->getId());
		$face7  = $this->createFace($image->getId(), $person2->getId());
		$face8  = $this->createFace($image->getId(), $person2->getId());
		$face9  = $this->createFace($image->getId(), $person3->getId());
		$face10 = $this->createFace($image->getId(), $person3->getId());
		$face11 = $this->createFace($image->getId(), $person3->getId());
		$face12 = $this->createFace($image->getId(), $person3->getId());
		$face13 = $this->createFace($image->getId(), $person4->getId());
		$face14 = $this->createFace($image->getId(), $person4->getId());
		$face15 = $this->createFace($image->getId(), $person4->getId());
		$face16 = $this->createFace($image->getId(), $person4->getId());
		$face17 = $this->createFace($image->getId());
		$face18 = $this->createFace($image->getId());
		$face19 = $this->createFace($image->getId());
		$face20 = $this->createFace($image->getId());
		$face21 = $this->createFace($image->getId());
		$personMapper->invalidatePersons($image->getId());

		$personMapper->mergeClusterToDatabase($this->user->getUid(),
			array(
				$person1->getId() => [$face1->getId(), $face2->getId(), $face3->getId(), $face4->getId()],
				$person2->getId() => [$face5->getId(), $face6->getId(), $face7->getId(), $face8->getId()],
				$person3->getId() => [$face9->getId(), $face10->getId(), $face11->getId(), $face12->getId()],
				$person4->getId() => [$face13->getId(), $face14->getId(), $face15->getId(), $face16->getId()],
			),
			array(
				$person1->getId() + 100 => [$face1->getId(), $face2->getId(), $face3->getId(), $face4->getId()],
				$person1->getId() + 101 => [$face5->getId(), $face6->getId(), $face7->getId(), $face8->getId(), $face17->getId()],
				$person1->getId() + 102 => [$face9->getId(), $face10->getId(), $face11->getId()],
				$person1->getId() + 103 => [$face13->getId(), $face14->getId(), $face15->getId()],
				$person1->getId() + 104 => [$face16->getId(), $face18->getId()],
				$person1->getId() + 105 => [$face19->getId(), $face20->getId(), $face21->getId()],
			)
		);

		$clusterCount = $personMapper->countClusters($this->user->getUID(), ModelManager::DEFAULT_FACE_MODEL_ID);
		$this->assertEquals(6, $clusterCount);
		$persons = $personMapper->findAll($this->user->getUID(), ModelManager::DEFAULT_FACE_MODEL_ID);
		$this->assertEquals(6, count($persons));
		usort($persons, function($p1, $p2) {
			return $p1->getId() - $p2->getId();
		});
		$this->assertPersonDoNotExist($person1->getId());
		$this->assertPersonDoNotExist($person2->getId());
		$this->assertPersonDoNotExist($person3->getId());
		$this->assertPersonDoNotExist($person4->getId());
		$this->assertTrue($persons[0]->getName() === null);
		$this->assertTrue($persons[1]->getName() === null);
		$this->assertTrue($persons[2]->getName() === null);
		$this->assertTrue($persons[3]->getName() === null);
		$this->assertTrue($persons[4]->getName() === null);
		$this->assertTrue($persons[5]->getName() === null);
		foreach ($persons as $person) {
			$this->assertTrue($person->getIsValid());
		}
		$person5Id = $persons[0]->getId();
		$person6Id = $persons[1]->getId();
		$person7Id = $persons[2]->getId();
		$person8Id = $persons[3]->getId();
		$person9Id = $persons[4]->getId();
		$person10Id = $persons[5]->getId();

		$this->assertFaces([
			$person5Id => [$face1->getId(), $face2->getId(), $face3->getId(), $face4->getId()],
			$person6Id => [$face5->getId(), $face6->getId(), $face7->getId(), $face8->getId(), $face17->getId()],
			$person7Id => [$face9->getId(), $face10->getId(), $face11->getId()],
			$person8Id => [$face13->getId(), $face14->getId(), $face15->getId()],
			$person9Id => [$face16->getId(), $face18->getId()],
			$person10Id => [$face19->getId(), $face20->getId(), $face21->getId()],
			null => [$face12->getId()]
		]);
	}

	private function createPerson($name = 'foo'): Person {
		$personMapper = $this->container->query('OCA\FaceRecognition\Db\PersonMapper');
		$person = new Person();
		$person->setUser($this->user->getUID());
		$person->setName($name);
		$person->setIsValid(true);
		$personMapper->insert($person);

		return $person;
	}

	private function createImage(): Image {
		$imageMapper = $this->container->query('OCA\FaceRecognition\Db\ImageMapper');
		$image = new Image();
		$image->setUser($this->user->getUid());
		$image->setFile(1);
		$image->setModel(ModelManager::DEFAULT_FACE_MODEL_ID);
		$imageMapper->insert($image);

		return $image;
	}

	private function createFace($imageId, $personId = null) {
		$faceMapper = $this->container->query('OCA\FaceRecognition\Db\FaceMapper');
		$face = Face::fromModel($imageId, array("left"=>0, "right"=>100, "top"=>0, "bottom"=>100, "detection_confidence"=>1.0));
		if ($personId !== null) {
			$face->setPerson($personId);
		}
		$faceMapper->insertFace($face);
		return $face;
	}

	private function assertOnePerson($name = null): int {
		$personMapper = $this->container->query('OCA\FaceRecognition\Db\PersonMapper');
		$clusterCount = $personMapper->countClusters($this->user->getUID(), ModelManager::DEFAULT_FACE_MODEL_ID);
		$this->assertEquals(1, $clusterCount);

		$persons = $personMapper->findAll($this->user->getUID(), ModelManager::DEFAULT_FACE_MODEL_ID);
		$this->assertEquals(1, count($persons));

		if ($name !== null) {
			// check that retains the name
			$this->assertTrue($persons[0]->getName() === $name);

			$personCount = $personMapper->countPersons($this->user->getUID(), ModelManager::DEFAULT_FACE_MODEL_ID);
			$this->assertEquals(1, $personCount);

			// Check that it can be found using this method too
			$persons = $personMapper->findByName($this->user->getUID(), ModelManager::DEFAULT_FACE_MODEL_ID, $name);
			$this->assertEquals(1, count($persons));
		}

		// Check that it can be found using this method too
		$personMapper->find($this->user->getUID(), $persons[0]->getId());

		// After clustering, person must be valid
		$this->assertTrue($persons[0]->getIsValid());

		return $persons[0]->getId();
	}

	private function assertPersonDoNotExist(int $personId) {
		$personMapper = $this->container->query('OCA\FaceRecognition\Db\PersonMapper');
		try {
			$personMapper->find($this->user->getUID(), $personId);
			$this->fail('Person still exist');
		} catch (DoesNotExistException $e) {
		}
	}

	/**
	 * Checks given array of faces exist in database and nothing more, and checks that faces are associated to persons.
	 * Keys in arrray are person IDs, and values are arrays with face IDs:
	 * [p1=>[f1], p2=>[f2, f3]...]
	 * It does as much asserts as possible by getting data from database. If key is empty, that means that face do not have person.
	 */
	private function assertFaces(array $personToFaces) {
		$totalFaces = 0;
		foreach ($personToFaces as $person=>$faces) {
			$totalFaces += count($faces);
		}

		// Check total faces in DB
		$faceMapper = $this->container->query('OCA\FaceRecognition\Db\FaceMapper');
		$faceCount = $faceMapper->countFaces($this->user->getUID(), ModelManager::DEFAULT_FACE_MODEL_ID);
		$this->assertEquals($totalFaces, $faceCount);

		// Check those faces have given persons
		$facesDb = $faceMapper->getFaces($this->user->getUID(), ModelManager::DEFAULT_FACE_MODEL_ID);
		$this->assertEquals($totalFaces, count($facesDb));
		foreach($facesDb as $faceDb) {
			foreach ($personToFaces as $person=>$faces) {
				if (in_array($faceDb->getId(), $faces)) {
					if ($person !== "") {
						$this->assertEquals($faceDb->getPerson(), $person);
					} else {
						$this->assertNull($faceDb->getPerson());
					}
				}
			}
		}

		// Check that each person have those faces (and no more)
		foreach($personToFaces as $person=>$faces) {
			if ($person === "") {
				continue;
			}

			$facesFromPerson = $faceMapper->findFromCluster($this->user->getUID(), $person, ModelManager::DEFAULT_FACE_MODEL_ID);
			$this->assertEquals(count($faces), count($facesFromPerson));

			usort($facesFromPerson, function($f1, $f2) {
				return $f1->getId() - $f2->getId();
			});
			for ($i = 0; $i < count($faces); $i++) {
				$this->assertEquals($faces[$i], $facesFromPerson[$i]->getId());
			}
		}
	}
}