Skip to content
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
3 changes: 2 additions & 1 deletion bash-completion/mtr
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ _mtr_module()
--filename --inet --inet6 --udp --tcp --address --first-ttl
--max-ttl --max-unknown --port --localport --psize --bitpattern
--interval --gracetime --tos --mpls --timeout --mark --report
--report-wide --report-cycles --json --xml --csv --raw --split
--report-wide --report-on-exit --report-cycles --json --xml --csv
--raw --split
--curses --displaymode --gtk --no-dns --show-ips --order --ipinfo
--aslookup --help --version
'
Expand Down
8 changes: 8 additions & 0 deletions man/mtr.8.in
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ mtr \- a network diagnostic tool
.B \-\-report-wide\c
]
[\c
.B \-\-report\-on\-exit\c
]
[\c
.B \-\-xml\c
]
[\c
Expand Down Expand Up @@ -202,6 +205,11 @@ mode. When in this mode,
.B mtr
will not cut hostnames in the report.
.TP
.B \-\-report\-on\-exit
Use this option with the curses interface to print a report snapshot after
leaving curses mode. This keeps the last trace visible in terminals that switch
to an alternate screen while curses is active.
.TP
.B \-x\fR, \fB\-\-xml
Use this option to tell
.B mtr
Expand Down
48 changes: 47 additions & 1 deletion test/cmdparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,13 @@
class TestMtrCommandParse(unittest.TestCase):
'''Test cases with malformed mtr command-line arguments.'''

def run_mtr(self, *args):
def run_mtr(self, *args, **kwargs):
env = kwargs.get('env')
return subprocess.run(
[MTR] + list(args),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env,
universal_newlines=True,
)

Expand Down Expand Up @@ -81,6 +83,50 @@ def test_port_with_icmp_fails_flag(self):
self.assertIn('port number specified (-P)', reply.stderr)
self.assertIn('protocol is ICMP', reply.stderr)

def test_report_loss_uses_two_decimal_places(self):
'Test report mode preserves fine-grained loss percentage precision.'

reply = self.run_mtr(
'--report',
'--report-cycles',
'1',
'--no-dns',
'127.0.0.1',
)

self.assertEqual(reply.returncode, 0)
self.assertEqual(reply.stderr, '')
self.assertIn('Loss%', reply.stdout)
self.assertIn('0.00%', reply.stdout)

def test_mtr_options_preserves_quoted_order_spaces(self):
'Test that quoted MTR_OPTIONS values can contain order separators.'

env = os.environ.copy()
env['MTR_OPTIONS'] = '--order "SRDL NBAGVW JMXI"'

reply = self.run_mtr(
'--report',
'--report-cycles',
'1',
'--no-dns',
'127.0.0.1',
env=env,
)

self.assertEqual(reply.returncode, 0)
self.assertEqual(reply.stderr, '')
self.assertIn('Drop', reply.stdout)
self.assertIn('Jint', reply.stdout)

def test_help_lists_report_on_exit(self):
'Test that the curses exit snapshot option remains advertised.'

reply = self.run_mtr('--help')

self.assertEqual(reply.returncode, 0)
self.assertIn('--report-on-exit', reply.stdout)

def test_port_with_tcp_succeeds_flag(self):
'Test that specifying -P with -T (TCP) succeeds.'

Expand Down
2 changes: 2 additions & 0 deletions ui/display.c
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ void display_close(
asn_close(ctl);
#endif
mtr_curses_close();
if (ctl->ReportOnExit)
report_close(ctl);
break;
#endif
case DisplaySplit:
Expand Down
2 changes: 1 addition & 1 deletion ui/gtk.c
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ static void percent_formatter(
gfloat f;
gchar text[64];
gtk_tree_model_get(tree_model, iter, POINTER_TO_INT(data), &f, -1);
snprintf(text, sizeof(text), "%.1f%%", f);
snprintf(text, sizeof(text), "%.2f%%", f);
g_object_set(cell, "text", text, NULL);
}

Expand Down
16 changes: 11 additions & 5 deletions ui/mtr.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ char *myname;
const struct fields data_fields[MAXFLD] = {
/* key, Remark, Header, Format, Width, CallBackFunc */
{' ', "<sp>: Space between fields", " ", " ", 1, &net_drop},
{'L', "L: Loss Ratio", "Loss%", " %4.1f%%", 6, &net_loss},
{'L', "L: Loss Ratio", "Loss%", " %6.2f%%", 8, &net_loss},
{'D', "D: Dropped Packets", "Drop", " %4d", 5, &net_drop},
{'R', "R: Received Packets", "Rcv", " %5d", 6, &net_returned},
{'S', "S: Sent Packets", "Snt", " %5d", 6, &net_xmit},
Expand Down Expand Up @@ -127,6 +127,7 @@ static void __attribute__ ((__noreturn__)) usage(FILE * out)
#endif
fputs(" -r, --report output using report mode\n", out);
fputs(" -w, --report-wide output wide report\n", out);
fputs(" --report-on-exit print report after curses exits\n", out);
fputs(" -c, --report-cycles COUNT set the number of pings sent\n", out);
#ifdef HAVE_JANSSON
fputs(" -j, --json output json\n", out);
Expand Down Expand Up @@ -420,12 +421,13 @@ static void parse_arg(
*/
enum {
OPT_DISPLAYMODE = CHAR_MAX + 1,
OPT_IPINFO4 = CHAR_MAX + 2,
OPT_REPORT_ON_EXIT = CHAR_MAX + 2,
OPT_IPINFO4 = CHAR_MAX + 3,
#ifdef ENABLE_IPV6
OPT_IPINFO6 = CHAR_MAX + 3,
OPT_CACHE = CHAR_MAX + 4,
OPT_IPINFO6 = CHAR_MAX + 4,
OPT_CACHE = CHAR_MAX + 5,
#else
OPT_CACHE = CHAR_MAX + 3,
OPT_CACHE = CHAR_MAX + 4,
#endif /* ifdef ENABLE_IPV6 */
};
static const struct option long_options[] = {
Expand All @@ -441,6 +443,7 @@ static void parse_arg(

{"report", 0, NULL, 'r'},
{"report-wide", 0, NULL, 'w'},
{"report-on-exit", 0, NULL, OPT_REPORT_ON_EXIT},
{"xml", 0, NULL, 'x'},
#ifdef HAVE_CURSES
{"curses", 0, NULL, 't'},
Expand Down Expand Up @@ -535,6 +538,9 @@ static void parse_arg(
ctl->reportwide = 1;
ctl->DisplayMode = DisplayReport;
break;
case OPT_REPORT_ON_EXIT:
ctl->ReportOnExit = 1;
break;
#ifdef HAVE_CURSES
case 't':
ctl->DisplayMode = DisplayCurses;
Expand Down
3 changes: 2 additions & 1 deletion ui/mtr.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ struct mtr_ctl {
ForceMaxPing:1,
use_dns:1, cache:1,
show_ips:1,
enablempls:1, dns:1, reportwide:1, Interactive:1, DisplayMode:5, CompactLayout:1;
enablempls:1, dns:1, reportwide:1, Interactive:1, DisplayMode:5,
CompactLayout:1, ReportOnExit:1;
#ifdef HAVE_IPINFO
#ifdef ENABLE_IPV6
char *ipinfo_provider6;
Expand Down
Loading