diff options
36 files changed, 530 insertions, 352 deletions
@@ -1,6 +1,7 @@ [flake8] exclude = .*,__pycache__,resources.py # B001: bare except +# B008: Do not perform calls in argument defaults. (fine with some Qt stuff) # B305: .next() (false-positives) # E128: continuation line under-indented for visual indent # E226: missing whitespace around arithmetic operator @@ -33,7 +34,7 @@ exclude = .*,__pycache__,resources.py # D413: Missing blank line after last section (not in pep257?) # A003: Builtin name for class attribute (needed for attrs) ignore = - B001,B305, + B001,B008,B305, E128,E226,E265,E501,E402,E266,E722,E731, F401, N802, diff --git a/.gitignore b/.gitignore index cb244557b..54a0dcae6 100644 --- a/.gitignore +++ b/.gitignore @@ -31,12 +31,12 @@ __pycache__ /prof /venv TODO -/scripts/testbrowser_cpp/webkit/Makefile -/scripts/testbrowser_cpp/webkit/main.o -/scripts/testbrowser_cpp/webkit/testbrowser -/scripts/testbrowser_cpp/webkit/.qmake.stash -/scripts/testbrowser_cpp/webengine/Makefile -/scripts/testbrowser_cpp/webengine/main.o -/scripts/testbrowser_cpp/webengine/testbrowser -/scripts/testbrowser_cpp/webengine/.qmake.stash +/scripts/testbrowser/cpp/webkit/Makefile +/scripts/testbrowser/cpp/webkit/main.o +/scripts/testbrowser/cpp/webkit/testbrowser +/scripts/testbrowser/cpp/webkit/.qmake.stash +/scripts/testbrowser/cpp/webengine/Makefile +/scripts/testbrowser/cpp/webengine/main.o +/scripts/testbrowser/cpp/webengine/testbrowser +/scripts/testbrowser/cpp/webengine/.qmake.stash /scripts/dev/pylint_checkers/qute_pylint.egg-info diff --git a/MANIFEST.in b/MANIFEST.in index 54bb613f3..b20c2bc77 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -23,7 +23,7 @@ include qutebrowser/config/configdata.yml prune www prune scripts/dev -prune scripts/testbrowser_cpp +prune scripts/testbrowser/cpp prune .github exclude scripts/asciidoc2html.py exclude doc/notes diff --git a/doc/changelog.asciidoc b/doc/changelog.asciidoc index 2fb8af4d7..47bf1e8e8 100644 --- a/doc/changelog.asciidoc +++ b/doc/changelog.asciidoc @@ -61,6 +61,8 @@ Added - Support for incremental search, with a new `search.incremental` setting. - New `--rapid` flag for `:command-accept` (bound to `Ctrl-Enter` by default), which allows executing a command in the completion without closing it. +- The `colors.completion.fg` setting can now be a list, allowing to specify + different colors for the three completion columns. Changed ~~~~~~~ @@ -131,6 +133,8 @@ Fixed - Fix :click-element with an ID containing non-alphanumeric characters. - Fix crash when a subprocess outputs data which is not decodable as UTF-8. - Fix crash when closing a tab immediately after hinting. +- Worked around issues in Qt 5.10 with loading progress never being finished. +- Fixed a crash when writing a flag before a command (e.g. `:-w open `). Deprecated ~~~~~~~~~~ diff --git a/doc/faq.asciidoc b/doc/faq.asciidoc index 7794c9d13..0d94796a4 100644 --- a/doc/faq.asciidoc +++ b/doc/faq.asciidoc @@ -221,5 +221,5 @@ My issue is not listed.:: https://github.com/qutebrowser/qutebrowser/issues[the issue tracker] or using the `:report` command. If you are reporting a segfault, make sure you read the - link:doc/stacktrace.asciidoc[guide] on how to report them with all needed + link:stacktrace.asciidoc[guide] on how to report them with all needed information. diff --git a/doc/help/configuring.asciidoc b/doc/help/configuring.asciidoc index 766e6bc7f..a9b7b6ddf 100644 --- a/doc/help/configuring.asciidoc +++ b/doc/help/configuring.asciidoc @@ -264,7 +264,7 @@ get a string: .config.py: [source,python] ---- -print(str(config.configdir / 'config.py') +print(str(config.configdir / 'config.py')) ---- Handling errors diff --git a/doc/help/settings.asciidoc b/doc/help/settings.asciidoc index 35c90ba61..53af8399d 100644 --- a/doc/help/settings.asciidoc +++ b/doc/help/settings.asciidoc @@ -701,10 +701,15 @@ Default: +pass:[#333333]+ [[colors.completion.fg]] === colors.completion.fg Text color of the completion widget. +May be a single color to use for all columns or a list of three colors, one for each column. -Type: <<types,QtColor>> +Type: <<types,List of QtColor, or QtColor>> -Default: +pass:[white]+ +Default: + +- +pass:[white]+ +- +pass:[white]+ +- +pass:[white]+ [[colors.completion.item.selected.bg]] === colors.completion.item.selected.bg diff --git a/icons/qutebrowser.xpm b/icons/qutebrowser.xpm index 8fac91045..04e183154 100644 --- a/icons/qutebrowser.xpm +++ b/icons/qutebrowser.xpm @@ -1,207 +1,302 @@ /* XPM */ -static char *qutebrowser[] = { -/* columns rows colors chars-per-pixel */ -"32 32 169 2 ", -" c #0A396E", -". c #0B3C72", -"X c #0B4077", -"o c #0C437B", -"O c #134175", -"+ c #15467C", -"@ c #18477B", -"# c #1A497D", -"$ c #0D4B86", -"% c #0F4E8D", -"& c #124A80", -"* c #1F4F83", -"= c #0E518C", -"- c #1F5084", -"; c #11508C", -": c #0F5193", -"> c #115799", -", c #115B9C", -"< c #204F83", -"1 c #245287", -"2 c #2A598C", -"3 c #325E8F", -"4 c #11609F", -"5 c #346496", -"6 c #3B6898", -"7 c #115CA1", -"8 c #115EAC", -"9 c #1263A3", -"0 c #1260AD", -"q c #136BAC", -"w c #136BB2", -"e c #1366BA", -"r c #196BB2", -"t c #157ABB", -"y c #1577BB", -"u c #2E6DB0", -"i c #387FB1", -"p c #456E9A", -"a c #4873A1", -"s c #4375AA", -"d c #507AA6", -"f c #597EA4", -"g c #4D7EB3", -"h c #156FCB", -"j c #167AC5", -"k c #1675CA", -"l c #177BCE", -"z c #1777D8", -"x c #1476E4", -"c c #167BE6", -"v c #167DE8", -"b c #197EEF", -"n c #1A7FF0", -"m c #1A80BE", -"M c #5F87AF", -"N c #5D8BBA", -"B c #5A84B1", -"V c #6C8FB3", -"C c #6F96BE", -"Z c #1886CC", -"A c #1883D7", -"S c #198DD5", -"D c #1987D9", -"F c #198ADC", -"G c #1A96DC", -"H c #3090D9", -"J c #1682E9", -"K c #1983ED", -"L c #1689E9", -"P c #1A8DEE", -"I c #1B95ED", -"U c #1C9EEA", -"Y c #1B97E4", -"T c #1A84F2", -"R c #1A8BF2", -"E c #1C94F4", -"W c #1D9CF5", -"Q c #3388E6", -"! c #3D90E9", -"~ c #228EF3", -"^ c #229FF6", -"/ c #3294F4", -"( c #3D9FF6", -") c #339CF4", -"_ c #1CA2E5", -"` c #1DABEE", -"' c #1DA4F6", -"] c #1EA9F7", -"[ c #1EADF8", -"{ c #1FB4F9", -"} c #1FB9FA", -"| c #20ACF8", -" . c #27A4F6", -".. c #3DA9F6", -"X. c #20B9FA", -"o. c #2EB6F9", -"O. c #458DC9", -"+. c #5C8DC1", -"@. c #5795C6", -"#. c #709DCB", -"$. c #74A8DD", -"%. c #4A97EA", -"&. c #4896EA", -"*. c #559EEA", -"=. c #439AF5", -"-. c #46A3F6", -";. c #5FA9F6", -":. c #5EA6F3", -">. c #47BCF9", -",. c #51B5F8", -"<. c #58BDF8", -"1. c #68ABEF", -"2. c #7DB9E7", -"3. c #63AEF7", -"4. c #6FB1F7", -"5. c #66B9F8", -"6. c #61B2F6", -"7. c #71B4F7", -"8. c #78B7F4", -"9. c #72BFF9", -"0. c #3BC0FA", -"q. c #6FCEFB", -"w. c #6CC5FA", -"e. c #7BCAF9", -"r. c #89A7C3", -"t. c #83A2C1", -"y. c #98B6D3", -"u. c #9DB9D3", -"i. c #89B6E4", -"p. c #83B6E9", -"a. c #81BDF7", -"s. c #83BFF8", -"d. c #9EC4E9", -"f. c #8CC2F9", -"g. c #85CDFB", -"h. c #87C4F9", -"j. c #92C6F9", -"k. c #95CAFA", -"l. c #9CCBFA", -"z. c #89D7FC", -"x. c #91D9FC", -"c. c #9CDEFD", -"v. c #9ED2FB", -"b. c #A7CAEC", -"n. c #B5CEE3", -"m. c #A1CEFA", -"M. c #AED0F0", -"N. c #ACD6FA", -"B. c #A0DFFC", -"V. c #AFD8FC", -"C. c #B5D9FB", -"Z. c #BCDDFC", -"A. c #BFDCF5", -"S. c #ACE3FD", -"D. c #B5E5FE", -"F. c #BBE2FC", -"G. c #CFE5F5", -"H. c #C3E1FC", -"J. c #CAE6FD", -"K. c #CCEBFD", -"L. c #C4EBFE", -"P. c #D6EDFE", -"I. c #DAEEFD", -"U. c #DEF1FE", -"Y. c #D6F2FE", -"T. c #E4F4FE", -"R. c #E9F6FE", -"E. c #EBF8FF", -"W. c None", -/* pixels */ -"W.W.W.W.W.W.W.W.W.W.W.c.S.L.Y.E.E.S.X.} W.W.W.W.W.W.W.W.W.W.W.W.", -"W.W.W.W.W.W.W.W.W.D.T.E.E.T.L.D.c.z.} } X.} } W.W.W.W.W.W.W.W.W.", -"W.W.W.W.W.W.W.B.T.T.R.T.R.U.0.X.z.S.} } } } { { X.W.W.W.W.W.W.W.", -"W.W.W.W.W.W.x.x.K.T.T.T.L.P.q.o.{ } } ` _ { { { { { W.W.W.W.W.W.", -"W.W.W.W.W.c.P.D.G.u.r.i 9 Z _ { { G 4 X t { { { { { { W.W.W.W.W.", -"W.W.W.W.K.U.n.f O { = t { { { { [ { { W.W.W.W.", -"W.W.W.F.I.t.. ' t { { [ [ [ [ [ >.W.W.W.", -"W.W.x.P.V ' X t ` [ [ [ [ [ [ o.e.W.W.", -"W.W.J.y. X t S Y Z $ ' . y [ [ [ ] [ [ | Z.J.W.W.", -"W.<.e.& , _ ] ] [ ] U . ' . y [ ' [ ] ] ] w.K.J.g.W.", -"W.' S o ' ' [ ' [ ' ] o ' . y Y 9 = = 9 @.J.J.J.F.W.", -"W.| , j ' ' ' ' ' ' ' o ' . $ p A.J.J.g.", -"' .. G ' ' ' ' ' ' ' o ' . M H.H.h.", -",.2. . W ' W ' ' ' ' W . ' . M.A.x.", -"N.M.. . W W W ' W W W W .w 9 I U 0 #.Z.m.", -" .9.O D W W W W ' W j $ % F W W W .5 d Z.C.", -"W W ; 9 9.h.5...Q % o j W W W W W W O. 3 C.N.", -"E W 7 B b.d.a . w E E W W W E W E A @ C.l.", -"I E l u W E W E W E E E E A . - k.6.", -"P E E 7 m.o E E E E E E E E l . = E P ", -"L E E E > . O s.o E E E E E E E E 7 , E L ", -"W.R E R ) #.5 1 6 N i.2 s.+ E E E E E E R L . k R W.", -"W.L R E -.m.m.m.m.m.m.2 m.@ N m.m.s.( R R % X E J W.", -"W.W.K R ~ a.m.l.l.l.l.2 s.+ < i.l.m.j.h % e K W.W.", -"W.W.J R R / l.l.l.l.k.2 s.+ * 5 + 8 R J W.W.", -"W.W.W.v T R 3.k.k.j.k.2 2 j.& . 8 R v W.W.W.", -"W.W.W.W.J T ~ 7.j.j.j.g +.p.j.s.+. . . : z T v W.W.W.W.", -"W.W.W.W.W.c T T =.f.j.j.s.j.j.j.j.$.g s u e h b T T v W.W.W.W.W.", -"W.W.W.W.W.W.c b n 4.f.f.s.m.s.s.s.j.s.j./ T n T b c W.W.W.W.W.W.", -"W.W.W.W.W.W.W.c x 1.s.s.s.s.s.s.s.s.4.=.n T n c c W.W.W.W.W.W.W.", -"W.W.W.W.W.W.W.W.W.&.*.1.a.s.s.s.s.3.n n v x x W.W.W.W.W.W.W.W.W.", -"W.W.W.W.W.W.W.W.W.W.W.%.%.%.%.*.*.Q x x x W.W.W.W.W.W.W.W.W.W.W." -}; +static char * qutebrowser_xpm[] = { +"32 32 267 2", +" c None", +". c #9FD4FD", +"+ c #99CBFE", +"@ c #90C3FE", +"# c #89BFFE", +"$ c #81BCFF", +"% c #80BBFF", +"& c #9BCAFD", +"* c #A9DBFB", +"= c #88D3FB", +"- c #98CBFE", +"; c #81BBFF", +"> c #7EBAFF", +", c #84BDFF", +"' c #8DC2FF", +") c #96C7FE", +"! c #A0CCFE", +"~ c #A9D1FE", +"{ c #CEE5FD", +"] c #C7E3FC", +"^ c #8AD3FB", +"/ c #9DCFFD", +"( c #C3DFFD", +"_ c #CDE4FD", +": c #A3CEFE", +"< c #94C6FE", +"[ c #CAE5FC", +"} c #7DD0FB", +"| c #9ECDFD", +"1 c #A1CDFE", +"2 c #8BC1FF", +"3 c #87BFFF", +"4 c #ADD4FE", +"5 c #C6E1FD", +"6 c #CCE3FC", +"7 c #A7DAFB", +"8 c #9DCBFE", +"9 c #78AFF1", +"0 c #6096D4", +"a c #4B82C0", +"b c #5A84B3", +"c c #6589B1", +"d c #6F92B9", +"e c #90AED0", +"f c #C4DBF5", +"g c #6286AE", +"h c #7D9EC2", +"i c #BADFFC", +"j c #85BDFE", +"k c #78B4F8", +"l c #4C83C0", +"m c #1E4F87", +"n c #0A396E", +"o c #345D8D", +"p c #CDE4FC", +"q c #88A7CA", +"r c #1D497C", +"s c #799BBF", +"t c #8AC1FD", +"u c #5E97D7", +"v c #14457B", +"w c #4F76A0", +"x c #A9D5FC", +"y c #95C9FD", +"z c #4C82C1", +"A c #0A3A6F", +"B c #C9E3FD", +"C c #95CCFC", +"D c #629BDB", +"E c #0B3A6F", +"F c #0C3B6F", +"G c #4E749F", +"H c #8CACCE", +"I c #6185AD", +"J c #CBE4FD", +"K c #89C0FF", +"L c #98CDFA", +"M c #27558A", +"N c #144175", +"O c #9BB8D8", +"P c #335D8C", +"Q c #AFC9E6", +"R c #AFD4FE", +"S c #91C7FD", +"T c #A0C0DE", +"U c #194779", +"V c #80A1C5", +"W c #C8E1F9", +"X c #9CB9D8", +"Y c #7799BE", +"Z c #6489B0", +"` c #7092B9", +" . c #6E9DCF", +".. c #79B5F9", +"+. c #83BDFE", +"@. c #7395BA", +"#. c #315C8B", +"$. c #7C9EC2", +"%. c #C0D9F3", +"&. c #7294BA", +"*. c #5C94D4", +"=. c #91CCFC", +"-. c #88CBFA", +";. c #5179A3", +">. c #6E91B7", +",. c #6084AC", +"'. c #96B3D4", +"). c #275283", +"!. c #0C3C71", +"~. c #629CDC", +"{. c #94C6FD", +"]. c #A7D2FC", +"^. c #36659A", +"/. c #2C5788", +"(. c #9DBAD9", +"_. c #B4CEEA", +":. c #476E9A", +"<. c #7EB9FE", +"[. c #8DC3FD", +"}. c #8CC2FE", +"|. c #2F619B", +"1. c #87A6C9", +"2. c #7A9BC0", +"3. c #CBE2FB", +"4. c #C7DFF8", +"5. c #6C8FB5", +"6. c #113F73", +"7. c #0F3D71", +"8. c #547AA4", +"9. c #9CBAD9", +"0. c #B9D3EE", +"a. c #A3C0DE", +"b. c #31629A", +"c. c #659EE0", +"d. c #87BFFE", +"e. c #C3E0FD", +"f. c #4371A4", +"g. c #7496BB", +"h. c #90AFD1", +"i. c #245081", +"j. c #416A96", +"k. c #B0CBE7", +"l. c #CCE4FD", +"m. c #7DB8FD", +"n. c #1E5088", +"o. c #497EBC", +"p. c #C9E3FC", +"q. c #7193B9", +"r. c #C6E0FB", +"s. c #A2CDFE", +"t. c #97C8FE", +"u. c #A7D0FE", +"v. c #BDDCFD", +"w. c #9EC2E8", +"x. c #416996", +"y. c #366AA6", +"z. c #C0DEFC", +"A. c #A2BFDD", +"B. c #326299", +"C. c #649DDF", +"D. c #71ABED", +"E. c #3569A4", +"F. c #0D3C71", +"G. c #6998CD", +"H. c #30639D", +"I. c #A8D3F8", +"J. c #2B5686", +"K. c #3A679B", +"L. c #ADCAEA", +"M. c #85A6C9", +"N. c #33639B", +"O. c #9CCBFD", +"P. c #86C2F7", +"Q. c #0E3C71", +"R. c #1B4C83", +"S. c #5D95D5", +"T. c #557BA5", +"U. c #85C0F6", +"V. c #55A8EF", +"W. c #94B3D3", +"X. c #1C497C", +"Y. c #13437A", +"Z. c #487DBB", +"`. c #7BB7FB", +" + c #76B1F5", +".+ c #4E85C3", +"++ c #ACD3FE", +"@+ c #2F5989", +"#+ c #7597BC", +"$+ c #53A7EF", +"%+ c #C6E1FC", +"&+ c #B6D5F7", +"*+ c #5890D0", +"=+ c #4076B2", +"-+ c #619ADB", +";+ c #7CB7FC", +">+ c #7DB9FE", +",+ c #5087C6", +"'+ c #134479", +")+ c #23548D", +"!+ c #24558D", +"~+ c #8AAACC", +"{+ c #A2C1E1", +"]+ c #86C1F5", +"^+ c #B4D7FE", +"/+ c #6CA5E8", +"(+ c #22548C", +"_+ c #6D94BF", +":+ c #98B6D6", +"<+ c #134174", +"[+ c #84BDF5", +"}+ c #CAE4FC", +"|+ c #CBE3FD", +"1+ c #8FC3FF", +"2+ c #3F72AD", +"3+ c #49719C", +"4+ c #0C3B70", +"5+ c #9CBBDB", +"6+ c #79B7F3", +"7+ c #BFDCFD", +"8+ c #7FBBFF", +"9+ c #7E9FC3", +"0+ c #77B6F3", +"a+ c #A5CEF7", +"b+ c #9FCBFE", +"c+ c #3267A1", +"d+ c #A4CDF7", +"e+ c #B9D9FA", +"f+ c #C7E1FD", +"g+ c #90C3FF", +"h+ c #15457C", +"i+ c #558CCB", +"j+ c #2E5889", +"k+ c #7B9CC1", +"l+ c #C4DDF6", +"m+ c #BBDAFA", +"n+ c #CDE5FD", +"o+ c #B3D6FE", +"p+ c #80BAFF", +"q+ c #4E84C3", +"r+ c #3E73AF", +"s+ c #78B3F7", +"t+ c #5991D1", +"u+ c #477DBA", +"v+ c #4075B2", +"w+ c #5783B6", +"x+ c #BDD6F0", +"y+ c #A1CBF6", +"z+ c #90C4FF", +"A+ c #BCDBFD", +"B+ c #73B0F1", +"C+ c #C5E0FB", +"D+ c #91C5FF", +"E+ c #AED3FE", +"F+ c #C9E2FC", +"G+ c #76B2F2", +"H+ c #8BBFF9", +"I+ c #81BBFE", +"J+ c #9ECBFE", +"K+ c #84B8F3", +"L+ c #79B4F4", +"M+ c #88BEFA", +"N+ c #83BCFE", +"O+ c #A4CFFC", +"P+ c #A6CDF6", +"Q+ c #82B8F2", +"R+ c #529BEC", +" . + @ # $ % & * = ", +" - ; > > , ' ) ! ~ { { { ] ^ ", +" / ; > > > > ; ( _ : < { { { { { [ } ", +" | 1 2 > > > 2 3 4 5 { { { { { 6 { { { 7 ", +" 8 $ < 9 0 a b c d e { { { { f g h { { { { i ", +" j k l m n n n n n n o { { p q r n s { { { { { i ", +" t u v n n n n n n n n o { { w n n n s { { { { { { x ", +" y z A n n n n n n n n n o { { o n n n s { { { { { { B C ", +" D E n n n F G H I n n n o { { o n n n s { { { { { J K % ", +" L M n n n N O { { s n n n o { { o n n P Q { { { { { R > > S ", +" T n n n n H { { { s n n n o { { o U V 6 W X Y Z ` ...> > +. ", +" @.n n n #.{ { { { s n n n o { { $.%.W &.U n n n n n v *.> > =.", +"-.;.n n n >.{ { { { s n n n ,.{ { { '.).n n n n n n n n !.~.> {.", +"].^.n n n q { { { { s n /.(.{ { _.:.n n n n n n n n n n n m <.[.", +"}.|.n n n H { { { { 1.2.3.{ 4.5.6.n n n 7.8.9.0.a.b.n n n n c.d.", +"e.f.n n n g.{ { { { { { { h.i.n n n n j.k.{ { { l.m.n.n n n o.$ ", +"p.q.n n n /.r.s.t.u.v.w.x.n n n n i.h.{ { { { { { u.o.n n n y.$ ", +"z.A.n n n n B.C.D.u E.F.n n n 6.5.4.{ 3.2.1.{ { { { G.n n n H.d.", +"I.p J.n n n n n n n n n n n K.L.{ { (./.n s { { { { M.n n n N.O.", +"P.{ (.Q.n n n n n n n n R.S.> K _ ,.n n n s { { { { 5.n n n T.U.", +"V.{ { W.X.n n n n n Y.Z.`. +.+> ++o n n n s { { { { @+n n n #+$+", +" %+{ { &+*+Z.=+a -+;+>+,+'+)+> > !+n n n s { { { ~+n n n n {+ ", +" ]+{ { ^+> > > > > /+(+n n )+> > )+n n n _+{ { :+<+n n n o [+ ", +" }+{ |+1+> > > > l n n n )+> > )+n n n 2+~+3+E n n n 4+5+ ", +" 6+{ { 7+8+> > > l n n n )+> > )+n n n n n n n n n F 9+0+ ", +" a+{ { b+> > > l n n n c+> > )+n n n n n n n n r O d+ ", +" e+{ f+g+> > l n h+i+<.> > )+n n n n n E j+k+l+m+ ", +" e+{ n+o+p+q+r+s+> > > > t+u+v+w+2.W.x+{ { e+ ", +" y+{ { z+>+> > > > > > > > > A+{ { { { d+ ", +" B+C+) > > > > > > > > D+E+{ { { F+G+ ", +" H+I+> > > > > > J+{ { { C+K+ ", +" L+M+# N+; 8+O+P+Q+R+ "}; diff --git a/misc/requirements/requirements-codecov.txt b/misc/requirements/requirements-codecov.txt index 6601cfb12..df7a12ed6 100644 --- a/misc/requirements/requirements-codecov.txt +++ b/misc/requirements/requirements-codecov.txt @@ -2,7 +2,7 @@ certifi==2017.11.5 chardet==3.0.4 -codecov==2.0.10 +codecov==2.0.13 coverage==4.4.2 idna==2.6 requests==2.18.4 diff --git a/misc/requirements/requirements-flake8.txt b/misc/requirements/requirements-flake8.txt index 37ceb7f31..0ae43d663 100644 --- a/misc/requirements/requirements-flake8.txt +++ b/misc/requirements/requirements-flake8.txt @@ -1,23 +1,23 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py -attrs==17.3.0 +attrs==17.4.0 flake8==3.5.0 -flake8-bugbear==17.4.0 +flake8-bugbear==17.12.0 flake8-builtins==1.0.post0 flake8-comprehensions==1.4.1 flake8-copyright==0.2.0 flake8-debugger==3.0.0 flake8-deprecated==1.3 -flake8-docstrings==1.1.0 -flake8-future-import==0.4.3 +flake8-docstrings==1.3.0 +flake8-future-import==0.4.4 flake8-mock==0.3 flake8-per-file-ignores==0.4 -flake8-polyfill==1.0.1 +flake8-polyfill==1.0.2 flake8-string-format==0.2.3 flake8-tidy-imports==1.1.0 flake8-tuple==0.2.13 mccabe==0.6.1 -pep8-naming==0.4.1 +pep8-naming==0.5.0 pycodestyle==2.3.1 pydocstyle==2.1.1 pyflakes==1.6.0 diff --git a/misc/requirements/requirements-pip.txt b/misc/requirements/requirements-pip.txt index b7914cac5..54cacc3c3 100644 --- a/misc/requirements/requirements-pip.txt +++ b/misc/requirements/requirements-pip.txt @@ -3,6 +3,6 @@ appdirs==1.4.3 packaging==16.8 pyparsing==2.2.0 -setuptools==38.2.4 +setuptools==38.4.0 six==1.11.0 wheel==0.30.0 diff --git a/misc/requirements/requirements-tests.txt b/misc/requirements/requirements-tests.txt index bf80acee7..e5f9d35af 100644 --- a/misc/requirements/requirements-tests.txt +++ b/misc/requirements/requirements-tests.txt @@ -1,6 +1,6 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py -attrs==17.3.0 +attrs==17.4.0 beautifulsoup4==4.6.0 cheroot==6.0.0 click==6.7 @@ -11,7 +11,7 @@ fields==5.0.0 Flask==0.12.2 glob2==0.6 hunter==2.0.2 -hypothesis==3.42.1 +hypothesis==3.44.13 itsdangerous==0.24 # Jinja2==2.10 Mako==1.0.7 @@ -21,19 +21,19 @@ parse-type==0.4.2 pluggy==0.6.0 py==1.5.2 py-cpuinfo==3.3.0 -pytest==3.3.1 +pytest==3.3.1 # rq.filter: != 3.3.2 pytest-bdd==2.19.0 pytest-benchmark==3.1.1 pytest-cov==2.5.1 pytest-faulthandler==1.3.1 pytest-instafail==0.3.0 pytest-mock==1.6.3 -pytest-qt==2.3.0 +pytest-qt==2.3.1 pytest-repeat==0.4.1 -pytest-rerunfailures==3.1 +pytest-rerunfailures==4.0 pytest-travis-fold==1.3.0 pytest-xvfb==1.0.0 PyVirtualDisplay==0.2.1 six==1.11.0 vulture==0.26 -Werkzeug==0.13 +Werkzeug==0.14.1 diff --git a/misc/requirements/requirements-tests.txt-raw b/misc/requirements/requirements-tests.txt-raw index 121689980..174eeb7df 100644 --- a/misc/requirements/requirements-tests.txt-raw +++ b/misc/requirements/requirements-tests.txt-raw @@ -4,7 +4,7 @@ coverage Flask hunter hypothesis -pytest +pytest==3.3.1 pytest-bdd pytest-benchmark pytest-cov @@ -19,3 +19,4 @@ pytest-xvfb vulture #@ ignore: Jinja2, MarkupSafe, colorama +#@ filter: pytest != 3.3.2 diff --git a/qutebrowser/app.py b/qutebrowser/app.py index a2d768923..e3d06391d 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -872,10 +872,6 @@ class EventFilter(QObject): super().__init__(parent) self._activated = True self._handlers = { - QEvent.MouseButtonDblClick: self._handle_mouse_event, - QEvent.MouseButtonPress: self._handle_mouse_event, - QEvent.MouseButtonRelease: self._handle_mouse_event, - QEvent.MouseMove: self._handle_mouse_event, QEvent.KeyPress: self._handle_key_event, QEvent.KeyRelease: self._handle_key_event, } @@ -900,19 +896,6 @@ class EventFilter(QObject): # No window available yet, or not a MainWindow return False - def _handle_mouse_event(self, _event): - """Handle a mouse event. - - Args: - _event: The QEvent which is about to be delivered. - - Return: - True if the event should be filtered, False if it's passed through. - """ - # Mouse cursor shown (overrideCursor None) -> don't filter event - # Mouse cursor hidden (overrideCursor not None) -> filter event - return qApp.overrideCursor() is not None - def eventFilter(self, obj, event): """Handle an event. diff --git a/qutebrowser/browser/history.py b/qutebrowser/browser/history.py index ecab730ae..04bc1be15 100644 --- a/qutebrowser/browser/history.py +++ b/qutebrowser/browser/history.py @@ -32,7 +32,7 @@ from qutebrowser.misc import objects, sql # increment to indicate that HistoryCompletion must be regenerated -_USER_VERSION = 1 +_USER_VERSION = 2 class CompletionHistory(sql.SqlTable): @@ -102,7 +102,8 @@ class WebHistory(sql.SqlTable): data = {'url': [], 'title': [], 'last_atime': []} # select the latest entry for each url q = sql.Query('SELECT url, title, max(atime) AS atime FROM History ' - 'WHERE NOT redirect GROUP BY url ORDER BY atime asc') + 'WHERE NOT redirect and url NOT LIKE "qute://back%" ' + 'GROUP BY url ORDER BY atime asc') for entry in q.run(): data['url'].append(self._format_completion_url(QUrl(entry.url))) data['title'].append(entry.title) @@ -171,7 +172,9 @@ class WebHistory(sql.SqlTable): @pyqtSlot(QUrl, QUrl, str) def add_from_tab(self, url, requested_url, title): """Add a new history entry as slot, called from a BrowserTab.""" - if url.scheme() == 'data' or requested_url.scheme() == 'data': + if any(url.scheme() == 'data' or + (url.scheme(), url.host()) == ('qute', 'back') + for url in (url, requested_url)): return if url.isEmpty(): # things set via setHtml diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 813f1eb9c..9328698bc 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -24,7 +24,8 @@ import functools import html as html_utils import sip -from PyQt5.QtCore import pyqtSlot, Qt, QEvent, QPoint, QPointF, QUrl, QTimer +from PyQt5.QtCore import (pyqtSignal, pyqtSlot, Qt, QEvent, QPoint, QPointF, + QUrl, QTimer) from PyQt5.QtGui import QKeyEvent from PyQt5.QtNetwork import QAuthenticator from PyQt5.QtWidgets import QApplication @@ -539,7 +540,15 @@ class WebEngineElements(browsertab.AbstractElements): class WebEngineTab(browsertab.AbstractTab): - """A QtWebEngine tab in the browser.""" + """A QtWebEngine tab in the browser. + + Signals: + _load_finished_fake: + Used in place of unreliable loadFinished + """ + + # WORKAROUND for https://bugreports.qt.io/browse/QTBUG-65223 + _load_finished_fake = pyqtSignal(bool) def __init__(self, *, win_id, mode_manager, private, parent=None): super().__init__(win_id=win_id, mode_manager=mode_manager, @@ -793,6 +802,24 @@ class WebEngineTab(browsertab.AbstractTab): } self.renderer_process_terminated.emit(status_map[status], exitcode) + @pyqtSlot(int) + def _on_load_progress_workaround(self, perc): + """Use loadProgress(100) to emit loadFinished(True). + + See https://bugreports.qt.io/browse/QTBUG-65223 + """ + if perc == 100 and self.load_status() != usertypes.LoadStatus.error: + self._load_finished_fake.emit(True) + + @pyqtSlot(bool) + def _on_load_finished_workaround(self, ok): + """Use only loadFinished(False). + + See https://bugreports.qt.io/browse/QTBUG-65223 + """ + if not ok: + self._load_finished_fake.emit(False) + def _connect_signals(self): view = self._widget page = view.page() @@ -801,9 +828,6 @@ class WebEngineTab(browsertab.AbstractTab): page.linkHovered.connect(self.link_hovered) page.loadProgress.connect(self._on_load_progress) page.loadStarted.connect(self._on_load_started) - page.loadFinished.connect(self._on_history_trigger) - page.loadFinished.connect(self._restore_zoom) - page.loadFinished.connect(self._on_load_finished) page.certificate_error.connect(self._on_ssl_errors) page.authenticationRequired.connect(self._on_authentication_required) page.proxyAuthenticationRequired.connect( @@ -816,6 +840,19 @@ class WebEngineTab(browsertab.AbstractTab): view.renderProcessTerminated.connect( self._on_render_process_terminated) view.iconChanged.connect(self.icon_changed) + # WORKAROUND for https://bugreports.qt.io/browse/QTBUG-65223 + if qtutils.version_check('5.10', compiled=False): + page.loadProgress.connect(self._on_load_progress_workaround) + self._load_finished_fake.connect(self._on_history_trigger) + self._load_finished_fake.connect(self._restore_zoom) + self._load_finished_fake.connect(self._on_load_finished) + page.loadFinished.connect(self._on_load_finished_workaround) + else: + # for older Qt versions which break with the above + page.loadProgress.connect(self._on_load_progress) + page.loadFinished.connect(self._on_history_trigger) + page.loadFinished.connect(self._restore_zoom) + page.loadFinished.connect(self._on_load_finished) def event_target(self): return self._widget.focusProxy() diff --git a/qutebrowser/completion/completer.py b/qutebrowser/completion/completer.py index 30a180554..4e187750d 100644 --- a/qutebrowser/completion/completer.py +++ b/qutebrowser/completion/completer.py @@ -87,8 +87,6 @@ class Completer(QObject): # cursor on a flag or after an explicit split (--) return None log.completion.debug("Before removing flags: {}".format(before_cursor)) - before_cursor = [x for x in before_cursor if not x.startswith('-')] - log.completion.debug("After removing flags: {}".format(before_cursor)) if not before_cursor: # '|' or 'set|' log.completion.debug('Starting command completion') @@ -99,6 +97,9 @@ class Completer(QObject): log.completion.debug("No completion for unknown command: {}" .format(before_cursor[0])) return None + + before_cursor = [x for x in before_cursor if not x.startswith('-')] + log.completion.debug("After removing flags: {}".format(before_cursor)) argpos = len(before_cursor) - 1 try: func = cmd.get_pos_arg_info(argpos).completion diff --git a/qutebrowser/completion/completiondelegate.py b/qutebrowser/completion/completiondelegate.py index 6688a2dfa..b4f9c5a33 100644 --- a/qutebrowser/completion/completiondelegate.py +++ b/qutebrowser/completion/completiondelegate.py @@ -138,10 +138,10 @@ class CompletionItemDelegate(QStyledItemDelegate): self._painter.translate(text_rect.left(), text_rect.top()) self._get_textdoc(index) - self._draw_textdoc(text_rect) + self._draw_textdoc(text_rect, index.column()) self._painter.restore() - def _draw_textdoc(self, rect): + def _draw_textdoc(self, rect, col): """Draw the QTextDocument of an item. Args: @@ -156,7 +156,9 @@ class CompletionItemDelegate(QStyledItemDelegate): elif not self._opt.state & QStyle.State_Enabled: color = config.val.colors.completion.category.fg else: - color = config.val.colors.completion.fg + colors = config.val.colors.completion.fg + # if multiple colors are set, use different colors per column + color = colors[col % len(colors)] self._painter.setPen(color) ctx = QAbstractTextDocumentLayout.PaintContext() diff --git a/qutebrowser/config/configdata.yml b/qutebrowser/config/configdata.yml index 8778cc013..a118a8b59 100644 --- a/qutebrowser/config/configdata.yml +++ b/qutebrowser/config/configdata.yml @@ -1540,9 +1540,15 @@ zoom.text_only: ## colors colors.completion.fg: - default: white - type: QtColor - desc: Text color of the completion widget. + default: ["white", "white", "white"] + type: + name: ListOrValue + valtype: QtColor + desc: >- + Text color of the completion widget. + + May be a single color to use for all columns or a list of three colors, + one for each column. colors.completion.odd.bg: default: '#444444' diff --git a/qutebrowser/keyinput/modeman.py b/qutebrowser/keyinput/modeman.py index ad9bd06ee..b8c46476f 100644 --- a/qutebrowser/keyinput/modeman.py +++ b/qutebrowser/keyinput/modeman.py @@ -322,10 +322,13 @@ class ModeManager(QObject): if self.mode is None: # We got events before mode is set, so just pass them through. return False - if event.type() == QEvent.KeyPress: - return self._eventFilter_keypress(event) - else: - return self._eventFilter_keyrelease(event) + + handlers = { + QEvent.KeyPress: self._eventFilter_keypress, + QEvent.KeyRelease: self._eventFilter_keyrelease, + } + handler = handlers[event.type()] + return handler(event) @cmdutils.register(instance='mode-manager', scope='window') def clear_keychain(self): diff --git a/requirements.txt b/requirements.txt index 8d95759b2..0d2652698 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py -attrs==17.3.0 +attrs==17.4.0 colorama==0.3.9 cssutils==1.0.2 Jinja2==2.10 diff --git a/scripts/asciidoc2html.py b/scripts/asciidoc2html.py index dce1f27bd..01af53693 100755 --- a/scripts/asciidoc2html.py +++ b/scripts/asciidoc2html.py @@ -147,13 +147,14 @@ class AsciiDoc: last_line = "" for line in infp: - if line.strip() == '// QUTE_WEB_HIDE': + line = line.rstrip() + if line == '// QUTE_WEB_HIDE': assert not hidden hidden = True - elif line.strip() == '// QUTE_WEB_HIDE_END': + elif line == '// QUTE_WEB_HIDE_END': assert hidden hidden = False - elif line == "The Compiler <mail@qutebrowser.org>\n": + elif line == "The Compiler <mail@qutebrowser.org>": continue elif re.fullmatch(r':\w+:.*', line): # asciidoc field @@ -163,16 +164,16 @@ class AsciiDoc: if re.fullmatch(r'=+', line): line = line.replace('=', '-') found_title = True - title = last_line.rstrip('\n') + " | qutebrowser\n" + title = last_line + " | qutebrowser\n" title += "=" * (len(title) - 1) elif re.fullmatch(r'= .+', line): line = '==' + line[1:] found_title = True - title = last_line.rstrip('\n') + " | qutebrowser\n" + title = last_line + " | qutebrowser\n" title += "=" * (len(title) - 1) if not hidden: - outfp.write(line.replace(".asciidoc[", ".html[")) + outfp.write(line.replace(".asciidoc[", ".html[") + '\n') last_line = line current_lines = outfp.getvalue() diff --git a/scripts/testbrowser_cpp/webengine/main.cpp b/scripts/testbrowser/cpp/webengine/main.cpp index 311432e92..311432e92 100644 --- a/scripts/testbrowser_cpp/webengine/main.cpp +++ b/scripts/testbrowser/cpp/webengine/main.cpp diff --git a/scripts/testbrowser_cpp/webengine/testbrowser.pro b/scripts/testbrowser/cpp/webengine/testbrowser.pro index 12a1cf7f6..12a1cf7f6 100644 --- a/scripts/testbrowser_cpp/webengine/testbrowser.pro +++ b/scripts/testbrowser/cpp/webengine/testbrowser.pro diff --git a/scripts/testbrowser_cpp/webkit/main.cpp b/scripts/testbrowser/cpp/webkit/main.cpp index 06c3d1a4f..06c3d1a4f 100644 --- a/scripts/testbrowser_cpp/webkit/main.cpp +++ b/scripts/testbrowser/cpp/webkit/main.cpp diff --git a/scripts/testbrowser_cpp/webkit/testbrowser.pro b/scripts/testbrowser/cpp/webkit/testbrowser.pro index 59f55ddfc..59f55ddfc 100644 --- a/scripts/testbrowser_cpp/webkit/testbrowser.pro +++ b/scripts/testbrowser/cpp/webkit/testbrowser.pro diff --git a/scripts/testbrowser/testbrowser_webengine.py b/scripts/testbrowser/testbrowser_webengine.py new file mode 100755 index 000000000..e6353dc23 --- /dev/null +++ b/scripts/testbrowser/testbrowser_webengine.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2017 Florian Bruhin (The Compiler) <mail@qutebrowser.org> +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. + +"""Very simple browser for testing purposes.""" + +import sys +import argparse + +from PyQt5.QtCore import QUrl +from PyQt5.QtWidgets import QApplication +from PyQt5.QtWebEngineWidgets import QWebEngineView + + +def parse_args(): + """Parse commandline arguments.""" + parser = argparse.ArgumentParser() + parser.add_argument('url', help='The URL to open') + return parser.parse_known_args()[0] + + +if __name__ == '__main__': + args = parse_args() + app = QApplication(sys.argv) + wv = QWebEngineView() + + wv.loadStarted.connect(lambda: print("Loading started")) + wv.loadProgress.connect(lambda p: print("Loading progress: {}%".format(p))) + wv.loadFinished.connect(lambda: print("Loading finished")) + + wv.load(QUrl.fromUserInput(args.url)) + wv.show() + + app.exec_() diff --git a/scripts/testbrowser.py b/scripts/testbrowser/testbrowser_webkit.py index fbe48c451..7c7081f18 100755 --- a/scripts/testbrowser.py +++ b/scripts/testbrowser/testbrowser_webkit.py @@ -25,16 +25,8 @@ import argparse from PyQt5.QtCore import QUrl from PyQt5.QtWidgets import QApplication - -try: - from PyQt5.QtWebKitWidgets import QWebView -except ImportError: - QWebView = None - -try: - from PyQt5.QtWebEngineWidgets import QWebEngineView -except ImportError: - QWebEngineView = None +from PyQt5.QtWebKit import QWebSettings +from PyQt5.QtWebKitWidgets import QWebView def parse_args(): @@ -43,39 +35,19 @@ def parse_args(): parser.add_argument('url', help='The URL to open') parser.add_argument('--plugins', '-p', help='Enable plugins', default=False, action='store_true') - if QWebEngineView is not None: - parser.add_argument('--webengine', help='Use QtWebEngine', - default=False, action='store_true') return parser.parse_known_args()[0] if __name__ == '__main__': args = parse_args() app = QApplication(sys.argv) - - if QWebView is None and QWebEngineView is None: - print("Found no suitable backend to run with!") - sys.exit(1) - elif QWebView is None and not args.webengine: - print("Using QtWebEngine because QtWebKit is unavailable") - wv = QWebEngineView() - using_webengine = True - elif args.webengine: - if QWebEngineView is None: - print("Requested QtWebEngine, but it could not be imported!") - sys.exit(1) - wv = QWebEngineView() - using_webengine = True - else: - wv = QWebView() - using_webengine = False + wv = QWebView() wv.loadStarted.connect(lambda: print("Loading started")) wv.loadProgress.connect(lambda p: print("Loading progress: {}%".format(p))) wv.loadFinished.connect(lambda: print("Loading finished")) - if args.plugins and not using_webengine: - from PyQt5.QtWebKit import QWebSettings + if args.plugins: wv.settings().setAttribute(QWebSettings.PluginsEnabled, True) wv.load(QUrl.fromUserInput(args.url)) diff --git a/tests/end2end/features/marks.feature b/tests/end2end/features/marks.feature index 605bd3971..f7da07255 100644 --- a/tests/end2end/features/marks.feature +++ b/tests/end2end/features/marks.feature @@ -86,7 +86,7 @@ Feature: Setting positional marks And I wait until the scroll position changed to 10/10 Then the page should be scrolled to 10 10 - @qtwebengine_todo: Does not emit loaded signal for fragments? + @qtwebengine_skip: Does not emit loaded signal for fragments? Scenario: Jumping back after following a link When I hint with args "links normal" and follow s And I wait until data/marks.html#bottom is loaded diff --git a/tests/end2end/fixtures/quteprocess.py b/tests/end2end/fixtures/quteprocess.py index 3f5e727e6..a2947e5af 100644 --- a/tests/end2end/fixtures/quteprocess.py +++ b/tests/end2end/fixtures/quteprocess.py @@ -44,9 +44,9 @@ from end2end.fixtures import testprocess instance_counter = itertools.count() -def is_ignored_qt_message(message): +def is_ignored_qt_message(pytestconfig, message): """Check if the message is listed in qt_log_ignore.""" - regexes = pytest.config.getini('qt_log_ignore') + regexes = pytestconfig.getini('qt_log_ignore') for regex in regexes: if re.search(regex, message): return True @@ -207,7 +207,7 @@ class LogLine(testprocess.Line): expected: Whether the message was expected or not. """ - def __init__(self, data): + def __init__(self, pytestconfig, data): super().__init__(data) try: line = json.loads(data) @@ -229,7 +229,7 @@ class LogLine(testprocess.Line): self.traceback = line.get('traceback') self.message = line['message'] - self.expected = is_ignored_qt_message(self.message) + self.expected = is_ignored_qt_message(pytestconfig, self.message) self.use_color = False def __str__(self): @@ -299,14 +299,13 @@ class QuteProc(testprocess.Process): 'message'] def __init__(self, request, *, parent=None): - super().__init__(parent) + super().__init__(request, parent) self._ipc_socket = None self.basedir = None self._focus_ready = False self._load_ready = False self._instance_id = next(instance_counter) self._run_counter = itertools.count() - self.request = request def _is_ready(self, what): """Called by _parse_line if loading/focusing is done. @@ -372,11 +371,11 @@ class QuteProc(testprocess.Process): def _parse_line(self, line): try: - log_line = LogLine(line) + log_line = LogLine(self.request.config, line) except testprocess.InvalidLine: if not line.strip(): return None - elif (is_ignored_qt_message(line) or + elif (is_ignored_qt_message(self.request.config, line) or is_ignored_lowlevel_message(line) or is_ignored_chromium_message(line) or self.request.node.get_marker('no_invalid_lines')): diff --git a/tests/end2end/fixtures/test_quteprocess.py b/tests/end2end/fixtures/test_quteprocess.py index f8241397f..aa3fb5857 100644 --- a/tests/end2end/fixtures/test_quteprocess.py +++ b/tests/end2end/fixtures/test_quteprocess.py @@ -45,6 +45,10 @@ class FakeConfig: '--qute-delay': 0, '--color': True, '--verbose': False, + '--capture': None, + } + INI = { + 'qt_log_ignore': [], } def __init__(self): @@ -53,6 +57,9 @@ class FakeConfig: def getoption(self, name): return self.ARGS[name] + def getini(self, name): + return self.INI[name] + class FakeNode: @@ -222,8 +229,8 @@ def test_quteprocess_quitting(qtbot, quteproc_process): {'category': 'py.warnings'}, id='resourcewarning'), ]) -def test_log_line_parse(data, attrs): - line = quteprocess.LogLine(data) +def test_log_line_parse(pytestconfig, data, attrs): + line = quteprocess.LogLine(pytestconfig, data) for name, expected in attrs.items(): actual = getattr(line, name) assert actual == expected, name @@ -283,9 +290,10 @@ def test_log_line_parse(data, attrs): '\033[36mfoo bar:qux:10\033[0m \033[37mquux\033[0m', id='expected error colorized'), ]) -def test_log_line_formatted(data, colorized, expect_error, expected): +def test_log_line_formatted(pytestconfig, + data, colorized, expect_error, expected): line = json.dumps(data) - record = quteprocess.LogLine(line) + record = quteprocess.LogLine(pytestconfig, line) record.expected = expect_error ts = datetime.datetime.fromtimestamp(data['created']).strftime('%H:%M:%S') ts += '.{:03.0f}'.format(data['msecs']) @@ -293,9 +301,9 @@ def test_log_line_formatted(data, colorized, expect_error, expected): assert record.formatted_str(colorized=colorized) == expected -def test_log_line_no_match(): +def test_log_line_no_match(pytestconfig): with pytest.raises(testprocess.InvalidLine): - quteprocess.LogLine("Hello World!") + quteprocess.LogLine(pytestconfig, "Hello World!") class TestClickElementByText: diff --git a/tests/end2end/fixtures/test_testprocess.py b/tests/end2end/fixtures/test_testprocess.py index 1811b7fb1..6ceb032af 100644 --- a/tests/end2end/fixtures/test_testprocess.py +++ b/tests/end2end/fixtures/test_testprocess.py @@ -51,8 +51,8 @@ class PythonProcess(testprocess.Process): """A testprocess which runs the given Python code.""" - def __init__(self): - super().__init__() + def __init__(self, request): + super().__init__(request) self.proc.setReadChannel(QProcess.StandardOutput) self.code = None @@ -103,22 +103,22 @@ class NoReadyPythonProcess(PythonProcess): @pytest.fixture -def pyproc(): - proc = PythonProcess() +def pyproc(request): + proc = PythonProcess(request) yield proc proc.terminate() @pytest.fixture -def quit_pyproc(): - proc = QuitPythonProcess() +def quit_pyproc(request): + proc = QuitPythonProcess(request) yield proc proc.terminate() @pytest.fixture -def noready_pyproc(): - proc = NoReadyPythonProcess() +def noready_pyproc(request): + proc = NoReadyPythonProcess(request) yield proc proc.terminate() @@ -149,9 +149,9 @@ def test_process_never_started(qtbot, quit_pyproc): quit_pyproc.after_test() -def test_wait_signal_raising(qtbot): +def test_wait_signal_raising(request, qtbot): """testprocess._wait_signal should raise by default.""" - proc = testprocess.Process() + proc = testprocess.Process(request) with pytest.raises(qtbot.TimeoutError): with proc._wait_signal(proc.proc.started, timeout=0): pass diff --git a/tests/end2end/fixtures/testprocess.py b/tests/end2end/fixtures/testprocess.py index ac220af58..3fb259e47 100644 --- a/tests/end2end/fixtures/testprocess.py +++ b/tests/end2end/fixtures/testprocess.py @@ -73,12 +73,11 @@ class Line: waited_for = attr.ib(False) -def _render_log(data, threshold=100): +def _render_log(data, *, verbose, threshold=100): """Shorten the given log without -v and convert to a string.""" data = [str(d) for d in data] is_exception = any('Traceback (most recent call last):' in line or 'Uncaught exception' in line for line in data) - verbose = pytest.config.getoption('--verbose') if len(data) > threshold and not verbose and not is_exception: msg = '[{} lines suppressed, use -v to show]'.format( len(data) - threshold) @@ -105,15 +104,17 @@ def pytest_runtest_makereport(item, call): # is actually a tuple. This is handled similarily in pytest-qt too. return - if pytest.config.getoption('--capture') == 'no': + if item.config.getoption('--capture') == 'no': # Already printed live return + verbose = item.config.getoption('--verbose') if quteproc_log is not None: report.longrepr.addsection("qutebrowser output", - _render_log(quteproc_log)) + _render_log(quteproc_log, verbose=verbose)) if server_log is not None: - report.longrepr.addsection("server output", _render_log(server_log)) + report.longrepr.addsection("server output", + _render_log(server_log, verbose=verbose)) class Process(QObject): @@ -128,6 +129,7 @@ class Process(QObject): _started: Whether the process was ever started. proc: The QProcess for the underlying process. exit_expected: Whether the process is expected to quit. + request: The request object for the current test. Signals: ready: Emitted when the server finished starting up. @@ -138,8 +140,9 @@ class Process(QObject): new_data = pyqtSignal(object) KEYS = ['data'] - def __init__(self, parent=None): + def __init__(self, request, parent=None): super().__init__(parent) + self.request = request self.captured_log = [] self._started = False self._invalid = [] @@ -150,7 +153,7 @@ class Process(QObject): def _log(self, line): """Add the given line to the captured log output.""" - if pytest.config.getoption('--capture') == 'no': + if self.request.config.getoption('--capture') == 'no': print(line) self.captured_log.append(line) @@ -225,6 +228,8 @@ class Process(QObject): """Start the process and wait until it started.""" self._start(args, env=env) self._started = True + verbose = self.request.config.getoption('--verbose') + timeout = 60 if 'CI' in os.environ else 20 for _ in range(timeout): with self._wait_signal(self.ready, timeout=1000, @@ -236,14 +241,15 @@ class Process(QObject): return # _start ensures it actually started, but it might quit shortly # afterwards - raise ProcessExited('\n' + _render_log(self.captured_log)) + raise ProcessExited('\n' + _render_log(self.captured_log, + verbose=verbose)) if blocker.signal_triggered: self._after_start() return raise WaitForTimeout("Timed out while waiting for process start.\n" + - _render_log(self.captured_log)) + _render_log(self.captured_log, verbose=verbose)) def _start(self, args, env): """Actually start the process.""" diff --git a/tests/end2end/fixtures/webserver.py b/tests/end2end/fixtures/webserver.py index a40c62015..93ef04f03 100644 --- a/tests/end2end/fixtures/webserver.py +++ b/tests/end2end/fixtures/webserver.py @@ -137,8 +137,8 @@ class WebserverProcess(testprocess.Process): KEYS = ['verb', 'path'] - def __init__(self, script, parent=None): - super().__init__(parent) + def __init__(self, request, script, parent=None): + super().__init__(request, parent) self._script = script self.port = utils.random_port() self.new_data.connect(self.new_request) @@ -174,9 +174,9 @@ class WebserverProcess(testprocess.Process): @pytest.fixture(scope='session', autouse=True) -def server(qapp): +def server(qapp, request): """Fixture for an server object which ensures clean setup/teardown.""" - server = WebserverProcess('webserver_sub') + server = WebserverProcess(request, 'webserver_sub') server.start() yield server server.terminate() @@ -198,7 +198,7 @@ def ssl_server(request, qapp): This needs to be explicitly used in a test, and overwrites the server log used in that test. """ - server = WebserverProcess('webserver_sub_ssl') + server = WebserverProcess(request, 'webserver_sub_ssl') request.node._server_log = server.captured_log server.start() yield server diff --git a/tests/unit/completion/test_completer.py b/tests/unit/completion/test_completer.py index e25d4e5d1..4b39b7032 100644 --- a/tests/unit/completion/test_completer.py +++ b/tests/unit/completion/test_completer.py @@ -190,6 +190,7 @@ def _set_cmd_prompt(cmd, txt): (':gibberish nonesense |', None, '', []), ('/:help|', None, '', []), ('::bind|', 'command', ':bind', []), + (':-w open |', None, '', []), ]) def test_update_completion(txt, kind, pattern, pos_args, status_command_stub, completer_obj, completion_widget_stub, config_stub, diff --git a/tests/unit/config/test_config.py b/tests/unit/config/test_config.py index 32a7a8119..d8bf73700 100644 --- a/tests/unit/config/test_config.py +++ b/tests/unit/config/test_config.py @@ -385,7 +385,7 @@ class TestConfig: def test_get(self, conf): """Test conf.get() with a QColor (where get/get_obj is different).""" - assert conf.get('colors.completion.fg') == QColor('white') + assert conf.get('colors.completion.category.fg') == QColor('white') @pytest.mark.parametrize('value', [{}, {'normal': {'a': 'nop'}}]) def test_get_bindings(self, config_stub, conf, value): @@ -400,7 +400,7 @@ class TestConfig: assert not conf._mutables def test_get_obj_simple(self, conf): - assert conf.get_obj('colors.completion.fg') == 'white' + assert conf.get_obj('colors.completion.category.fg') == 'white' @pytest.mark.parametrize('option', ['content.headers.custom', 'keyhint.blacklist', |