Rubyでランダムにディレクトリの中身を書き換えるライブラリを作った

Groongaのデータベース肥大化をテストしようと思って、

  1. テスト用のGroongaを仮想環境にインストール
  2. Milkodeデータベースを作成 (milk init)
  3. 適当なソースコードをデータベースに追加(milk add)
  4. ディレクトリの中身を更新、追加、削除で適当に書き換える
  5. データベースを更新(milk update)
  6. 4, 5 を任意の回数繰り返す

という方法でやろうと思ったのですが、4のディレクトリの中身を書き換えるライブラリが見つからなかったので作成しました。

※ 思いついてから動かすまで1時間位で出来た、Ruby素晴らしい。

使い方

ディレクトリを指定して生成し、メソッドを呼ぶだけです

# 書き換えるディレクトリを指定
rdm = RandomDirectoryManipulator.new('dir')

# 追加、更新、削除を1000回繰り返す
rdm.add_remove_update(1000)

乱数の種やデバッグ表示を表示することが出来ます。乱数の種を固定すると、書き換えるディレクトリの内容が同じであれば、常に同じ書き換え処理を行うことが出来ます。

# 乱数の種 = 1 で実行
rdm = RandomDirectoryManipulator.new('dir', {seed: 1})

# デバッグ表示ON
rdm = RandomDirectoryManipulator.new('dir', {verbose: true})

実行結果

verbose=true, 10回繰り返しだと以下のようになります。

rdm = RandomDirectoryManipulator.new('dir', {verbose: true})
rdm.add_remove_update(10)
$ ruby ./sample.rb
U dir/ext/tk/lib/tkextlib/iwidgets/dialog.rb
U dir/ext/stringio/stringio.c
A dir/test/json/fixtures/pass1.json-random_directory_manipulator
R dir/benchmark/other-lang/fib.pl
U dir/tool/change_maker.rb
U dir/test/ruby/test_complex.rb-random_directory_manipulator
U dir/ext/tk/sample/demos-jp/ctext.rb
U dir/test/open-uri/test_open-uri.rb-random_directory_manipulator

U(更新), A(追加), R(削除) です。 Uの場合はファイルの末尾にrandom_directory_manipulatorを追記します。 Aの場合はファイル名に-random_directory_manipulatorを付けてコピーします。 Rはそのままファイルを削除します。

更新(80%)、追加(10%)、削除(10%) なのでファイル数はあまり変わらずに少しずつファイルの総容量が増えていくイメージです。ここの確率をいじると色々とエミュレーション出来るかもしれません。

ソースコード

ongaeshi/groonga-broated-test/Rakefile に実体があります。

class RandomDirectoryManipulator
  def initialize(dir, options = {})
    @dir = dir
    @options = options
    srand(@options[:seed]) if @options[:seed]
  end

  def add_remove_update(num)
    files = dir_files
    
    (1..num).each do
      file = files[rand(files.size)]

      case rand(10)
      when 0
        # Add
        dst = file + "-random_directory_manipulator"
        message "A #{dst}"
        FileUtils.cp(file, dst)
        files << dst
      when 1
        # Remove
        message "R #{file}"
        File.unlink(file)
        files.delete(file)
      else
        # Update
        message "U #{file}"
        open(file, "a") do |io|
          io.puts "random_directory_manipulator"
        end
      end
    end
  end

  def dir_files
    files = []
    
    Find.find(@dir) do |path|
      next if FileTest.directory?(path)
      files << path
    end

    files
  end

  def message(str)
    puts str if @options[:verbose]
  end
end