sup 0.17.0 → 0.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +17 -0
  3. data/.travis.yml +12 -0
  4. data/Gemfile +3 -0
  5. data/HACKING +42 -0
  6. data/History.txt +8 -0
  7. data/Rakefile +12 -0
  8. data/ReleaseNotes +4 -0
  9. data/bin/sup-sync +1 -1
  10. data/bin/sup-tweak-labels +6 -1
  11. data/contrib/colorpicker.rb +100 -0
  12. data/contrib/completion/_sup.zsh +114 -0
  13. data/devel/console.sh +3 -0
  14. data/devel/count-loc.sh +3 -0
  15. data/devel/load-index.rb +9 -0
  16. data/devel/profile.rb +12 -0
  17. data/devel/start-console.rb +5 -0
  18. data/doc/FAQ.txt +119 -0
  19. data/doc/Hooks.txt +79 -0
  20. data/doc/Philosophy.txt +69 -0
  21. data/lib/sup/colormap.rb +6 -0
  22. data/lib/sup/modes/thread_index_mode.rb +12 -1
  23. data/lib/sup/modes/thread_view_mode.rb +20 -0
  24. data/lib/sup/version.rb +1 -1
  25. data/sup.gemspec +55 -0
  26. data/test/dummy_source.rb +61 -0
  27. data/test/gnupg_test_home/gpg.conf +1 -0
  28. data/test/gnupg_test_home/pubring.gpg +0 -0
  29. data/test/gnupg_test_home/receiver_pubring.gpg +0 -0
  30. data/test/gnupg_test_home/receiver_secring.gpg +0 -0
  31. data/test/gnupg_test_home/receiver_trustdb.gpg +0 -0
  32. data/test/gnupg_test_home/secring.gpg +0 -0
  33. data/test/gnupg_test_home/sup-test-2@foo.bar.asc +20 -0
  34. data/test/gnupg_test_home/trustdb.gpg +0 -0
  35. data/test/integration/test_label_service.rb +18 -0
  36. data/test/messages/bad-content-transfer-encoding-1.eml +8 -0
  37. data/test/messages/binary-content-transfer-encoding-2.eml +21 -0
  38. data/test/messages/missing-line.eml +9 -0
  39. data/test/test_crypto.rb +109 -0
  40. data/test/test_header_parsing.rb +168 -0
  41. data/test/test_helper.rb +7 -0
  42. data/test/test_message.rb +532 -0
  43. data/test/test_messages_dir.rb +147 -0
  44. data/test/test_yaml_migration.rb +85 -0
  45. data/test/test_yaml_regressions.rb +17 -0
  46. data/test/unit/service/test_label_service.rb +19 -0
  47. data/test/unit/test_horizontal_selector.rb +40 -0
  48. data/test/unit/util/test_query.rb +46 -0
  49. data/test/unit/util/test_string.rb +57 -0
  50. data/test/unit/util/test_uri.rb +19 -0
  51. metadata +81 -36
  52. checksums.yaml.gz.sig +0 -1
  53. data.tar.gz.sig +0 -0
  54. metadata.gz.sig +0 -0
