<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Unit Tests for Loris WSGI JPEG 2000 Server
==========================================================
	Unit tests are named so that they run in a logical order, which means 
	running e.g. `python -m unittest -vf test.suite` should make it clearer
	where the actual failure happened, rather than having to trace back through
	a cascade of failures to find the original fault.

Author: <NAME> <<EMAIL>>
Since: 2012-08-25

"""
from datetime import datetime, timedelta
from decimal import getcontext
from json import loads
from loris.app import create_app
from loris.deepzoom import DeepZoomImageDescriptor
from loris.exceptions import BadRegionRequestException
from loris.exceptions import BadRegionSyntaxException
from loris.exceptions import BadSizeSyntaxException
from loris.img_info import ImgInfo
from loris.parameters import RegionParameter
from loris.parameters import RotationParameter
from loris.parameters import SizeParameter
from loris.resolver import resolve
from os import listdir, path, remove
from shutil import rmtree
from sys import stderr, stdout
from werkzeug.datastructures import Headers
from werkzeug.http import http_date, parse_date
from werkzeug.test import Client
from werkzeug.wrappers import BaseResponse, Request
from xml.dom.minidom import parseString
import loris.app
import struct
import subprocess
import unittest

class LorisTest(unittest.TestCase):
	def setUp(self):
		unittest.TestCase.setUp(self)
		# create an instance of the app here that we can use in tests
		# see http://werkzeug.pocoo.org/docs/test/
		self.app = create_app(test=True)
		getcontext().prec = self.app.decimal_precision
		self.client = Client(self.app, BaseResponse)
		abs_path = path.abspath(path.dirname(__file__))
		self.test_img_dir = path.join(abs_path, 'img')
		self.test_jp2_id = 'another/arbitrary/path/0004' # color; 2717 x 3600
		self.test_jp2_1_id = 'yet/another/path/0001' # color; 5283 x 7200
		self.test_jp2_2_id = 'some/path/0042' # grey; 2477 x 3200

	def tearDown(self):
		# empty the cache
		for d in listdir(loris.app.CACHE):
			rmtree(path.join(loris.app.CACHE, d))
		rmtree(loris.app.CACHE)

	def get_jpeg_dimensions(self, path):
		"""Get the dimensions of a JPEG
		"""
		jpeg = open(path, 'r')
		jpeg.read(2)
		b = jpeg.read(1)
		try:
			while (b and ord(b) != 0xDA):
				while (ord(b) != 0xFF): b = jpeg.read(1)
				while (ord(b) == 0xFF): b = jpeg.read(1)
			   
				if (ord(b) >= 0xC0 and ord(b) <= 0xC3):
					jpeg.read(3)
					h, w = struct.unpack(">HH", jpeg.read(4))
					break
				else:
					jpeg.read(int(struct.unpack(">H", jpeg.read(2))[0]) - 2)
				   
				b = jpeg.read(1)
			width = int(w)
			height = int(h)
		except Exception, e:
			raise
		finally:
			jpeg.close()
		return (width, height)

	def _dims_from_uri(self, uri):
		"""Make a request, save it out, and return the dimensions.
		"""
		resp = self.client.get(uri)
		f_name = path.join(loris.app.TMP, 'result.jpg')
		f = open(f_name, 'w')
		f.write(resp.data)
		f.close()
		return self.get_jpeg_dimensions(f_name)


class Test_A_ResolveId(LorisTest):
	"""Test that the ID resolver works.
	"""
	def test_loris_resolve_id(self):
		expected_path = path.join(self.test_img_dir, 'another/arbitrary/path/0004.jp2')
		resolved_path = resolve(self.test_jp2_id)
		self.assertEqual(expected_path, resolved_path)
		self.assertTrue(path.isfile(resolved_path))

class Test_B_InfoExtraction(LorisTest):
	"""Here we extract info from JPEG 2000 files and test against known values.
	"""
	
	def test_img_info(self):
		img = resolve(self.test_jp2_id)
		info = ImgInfo(img, self.test_jp2_id)

		self.assertEqual(info.width, 2717)
		self.assertEqual(info.height, 3600)
		self.assertEqual(info.tile_width, 256)
		self.assertEqual(info.tile_height, 256)
		self.assertEqual(info.levels, 5)
		self.assertEqual(info.qualities, ['native', 'bitonal', 'grey', 'color'])
		self.assertEqual(info.ident, self.test_jp2_id)
	
	def test_img_info_1(self):
		img = self.app._resolve_identifier(self.test_jp2_1_id)
		info = ImgInfo(img, self.test_jp2_1_id)

		self.assertEqual(info.width, 5283)
		self.assertEqual(info.height, 7200)
		self.assertEqual(info.tile_width, 256)
		self.assertEqual(info.tile_height, 256)
		self.assertEqual(info.levels, 7)
		self.assertEqual(info.qualities, ['native', 'bitonal', 'grey', 'color'])
		self.assertEqual(info.ident, self.test_jp2_1_id)

	def test_img_info_2(self):
		img = self.app._resolve_identifier(self.test_jp2_2_id)
		info = ImgInfo(img, self.test_jp2_2_id)

		self.assertEqual(info.width, 2477)
		self.assertEqual(info.height, 3200)
		self.assertEqual(info.tile_width, 256)
		self.assertEqual(info.tile_height, 256)
		self.assertEqual(info.levels, 6)
		self.assertEqual(info.qualities, ['native', 'bitonal', 'grey'])
		self.assertEqual(info.ident, self.test_jp2_2_id)

	def test_info_json(self):
		resp = self.client.get('/' + self.test_jp2_id + '/info.json')
		resp_json = loads(resp.data)
		self.assertTrue(resp_json.has_key(u'identifier'))
		self.assertEqual(resp_json.get(u'identifier'), self.test_jp2_id)
		self.assertTrue(resp_json.has_key(u'width'))
		self.assertEqual(resp_json.get(u'width'), 2717)
		self.assertTrue(resp_json.has_key(u'height'))
		self.assertEqual(resp_json.get(u'height'), 3600)
		self.assertTrue(resp_json.has_key(u'scale_factors'))
		self.assertEqual(resp_json.get(u'scale_factors'), [1,2,3,4,5])
		self.assertTrue(resp_json.has_key(u'tile_width'))
		self.assertEqual(resp_json.get(u'tile_width'), 256)
		self.assertTrue(resp_json.has_key(u'tile_height'))
		self.assertEqual(resp_json.get(u'tile_height'), 256)
		self.assertTrue(resp_json.has_key(u'formats'))
		self.assertEqual(resp_json.get(u'formats'), [u'jpg', u'png'])
		self.assertTrue(resp_json.has_key(u'qualities'))
		self.assertEqual(resp_json.get(u'qualities'), [u'native', u'bitonal', u'grey', u'color'])
		self.assertTrue(resp_json.has_key(u'profile'))
		self.assertEqual(resp_json.get(u'profile'), u'http://library.stanford.edu/iiif/image-api/compliance.html#level1')

	def test_info_json_callback(self):
		self.app.allow_callback = True
		resp = self.client.get('/' + self.test_jp2_id + '/info.json?callback=myfunct')
		self.assertTrue(resp.data.startswith('myfunct('))

		self.app.allow_callback = False
		resp = self.client.get('/' + self.test_jp2_id + '/info.json?callback=myfunct')
		self.assertFalse(resp.data.startswith('myfunct('))

	def test_info_xml(self):
		resp = self.client.get('/' + self.test_jp2_id + '/info.xml')
		#This is parsable
		dom = parseString(resp.data)
		# info is the root
		self.assertEqual(dom.documentElement.tagName, 'info')

		# TODO: finish. Right now values are tested with the object and json, 
		# but not here

class Test_C_RegionParameter(LorisTest):
	"""Here we construct RegionParameter objects from would-be URI slices and
	test their attributes and methods.
	"""
	def test_full(self):
		url_segment = 'full'
		expected_mode = 'full'<commit_msg>added test for application/javascript content-type w/jsonp<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Unit Tests for Loris WSGI JPEG 2000 Server
==========================================================
	Unit tests are named so that they run in a logical order, which means 
	running e.g. `python -m unittest -vf test.suite` should make it clearer
	where the actual failure happened, rather than having to trace back through
	a cascade of failures to find the original fault.

Author: <NAME> <<EMAIL>>
Since: 2012-08-25

"""
from datetime import datetime, timedelta
from decimal import getcontext
from json import loads
from loris.app import create_app
from loris.deepzoom import DeepZoomImageDescriptor
from loris.exceptions import BadRegionRequestException
from loris.exceptions import BadRegionSyntaxException
from loris.exceptions import BadSizeSyntaxException
from loris.img_info import ImgInfo
from loris.parameters import RegionParameter
from loris.parameters import RotationParameter
from loris.parameters import SizeParameter
from loris.resolver import resolve
from os import listdir, path, remove
from shutil import rmtree
from sys import stderr, stdout
from werkzeug.datastructures import Headers
from werkzeug.http import http_date, parse_date
from werkzeug.test import Client
from werkzeug.wrappers import BaseResponse, Request
from xml.dom.minidom import parseString
import loris.app
import struct
import subprocess
import unittest

