1#encoding:BINARY
2
3#
4# directory_extension.rb
5#
6# This source file is part of the FoundationDB open source project
7#
8# Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
9#
10# Licensed under the Apache License, Version 2.0 (the "License");
11# you may not use this file except in compliance with the License.
12# You may obtain a copy of the License at
13#
14#     http://www.apache.org/licenses/LICENSE-2.0
15#
16# Unless required by applicable law or agreed to in writing, software
17# distributed under the License is distributed on an "AS IS" BASIS,
18# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19# See the License for the specific language governing permissions and
20# limitations under the License.
21#
22
23require 'fdb'
24
25module DirectoryExtension
26  class DirectoryTester
27    @@ops_that_create_dirs = [
28      'DIRECTORY_CREATE_SUBSPACE',
29      'DIRECTORY_CREATE_LAYER',
30      'DIRECTORY_CREATE_OR_OPEN',
31      'DIRECTORY_CREATE',
32      'DIRECTORY_OPEN',
33      'DIRECTORY_MOVE',
34      'DIRECTORY_MOVE_TO',
35      'DIRECTORY_OPEN_SUBSPACE',
36    ]
37
38    def initialize
39      @dir_list = [FDB.directory]
40      @dir_index = 0
41      @error_index = 0
42    end
43
44    def pop_tuples(inst, num=nil)
45      actual_num = num
46      if actual_num.nil?
47        actual_num = 1
48      end
49
50      tuples = (0...actual_num).map do
51        (0...inst.wait_and_pop).map do
52          inst.wait_and_pop
53        end
54      end
55
56      if num.nil?
57        return tuples[0]
58      end
59
60      return tuples
61    end
62
63    def process_instruction(inst)
64      begin
65        #puts "#{inst.index} #{inst.op}"
66        directory = @dir_list[@dir_index]
67        if inst.op == 'DIRECTORY_CREATE_SUBSPACE'
68          path = pop_tuples(inst)
69          raw_prefix = inst.wait_and_pop
70          @dir_list << FDB::Subspace.new(path, raw_prefix)
71        elsif inst.op == 'DIRECTORY_CREATE_LAYER'
72          index1 = inst.wait_and_pop
73          index2 = inst.wait_and_pop
74          allow_manual_prefixes = inst.wait_and_pop
75          if @dir_list[index1].nil? or @dir_list[index2].nil?
76            @dir_list << nil
77          else
78            @dir_list << FDB::DirectoryLayer.new(:node_subspace => @dir_list[index1],
79                                                 :content_subspace => @dir_list[index2],
80                                                 :allow_manual_prefixes => allow_manual_prefixes == 1)
81          end
82        elsif inst.op == 'DIRECTORY_CHANGE'
83          @dir_index = inst.wait_and_pop
84          if @dir_list[@dir_index].nil?
85            @dir_index = @error_index
86          end
87        elsif inst.op == 'DIRECTORY_SET_ERROR_INDEX'
88          @error_index = inst.wait_and_pop
89        elsif inst.op == 'DIRECTORY_CREATE_OR_OPEN'
90          @dir_list << directory.create_or_open(inst.tr, pop_tuples(inst), :layer=>inst.wait_and_pop || '')
91        elsif inst.op == 'DIRECTORY_CREATE'
92          @dir_list << directory.create(inst.tr, pop_tuples(inst), :layer=>inst.wait_and_pop || '', :prefix=>inst.wait_and_pop)
93        elsif inst.op == 'DIRECTORY_OPEN'
94          @dir_list << directory.open(inst.tr, pop_tuples(inst), :layer=>inst.wait_and_pop || '')
95        elsif inst.op == 'DIRECTORY_MOVE'
96          @dir_list << directory.move(inst.tr, pop_tuples(inst), pop_tuples(inst))
97        elsif inst.op == 'DIRECTORY_MOVE_TO'
98          @dir_list << directory.move_to(inst.tr, pop_tuples(inst))
99        elsif inst.op == 'DIRECTORY_REMOVE'
100          count = inst.wait_and_pop
101          if count == 0
102            directory.remove(inst.tr)
103          else
104            directory.remove(inst.tr, pop_tuples(inst))
105          end
106        elsif inst.op == 'DIRECTORY_REMOVE_IF_EXISTS'
107          count = inst.wait_and_pop
108          if count == 0
109            directory.remove_if_exists(inst.tr)
110          else
111            directory.remove_if_exists(inst.tr, pop_tuples(inst))
112          end
113        elsif inst.op == 'DIRECTORY_LIST'
114          count = inst.wait_and_pop
115          results =
116            if count == 0
117              directory.list(inst.tr)
118            else
119              directory.list(inst.tr, pop_tuples(inst))
120            end
121
122          inst.push(FDB::Tuple.pack(results))
123        elsif inst.op == 'DIRECTORY_EXISTS'
124          count = inst.wait_and_pop
125          result =
126            if count == 0
127              directory.exists?(inst.tr)
128            else
129              directory.exists?(inst.tr, pop_tuples(inst))
130            end
131
132          if result
133            inst.push(1)
134          else
135            inst.push(0)
136          end
137        elsif inst.op == 'DIRECTORY_PACK_KEY'
138          inst.push(directory.pack(pop_tuples(inst)))
139        elsif inst.op == 'DIRECTORY_UNPACK_KEY'
140          tup = directory.unpack(inst.wait_and_pop)
141          tup.each do |t| inst.push(t) end
142        elsif inst.op == 'DIRECTORY_RANGE'
143          rng = directory.range(pop_tuples(inst))
144          inst.push(rng[0])
145          inst.push(rng[1])
146        elsif inst.op == 'DIRECTORY_CONTAINS'
147          if directory.contains?(inst.wait_and_pop)
148            inst.push(1)
149          else
150            inst.push(0)
151          end
152        elsif inst.op == 'DIRECTORY_OPEN_SUBSPACE'
153          @dir_list << directory.subspace(pop_tuples(inst))
154        elsif inst.op == 'DIRECTORY_LOG_SUBSPACE'
155          inst.tr[inst.wait_and_pop + FDB::Tuple.pack([@dir_index])] = directory.key
156        elsif inst.op == 'DIRECTORY_LOG_DIRECTORY'
157          exists = directory.exists?(inst.tr)
158          children = exists ? directory.list(inst.tr) : []
159          log_subspace = FDB::Subspace.new([@dir_index], inst.wait_and_pop)
160          inst.tr[log_subspace['path'.encode('utf-8')]] = FDB::Tuple.pack(directory.path)
161          inst.tr[log_subspace['layer'.encode('utf-8')]] = FDB::Tuple.pack([directory.layer])
162          inst.tr[log_subspace['exists'.encode('utf-8')]] = FDB::Tuple.pack([exists ? 1 : 0])
163          inst.tr[log_subspace['children'.encode('utf-8')]] = FDB::Tuple.pack(children)
164        elsif inst.op == 'DIRECTORY_STRIP_PREFIX'
165          str = inst.wait_and_pop
166          throw "String #{str} does not start with raw prefix #{directory.key}" if !str.start_with?(directory.key)
167          inst.push(str[directory.key.length..-1])
168        else
169          raise "Unknown op #{inst.op}"
170        end
171      rescue Exception => e
172        #puts "#{e}"
173        #puts e.backtrace
174        #raise
175        if @@ops_that_create_dirs.include?(inst.op)
176          @dir_list << nil
177        end
178
179        inst.push('DIRECTORY_ERROR')
180      end
181    end
182  end
183end
184
185