def test_alwaysAllowedObjects(self):
		"""Matches test_threadLimit_canaryStartAndEnd, but allows events from the first object
		"""
		limiter = OrderedWinEventLimiter(maxFocusItems=4)

		# Window, objectID, childID
		canaryObject = (1, 1, 1)
		eventStartCanary = (winUser.EVENT_OBJECT_VALUECHANGE, *canaryObject)
		limiter.addEvent(*eventStartCanary, threadID=0)

		for n in range(orderedWinEventLimiter.MAX_WINEVENTS_PER_THREAD):  # exceed the limit for a single thread
			eventId = nonSpecialCaseEvents[n % len(nonSpecialCaseEvents)]
			# same thread, different object. Ensure there are no duplicates
			# Window, objectID, childID
			source = (2, 2, n,)
			limiter.addEvent(eventId, *source, threadID=0)

		eventEndCanary = (winUser.EVENT_OBJECT_NAMECHANGE, *canaryObject)
		limiter.addEvent(*eventEndCanary, threadID=0)

		events = limiter.flushEvents(alwaysAllowedObjects=[canaryObject, ])
		# only the most recent event of each object is kept, all previous duplicates are discarded
		self.assertEqual(11, len(events))
		self.assertIn(eventStartCanary, events)
		self.assertEqual(eventStartCanary, events[0])
		self.assertIn(eventEndCanary, events)
	def test_threadLimit_canaryStartAndEnd(self):
		"""Test that only the latest events are kept when the thread limit is exceeded
		"""
		limiter = OrderedWinEventLimiter(maxFocusItems=4)

		# Window, objectID, childID
		canaryObject = (1, 1, 1)
		eventStartCanary = (winUser.EVENT_OBJECT_VALUECHANGE, *canaryObject)
		limiter.addEvent(*eventStartCanary, threadID=0)

		for n in range(500):  # exceed the limit for a single thread
			eventId = nonSpecialCaseEvents[n % len(nonSpecialCaseEvents)]
			# same thread, different object. Ensure there are no duplicates
			# Window, objectID, childID
			source = (2, 2, n,)
			limiter.addEvent(eventId, *source, threadID=0)

		# Note event type must differ from start canary to ensure they are not duplicates
		eventEndCanary = (winUser.EVENT_OBJECT_NAMECHANGE, *canaryObject)
		limiter.addEvent(*eventEndCanary, threadID=0)

		events = limiter.flushEvents()
		errors = []
		expectedEventCount = orderedWinEventLimiter.MAX_WINEVENTS_PER_THREAD
		softAssert(errors, self.assertEqual, expectedEventCount, len(events))  # Fails with 11 actual events
		softAssert(errors, self.assertIn, eventEndCanary, events)
		softAssert(errors, self.assertNotIn, eventStartCanary, events)
		self.assertListEqual([], errors)
	def test_maxFocusEvents(self):
		limiter = OrderedWinEventLimiter(maxFocusItems=4)
		for n in range(0, 5):
			limiter.addEvent(
				eventID=winUser.EVENT_OBJECT_FOCUS,
				window=n, objectID=n, childID=n, threadID=n,
			)
		events = limiter.flushEvents()
		windowIds = [
			e[1]  # window
			for e in events
		]
		expectedIds = [1, 2, 3, 4]
		self.assertEqual(expectedIds, windowIds)
	def test_threadLimit_singleObject(self):
		"""Test that only the latest events are kept when the thread limit is exceeded
		"""
		# We have events from two unique objects:
		# Window, objectID, childID
		source = (2, 2, 2,)

		limiter = OrderedWinEventLimiter(maxFocusItems=4)

		for n in range(500):  # exceed the limit for a single thread
			eventId = nonSpecialCaseEvents[n % len(nonSpecialCaseEvents)]
			# same thread, different object. Use a second object to aid tracking.
			limiter.addEvent(eventId, *source, threadID=0)

		events = limiter.flushEvents()
		expectedEventCount = orderedWinEventLimiter.MAX_WINEVENTS_PER_THREAD
		self.assertEqual(expectedEventCount, len(events))
 def test_foregroundOverwritesFocus(self):
     limiter = OrderedWinEventLimiter(maxFocusItems=4)
     n = 1
     limiter.addEvent(
         eventID=winUser.EVENT_OBJECT_FOCUS,
         window=n,
         objectID=n,
         childID=n,
         threadID=n,
     )
     limiter.addEvent(
         eventID=winUser.EVENT_SYSTEM_FOREGROUND,
         window=n,
         objectID=n,
         childID=n,
         threadID=n,
     )
     events = limiter.flushEvents()
     actualEvents = [
         (
             e[0],  # eventID
             e[1],  # window
         ) for e in events
     ]
     expectedEvents = [(winUser.EVENT_SYSTEM_FOREGROUND, n),
                       (winUser.EVENT_SYSTEM_FOREGROUND, n)]
     self.assertEqual(expectedEvents, actualEvents)
 def test_hideNoOverridesShow_whenDetailsDontMatch(self):
     limiter = OrderedWinEventLimiter(maxFocusItems=4)
     n = 1
     limiter.addEvent(
         eventID=winUser.EVENT_OBJECT_SHOW,
         window=n,
         objectID=n,
         childID=n,
         threadID=n,
     )
     n = 2
     limiter.addEvent(
         eventID=winUser.EVENT_OBJECT_HIDE,
         window=n,
         objectID=n,
         childID=n,
         threadID=n,
     )
     events = limiter.flushEvents()
     actualEvents = [(e[0], e[1]) for e in events]
     expectedEvents = [
         (winUser.EVENT_OBJECT_SHOW, 1),
         (winUser.EVENT_OBJECT_HIDE, 2),
     ]
     self.assertEqual(expectedEvents, actualEvents)
	def test_threadLimit_noCanary(self):
		"""Test that only the latest events are kept when the thread limit is exceeded
		"""
		limiter = OrderedWinEventLimiter(maxFocusItems=4)

		for n in range(500):  # exceed the limit for a single thread
			eventId = nonSpecialCaseEvents[n % len(nonSpecialCaseEvents)]
			# same thread, different object. Ensure there are no duplicates
			# Window, objectID, childID
			source = (2, 2, n,)
			limiter.addEvent(eventId, *source, threadID=0)

		events = limiter.flushEvents()

		errors = []
		expectedEventCount = orderedWinEventLimiter.MAX_WINEVENTS_PER_THREAD
		softAssert(errors, self.assertEqual, expectedEventCount, len(events))  # Fails with 11 actual events
		self.assertListEqual([], errors)
	def test_limitEventsPerThread(self):
		limiter = OrderedWinEventLimiter(maxFocusItems=4)
		for n in reversed(range(2000)):  # send many events, to saturate all limits.
			limiter.addEvent(
				eventID=specialCaseEvents[n % len(specialCaseEvents)],
				window=n, objectID=n, childID=n,
				threadID=0,  # all events for same thread
			)
		events = limiter.flushEvents()
		windowIds = [
			e[1]  # window
			for e in events
		]
		# TODO: Note: repeated Id's (0 and 8) are EVENT_SYSTEM_FOREGROUND see test_maxFocusEvents
		expectedIds = [24, 19, 18, 16, 11, 10, 9, 8, 8, 4, 3, 2, 1, 0, 0]
		self.assertEqual(expectedIds, windowIds)
		#  equal to MAX_WINEVENTS_PER_THREAD=10
		# Plus 4 focus events,
		# Plus the last menu event.
		#  All totalling 15.
		self.assertEqual(len(windowIds), 15)
	def test_alwaysAllowedObjects_specialCaseEvents(self):
		# We have events from two unique objects:
		# Window, objectID, childID
		allowedSource = (1, 1, 1)

		limiter = OrderedWinEventLimiter(maxFocusItems=4)
		for n in range(2000):  # send many events, to saturate all limits.
			eventId = specialCaseEvents[n % len(specialCaseEvents)]
			limiter.addEvent(eventId, *allowedSource, threadID=0)
		events = limiter.flushEvents(alwaysAllowedObjects=[allowedSource, ])

		expected = [
			# Two Foreground events, because they are added to multiple queues.
			# Added to both _focusEventCache and _genericEventCache
			# See also test_limitEventsPerThread
			(winUser.EVENT_SYSTEM_FOREGROUND, *allowedSource),
			(winUser.EVENT_SYSTEM_FOREGROUND, *allowedSource),
			(winUser.EVENT_OBJECT_FOCUS, *allowedSource),
			(winUser.EVENT_OBJECT_HIDE, *allowedSource),  # latest out of show / hide is kept
			(winUser.EVENT_SYSTEM_MENUPOPUPEND, *allowedSource),  # Only one menu event is allowed
		]
		self.assertEqual(expected, events)
	def test_alwaysAllowedObjects_onlyLatestEventKept(self):
		# We have events from two unique objects:
		# Window, objectID, childID
		allowedSource = (1, 1, 1)
		otherSource = (2, 2, 2,)

		limiter = OrderedWinEventLimiter(maxFocusItems=4)
		for n in range(50):  # send many value changed events
			limiter.addEvent(winUser.EVENT_OBJECT_VALUECHANGE, *allowedSource, threadID=0)
			limiter.addEvent(winUser.EVENT_OBJECT_VALUECHANGE, *otherSource, threadID=0)
		events = limiter.flushEvents(alwaysAllowedObjects=[allowedSource, ])
		# only the most recent event of each object is kept, all previous duplicates are discarded
		self.assertEqual(2, len(events))