class LorisTest(unittest.TestCase):
	def setUp(self):
		unittest.TestCase.setUp(self)
		# create an instance of the app here that we can use in tests
		# see http://werkzeug.pocoo.org/docs/test/
		self.app = create_app(test=True)
		getcontext().prec = self.app.decimal_precision
		self.client = Client(self.app, BaseResponse)
		abs_path = path.abspath(path.dirname(__file__))
		self.test_img_dir = path.join(abs_path, 'img')
		self.test_jp2_id = 'another/arbitrary/path/0004' # color; 2717 x 3600
		self.test_jp2_1_id = 'yet/another/path/0001' # color; 5283 x 7200
		self.test_jp2_2_id = 'some/path/0042' # grey; 2477 x 3200

	def tearDown(self):
		# empty the cache
		for d in listdir(loris.app.CACHE):
			rmtree(path.join(loris.app.CACHE, d))
		rmtree(loris.app.CACHE)

	def get_jpeg_dimensions(self, path):
		"""Get the dimensions of a JPEG
		"""
		jpeg = open(path, 'r')
		jpeg.read(2)
		b = jpeg.read(1)
		try:
			while (b and ord(b) != 0xDA):
				while (ord(b) != 0xFF): b = jpeg.read(1)
				while (ord(b) == 0xFF): b = jpeg.read(1)
			   
				if (ord(b) >= 0xC0 and ord(b) <= 0xC3):
					jpeg.read(3)
					h, w = struct.unpack(">HH", jpeg.read(4))
					break
				else:
					jpeg.read(int(struct.unpack(">H", jpeg.read(2))[0]) - 2)
				   
				b = jpeg.read(1)
			width = int(w)
			height = int(h)
		except Exception, e:
			raise
		finally:
			jpeg.close()
		return (width, height)

	def _dims_from_uri(self, uri):
		"""Make a request, save it out, and return the dimensions.
		"""
		resp = self.client.get(uri)
		f_name = path.join(loris.app.TMP, 'result.jpg')
		f = open(f_name, 'w')
		f.write(resp.data)
		f.close()
		return self.get_jpeg_dimensions(f_name)


