File: test_discarding.rb

package info (click to toggle)
ruby-sqlite3 2.9.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 680 kB
  • sloc: ruby: 4,827; ansic: 1,868; sh: 91; makefile: 7
file content (172 lines) | stat: -rw-r--r-- 5,381 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
require_relative "helper"

module SQLite3
  class TestDiscardDatabase < SQLite3::TestCase
    DBPATH = "test.db"

    def setup
      FileUtils.rm_f(DBPATH)
      super
    end

    def teardown
      super
      FileUtils.rm_f(DBPATH)
    end

    def in_a_forked_process
      @read, @write = IO.pipe
      old_stderr, $stderr = $stderr, StringIO.new

      Process.fork do
        @read.close
        begin
          yield @write
        rescue => e
          old_stderr.write("child exception: #{e.message}")
        end
        @write.write($stderr.string)
        @write.close
        exit!
      end

      $stderr = old_stderr
      @write.close
      *@results = *@read.readlines
      @read.close
    end

    def test_fork_discards_an_open_readwrite_connection
      skip("interpreter doesn't support fork") unless Process.respond_to?(:fork)
      skip("valgrind doesn't handle forking") if i_am_running_in_valgrind

      GC.start
      begin
        db = SQLite3::Database.new(DBPATH)

        in_a_forked_process do |write|
          write.write(db.closed? ? "ok\n" : "fail\n")
        end

        assertion, *stderr = *@results

        assert_equal("ok", assertion.chomp, "closed? did not return true")
        assert_equal(1, stderr.count, "unexpected output on stderr: #{stderr.inspect}")
        assert_match(
          /warning: Writable sqlite database connection\(s\) were inherited from a forked process/,
          stderr.first,
          "expected warning was not emitted"
        )
      ensure
        db&.close
      end
    end

    def test_fork_does_not_discard_closed_connections
      skip("interpreter doesn't support fork") unless Process.respond_to?(:fork)
      skip("valgrind doesn't handle forking") if i_am_running_in_valgrind

      GC.start
      begin
        db = SQLite3::Database.new(DBPATH)
        db.close

        in_a_forked_process do |write|
          write.write(db.closed? ? "ok\n" : "fail\n")
          write.write($stderr.string) # should be empty write, no warnings emitted
          write.write("done\n")
        end

        assertion, *rest = *@results

        assert_equal("ok", assertion.chomp, "closed? did not return true")
        assert_equal(1, rest.count, "unexpected output on stderr: #{rest.inspect}")
        assert_equal("done", rest.first.chomp, "unexpected output on stderr: #{rest.inspect}")
      ensure
        db&.close
      end
    end

    def test_fork_does_not_discard_readonly_connections
      skip("interpreter doesn't support fork") unless Process.respond_to?(:fork)
      skip("valgrind doesn't handle forking") if i_am_running_in_valgrind

      GC.start
      begin
        SQLite3::Database.open(DBPATH) do |db|
          db.execute("create table foo (bar int)")
          db.execute("insert into foo values (1)")
        end

        db = SQLite3::Database.new(DBPATH, readonly: true)

        in_a_forked_process do |write|
          write.write(db.closed? ? "fail\n" : "ok\n") # should be open and readable
          write.write((db.execute("select * from foo") == [[1]]) ? "ok\n" : "fail\n")
          write.write($stderr.string) # should be an empty write, no warnings emitted
          write.write("done\n")
        end

        assertion1, assertion2, *rest = *@results

        assert_equal("ok", assertion1.chomp, "closed? did not return false")
        assert_equal("ok", assertion2.chomp, "could not read from database")
        assert_equal(1, rest.count, "unexpected output on stderr: #{rest.inspect}")
        assert_equal("done", rest.first.chomp, "unexpected output on stderr: #{rest.inspect}")
      ensure
        db&.close
      end
    end

    def test_close_does_not_discard_readonly_connections
      skip("interpreter doesn't support fork") unless Process.respond_to?(:fork)
      skip("valgrind doesn't handle forking") if i_am_running_in_valgrind

      GC.start
      begin
        SQLite3::Database.open(DBPATH) do |db|
          db.execute("create table foo (bar int)")
          db.execute("insert into foo values (1)")
        end

        db = SQLite3::Database.new(DBPATH, readonly: true)

        in_a_forked_process do |write|
          write.write(db.closed? ? "fail\n" : "ok\n") # should be open and readable
          db.close
          write.write($stderr.string) # should be an empty write, no warnings emitted
          write.write("done\n")
        end

        assertion, *rest = *@results

        assert_equal("ok", assertion.chomp, "closed? did not return false")
        assert_equal(1, rest.count, "unexpected output on stderr: #{rest.inspect}")
        assert_equal("done", rest.first.chomp, "unexpected output on stderr: #{rest.inspect}")
      ensure
        db&.close
      end
    end

    def test_a_discarded_connection_with_statements
      skip("discard leaks memory") if i_am_running_in_valgrind

      begin
        db = SQLite3::Database.new(DBPATH)
        db.execute("create table foo (bar int)")
        db.execute("insert into foo values (1)")
        stmt = db.prepare("select * from foo")

        db.send(:discard)

        e = assert_raises(SQLite3::Exception) { stmt.execute }
        assert_match(/cannot use a statement associated with a discarded database/, e.message)

        assert_nothing_raised { stmt.close }
        assert_predicate(stmt, :closed?)
      ensure
        db&.close
      end
    end
  end
end