theft 0.0.1.placeholder → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +13 -5
- data/examples/stable_sort.rb +116 -0
- data/lib/theft.rb +90 -2
- data/lib/theft/version.rb +1 -1
- metadata +8 -7
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,15 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 | 
            -
             | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
             | 
| 2 | 
            +
            !binary "U0hBMQ==":
         | 
| 3 | 
            +
              metadata.gz: !binary |-
         | 
| 4 | 
            +
                YjBiNDUwODExOTFjYjM4NzVhYmRjZGM2OWFjNmVlOTE5NGU4YzM1Zg==
         | 
| 5 | 
            +
              data.tar.gz: !binary |-
         | 
| 6 | 
            +
                YzRmM2UyZmJkNWUwOTUxZmQwYzY2MzM2OTA1NTZlNjNlNTQ2NDU3YQ==
         | 
| 5 7 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
             | 
| 8 | 
            +
              metadata.gz: !binary |-
         | 
| 9 | 
            +
                NTY0NDg0N2I5ZGRjYTIwYjc1OWJiN2QzMzhiYmUzZjhlYmZkNWQ5MmYyODY0
         | 
| 10 | 
            +
                ZTMyMDQ1ODczN2U5Zjk1ZjE3MDJkOGRmOTg1MGI1M2I1ZTk3ZjMzZGMyYWY3
         | 
| 11 | 
            +
                YTNiNzVlZDUyYWIxMmUwZDQ0OWE2YjBkYjFkZDNlMjk3MjU4ZGY=
         | 
| 12 | 
            +
              data.tar.gz: !binary |-
         | 
| 13 | 
            +
                NTcxMGM5NzU4Y2NiNTdjODhmZGUwNzVkZTdlOTg1MjEzYTFhMjBiZTY4Njlh
         | 
| 14 | 
            +
                YzJjMjY0NDFjMWIxODdjYTlmZjFjY2UzNTJhNWIzYmE0NjQ1ODkzNjFlNDgy
         | 
| 15 | 
            +
                MTA5NGE1YzRhMzg2YjZmNTAzMGE3MmNjMGU3ZmFmNmU5MTE3Mzg=
         | 
| @@ -0,0 +1,116 @@ | |
| 1 | 
            +
            require 'digest'
         | 
| 2 | 
            +
            require_relative '../lib/theft'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            class BoxedFixNum
         | 
| 5 | 
            +
              attr_reader :n
         | 
| 6 | 
            +
              def initialize(n)
         | 
| 7 | 
            +
                @n = n
         | 
| 8 | 
            +
              end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              def <=>(other)
         | 
| 11 | 
            +
                @n <=> other.n
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
            end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            class BoxedFixNumArgDescriptor
         | 
| 16 | 
            +
              class << self
         | 
| 17 | 
            +
                def setup(rng, args={})
         | 
| 18 | 
            +
                  [].tap do |ar|
         | 
| 19 | 
            +
                    rng.rand(0..100).times do |i|
         | 
| 20 | 
            +
                      ar << b(rng.rand(0..100))
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                def teardown(list)
         | 
| 26 | 
            +
                  # do stuff
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                def hash(list)
         | 
| 30 | 
            +
                  Digest::MD5.new.digest to_s(list)
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                def to_s(list)
         | 
| 34 | 
            +
                  "[#{list.size}] " + list.map(&:n).map(&:to_s).join(",")
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                def shrink(list, tactic)
         | 
| 38 | 
            +
                  size = list.size
         | 
| 39 | 
            +
                  if size < 2
         | 
| 40 | 
            +
                    puts "#shrink .. dead end"
         | 
| 41 | 
            +
                    return :dead_end 
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
                  case tactic
         | 
| 44 | 
            +
                  when 0
         | 
| 45 | 
            +
                    return list[0..size/2]
         | 
| 46 | 
            +
                  when 1
         | 
| 47 | 
            +
                    return list[size/2..-1]
         | 
| 48 | 
            +
                  when 2
         | 
| 49 | 
            +
                    return list[1..-1]
         | 
| 50 | 
            +
                  when 3
         | 
| 51 | 
            +
                    return list[0..-2]
         | 
| 52 | 
            +
                  when 4
         | 
| 53 | 
            +
                    mid = size/2
         | 
| 54 | 
            +
                    return list[0..mid-1] + list[mid+1..-1]
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  return :tried_all_tactics
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                def b(n)
         | 
| 61 | 
            +
                  BoxedFixNum.new n
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
              end
         | 
| 64 | 
            +
            end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
            class Sorter
         | 
| 67 | 
            +
              def self.sort(items)
         | 
| 68 | 
            +
                unstable items
         | 
| 69 | 
            +
                # stable items
         | 
| 70 | 
            +
              end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
              def self.stable(items)
         | 
| 73 | 
            +
                items.sort_by.with_index { |x, idx| [x, idx] }
         | 