class Test_A_ResolveId(LorisTest):
	"""Test that the ID resolver works.
	"""
	def test_loris_resolve_id(self):
		expected_path = path.join(self.test_img_dir, 'another/arbitrary/path/0004.jp2')
		resolved_path = resolve(self.test_jp2_id)
		self.assertEqual(expected_path, resolved_path)
		self.assertTrue(path.isfile(resolved_path))

class Test_B_InfoExtraction(LorisTest):
	"""Here we extract info from JPEG 2000 files and test against known values.
	"""
	
	def test_img_info(self):
		img = resolve(self.test_jp2_id)
		info = ImgInfo(img, self.test_jp2_id)

		self.assertEqual(info.width, 2717)
		self.assertEqual(info.height, 3600)
		self.assertEqual(info.tile_width, 256)
		self.assertEqual(info.tile_height, 256)
		self.assertEqual(info.levels, 5)
		self.assertEqual(info.qualities, ['native', 'bitonal', 'grey', 'color'])
		self.assertEqual(info.ident, self.test_jp2_id)
	
	def test_img_info_1(self):
		img = self.app._resolve_identifier(self.test_jp2_1_id)
		info = ImgInfo(img, self.test_jp2_1_id)

		self.assertEqual(info.width, 5283)
		self.assertEqual(info.height, 7200)
		self.assertEqual(info.tile_width, 256)
		self.assertEqual(info.tile_height, 256)
		self.assertEqual(info.levels, 7)
		self.assertEqual(info.qualities, ['native', 'bitonal', 'grey', 'color'])
		self.assertEqual(info.ident, self.test_jp2_1_id)

	def test_img_info_2(self):
		img = self.app._resolve_identifier(self.test_jp2_2_id)
		info = ImgInfo(img, self.test_jp2_2_id)

		self.assertEqual(info.width, 2477)
		self.assertEqual(info.height, 3200)
		self.assertEqual(info.tile_width, 256)
		self.assertEqual(info.tile_height, 256)
		self.assertEqual(info.levels, 6)
		self.assertEqual(info.qualities, ['native', 'bitonal', 'grey'])
		self.assertEqual(info.ident, self.test_jp2_2_id)

	def test_info_json(self):
		resp = self.client.get('/' + self.test_jp2_id + '/info.json')
		resp_json = loads(resp.data)
		self.assertTrue(resp_json.has_key(u'identifier'))
		self.assertEqual(resp_json.get(u'identifier'), self.test_jp2_id)
		self.assertTrue(resp_json.has_key(u'width'))
		self.assertEqual(resp_json.get(u'width'), 2717)
		self.assertTrue(resp_json.has_key(u'height'))
		self.assertEqual(resp_json.get(u'height'), 3600)
		self.assertTrue(resp_json.has_key(u'scale_factors'))
		self.assertEqual(resp_json.get(u'scale_factors'), [1,2,3,4,5])
		self.assertTrue(resp_json.has_key(u'tile_width'))
		self.assertEqual(resp_json.get(u'tile_width'), 256)
		self.assertTrue(resp_json.has_key(u'tile_height'))
		self.assertEqual(resp_json.get(u'tile_height'), 256)
		self.assertTrue(resp_json.has_key(u'formats'))
		self.assertEqual(resp_json.get(u'formats'), [u'jpg', u'png'])
		self.assertTrue(resp_json.has_key(u'qualities'))
		self.assertEqual(resp_json.get(u'qualities'), [u'native', u'bitonal', u'grey', u'color'])
		self.assertTrue(resp_json.has_key(u'profile'))
		self.assertEqual(resp_json.get(u'profile'), u'http://library.stanford.edu/iiif/image-api/compliance.html#level1')

	def test_info_json_callback(self):
		self.app.allow_callback = True
		resp = self.client.get('/' + self.test_jp2_id + '/info.json?callback=myfunct')
		self.assertEqual(resp.headers.get('content-type'), 'application/javascript')
		self.assertTrue(resp.data.startswith('myfunct('))

		self.app.allow_callback = False
		resp = self.client.get('/' + self.test_jp2_id + '/info.json?callback=myfunct')
		self.assertEqual(resp.headers.get('content-type'), 'text/json')
		self.assertFalse(resp.data.startswith('myfunct('))

	def test_info_xml(self):
		resp = self.client.get('/' + self.test_jp2_id + '/info.xml')
		#This is parsable
		dom = parseString(resp.data)
		# info is the root
		self.assertEqual(dom.documentElement.tagName, 'info')

		# TODO: finish. Right now values are tested with the object and json, 
		# but not here

class Test_C_RegionParameter(LorisTest):
	"""Here we construct RegionParameter objects from would-be URI slices and
	test their attributes and methods.
	"""
	def test_full(self):
		url_segment = 'full'
		expected_mode = 'full'
{'fraction_non_alphanumeric': 0.13735640334704297, 'fraction_numerical': 0.023826407601758616, 'mean_word_length': 5.014072494669509, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 6}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6908045', 'n_tokens_mistral': 5613, 'n_tokens_neox': 5006, 'n_words': 1056}