459 lines
18 KiB
PHP
459 lines
18 KiB
PHP
<?php
|
|
|
|
namespace Drupal\Tests\farm_location\Kernel;
|
|
|
|
use Drupal\asset\Entity\Asset;
|
|
use Drupal\farm_geo\Traits\WktTrait;
|
|
use Drupal\KernelTests\KernelTestBase;
|
|
use Drupal\log\Entity\Log;
|
|
use Drupal\Tests\farm_test\Kernel\FarmEntityCacheTestTrait;
|
|
|
|
/**
|
|
* Tests for farmOS location logic.
|
|
*
|
|
* @group farm
|
|
*/
|
|
class LocationTest extends KernelTestBase {
|
|
|
|
use FarmEntityCacheTestTrait;
|
|
use WktTrait;
|
|
|
|
/**
|
|
* WKT Generator service.
|
|
*
|
|
* @var \Drupal\geofield\WktGeneratorInterface
|
|
*/
|
|
protected $wktGenerator;
|
|
|
|
/**
|
|
* Asset location service.
|
|
*
|
|
* @var \Drupal\farm_location\AssetLocationInterface
|
|
*/
|
|
protected $assetLocation;
|
|
|
|
/**
|
|
* Log location service.
|
|
*
|
|
* @var \Drupal\farm_location\LogLocationInterface
|
|
*/
|
|
protected $logLocation;
|
|
|
|
/**
|
|
* Array of polygon WKT strings.
|
|
*
|
|
* @var string[]
|
|
*/
|
|
protected $polygons = [];
|
|
|
|
/**
|
|
* Array of location assets.
|
|
*
|
|
* @var \Drupal\asset\Entity\AssetInterface[]
|
|
*/
|
|
protected $locations = [];
|
|
|
|
/**
|
|
* {@inheritdoc}
|
|
*/
|
|
protected static $modules = [
|
|
'asset',
|
|
'geofield',
|
|
'log',
|
|
'farm_field',
|
|
'farm_location',
|
|
'farm_location_test',
|
|
'farm_log',
|
|
'state_machine',
|
|
'user',
|
|
];
|
|
|
|
/**
|
|
* {@inheritdoc}
|
|
*/
|
|
protected function setUp(): void {
|
|
parent::setUp();
|
|
$this->wktGenerator = \Drupal::service('geofield.wkt_generator');
|
|
$this->assetLocation = \Drupal::service('asset.location');
|
|
$this->logLocation = \Drupal::service('log.location');
|
|
$this->installEntitySchema('asset');
|
|
$this->installEntitySchema('log');
|
|
$this->installEntitySchema('user');
|
|
$this->installConfig([
|
|
'farm_location_test',
|
|
]);
|
|
|
|
// Generate random WKT polygons.
|
|
for ($i = 0; $i < 3; $i++) {
|
|
$segments = rand(3, 7);
|
|
$this->polygons[] = $this->reduceWkt($this->wktGenerator->wktGeneratePolygon(NULL, $segments));
|
|
}
|
|
|
|
// Generate location assets.
|
|
for ($i = 0; $i < 3; $i++) {
|
|
$location = Asset::create([
|
|
'type' => 'location',
|
|
'name' => $this->randomMachineName(),
|
|
'status' => 'active',
|
|
'intrinsic_geometry' => $this->polygons[$i],
|
|
'is_fixed' => TRUE,
|
|
]);
|
|
$location->save();
|
|
$this->locations[] = $location;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test auto population of log geometry field.
|
|
*/
|
|
public function testPopulateLogGeometry() {
|
|
|
|
// When a log is saved with a location and without a geometry, the geometry
|
|
// is copied from the location.
|
|
/** @var \Drupal\log\Entity\LogInterface $log */
|
|
$log = Log::create([
|
|
'type' => 'movement',
|
|
'status' => 'pending',
|
|
'location' => ['target_id' => $this->locations[0]->id()],
|
|
]);
|
|
$log->save();
|
|
$this->assertEquals($this->locations[0]->get('intrinsic_geometry')->value, $log->get('geometry')->value, 'Empty geometry is populated from location.');
|
|
|
|
// When multiple locations are added, all of their geometries are combined.
|
|
$log->location = [
|
|
['target_id' => $this->locations[0]->id()],
|
|
['target_id' => $this->locations[1]->id()],
|
|
];
|
|
$log->geometry->value = '';
|
|
$log->save();
|
|
$combined = $this->combineWkt([$this->polygons[0], $this->polygons[1]]);
|
|
$this->assertEquals($combined, $log->get('geometry')->value, 'Geometries from multiple locations are combined.');
|
|
|
|
// When a log's locations change, and the geometry is not customized, the
|
|
// geometry is updated.
|
|
$log->location = ['target_id' => $this->locations[1]->id()];
|
|
$log->save();
|
|
$this->assertEquals($this->locations[1]->get('intrinsic_geometry')->value, $log->get('geometry')->value, 'Geometry is updated when locations are changed.');
|
|
|
|
// When a log's geometry is set, it is saved.
|
|
$log->geometry->value = $this->polygons[2];
|
|
$log->save();
|
|
$this->assertEquals($this->polygons[2], $log->get('geometry')->value, 'Custom geometry can be saved.');
|
|
|
|
// When a log's locations change, and the geometry is customized, the
|
|
// geometry is not updated.
|
|
$log->location = ['target_id' => $this->locations[0]->id()];
|
|
$log->save();
|
|
$this->assertEquals($this->polygons[2], $log->get('geometry')->value, 'Custom geometry is not overwritten when locations change.');
|
|
}
|
|
|
|
/**
|
|
* Test asset location.
|
|
*/
|
|
public function testAssetLocation() {
|
|
|
|
// Create a new asset.
|
|
/** @var \Drupal\asset\Entity\AssetInterface $asset */
|
|
$asset = Asset::create([
|
|
'type' => 'object',
|
|
'name' => $this->randomMachineName(),
|
|
'status' => 'active',
|
|
]);
|
|
$asset->save();
|
|
|
|
// Populate a cache value dependent on the asset's cache tags.
|
|
$this->populateEntityTestCache($asset);
|
|
|
|
// When an asset has no movement logs, it has no location or geometry.
|
|
$this->assertFalse($this->assetLocation->hasLocation($asset), 'New assets do not have location.');
|
|
$this->assertFalse($this->assetLocation->hasGeometry($asset), 'New assets do not have geometry.');
|
|
|
|
// Assert that the asset's cache tags were not invalidated.
|
|
$this->assertEntityTestCache($asset, TRUE);
|
|
|
|
// Create a "done" movement log that references the asset.
|
|
/** @var \Drupal\log\Entity\LogInterface $first_log */
|
|
$first_log = Log::create([
|
|
'type' => 'movement',
|
|
'status' => 'done',
|
|
'asset' => ['target_id' => $asset->id()],
|
|
'location' => ['target_id' => $this->locations[0]->id()],
|
|
'is_movement' => TRUE,
|
|
]);
|
|
$first_log->save();
|
|
|
|
// When a movement log is created and marked as "done", the asset has
|
|
// the same location and geometry as the log.
|
|
$this->assertTrue($this->assetLocation->hasLocation($asset), 'Asset with movement log has location.');
|
|
$this->assertTrue($this->assetLocation->hasGeometry($asset), 'Asset with movement log has geometry.');
|
|
$this->assertEquals($this->logLocation->getLocation($first_log), $this->assetLocation->getLocation($asset), 'Asset with movement log has same location as log.');
|
|
$this->assertEquals($this->logLocation->getGeometry($first_log), $this->assetLocation->getGeometry($asset), 'Asset with movement log has same geometry as log.');
|
|
|
|
// Assert that the asset's cache tags were invalidated.
|
|
$this->assertEntityTestCache($asset, FALSE);
|
|
|
|
// Re-populate a cache value dependent on the asset's cache tags.
|
|
$this->populateEntityTestCache($asset);
|
|
|
|
// When a movement log's locations are changed, the asset location changes.
|
|
$first_log->location = ['target_id' => $this->locations[1]->id()];
|
|
$first_log->save();
|
|
$this->assertEquals($this->logLocation->getLocation($first_log), $this->assetLocation->getLocation($asset), 'Asset with changed movement log has same location as log.');
|
|
$this->assertEquals($this->logLocation->getGeometry($first_log), $this->assetLocation->getGeometry($asset), 'Asset with changed movement log has same geometry as log.');
|
|
|
|
// Assert that the asset's cache tags were invalidated.
|
|
$this->assertEntityTestCache($asset, FALSE);
|
|
|
|
// Re-populate a cache value dependent on the asset's cache tags.
|
|
$this->populateEntityTestCache($asset);
|
|
|
|
// Create a "pending" movement log that references the asset.
|
|
/** @var \Drupal\log\Entity\LogInterface $second_log */
|
|
$second_log = Log::create([
|
|
'type' => 'movement',
|
|
'status' => 'pending',
|
|
'asset' => ['target_id' => $asset->id()],
|
|
'location' => ['target_id' => $this->locations[2]->id()],
|
|
'is_movement' => TRUE,
|
|
]);
|
|
$second_log->save();
|
|
|
|
// When an asset has a "pending" movement log, the asset location and
|
|
// geometry remain the same as the previous "done" movement log.
|
|
$this->assertEquals($this->logLocation->getLocation($first_log), $this->assetLocation->getLocation($asset), 'Asset with pending movement log has original location');
|
|
$this->assertEquals($this->logLocation->getGeometry($first_log), $this->assetLocation->getGeometry($asset), 'Asset with pending movement log has original geometry.');
|
|
|
|
// Assert that the asset's cache tags were not invalidated.
|
|
$this->assertEntityTestCache($asset, TRUE);
|
|
|
|
// When the log is marked as "done", the asset location is updated.
|
|
$second_log->status = 'done';
|
|
$second_log->save();
|
|
$this->assertEquals($this->logLocation->getLocation($second_log), $this->assetLocation->getLocation($asset), 'Asset with second movement log has new location');
|
|
$this->assertEquals($this->logLocation->getGeometry($second_log), $this->assetLocation->getGeometry($asset), 'Asset with second movement log has new geometry.');
|
|
|
|
// Assert that the asset's cache tags were invalidated.
|
|
$this->assertEntityTestCache($asset, FALSE);
|
|
|
|
// Re-populate a cache value dependent on the asset's cache tags.
|
|
$this->populateEntityTestCache($asset);
|
|
|
|
// Create a third "done" movement log in the future.
|
|
/** @var \Drupal\log\Entity\LogInterface $third_log */
|
|
$third_log = Log::create([
|
|
'type' => 'movement',
|
|
'timestamp' => \Drupal::time()->getRequestTime() + 86400,
|
|
'status' => 'done',
|
|
'asset' => ['target_id' => $asset->id()],
|
|
'location' => ['target_id' => $this->locations[0]->id()],
|
|
'is_movement' => TRUE,
|
|
]);
|
|
$third_log->save();
|
|
|
|
// When an asset has a "done" movement log in the future, the asset
|
|
// location and geometry remain the same as the previous "done" movement
|
|
// log.
|
|
$this->assertEquals($this->logLocation->getLocation($second_log), $this->assetLocation->getLocation($asset), 'Asset with future movement log has current location');
|
|
$this->assertEquals($this->logLocation->getGeometry($second_log), $this->assetLocation->getGeometry($asset), 'Asset with future movement log has current geometry.');
|
|
|
|
// Assert that the asset's cache tags were not invalidated.
|
|
$this->assertEntityTestCache($asset, TRUE);
|
|
|
|
// Create a fourth "done" movement log without location.
|
|
/** @var \Drupal\log\Entity\LogInterface $fourth_log */
|
|
$fourth_log = Log::create([
|
|
'type' => 'movement',
|
|
'status' => 'done',
|
|
'asset' => ['target_id' => $asset->id()],
|
|
'is_movement' => TRUE,
|
|
]);
|
|
$fourth_log->save();
|
|
|
|
// When a movement log is created with no location/geometry, it effectively
|
|
// "unsets" the asset's location/geometry.
|
|
$this->assertFalse($this->assetLocation->hasLocation($asset), 'Asset location can be unset.');
|
|
$this->assertFalse($this->assetLocation->hasGeometry($asset), 'Asset geometry can be unset.');
|
|
|
|
// Assert that the asset's cache tags were invalidated.
|
|
$this->assertEntityTestCache($asset, FALSE);
|
|
|
|
// Re-populate a cache value dependent on the asset's cache tags.
|
|
$this->populateEntityTestCache($asset);
|
|
|
|
// Delete the fourth log.
|
|
$fourth_log->delete();
|
|
|
|
// When a movement log is deleted, the previous location is used.
|
|
$this->assertEquals($this->logLocation->getLocation($second_log), $this->assetLocation->getLocation($asset), 'When a movement log is deleted, the previous location is used.');
|
|
$this->assertEquals($this->logLocation->getGeometry($second_log), $this->assetLocation->getGeometry($asset), 'When a movement log is deleted, the previous locations geometry is used. .');
|
|
|
|
// Assert that the asset's cache tags were invalidated.
|
|
$this->assertEntityTestCache($asset, FALSE);
|
|
}
|
|
|
|
/**
|
|
* Test fixed asset location.
|
|
*/
|
|
public function testFixedAssetLocation() {
|
|
|
|
// Create a new "fixed" asset.
|
|
/** @var \Drupal\asset\Entity\AssetInterface $asset */
|
|
$asset = Asset::create([
|
|
'type' => 'object',
|
|
'name' => $this->randomMachineName(),
|
|
'status' => 'active',
|
|
'is_fixed' => TRUE,
|
|
]);
|
|
$asset->save();
|
|
|
|
// When a new asset is saved, it does not have a geometry.
|
|
$this->assertFalse($this->assetLocation->hasGeometry($asset), 'New assets do not have geometry.');
|
|
|
|
// When an asset is fixed, and has intrinsic geometry, it is the asset's
|
|
// geometry.
|
|
$this->assetLocation->setIntrinsicGeometry($asset, $this->polygons[0]);
|
|
$asset->save();
|
|
$this->assertTrue($this->assetLocation->hasGeometry($asset), 'Assets with intrinsic geometry have geometry.');
|
|
$this->assertEquals($this->polygons[0], $this->assetLocation->getGeometry($asset), 'Asset intrinsic geometry is asset geometry.');
|
|
|
|
// Create a "done" movement log that references the asset.
|
|
/** @var \Drupal\log\Entity\LogInterface $log */
|
|
$log = Log::create([
|
|
'type' => 'movement',
|
|
'status' => 'done',
|
|
'asset' => ['target_id' => $asset->id()],
|
|
'location' => ['target_id' => $this->locations[0]->id()],
|
|
'is_movement' => TRUE,
|
|
]);
|
|
$log->save();
|
|
|
|
// Movement logs of a fixed asset do not affect that asset's location or
|
|
// geometry.
|
|
$this->assertEquals([], $this->assetLocation->getLocation($asset), 'Movement logs of a fixed asset do not affect location.');
|
|
$this->assertEquals($this->polygons[0], $this->assetLocation->getGeometry($asset), 'Movement logs of a fixed asset do not affect geometry.');
|
|
|
|
// Set is_fixed to FALSE on the asset.
|
|
$asset->is_fixed = FALSE;
|
|
$asset->save();
|
|
|
|
// If an asset has a movement log and is no longer fixed, it's location and
|
|
// geometry equal location and geometry of the log.
|
|
$this->assertEquals($this->logLocation->getLocation($log), $this->assetLocation->getLocation($asset), 'Movement logs of a not fixed asset do affect location.');
|
|
$this->assertEquals($this->logLocation->getGeometry($log), $this->assetLocation->getGeometry($asset), 'Movement logs of a not fixed asset do affect geometry.');
|
|
}
|
|
|
|
/**
|
|
* Test assets in location.
|
|
*/
|
|
public function testLocationAssets() {
|
|
|
|
// Locations have no assets.
|
|
$this->assertEmpty($this->assetLocation->getAssetsByLocation([$this->locations[0], $this->locations[1]]));
|
|
|
|
// Create an asset and move it to the first location.
|
|
/** @var \Drupal\asset\Entity\AssetInterface $first_asset */
|
|
$first_asset = Asset::create([
|
|
'type' => 'object',
|
|
'name' => $this->randomMachineName(),
|
|
'status' => 'active',
|
|
]);
|
|
$first_asset->save();
|
|
/** @var \Drupal\log\Entity\LogInterface $first_log */
|
|
$first_log = Log::create([
|
|
'type' => 'movement',
|
|
'status' => 'done',
|
|
'asset' => ['target_id' => $first_asset->id()],
|
|
'location' => ['target_id' => $this->locations[0]->id()],
|
|
'is_movement' => TRUE,
|
|
]);
|
|
$first_log->save();
|
|
|
|
// First location has one asset, second has none.
|
|
$this->assertEquals(1, count($this->assetLocation->getAssetsByLocation([$this->locations[0]])));
|
|
$this->assertEmpty($this->assetLocation->getAssetsByLocation([$this->locations[1]]));
|
|
$this->assertEquals(1, count($this->assetLocation->getAssetsByLocation([$this->locations[0], $this->locations[1]])));
|
|
|
|
// Create a second asset and move it to the second location.
|
|
/** @var \Drupal\asset\Entity\AssetInterface $second_asset */
|
|
$second_asset = Asset::create([
|
|
'type' => 'object',
|
|
'name' => $this->randomMachineName(),
|
|
'status' => 'active',
|
|
]);
|
|
$second_asset->save();
|
|
/** @var \Drupal\log\Entity\LogInterface $first_log */
|
|
$second_log = Log::create([
|
|
'type' => 'movement',
|
|
'status' => 'done',
|
|
'asset' => ['target_id' => $second_asset->id()],
|
|
'location' => ['target_id' => $this->locations[1]->id()],
|
|
'is_movement' => TRUE,
|
|
]);
|
|
$second_log->save();
|
|
|
|
// Both locations have one asset.
|
|
$this->assertEquals(1, count($this->assetLocation->getAssetsByLocation([$this->locations[0]])));
|
|
$this->assertEquals(1, count($this->assetLocation->getAssetsByLocation([$this->locations[1]])));
|
|
$this->assertEquals(2, count($this->assetLocation->getAssetsByLocation([$this->locations[0], $this->locations[1]])));
|
|
|
|
// Create a third log that moves both assets to the first location.
|
|
/** @var \Drupal\log\Entity\LogInterface $third_log */
|
|
$third_log = Log::create([
|
|
'type' => 'movement',
|
|
'status' => 'done',
|
|
'asset' => [
|
|
['target_id' => $first_asset->id()],
|
|
['target_id' => $second_asset->id()],
|
|
],
|
|
'location' => ['target_id' => $this->locations[0]->id()],
|
|
'is_movement' => TRUE,
|
|
]);
|
|
$third_log->save();
|
|
|
|
// First location has two assets, second has none.
|
|
$this->assertEquals(2, count($this->assetLocation->getAssetsByLocation([$this->locations[0]])));
|
|
$this->assertEmpty($this->assetLocation->getAssetsByLocation([$this->locations[1]]));
|
|
$this->assertEquals(2, count($this->assetLocation->getAssetsByLocation([$this->locations[0], $this->locations[1]])));
|
|
|
|
// Create a fourth log that moves first asset to the second location.
|
|
/** @var \Drupal\log\Entity\LogInterface $fourth_log */
|
|
$fourth_log = Log::create([
|
|
'type' => 'movement',
|
|
'status' => 'done',
|
|
'asset' => [
|
|
['target_id' => $first_asset->id()],
|
|
],
|
|
'location' => ['target_id' => $this->locations[1]->id()],
|
|
'is_movement' => TRUE,
|
|
]);
|
|
$fourth_log->save();
|
|
|
|
// Both locations have one asset.
|
|
$this->assertEquals(1, count($this->assetLocation->getAssetsByLocation([$this->locations[0]])));
|
|
$this->assertEquals(1, count($this->assetLocation->getAssetsByLocation([$this->locations[1]])));
|
|
$this->assertEquals(2, count($this->assetLocation->getAssetsByLocation([$this->locations[0], $this->locations[1]])));
|
|
|
|
// Create a fifth log that moves first asset to the both locations.
|
|
/** @var \Drupal\log\Entity\LogInterface $fifth_log */
|
|
$fifth_log = Log::create([
|
|
'type' => 'movement',
|
|
'status' => 'done',
|
|
'asset' => [
|
|
['target_id' => $first_asset->id()],
|
|
],
|
|
'location' => [
|
|
['target_id' => $this->locations[0]->id()],
|
|
['target_id' => $this->locations[1]->id()],
|
|
],
|
|
'is_movement' => TRUE,
|
|
]);
|
|
$fifth_log->save();
|
|
|
|
// First location has two asset, second location has one asset.
|
|
$this->assertEquals(2, count($this->assetLocation->getAssetsByLocation([$this->locations[0]])));
|
|
$this->assertEquals(1, count($this->assetLocation->getAssetsByLocation([$this->locations[1]])));
|
|
$this->assertEquals(2, count($this->assetLocation->getAssetsByLocation([$this->locations[0], $this->locations[1]])));
|
|
}
|
|
|
|
}
|