-
Notifications
You must be signed in to change notification settings - Fork 3.2k
/
test_determinism.rb
123 lines (106 loc) · 2.55 KB
/
test_determinism.rb
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
# frozen_string_literal: true
require_relative 'test_helper'
# rubocop:disable Security/Eval,Style/EvalWithLocation
class TestDeterminism < Test::Unit::TestCase
def setup
@all_methods = all_methods.freeze
@first_run = []
end
def test_determinism
Faker::Config.random = Random.new(42)
@all_methods.each_index do |index|
store_result @all_methods[index]
end
@first_run.freeze
Faker::Config.random = Random.new(42)
@all_methods.each_index do |index|
assert deterministic_random? @first_run[index], @all_methods[index]
end
end
def test_thread_safety
expected_values = 2.times.map do |index|
Faker::Config.random = Random.new(index)
Faker::Number.digit
end
threads = expected_values.each_with_index.map do |expected_value, index|
Thread.new do
100_000.times.each do
Faker::Config.random = Random.new(index)
output = Faker::Number.digit
assert_equal output, expected_value
end
end
end
threads.each(&:join)
end
private
def deterministic_random?(first, method_name)
second = eval(method_name)
(first == second) || raise(
"#{method_name} has an entropy leak; use \"Faker::Config.random.rand\" or \"Array#sample(random: Faker::Config.random)\". Method to lookup for: sample, shuffle, rand"
)
end
def store_result(method_name)
@first_run << eval(method_name)
rescue StandardError => e
raise %(#{method_name} raised "#{e}")
end
def all_methods
subclasses.map do |subclass|
subclass_methods(subclass).flatten
end.flatten.sort
end
def subclasses
Faker.constants.delete_if do |subclass|
skipped_classes.include?(subclass)
end.sort
end
def subclass_methods(subclass)
eval("Faker::#{subclass}.public_methods(false) - Faker::Base.public_methods(false)").sort.map do |method|
"Faker::#{subclass}.#{method}"
end.sort
end
def skipped_classes
%i[
Bank
Base
Base58
Books
Cat
Char
ChileRut
CLI
Config
Creature
Date
Deprecator
Dog
DragonBall
Dota
ElderScrolls
Fallout
Games
GamesHalfLife
HeroesOfTheStorm
Internet
JapaneseMedia
LeagueOfLegends
Locations
Movies
Myst
Overwatch
OnePiece
Pokemon
Religion
Sports
SwordArtOnline
TvShows
Time
VERSION
Witcher
WorldOfWarcraft
Zelda
]
end
end
# rubocop:enable Security/Eval,Style/EvalWithLocation