| 74 | 
            +
              end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
              def self.unstable(items)
         | 
| 77 | 
            +
                items.sort
         | 
| 78 | 
            +
              end
         | 
| 79 | 
            +
            end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
            if $0 == __FILE__
         | 
| 82 | 
            +
              t = Theft::Runner.new autosize: true
         | 
| 83 | 
            +
             | 
| 84 | 
            +
              property_sorting_should_be_stable = lambda do |generated_arg| # , other_generated_arg, etc 
         | 
| 85 | 
            +
                sorted = Sorter.sort(generated_arg)
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                sorted.chunk{|item| item.n}.each do |n, items|
         | 
| 88 | 
            +
                  if items.size > 1
         | 
| 89 | 
            +
                    # lazily spot check
         | 
| 90 | 
            +
                    first = items.first
         | 
| 91 | 
            +
                    last = items.last
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                    if generated_arg.index(first) > generated_arg.index(last)
         | 
| 94 | 
            +
                      return :fail unless sorted.index(first) > sorted.index(last)
         | 
| 95 | 
            +
                    else
         | 
| 96 | 
            +
                      return :fail unless sorted.index(first) < sorted.index(last)
         | 
| 97 | 
            +
                    end
         | 
| 98 | 
            +
                  end
         | 
| 99 | 
            +
                end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                :pass
         | 
| 102 | 
            +
              end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
              config = {
         | 
| 105 | 
            +
                description: "sorting should be stable",
         | 
| 106 | 
            +
                property: property_sorting_should_be_stable,
         | 
| 107 | 
            +
                arg_descriptors: [BoxedFixNumArgDescriptor],
         | 
| 108 | 
            +
                trials: 3,
         | 
| 109 | 
            +
                # must_have_seeds: [],
         | 
| 110 | 
            +
              #  seed: 1408486348,
         | 
| 111 | 
            +
                progress: lambda{|trial_num, args, status| STDOUT.write '.' if trial_num % 2 == 0 },
         | 
| 112 | 
            +
                env: :whatevs
         | 
| 113 | 
            +
              }
         | 
| 114 | 
            +
             | 
| 115 | 
            +
              t.run config
         | 
| 116 | 
            +
            end
         | 
    
        data/lib/theft.rb
    CHANGED
    
    | @@ -1,5 +1,93 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            require_relative "theft/version"
         | 
| 2 | 
            +
            require 'set'
         | 
| 2 3 |  | 
| 3 4 | 
             
            module Theft
         | 
| 4 | 
            -
               | 
| 5 | 
            +
              class Runner
         | 
| 6 | 
            +
                def initialize(args={})
         | 
| 7 | 
            +
                  @auto_size = args.has_key?(:auto_size) ? args[:auto_size] : true
         | 
| 8 | 
            +
                  @evaluated_inputs = Set.new
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def run(config)
         | 
| 12 | 
            +
                  @seed = config[:seed] || Time.now.to_i
         | 
| 13 | 
            +
                  @rng = Random.new(@seed)
         | 
| 14 | 
            +
                  puts "seed: #{@seed}"
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  @trials = config[:trials] || 100
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  property = config[:property]
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  fails = 0
         | 
| 21 | 
            +
                  passes = 0
         | 
| 22 | 
            +
                  @trials.times do |trial_num|
         | 
| 23 | 
            +
                    @descriptors = config[:arg_descriptors]
         | 
| 24 | 
            +
                    args = @descriptors.map do |desc|
         | 
| 25 | 
            +
                      desc.setup(@rng, config[:env])
         | 
| 26 | 
            +
                    end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                    unless has_been_tried?(@descriptors, args)
         | 
| 29 | 
            +
                      result = property.call(*args)
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                      case result
         | 
| 32 | 
            +
                      when :fail
         | 
| 33 | 
            +
                        fails += 1
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                        args = try_to_shrink(args, property)
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                        puts "failed on trial: #{trial_num}"
         | 
| 38 | 
            +
                        @descriptors.each.with_index do |desc, i|
         | 
| 39 | 
            +
                          puts desc.to_s(args[i])
         | 
| 40 | 
            +
                        end
         | 
| 41 | 
            +
                      when :pass
         | 
| 42 | 
            +
                        passes += 1
         | 
| 43 | 
            +
                      end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                      config[:progress].call(trial_num, args, :passing_for_now)
         | 
| 46 | 
            +
                    end
         | 
| 47 | 
            +
                  end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                  puts
         | 
| 50 | 
            +
                  puts "FAILS: #{fails}"
         | 
| 51 | 
            +
                  puts "PASSES: #{passes}"
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                def try_to_shrink(args, property)
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                  @descriptors.each.with_index do |desc, i|
         | 
| 57 | 
            +
                    tactic_counter = 0
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                    shrink_result = desc.shrink(args[i], tactic_counter) 
         | 
