| # encoding: utf-8 |
| """ |
| Test lldb data formatter subsystem. |
| """ |
| |
| from __future__ import print_function |
| |
| |
| import datetime |
| import os |
| import time |
| import lldb |
| from lldbsuite.test.decorators import * |
| from lldbsuite.test.lldbtest import * |
| from lldbsuite.test import lldbutil |
| |
| |
| class ObjCDataFormatterTestCase(TestBase): |
| |
| mydir = TestBase.compute_mydir(__file__) |
| |
| @skipUnlessDarwin |
| def test_plain_objc_with_run_command(self): |
| """Test basic ObjC formatting behavior.""" |
| self.build() |
| self.plain_data_formatter_commands() |
| |
| def appkit_tester_impl(self, commands): |
| self.build() |
| self.appkit_common_data_formatters_command() |
| commands() |
| |
| @skipUnlessDarwin |
| def test_nsnumber_with_run_command(self): |
| """Test formatters for NSNumber.""" |
| self.appkit_tester_impl(self.nsnumber_data_formatter_commands) |
| |
| @skipUnlessDarwin |
| def test_nscontainers_with_run_command(self): |
| """Test formatters for NS container classes.""" |
| self.appkit_tester_impl(self.nscontainers_data_formatter_commands) |
| |
| @skipUnlessDarwin |
| def test_nsdata_with_run_command(self): |
| """Test formatters for NSData.""" |
| self.appkit_tester_impl(self.nsdata_data_formatter_commands) |
| |
| @skipUnlessDarwin |
| def test_nsurl_with_run_command(self): |
| """Test formatters for NSURL.""" |
| self.appkit_tester_impl(self.nsurl_data_formatter_commands) |
| |
| @skipUnlessDarwin |
| def test_nserror_with_run_command(self): |
| """Test formatters for NSError.""" |
| self.appkit_tester_impl(self.nserror_data_formatter_commands) |
| |
| @skipUnlessDarwin |
| def test_nsbundle_with_run_command(self): |
| """Test formatters for NSBundle.""" |
| self.appkit_tester_impl(self.nsbundle_data_formatter_commands) |
| |
| @skipUnlessDarwin |
| def test_nsexception_with_run_command(self): |
| """Test formatters for NSException.""" |
| self.appkit_tester_impl(self.nsexception_data_formatter_commands) |
| |
| @skipUnlessDarwin |
| def test_nsdate_with_run_command(self): |
| """Test formatters for NSDate.""" |
| self.appkit_tester_impl(self.nsdate_data_formatter_commands) |
| |
| @skipUnlessDarwin |
| def test_coreframeworks_and_run_command(self): |
| """Test formatters for Core OSX frameworks.""" |
| self.build() |
| self.cf_data_formatter_commands() |
| |
| @skipUnlessDarwin |
| def test_kvo_with_run_command(self): |
| """Test the behavior of formatters when KVO is in use.""" |
| self.build() |
| self.kvo_data_formatter_commands() |
| |
| @skipUnlessDarwin |
| def test_expr_with_run_command(self): |
| """Test common cases of expression parser <--> formatters interaction.""" |
| self.build() |
| self.expr_objc_data_formatter_commands() |
| |
| def setUp(self): |
| # Call super's setUp(). |
| TestBase.setUp(self) |
| # Find the line number to break at. |
| self.line = line_number('main.m', '// Set break point at this line.') |
| |
| def plain_data_formatter_commands(self): |
| """Test basic ObjC formatting behavior.""" |
| self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) |
| |
| lldbutil.run_break_set_by_file_and_line( |
| self, "main.m", self.line, num_expected_locations=1, loc_exact=True) |
| |
| self.runCmd("run", RUN_SUCCEEDED) |
| |
| # The stop reason of the thread should be breakpoint. |
| self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, |
| substrs=['stopped', |
| 'stop reason = breakpoint']) |
| |
| # This is the function to remove the custom formats in order to have a |
| # clean slate for the next test case. |
| def cleanup(): |
| self.runCmd('type format clear', check=False) |
| self.runCmd('type summary clear', check=False) |
| self.runCmd('type synth clear', check=False) |
| |
| # Execute the cleanup function during test case tear down. |
| self.addTearDownHook(cleanup) |
| |
| self.runCmd("type summary add --summary-string \"${var%@}\" MyClass") |
| |
| self.expect("frame variable object2", |
| substrs=['MyOtherClass']) |
| |
| self.expect("frame variable *object2", |
| substrs=['MyOtherClass']) |
| |
| # Now let's delete the 'MyClass' custom summary. |
| self.runCmd("type summary delete MyClass") |
| |
| # The type format list should not show 'MyClass' at this point. |
| self.expect("type summary list", matching=False, |
| substrs=['MyClass']) |
| |
| self.runCmd("type summary add --summary-string \"a test\" MyClass") |
| |
| self.expect("frame variable *object2", |
| substrs=['*object2 =', |
| 'MyClass = a test', |
| 'backup = ']) |
| |
| self.expect("frame variable object2", matching=False, |
| substrs=['a test']) |
| |
| self.expect("frame variable object", |
| substrs=['a test']) |
| |
| self.expect("frame variable *object", |
| substrs=['a test']) |
| |
| self.expect('frame variable myclass', |
| substrs=['(Class) myclass = NSValue']) |
| self.expect('frame variable myclass2', |
| substrs=['(Class) myclass2 = ', 'NS', 'String']) |
| self.expect('frame variable myclass3', |
| substrs=['(Class) myclass3 = Molecule']) |
| self.expect('frame variable myclass4', |
| substrs=['(Class) myclass4 = NSMutableArray']) |
| self.expect('frame variable myclass5', |
| substrs=['(Class) myclass5 = nil']) |
| |
| def appkit_common_data_formatters_command(self): |
| """Test formatters for AppKit classes.""" |
| self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) |
| |
| lldbutil.run_break_set_by_file_and_line( |
| self, "main.m", self.line, num_expected_locations=1, loc_exact=True) |
| |
| self.runCmd("run", RUN_SUCCEEDED) |
| |
| # The stop reason of the thread should be breakpoint. |
| self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, |
| substrs=['stopped', |
| 'stop reason = breakpoint']) |
| |
| # This is the function to remove the custom formats in order to have a |
| # clean slate for the next test case. |
| def cleanup(): |
| self.runCmd('type format clear', check=False) |
| self.runCmd('type summary clear', check=False) |
| self.runCmd('type synth clear', check=False) |
| |
| # Execute the cleanup function during test case tear down. |
| self.addTearDownHook(cleanup) |
| |
| def nsnumber_data_formatter_commands(self): |
| # Now enable AppKit and check we are displaying Cocoa classes correctly |
| self.expect('frame variable num1 num2 num3 num5 num6 num7 num8_Y num8_N num9', |
| substrs=['(NSNumber *) num1 = ', ' (int)5', |
| '(NSNumber *) num2 = ', ' (float)3.1', |
| '(NSNumber *) num3 = ', ' (double)3.14', |
| '(NSNumber *) num5 = ', ' (char)65', |
| '(NSNumber *) num6 = ', ' (long)255', |
| '(NSNumber *) num7 = ', '2000000', |
| '(NSNumber *) num8_Y = ', 'YES', |
| '(NSNumber *) num8_N = ', 'NO', |
| '(NSNumber *) num9 = ', ' (short)-31616']) |
| |
| |
| self.runCmd('frame variable num4', check=True) |
| output = self.res.GetOutput() |
| i128_handled_correctly = False |
| |
| if output.find('long') >= 0: |
| i128_handled_correctly = (output.find('(long)-2') >= 0) |
| if output.find('int128_t') >= 0: |
| i128_handled_correctly = (output.find('(int128_t)18446744073709551614') >= 0) # deliberately broken, should be ..14 |
| |
| self.assertTrue(i128_handled_correctly, "Expected valid output for int128_t; got " + output) |
| |
| self.expect('frame variable num_at1 num_at2 num_at3 num_at4', |
| substrs=['(NSNumber *) num_at1 = ', ' (int)12', |
| '(NSNumber *) num_at2 = ', ' (int)-12', |
| '(NSNumber *) num_at3 = ', ' (double)12.5', |
| '(NSNumber *) num_at4 = ', ' (double)-12.5']) |
| |
| def nsdecimalnumber_data_formatter_commands(self): |
| self.expect('frame variable decimal_number decimal_neg_number decimal_one decimal_zero decimal_nan', |
| substrs=['(NSDecimalNumber *) decimal_number = ', '123456 x 10^-10', |
| '(NSDecimalNumber *) decimal_neg_number = ', '-123456 x 10^10', |
| '(NSDecimalNumber *) decimal_one = ', '1 x 10^0', |
| '(NSDecimalNumber *) decimal_zero = ', '0', |
| '(NSDecimalNumber *) decimal_nan = ', 'NaN']) |
| |
| def nscontainers_data_formatter_commands(self): |
| self.expect( |
| 'frame variable newArray nsDictionary newDictionary nscfDictionary cfDictionaryRef newMutableDictionary cfarray_ref mutable_array_ref', |
| substrs=[ |
| '(NSArray *) newArray = ', |
| '@"50 elements"', |
| '(NSDictionary *) newDictionary = ', |
| ' 12 key/value pairs', |
| '(NSDictionary *) newMutableDictionary = ', |
| ' 21 key/value pairs', |
| '(NSDictionary *) nsDictionary = ', |
| ' 2 key/value pairs', |
| '(CFDictionaryRef) cfDictionaryRef = ', |
| ' 3 key/value pairs', |
| '(CFArrayRef) cfarray_ref = ', |
| '@"3 elements"', |
| '(CFMutableArrayRef) mutable_array_ref = ', |
| '@"11 elements"']) |
| |
| self.expect('frame variable iset1 iset2 imset', |
| substrs=['4 indexes', '512 indexes', '10 indexes']) |
| |
| self.expect( |
| 'frame variable binheap_ref', |
| substrs=[ |
| '(CFBinaryHeapRef) binheap_ref = ', |
| '@"21 items"']) |
| |
| self.expect( |
| 'expression -d run -- (NSArray*)[NSArray new]', |
| substrs=['@"0 elements"']) |
| |
| def nsdata_data_formatter_commands(self): |
| self.expect( |
| 'frame variable immutableData mutableData data_ref mutable_data_ref mutable_string_ref concreteData concreteMutableData', |
| substrs=[ |
| '(NSData *) immutableData = ', |
| ' 4 bytes', |
| '(NSData *) mutableData = ', |
| ' 14 bytes', |
| '(CFDataRef) data_ref = ', |
| '@"5 bytes"', |
| '(CFMutableDataRef) mutable_data_ref = ', |
| '@"5 bytes"', |
| '(CFMutableStringRef) mutable_string_ref = ', |
| ' @"Wish ya knew"', |
| '(NSData *) concreteData = ', |
| ' 100000 bytes', |
| '(NSMutableData *) concreteMutableData = ', |
| ' 100000 bytes']) |
| |
| |
| def nsurl_data_formatter_commands(self): |
| self.expect( |
| 'frame variable cfurl_ref cfchildurl_ref cfgchildurl_ref', |
| substrs=[ |
| '(CFURLRef) cfurl_ref = ', |
| '@"http://www.foo.bar', |
| 'cfchildurl_ref = ', |
| '@"page.html -- http://www.foo.bar', |
| '(CFURLRef) cfgchildurl_ref = ', |
| '@"?whatever -- http://www.foo.bar/page.html"']) |
| |
| self.expect( |
| 'frame variable nsurl nsurl2 nsurl3', |
| substrs=[ |
| '(NSURL *) nsurl = ', |
| '@"http://www.foo.bar', |
| '(NSURL *) nsurl2 =', |
| '@"page.html -- http://www.foo.bar', |
| '(NSURL *) nsurl3 = ', |
| '@"?whatever -- http://www.foo.bar/page.html"']) |
| |
| def nserror_data_formatter_commands(self): |
| self.expect('frame variable nserror', |
| substrs=['domain: @"Foobar" - code: 12']) |
| |
| self.expect('frame variable nserrorptr', |
| substrs=['domain: @"Foobar" - code: 12']) |
| |
| self.expect('frame variable nserror->_userInfo', |
| substrs=['2 key/value pairs']) |
| |
| self.expect( |
| 'frame variable nserror->_userInfo --ptr-depth 1 -d run-target', |
| substrs=[ |
| '@"a"', |
| '@"b"', |
| "1", |
| "2"]) |
| |
| def nsbundle_data_formatter_commands(self): |
| self.expect( |
| 'frame variable bundle_string bundle_url main_bundle', |
| substrs=[ |
| '(NSBundle *) bundle_string = ', |
| ' @"/System/Library/Frameworks/Accelerate.framework"', |
| '(NSBundle *) bundle_url = ', |
| ' @"/System/Library/Frameworks/Foundation.framework"', |
| '(NSBundle *) main_bundle = ', |
| 'data-formatter-objc']) |
| |
| def nsexception_data_formatter_commands(self): |
| self.expect( |
| 'frame variable except0 except1 except2 except3', |
| substrs=[ |
| '(NSException *) except0 = ', |
| 'name: @"TheGuyWhoHasNoName" - reason: @"cuz it\'s funny"', |
| '(NSException *) except1 = ', |
| 'name: @"TheGuyWhoHasNoName~1" - reason: @"cuz it\'s funny"', |
| '(NSException *) except2 = ', |
| 'name: @"TheGuyWhoHasNoName`2" - reason: @"cuz it\'s funny"', |
| '(NSException *) except3 = ', |
| 'name: @"TheGuyWhoHasNoName/3" - reason: @"cuz it\'s funny"']) |
| |
| def nsdate_data_formatter_commands(self): |
| self.expect( |
| 'frame variable date1 date2', |
| patterns=[ |
| '(1985-04-10|1985-04-11)', |
| '(2011-01-01|2010-12-31)']) |
| |
| # this test might fail if we hit the breakpoint late on December 31st of some given year |
| # and midnight comes between hitting the breakpoint and running this line of code |
| # hopefully the output will be revealing enough in that case :-) |
| now_year = '%s-' % str(datetime.datetime.now().year) |
| |
| self.expect('frame variable date3', substrs=[now_year]) |
| self.expect('frame variable date4', substrs=['1970']) |
| self.expect('frame variable date5', substrs=[now_year]) |
| |
| self.expect('frame variable date1_abs date2_abs', |
| substrs=['1985-04', '2011-01']) |
| |
| self.expect('frame variable date3_abs', substrs=[now_year]) |
| self.expect('frame variable date4_abs', substrs=['1970']) |
| self.expect('frame variable date5_abs', substrs=[now_year]) |
| |
| self.expect('frame variable cupertino home europe', |
| substrs=['@"America/Los_Angeles"', |
| '@"Europe/Rome"', |
| '@"Europe/Paris"']) |
| |
| self.expect('frame variable cupertino_ns home_ns europe_ns', |
| substrs=['@"America/Los_Angeles"', |
| '@"Europe/Rome"', |
| '@"Europe/Paris"']) |
| |
| self.expect( |
| 'frame variable mut_bv', |
| substrs=[ |
| '(CFMutableBitVectorRef) mut_bv = ', |
| '1110 0110 1011 0000 1101 1010 1000 1111 0011 0101 1101 0001 00']) |
| |
| def expr_objc_data_formatter_commands(self): |
| """Test common cases of expression parser <--> formatters interaction.""" |
| self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) |
| |
| lldbutil.run_break_set_by_file_and_line( |
| self, "main.m", self.line, num_expected_locations=1, loc_exact=True) |
| |
| self.runCmd("run", RUN_SUCCEEDED) |
| |
| # The stop reason of the thread should be breakpoint. |
| self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, |
| substrs=['stopped', |
| 'stop reason = breakpoint']) |
| |
| # This is the function to remove the custom formats in order to have a |
| # clean slate for the next test case. |
| def cleanup(): |
| self.runCmd('type format clear', check=False) |
| self.runCmd('type summary clear', check=False) |
| self.runCmd('type synth clear', check=False) |
| |
| # Execute the cleanup function during test case tear down. |
| self.addTearDownHook(cleanup) |
| |
| # check that the formatters are able to deal safely and correctly |
| # with ValueObjects that the expression parser returns |
| self.expect( |
| 'expression ((id)@"Hello for long enough to avoid short string types")', |
| matching=False, |
| substrs=['Hello for long enough to avoid short string types']) |
| |
| self.expect( |
| 'expression -d run -- ((id)@"Hello for long enough to avoid short string types")', |
| substrs=['Hello for long enough to avoid short string types']) |
| |
| self.expect('expr -d run -- label1', |
| substrs=['Process Name']) |
| |
| self.expect( |
| 'expr -d run -- @"Hello for long enough to avoid short string types"', |
| substrs=['Hello for long enough to avoid short string types']) |
| |
| self.expect( |
| 'expr -d run --object-description -- @"Hello for long enough to avoid short string types"', |
| substrs=['Hello for long enough to avoid short string types']) |
| self.expect('expr -d run --object-description -- @"Hello"', |
| matching=False, substrs=['@"Hello" Hello']) |
| |
| def cf_data_formatter_commands(self): |
| """Test formatters for Core OSX frameworks.""" |
| self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) |
| |
| lldbutil.run_break_set_by_file_and_line( |
| self, "main.m", self.line, num_expected_locations=1, loc_exact=True) |
| |
| self.runCmd("run", RUN_SUCCEEDED) |
| |
| # The stop reason of the thread should be breakpoint. |
| self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, |
| substrs=['stopped', |
| 'stop reason = breakpoint']) |
| |
| # This is the function to remove the custom formats in order to have a |
| # clean slate for the next test case. |
| def cleanup(): |
| self.runCmd('type format clear', check=False) |
| self.runCmd('type summary clear', check=False) |
| self.runCmd('type synth clear', check=False) |
| |
| # Execute the cleanup function during test case tear down. |
| self.addTearDownHook(cleanup) |
| |
| # check formatters for common Objective-C types |
| expect_strings = [ |
| '(CFGregorianUnits) cf_greg_units = 1 years, 3 months, 5 days, 12 hours, 5 minutes 7 seconds', |
| '(CFRange) cf_range = location=4 length=4', |
| '(NSPoint) ns_point = (x = 4, y = 4)', |
| '(NSRange) ns_range = location=4, length=4', |
| '(NSRect) ns_rect = (origin = (x = 1, y = 1), size = (width = 5, height = 5))', |
| '(NSRectArray) ns_rect_arr = ((x = 1, y = 1), (width = 5, height = 5)), ...', |
| '(NSSize) ns_size = (width = 5, height = 7)', |
| '(CGSize) cg_size = (width = 1, height = 6)', |
| '(CGPoint) cg_point = (x = 2, y = 7)', |
| '(CGRect) cg_rect = (origin = (x = 1, y = 2), size = (width = 7, height = 7))', |
| '(Rect) rect = (t=4, l=8, b=4, r=7)', |
| '(Rect *) rect_ptr = (t=4, l=8, b=4, r=7)', |
| '(Point) point = (v=7, h=12)', |
| '(Point *) point_ptr = (v=7, h=12)', |
| '1985', |
| 'foo_selector_impl'] |
| |
| if self.getArchitecture() in ['i386', 'x86_64']: |
| expect_strings.append('(HIPoint) hi_point = (x=7, y=12)') |
| expect_strings.append( |
| '(HIRect) hi_rect = origin=(x = 3, y = 5) size=(width = 4, height = 6)') |
| expect_strings.append( |
| '(RGBColor) rgb_color = red=3 green=56 blue=35') |
| expect_strings.append( |
| '(RGBColor *) rgb_color_ptr = red=3 green=56 blue=35') |
| |
| self.expect("frame variable", |
| substrs=expect_strings) |
| |
| def kvo_data_formatter_commands(self): |
| """Test the behavior of formatters when KVO is in use.""" |
| self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) |
| |
| lldbutil.run_break_set_by_file_and_line( |
| self, "main.m", self.line, num_expected_locations=1, loc_exact=True) |
| |
| self.runCmd("run", RUN_SUCCEEDED) |
| |
| # The stop reason of the thread should be breakpoint. |
| self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, |
| substrs=['stopped', |
| 'stop reason = breakpoint']) |
| |
| # This is the function to remove the custom formats in order to have a |
| # clean slate for the next test case. |
| def cleanup(): |
| self.runCmd('type format clear', check=False) |
| self.runCmd('type summary clear', check=False) |
| self.runCmd('type synth clear', check=False) |
| |
| # Execute the cleanup function during test case tear down. |
| self.addTearDownHook(cleanup) |
| |
| # as long as KVO is implemented by subclassing, this test should succeed |
| # we should be able to dynamically figure out that the KVO implementor class |
| # is a subclass of Molecule, and use the appropriate summary for it |
| self.runCmd("type summary add -s JustAMoleculeHere Molecule") |
| self.expect('frame variable molecule', substrs=['JustAMoleculeHere']) |
| self.runCmd("next") |
| self.expect("thread list", |
| substrs=['stopped', |
| 'step over']) |
| self.expect('frame variable molecule', substrs=['JustAMoleculeHere']) |
| |
| self.runCmd("next") |
| # check that NSMutableDictionary's formatter is not confused when |
| # dealing with a KVO'd dictionary |
| self.expect( |
| 'frame variable newMutableDictionary', |
| substrs=[ |
| '(NSDictionary *) newMutableDictionary = ', |
| ' 21 key/value pairs']) |
| |
| lldbutil.run_break_set_by_regexp(self, 'setAtoms') |
| |
| self.runCmd("continue") |
| self.expect("frame variable _cmd", substrs=['setAtoms:']) |