Skip to content

bpo-31130: IDLE -- stop leak in test_configdialog. #3016

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

Merged
merged 3 commits into from
Aug 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Lib/idlelib/configdialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -1856,6 +1856,7 @@ def __init__(self):

def clear(self):
"Clear lists (for tests)."
# Call after all tests in a module to avoid memory leaks.
self.untraced.clear()
self.traced.clear()

Expand Down
86 changes: 48 additions & 38 deletions Lib/idlelib/idle_test/test_configdialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import unittest
from unittest import mock
from idlelib.idle_test.mock_idle import Func
from tkinter import Tk, Frame, IntVar, BooleanVar, DISABLED, NORMAL
from tkinter import Tk, Frame, StringVar, IntVar, BooleanVar, DISABLED, NORMAL
from idlelib import config
from idlelib.configdialog import idleConf, changes, tracers

Expand Down Expand Up @@ -41,6 +41,8 @@ def tearDownModule():
global root, dialog
idleConf.userCfg = usercfg
tracers.detach()
tracers.clear()
changes.clear()
del dialog
root.update_idletasks()
root.destroy()
Expand Down Expand Up @@ -442,97 +444,105 @@ def test_update_help_changes(self):

class VarTraceTest(unittest.TestCase):

@classmethod
def setUpClass(cls):
cls.tracers = configdialog.VarTrace()
cls.iv = IntVar(root)
cls.bv = BooleanVar(root)

@classmethod
def tearDownClass(cls):
del cls.tracers, cls.iv, cls.bv

def setUp(self):
changes.clear()
tracers.clear()
self.v1 = IntVar(root)
self.v2 = BooleanVar(root)
self.tracers.clear()
self.called = 0

def tearDown(self):
del self.v1, self.v2

def var_changed_increment(self, *params):
self.called += 13

def var_changed_boolean(self, *params):
pass

def test_init(self):
tracers.__init__()
self.assertEqual(tracers.untraced, [])
self.assertEqual(tracers.traced, [])
tr = self.tracers
tr.__init__()
self.assertEqual(tr.untraced, [])
self.assertEqual(tr.traced, [])

def test_clear(self):
tracers.untraced.append(0)
tracers.traced.append(1)
tracers.clear()
self.assertEqual(tracers.untraced, [])
self.assertEqual(tracers.traced, [])
tr = self.tracers
tr.untraced.append(0)
tr.traced.append(1)
tr.clear()
self.assertEqual(tr.untraced, [])
self.assertEqual(tr.traced, [])

def test_add(self):
tr = tracers
tr = self.tracers
func = Func()
cb = tr.make_callback = mock.Mock(return_value=func)

v1 = tr.add(self.v1, self.var_changed_increment)
self.assertIsInstance(v1, IntVar)
v2 = tr.add(self.v2, self.var_changed_boolean)
self.assertIsInstance(v2, BooleanVar)
iv = tr.add(self.iv, self.var_changed_increment)
self.assertIs(iv, self.iv)
bv = tr.add(self.bv, self.var_changed_boolean)
self.assertIs(bv, self.bv)

v3 = IntVar(root)
v3 = tr.add(v3, ('main', 'section', 'option'))
sv = StringVar(root)
sv2 = tr.add(sv, ('main', 'section', 'option'))
self.assertIs(sv2, sv)
cb.assert_called_once()
cb.assert_called_with(v3, ('main', 'section', 'option'))
cb.assert_called_with(sv, ('main', 'section', 'option'))

expected = [(v1, self.var_changed_increment),
(v2, self.var_changed_boolean),
(v3, func)]
expected = [(iv, self.var_changed_increment),
(bv, self.var_changed_boolean),
(sv, func)]
self.assertEqual(tr.traced, [])
self.assertEqual(tr.untraced, expected)

del tr.make_callback

def test_make_callback(self):
cb = tracers.make_callback(self.v1, ('main', 'section', 'option'))
cb = self.tracers.make_callback(self.iv, ('main', 'section', 'option'))
self.assertTrue(callable(cb))
self.v1.set(42)
self.iv.set(42)
# Not attached, so set didn't invoke the callback.
self.assertNotIn('section', changes['main'])
# Invoke callback manually.
cb()
self.assertIn('section', changes['main'])
self.assertEqual(changes['main']['section']['option'], '42')
changes.clear()

def test_attach_detach(self):
tr = tracers
v1 = tr.add(self.v1, self.var_changed_increment)
v2 = tr.add(self.v2, self.var_changed_boolean)
expected = [(v1, self.var_changed_increment),
(v2, self.var_changed_boolean)]
tr = self.tracers
iv = tr.add(self.iv, self.var_changed_increment)
bv = tr.add(self.bv, self.var_changed_boolean)
expected = [(iv, self.var_changed_increment),
(bv, self.var_changed_boolean)]

# Attach callbacks and test call increment.
tr.attach()
self.assertEqual(tr.untraced, [])
self.assertCountEqual(tr.traced, expected)
v1.set(1)
self.assertEqual(v1.get(), 1)
iv.set(1)
self.assertEqual(iv.get(), 1)
self.assertEqual(self.called, 13)

# Check that only one callback is attached to a variable.
# If more than one callback were attached, then var_changed_increment
# would be called twice and the counter would be 2.
self.called = 0
tr.attach()
v1.set(1)
iv.set(1)
self.assertEqual(self.called, 13)

# Detach callbacks.
self.called = 0
tr.detach()
self.assertEqual(tr.traced, [])
self.assertCountEqual(tr.untraced, expected)
v1.set(1)
iv.set(1)
self.assertEqual(self.called, 0)


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
IDLE -- stop leaks in test_configdialog. Initial patch by Victor Stinner.