Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add front end tests for memory leaks #16

Open
tnmygrwl opened this issue Oct 3, 2023 · 0 comments
Open

Add front end tests for memory leaks #16

tnmygrwl opened this issue Oct 3, 2023 · 0 comments
Labels
enhancement New feature or request good first issue Good for newcomers

Comments

@tnmygrwl
Copy link
Collaborator

tnmygrwl commented Oct 3, 2023

As a developer, I would like to have tests for the satellite imagery labeling tool to check for memory leaks and that the cache is appropriately cleared, so that I can verify that the tool does not have memory leaks and that new code changes do not introduce any.

Steps

  1. Test for Memory Leaks: We need to monitor the memory usage of our application over time, especially when interacting with discount.js and labeler.js. Tools like Chrome's Memory Profiler can be used to identify potential memory leaks.

  2. Test Cache Release: When the labeler.js page is reloaded, it offers to resume from the last spot. We need to ensure that this feature doesn't cause memory leaks. We should test that old cache data is appropriately released when a new session is started.

  3. Test Map Loading Speed: We need to ensure that maps load quickly when tiles are loaded. We should measure the time it takes to load tiles and aim to minimize this time.

The following code snippets are relevant to this ticket:

  • The auto-save feature in labeler.js that stores drawn data in local storage:
/** The main logic for the spatial annotation labeler app. */
export class LabelerApp {
	...
	/**
	 * Saves all drawn data in local storage for the project.
	 */
	#saveSession = async () => {
		//Get clean source data.
		const self = this;
		if (appSettings.autoSave.enabled) {
			const json = self.#getSourceData(false, true);

			//Store a copy of the data, date, and the name of the project.
			await self.#storage.setItem(self.config.properties.name, {
				data: json,
				date: Date.now()
			});
		}

		self.#calcStats();
	}

	/**
	 * Calculate stats on the drawn features.
	 */
	#calcStats = () => {
		if (this.#statsControl) {
			const statsInfo = [];

			const fc = this.#featureSource.toJson();

			statsInfo.push(`Number of shapes: ${fc.features.length}`)

			let c1 = 0;
			let c2 = 0;

			fc.features.forEach(f => {

			});

			this.#statsControl.setOptions({
				content: statsInfo.join(''),
				visible: true
			});
		}
	}

	/**
	 * Checks to see if any data for the project exists in local storage. If it does, asks if the user would like to recover it.
	 */
	async #checkStorage() {
		const self = this;

		//If auto save is disabled, don't try and load any data.
		if (appSettings.autoSave.enabled) {
			const today = new Date();

			//Calcuate the expiry date for old cached data.
			const expiryDate = new Date().setDate(today.getDate() - appSettings.autoSave.ttl);

			await self.#storage.iterate((value, key) => {
				//Check to see if there is cached data for the named project.
				if (key === self.config.properties.name) {
					//Check to see if the user wants to recover it.
					if (value && confirm('Found cached data for this project task. Continue from where you left off?')) {
						self.#featureSource.setShapes(value.data);
						self.#calcStats();
					} else {
						//If not, clear the cached data.
						self.#removeExpireData(key).then();
					}
				} else if (value.date < expiryDate) {
					//Check other cached data to see if it's older than the expiry date. If so, remove it.
					self.#removeExpireData(key).then();
				}
			});
		}
	}
  • The code that loads tiles in labeler.js:
/** The main logic for the spatial annotation labeler app. */
export class LabelerApp {
	...
	#initTilesPanel() {
		const self = this;
		let tilesListBox = document.getElementById('TilesList')
		let [incompleteTiles,completeTiles] = this.#tileManager.getMarkedAndUnmarkedTiles();
		tilesListBox.innerHTML = "";
		for(let tile of incompleteTiles){
			tilesListBox.add(new Option(tile,tile));
		}
		for(let tile of completeTiles){
			let option = new Option(tile,tile);
			option.style.background = 'gray'
			tilesListBox.add(option);
		}
		tilesListBox.addEventListener('change',()=>{
			self.#refreshTileSelection();
		})
		self.#refreshTileSelection();
	}

	#refreshTileSelection(){
		try{
			let tilesListBox = document.getElementById('TilesList')
			let currentTile = tilesListBox.value
			let boundaries = this.#tileManager.getTileBoundaries(currentTile);
			let bbox = atlas.data.BoundingBox(boundaries);
			// Set the camera
			this.map.setCamera({	
				bounds: boundaries,
				maxBounds: bbox,
				padding: 100
			});
			if(this.#currentTile !== currentTile){
				this.#currentTile = currentTile
				this.#updateTiffLayers(currentTile);
			}
			this.#featureSource.clear()
			this.#importFeatures(this.#tileManager.getTileFeatures(currentTile),'TileFeatures',false,true)
			this.#runAndUpdateDiscount()
		} catch(e){
			console.log(e)
		}
	}
  • The code that initializes the map in labeler.js:
/** The main logic for the spatial annotation labeler app. */
export class LabelerApp {
	...
	///////////////////////////
	// Constructor
	//////////////////////////

	/** The main logic for the spatial annotation labeler app. */
	constructor() {
		...
		self.flyout.on('closed', (item) => {
			self.navbar.setSelectedItem('');
			self.navbar.focus();
		});

		//Initialize a map instance.
		self.map = Utils.createMap('myMap', mapSettings.azureMapsAuth);

		self.map.events.add('ready', self.#mapReady);

		//Make clone of the default config.
		self.config = Object.assign({}, appSettings.defaultConfig.features[0]);

		//Check to see if the URL contains a URL path.
		const queryString = window.location.search;
		const urlParams = new URLSearchParams(queryString);

		if (urlParams.has("taskUrl")) {
			let taskUrl = urlParams.get('taskUrl');

			if (taskUrl && taskUrl !== '') {
				if (taskUrl.indexOf('%2F') > -1) {
					//Assume URL is encoded, decode it.
					taskUrl = decodeURIComponent(taskUrl);
				}

				fetch(taskUrl).then(x => {
					return x.json();
				}).then(fc => {
					if (fc.features && fc.features.length > 0) {
						self.config = fc.features[0];
					}
					self.#loadConfig();
				});
			}
		} else {
			self.#loadConfig();
		}

		document.getElementById('app-theme').onchange = self.#themeColorChanged;

		//Initialized the flyout panels.
		self.#initLoadPanel();
		self.#initLayerPanel();
		self.#initSavePanel();
		self.#initScreenshotPanel();
		self.#initPowerTools();

Acceptance Criteria

  • No significant increase in memory usage over time when interacting with discount.js and labeler.js.
  • Old cache data is properly released when a new session is started.
  • Maps load quickly when tiles are loaded.

Definition of Done:

  • Tests written
  • Documentation updated so that other developers know how to run the tests
  • Github action or git commit hook added so that tests are run automatically

Time Box:

5 days to complete it, including writing tests, running them, and fixing any identified issues.

@tnmygrwl tnmygrwl added enhancement New feature or request good first issue Good for newcomers labels Oct 3, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request good first issue Good for newcomers
Projects
None yet
Development

No branches or pull requests

1 participant