data/doc/Hooks.txt ADDED
@@ -0,0 +1,79 @@
1
+ Sup's Hook System
2
+ -----------------
3
+
4
+ Sup can be easily customized via its hook system, which allows custom
5
+ user code to be injected into Sup's execution path by "hooking" the
6
+ code onto pre-defined events. When those events occur, the code is
7
+ executed.
8
+
9
+ To see which hooks are available, simply run sup -l. Each hook sits in
10
+ a file in ~/.sup/hooks/. Hooks are written in Ruby, and require no
11
+ class or method definitions, just the executable code itself.
12
+
13
+ Information passes from Sup to the hook code via Ruby variables
14
+ (actually method calls), and from the hook code back to Sup via a
15
+ return value. The values of variables persists across calls to the
16
+ same hook, but is NOT available to other hooks. To make the value of a
17
+ variable available to other hooks, use the get and set methods. Each
18
+ hook description lists the variables and return value expected, if
19
+ any.
20
+
21
+ The following special functions are available to hooks:
22
+ * say msg
23
+ Displays the string msg to the user at the bottom of the screen.
24
+ * log msg
25
+ Adds the string msg to the log, which the user can access via the
26
+ buffer list.
27
+ * ask_yes_or_no question
28
+ Prompts the user with the string question for a yes or no
29
+ response. Returns true if the user answered yes, false otherwise.
30
+ * get key
31
+ Gets the cross-hook value associated with key (which is typically a
32
+ string). If there is no value for a given key, nil is returned.
33
+ * set key value
34
+ Sets the cross-hook value associated with key to value. key is
35
+ typically a string, while value can be whatever type it needs to be,
36
+ including nil.
37
+
38
+ Some example hooks:
39
+
40
+ before-poll:
41
+ ## runs fetchmail before polling
42
+ if (@last_fetchmail_time || Time.now) < Time.now - 60
43
+ say "Running fetchmail..."
44
+ system "fetchmail >& /dev/null"
45
+ say "Done running fetchmail."
46
+ end
47
+ @last_fetchmail_time = Time.now
48
+
49
+
50
+ mime-decode:
51
+ ## Please read:
52
+ https://github.com/sup-heliotrope/sup/wiki/Viewing-Attachments for
53
+ some security concerns on opening attachments.
54
+
55
+ ## turn text/html attachments into plain text, unless they are part
56
+ ## of a multipart/alternative pair
57
+ require 'shellwords'
58
+ unless sibling_types.member? "text/plain"
59
+ case content_type
60
+ when "text/html"
61
+ `/usr/bin/w3m -dump -T #{content_type} #{Shellwords.escape filename}`
62
+ end
63
+ end
64
+
65
+ startup:
66
+ ## runs a background task
67
+ @bgtask_pid = fork
68
+ if @bgtask_pid
69
+ set 'bgtask_pid' @bgtask_pid
70
+ Process.detach(@bgtask_pid) # so we don't have to wait on it when we go to kill it
71
+ else
72
+ exec "background-task args 2&>1 >> /tmp/logfile"
73
+ end
74
+
75
+ after-poll:
76
+ ## kills the background task after the first poll
77
+ @bgtask_pid = get 'bgtask_pid'
78
+ Process.kill("TERM", @bgtask_pid) unless @bgtask_pid == nil
79
+ set 'bgtask_pid' nil
@@ -0,0 +1,69 @@
1
+ Should an email client have a philosophy? For many people, email is
2
+ one of our primary means of communication, and email archives are an
3
+ integral part of our long-term memory. Something so important ought to
4
+ warrant a little thought.
5
+
6
+ Here's Sup's philosophy.
7
+
8
+ Using "traditional" email clients today is increasingly problematic.
9
+ Anyone who's on a high-traffic mailing list knows this. My ruby-talk
10
+ folder is 430 megs and Mutt sits there for 60 seconds while it opens
11
+ it. Keeping up with the all the new traffic is impossible, even with
12
+ Mutt's excellent threading features, simply because there's so much of
13
+ it. A single thread can span several pages in the folder index view
14
+ alone! And Mutt is probably the fastest, most mailing-list aware email
15
+ client out there. God help me if I try and use Thunderbird.
16
+
17
+ The problem with traditional clients like Mutt is that they deal with
18
+ individual pieces of email. This places a high mental cost on the user
19
+ for each incoming email, by forcing them to ask: Should I keep this
20
+ email, or delete it? If I keep it, where should I file it? I've spent
21
+ the last 10 years of my life laboriously hand-filing every email
22
+ message I received and feeling a mild sense of panic every time an
23
+ email was both "from Mom" and "about school". The massive amounts of
24
+ email that many people receive, and the cheap cost of storage, have
25
+ made these questions both more costly and less useful to answer.
26
+
27
+ Contrast that with using Gmail. As a long-time Mutt user, I was blown
28
+ away when I first saw someone use Gmail. They treated their email
29
+ differently from how I ever had. They never filed email and they never
30
+ deleted it. They relied on an immediate, global, full-text search, and
31
+ thread-level tagging, to do everything I'd ever done with Mutt, but
32
+ with a trivial cost to the user at message receipt time.
33
+
34
+ From Gmail I learned that making certain operations quantitatively
35
+ easier (namely, search) resulted in a qualitative improvement in
36
+ usage. I also learned how thread-centrism was advantageous over
37
+ message-centrism when message volume was high: most of the time, a
38
+ message and its context deserve the same treatment. I think it's to
39
+ the Gmail designers' credit that they started with a somewhat ad-hoc
40
+ idea (hey, we're really good at search engines, so maybe we can build
41
+ an email client on top of one) and managed to build something that was
42
+ actually better than everything else out there. At least, that's how I
43
+ imagine in happened. Maybe they knew what they were doing from the
44
+ start.
45
+
46
+ Unfortunately, there's a lot to Gmail I can't tolerate (top posting,
47
+ HTML mail, one-level threads, and ads come to mind, never mind the
48
+ fact that it's not FOSS). Thus Sup was born.
49
+
50
+ Sup is based on the following principles, which I stole directly from
51
+ Gmail:
52
+
53
+ - An immediately accessible and fast search capability over the entire
54
+ email archive eliminates most of the need for folders, and most of
55
+ the necessity of deleting email.
56
+
57
+ - Labels eliminate what little need for folders search doesn't cover.
58
+
59
+ - A thread-centric approach to the UI is much more in line with how
60
+ people operate than dealing with individual messages is. In the vast
61
+ majority of cases, a message and its context should be subject to
62
+ the same treatment.
63
+
64
+ Sup is also based on many ideas from mutt and Emacs and vi, having to
65
+ do with the fantastic productivity of a console- and keyboard-based
66
+ application, the usefulness of multiple buffers, the necessity of
67
+ handling multiple email accounts, etc. But those are just details!
68
+
69
+ Try it and let me know what you think.
data/lib/sup/colormap.rb CHANGED
@@ -50,6 +50,7 @@ class Colormap
50
50
  :quote => { :fg => "yellow", :bg => "default" },
51
51
  :sig => { :fg => "yellow", :bg => "default" },
52
52
  :to_me => { :fg => "green", :bg => "default" },
53
+ :with_attachment => { :fg => "green", :bg => "default" },
53
54
  :starred => { :fg => "yellow", :bg => "default", :attrs => ["bold"] },
54
55
  :starred_patina => { :fg => "yellow", :bg => "green", :attrs => ["bold"] },
55
56
  :alternate_starred_patina => { :fg => "yellow", :bg => "blue", :attrs => ["bold"] },
@@ -190,6 +191,11 @@ class Colormap
190
191
  Redwood::load_yaml_obj Redwood::COLOR_FN
191
192
  end
192
193
 
194
+ ## Set attachment sybmol to sane default for existing colorschemes
195
+ if user_colors and user_colors.has_key? :to_me
196
+ user_colors[:with_attachment] = user_colors[:to_me] unless user_colors.has_key? :with_attachment
197
+ end
198
+
193
199
  Colormap::DEFAULT_COLORS.merge(user_colors||{}).each_pair do |k, v|
194
200
  fg = begin
195
201
  Ncurses.const_get "COLOR_#{v[:fg].to_s.upcase}"
@@ -236,6 +236,13 @@ EOS
236
236
  update
237
237
  end
238
238
 
239
+ def handle_killed_update sender, m
240
+ t = @ts_mutex.synchronize { @ts.thread_for m }
241
+ return unless t
242
+ hide_thread t
243
+ update
244
+ end
245
+
239
246
  def handle_spammed_update sender, m
240
247
  t = @ts_mutex.synchronize { @ts.thread_for m }
241
248
  return unless t
@@ -247,6 +254,10 @@ EOS
247
254
  add_or_unhide m
248
255
  end
249
256
 
257
+ def handle_unkilled_update sender, m
258
+ add_or_unhide m
259
+ end
260
+
250
261
  def undo
251
262
  UndoManager.undo
252
263
  end
@@ -979,7 +990,7 @@ protected
979
990
  from +
980
991
  [
981
992
  [:size_widget_color, size_widget_text],
982
- [:to_me_color, t.labels.member?(:attachment) ? "@" : " "],
993
+ [:with_attachment_color , t.labels.member?(:attachment) ? "@" : " "],
983
994
  [:to_me_color, directly_participated ? ">" : (participated ? '+' : " ")],
984
995
  ] +
985
996
  (t.labels - @hidden_labels).sort_by {|x| x.to_s}.map {
@@ -78,11 +78,13 @@ EOS
78
78
 
79
79
  k.add :archive_and_next, "Archive this thread, kill buffer, and view next", 'a'
80
80
  k.add :delete_and_next, "Delete this thread, kill buffer, and view next", 'd'
81
+ k.add :kill_and_next, "Kill this thread, kill buffer, and view next", '&'
81
82
  k.add :toggle_wrap, "Toggle wrapping of text", 'w'
82
83
 
83
84
  k.add_multi "(a)rchive/(d)elete/mark as (s)pam/mark as u(N)read:", '.' do |kk|
84
85
  kk.add :archive_and_kill, "Archive this thread and kill buffer", 'a'
85
86
  kk.add :delete_and_kill, "Delete this thread and kill buffer", 'd'
87
+ kk.add :kill_and_kill, "Kill this thread and kill buffer", '&'
86
88
  kk.add :spam_and_kill, "Mark this thread as spam and kill buffer", 's'
87
89
  kk.add :unread_and_kill, "Mark this thread as unread and kill buffer", 'N'
88
90
  kk.add :do_nothing_and_kill, "Just kill this buffer", '.'
@@ -91,6 +93,7 @@ EOS
91
93
  k.add_multi "(a)rchive/(d)elete/mark as (s)pam/mark as u(N)read/do (n)othing:", ',' do |kk|
92
94
  kk.add :archive_and_next, "Archive this thread, kill buffer, and view next", 'a'
93
95
  kk.add :delete_and_next, "Delete this thread, kill buffer, and view next", 'd'
96
+ kk.add :kill_and_next, "Kill this thread, kill buffer, and view next", '&'
94
97
  kk.add :spam_and_next, "Mark this thread as spam, kill buffer, and view next", 's'
95
98
  kk.add :unread_and_next, "Mark this thread as unread, kill buffer, and view next", 'N'
96
99
  kk.add :do_nothing_and_next, "Kill buffer, and view next", 'n', ','
@@ -99,6 +102,7 @@ EOS
99
102
  k.add_multi "(a)rchive/(d)elete/mark as (s)pam/mark as u(N)read/do (n)othing:", ']' do |kk|
100
103
  kk.add :archive_and_prev, "Archive this thread, kill buffer, and view previous", 'a'
101
104
  kk.add :delete_and_prev, "Delete this thread, kill buffer, and view previous", 'd'
105
+ kk.add :kill_and_prev, "Kill this thread, kill buffer, and view previous", '&'
102
106
  kk.add :spam_and_prev, "Mark this thread as spam, kill buffer, and view previous", 's'
103
107
  kk.add :unread_and_prev, "Mark this thread as unread, kill buffer, and view previous", 'N'
104
108
  kk.add :do_nothing_and_prev, "Kill buffer, and view previous", 'n', ']'
@@ -581,18 +585,21 @@ EOS
581
585
  def archive_and_kill; archive_and_then :kill end
582
586
  def spam_and_kill; spam_and_then :kill end
583
587
  def delete_and_kill; delete_and_then :kill end
588
+ def kill_and_kill; kill_and_then :kill end
584
589
  def unread_and_kill; unread_and_then :kill end
585
590
  def do_nothing_and_kill; do_nothing_and_then :kill end
586
591
 
587
592
  def archive_and_next; archive_and_then :next end
588
593
  def spam_and_next; spam_and_then :next end
589
594
  def delete_and_next; delete_and_then :next end
595
+ def kill_and_next; kill_and_then :next end
590
596
  def unread_and_next; unread_and_then :next end
591
597
  def do_nothing_and_next; do_nothing_and_then :next end
592
598
 
593
599
  def archive_and_prev; archive_and_then :prev end
594
600
  def spam_and_prev; spam_and_then :prev end
595
601
  def delete_and_prev; delete_and_then :prev end
602
+ def kill_and_prev; kill_and_then :prev end
596
603
  def unread_and_prev; unread_and_then :prev end
597
604
  def do_nothing_and_prev; do_nothing_and_then :prev end
598
605
 
@@ -635,6 +642,19 @@ EOS
635
642
  end
636
643
  end
637
644
 
645
+ def kill_and_then op
646
+ dispatch op do
647
+ @thread.apply_label :killed
648
+ UpdateManager.relay self, :killed, @thread.first
649
+ Index.save_thread @thread
650
+ UndoManager.register "killed 1 thread" do
651
+ @thread.remove_label :killed
652
+ Index.save_thread @thread
653
+ UpdateManager.relay self, :unkilled, @thread.first
654
+ end
655
+ end
656
+ end
657
+
638
658
  def unread_and_then op
639
659
  dispatch op do
640
660
  @thread.apply_label :unread
data/lib/sup/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Redwood
2
- VERSION = "0.17.0"
2
+ VERSION = "0.18.0"
3
3
  end
data/sup.gemspec ADDED
@@ -0,0 +1,55 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+
3
+ require 'sup/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "sup"
7
+ s.version = ENV["REL"] || (::Redwood::VERSION == "git" ? "999" : ::Redwood::VERSION)
8
+ s.date = Time.now.strftime "%Y-%m-%d"
9
+ s.authors = ["William Morgan", "Gaute Hope", "Hamish Downer", "Matthieu Rakotojaona"]
10
+ s.email = "sup-talk@rubyforge.org"
11
+ s.summary = "A console-based email client with the best features of GMail, mutt and Emacs"
12
+ s.homepage = "http://supmua.org"
13
+ s.license = 'GPL-2'
14
+ s.description = <<-DESC
15
+ Sup is a console-based email client for people with a lot of email.
16
+
17
+ * GMail-like thread-centered archiving, tagging and muting
18
+ * Handling mail from multiple mbox and Maildir sources
19
+ * Blazing fast full-text search with a rich query language
20
+ * Multiple accounts - pick the right one when sending mail
21
+ * Ruby-programmable hooks
22
+ * Automatically tracking recent contacts
23
+ DESC
24
+ s.post_install_message = <<-EOF
25
+ SUP: If you are upgrading Sup from before version 0.14.0: Please
26
+ run `sup-psych-ify-config-files` to migrate from 0.13.
27
+
28
+ Check https://github.com/sup-heliotrope/sup/wiki/Migration-0.13-to-0.14
29
+ for more detailed and up-to-date instructions.
30
+ EOF
31
+
32
+ s.files = `git ls-files -z`.split("\x0")
33
+ s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
34
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
35
+ s.require_paths = ["lib"]
36
+
37
+ s.required_ruby_version = '>= 1.9.3'
38
+
39
+ s.add_runtime_dependency "xapian-ruby", "~> 1.2.15"
40
+ s.add_runtime_dependency "ncursesw", "~> 1.4.0"
41
+ s.add_runtime_dependency "rmail-sup", "~> 1.0.1"
42
+ s.add_runtime_dependency "highline"
43
+ s.add_runtime_dependency "trollop", ">= 1.12"
44
+ s.add_runtime_dependency "lockfile"
45
+ s.add_runtime_dependency "mime-types", "~> 1.0"
46
+ s.add_runtime_dependency "locale", "~> 2.0"
47
+ s.add_runtime_dependency "chronic", "~> 0.9.1"
48
+ s.add_runtime_dependency "unicode", "~> 0.4.4"
49
+
50
+ s.add_development_dependency "bundler", "~> 1.3"
51
+ s.add_development_dependency "rake"
52
+ s.add_development_dependency "minitest", "~> 4.7"
53
+ s.add_development_dependency "rr", "~> 1.0.5"
54
+ s.add_development_dependency "gpgme", ">= 2.0.2"
55
+ end
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'sup'
4
+ require 'stringio'
5
+ require 'rmail'
6
+ require 'uri'
7
+
8
+ module Redwood
9
+
10
+ class DummySource < Source
11
+
12
+ attr_accessor :messages
13
+
14
+ def initialize uri, last_date=nil, usual=true, archived=false, id=nil, labels=[]
15
+ super uri, usual, archived, id
16
+ @messages = nil
17
+ end
18
+
19
+ def start_offset
20
+ 0
21
+ end
22
+
23
+ def end_offset
24
+ # should contain the number of test messages -1
25
+ return @messages ? @messages.length - 1 : 0
26
+ end
27
+
28
+ def load_header offset
29
+ Source.parse_raw_email_header StringIO.new(raw_header(offset))
30
+ end
31
+
32
+ def load_message offset
33
+ RMail::Parser.read raw_message(offset)
34
+ end
35
+
36
+ def raw_header offset
37
+ ret = ""
38
+ f = StringIO.new(@messages[offset])
39
+ until f.eof? || (l = f.gets) =~ /^$/
40
+ ret += l
41
+ end
42
+ ret
43
+ end
44
+
45
+ def raw_message offset
46
+ @messages[offset]
47
+ end
48
+
49
+ def each_raw_message_line offset
50
+ ret = ""
51
+ f = StringIO.new(@messages[offset])
52
+ until f.eof?
53
+ yield f.gets
54
+ end
55
+ end
56
+ end
57
+
58
+ end
59
+
60
+ # vim:noai:ts=2:sw=2:
61
+
@@ -0,0 +1 @@
1
+ default-key 789E7011
Binary file
Binary file
@@ -0,0 +1,20 @@
1
+ -----BEGIN PGP PUBLIC KEY BLOCK-----
2
+ Version: GnuPG v2.0.20 (GNU/Linux)
3
+
4
+ mI0EUgi0fAEEAOLAcQW96NEUSB7YE/la8X56jGW5BMX3aAixOF8LvOwMBbUK1T+U
5
+ 0H2PGIrXVcYyHcPqWRpRahbsIAldBqzffPlzMa+aqJaB1xKkNruxSoIzwPdidZMe
6
+ l0Dxz2FDsoXD0KPyWnAYhGmQyz2MFpZxu2tlYqvwWVW//XGnk/KHvIXbABEBAAG0
7
+ PlN1cCBUZXN0IFJlY2VpdmVyIChUZXN0IHJlY2VpdmVyIGZvciBTdXApIDxzdXAt
8
+ dGVzdC0yQGZvby5iYXI+iL8EEwECACkFAlIItHwCGwMFCQHhM4AHCwkIBwMCAQYV
9
+ CAIJCgsEFgIDAQIeAQIXgAAKCRAsABl+cWpykMMVBADHkQPgTz0CqKKp3k+z3dbm
10
+ ocmI4tYNn1dOkDQqyfoBTfs6L3g4j5OE2UrguntRYyg5oon+uO5d18CQ5dY0sCw/
11
+ o5IwyzTrxI8IocbtZvBdSb+XjLndynGuIQoqaJq9i6n1V4klFHVOna8Q9JstLfRX
12
+ H1d4xPhnvKcaDDx/NV3X/biNBFIItHwBBADBpb43MpkrUWlg7HWJ1ZfOlxnOxrJ3
13
+ Gz9WFNV06UbcZEuFKA/vHRjM6gWzUn5903FLuCWu3eBrq5xQfWipbp187PmocpoG
14
+ skJ6gosLs1fMYRBjv2VbG9xJVKdKJMjqZw5FUpXKAaHr8P9jN6g2STQrbeQ8CVUK
15
+ h7zOWRXAXSKUgwARAQABiKUEGAECAA8FAlIItHwCGwwFCQHhM4AACgkQLAAZfnFq
16
+ cpDV1QQAzcxFXznEX92DjWxWRC7gRHgIsQk9WJnDzjtnDjSWCp3H85qeTZGZrn9W
17
+ NoneV/S5Y7K3Mkceh4rFaANQ3zx4b05y1LFt5N/lPwIe5VB0vcPumtZum2fSGfpK
18
+ nTXvzelcWcm2aGyUSaWvOkntWKEEt1kB5Oq6EtZoRZLMzAxLd7s=
19
+ =aKsV
20
+ -----END PGP PUBLIC KEY BLOCK-----
Binary file
@@ -0,0 +1,18 @@
1
+ require "test_helper"
2
+
3
+ require "sup/service/label_service"
4
+
5
+ require "tmpdir"
6
+
7
+ describe Redwood::LabelService do
8
+ let(:tmpdir) { Dir.mktmpdir }
9
+ after do
10
+ require "fileutils"
11
+ FileUtils.remove_entry_secure @tmpdir unless @tmpdir.nil?
12
+ end
13
+
14
+ describe "#add_labels" do
15
+ # Integration tests are hard to write at this moment :(
16
+ it "add labels to all messages matching the query"
17
+ end
18
+ end