| 60 | 
            +
                    while shrink_result != :tried_all_tactics
         | 
| 61 | 
            +
                      #puts desc.to_s(args[i])
         | 
| 62 | 
            +
                      if shrink_result == :dead_end
         | 
| 63 | 
            +
                        tactic_counter += 1
         | 
| 64 | 
            +
                      else
         | 
| 65 | 
            +
                        copy_args = args.dup
         | 
| 66 | 
            +
                        copy_args[i] = shrink_result
         | 
| 67 | 
            +
                        result = property.call(*copy_args)
         | 
| 68 | 
            +
                        if result == :fail
         | 
| 69 | 
            +
                          args[i] = shrink_result 
         | 
| 70 | 
            +
                        else
         | 
| 71 | 
            +
                          tactic_counter += 1
         | 
| 72 | 
            +
                        end
         | 
| 73 | 
            +
                      end
         | 
| 74 | 
            +
                      shrink_result = desc.shrink(args[i], tactic_counter) 
         | 
| 75 | 
            +
                    end
         | 
| 76 | 
            +
                  end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                  args
         | 
| 79 | 
            +
                end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                def has_been_tried?(descriptors, args)
         | 
| 82 | 
            +
                  @evaluated_inputs.include? hash_inputs(descriptors, args)
         | 
| 83 | 
            +
                end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                def hash_inputs(descriptors, args)
         | 
| 86 | 
            +
                  hashes = descriptors.map.with_index do |desc, i|
         | 
| 87 | 
            +
                    desc.hash(args[i])
         | 
| 88 | 
            +
                  end
         | 
| 89 | 
            +
                  hash = Digest::MD5.new.digest hashes.join(",")
         | 
| 90 | 
            +
                  @evaluated_inputs << hash
         | 
| 91 | 
            +
                end
         | 
| 92 | 
            +
              end
         | 
| 5 93 | 
             
            end
         | 
    
        data/lib/theft/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: theft
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.0.1 | 
| 4 | 
            +
              version: 0.0.1
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Scott Vokes
         | 
| @@ -9,7 +9,7 @@ authors: | |
| 9 9 | 
             
            autorequire: 
         | 
| 10 10 | 
             
            bindir: bin
         | 
| 11 11 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date: 2014- | 
| 12 | 
            +
            date: 2014-09-02 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies:
         | 
| 14 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 15 | 
             
              name: bundler
         | 
| @@ -29,14 +29,14 @@ dependencies: | |
| 29 29 | 
             
              name: rake
         | 
| 30 30 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 31 31 | 
             
                requirements:
         | 
| 32 | 
            -
                - - '>='
         | 
| 32 | 
            +
                - - ! '>='
         | 
| 33 33 | 
             
                  - !ruby/object:Gem::Version
         | 
| 34 34 | 
             
                    version: '0'
         | 
| 35 35 | 
             
              type: :development
         | 
| 36 36 | 
             
              prerelease: false
         | 
| 37 37 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 38 38 | 
             
                requirements:
         | 
| 39 | 
            -
                - - '>='
         | 
| 39 | 
            +
                - - ! '>='
         | 
| 40 40 | 
             
                  - !ruby/object:Gem::Version
         | 
| 41 41 | 
             
                    version: '0'
         | 
| 42 42 | 
             
            description: Ruby port of the theft C library.
         | 
| @@ -52,6 +52,7 @@ files: | |
| 52 52 | 
             
            - LICENSE.txt
         | 
| 53 53 | 
             
            - README.md
         | 
| 54 54 | 
             
            - Rakefile
         | 
| 55 | 
            +
            - examples/stable_sort.rb
         | 
| 55 56 | 
             
            - lib/theft.rb
         | 
| 56 57 | 
             
            - lib/theft/version.rb
         | 
| 57 58 | 
             
            - theft.gemspec
         | 
| @@ -65,14 +66,14 @@ require_paths: | |
| 65 66 | 
             
            - lib
         | 
| 66 67 | 
             
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 67 68 | 
             
              requirements:
         | 
| 68 | 
            -
              - - '>='
         | 
| 69 | 
            +
              - - ! '>='
         | 
| 69 70 | 
             
                - !ruby/object:Gem::Version
         | 
| 70 71 | 
             
                  version: '0'
         | 
| 71 72 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 72 73 | 
             
              requirements:
         | 
| 73 | 
            -
              - - ' | 
| 74 | 
            +
              - - ! '>='
         | 
| 74 75 | 
             
                - !ruby/object:Gem::Version
         | 
| 75 | 
            -
                  version:  | 
| 76 | 
            +
                  version: '0'
         | 
| 76 77 | 
             
            requirements: []
         | 
| 77 78 | 
             
            rubyforge_project: 
         | 
| 78 79 | 
             
            rubygems_version: 2.2.2
         |