├── CODEOWNERS ├── .github_changelog_generator ├── lib ├── beaker-qa-i18n │ ├── configure_locale.rb │ ├── format_validator.rb │ ├── version.rb │ └── i18n_string_generator.rb └── beaker-qa-i18n.rb ├── spec ├── spec_helper.rb ├── beaker-qa-i18n │ ├── helpers_spec.rb │ └── i18n_string_generator_spec.rb └── helpers.rb ├── acceptance ├── config │ └── nodes │ │ └── vagrant-ubuntu-1404.yml └── tests │ └── first.rb ├── .github ├── dependabot.yml └── workflows │ ├── security.yml │ └── release.yml ├── release-prep ├── Gemfile ├── README.md ├── beaker-qa-i18n.gemspec ├── CHANGELOG.md ├── Rakefile ├── Gemfile.lock └── LICENSE /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @puppetlabs/release-engineering 2 | 3 | -------------------------------------------------------------------------------- /.github_changelog_generator: -------------------------------------------------------------------------------- 1 | project=beaker-qa-i18n 2 | user=puppetlabs 3 | exclude_labels=maintenance 4 | -------------------------------------------------------------------------------- /lib/beaker-qa-i18n/configure_locale.rb: -------------------------------------------------------------------------------- 1 | module Beaker 2 | module DSL 3 | module Helpers 4 | module ConfigureLocale 5 | end 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/beaker-qa-i18n/format_validator.rb: -------------------------------------------------------------------------------- 1 | module Beaker 2 | module DSL 3 | module Helpers 4 | module FormatValidator 5 | end 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/beaker-qa-i18n/version.rb: -------------------------------------------------------------------------------- 1 | module Beaker 2 | module DSL 3 | module Helpers 4 | module BeakerQaI18n 5 | module Version 6 | STRING = '1.0.0' 7 | end 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'simplecov' 2 | require 'beaker-qa-i18n' 3 | require 'helpers' 4 | 5 | require 'rspec/its' 6 | 7 | RSpec.configure do |config| 8 | config.include TestFileHelpers 9 | config.include HostHelpers 10 | end 11 | -------------------------------------------------------------------------------- /acceptance/config/nodes/vagrant-ubuntu-1404.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | ubuntu-server-1404-x64: 3 | roles: 4 | - master 5 | platform: ubuntu-14.04-amd64 6 | box: puppetlabs/ubuntu-14.04-64-nocm 7 | box_url: https://vagrantcloud.com/puppetlabs/boxes/ubuntu-14.04-64-nocm 8 | hypervisor: vagrant 9 | -------------------------------------------------------------------------------- /acceptance/tests/first.rb: -------------------------------------------------------------------------------- 1 | 2 | # Acceptance level testing goes into files in the tests directory like this one, 3 | # Each file corresponding to a new test made up of individual testing steps 4 | test_name "Template Acceptance Test Example" 5 | 6 | step "Fail fast!" 7 | 8 | fail_test("There are no acceptance tests yet!") -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | open-pull-requests-limit: 10 8 | 9 | - package-ecosystem: bundler 10 | directory: "/" 11 | schedule: 12 | interval: "weekly" 13 | open-pull-requests-limit: 10 14 | -------------------------------------------------------------------------------- /lib/beaker-qa-i18n.rb: -------------------------------------------------------------------------------- 1 | module Beaker 2 | module DSL 3 | module Helpers 4 | module BeakerQaI18n 5 | require 'stringify-hash' 6 | require 'beaker-qa-i18n/i18n_string_generator' 7 | require 'beaker-qa-i18n/version' 8 | Beaker::TestCase.send(:include, I18nStringGenerator) 9 | end 10 | end 11 | end 12 | end 13 | Beaker::DSL.register(Beaker::DSL::Helpers::BeakerQaI18n) 14 | -------------------------------------------------------------------------------- /spec/beaker-qa-i18n/helpers_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | class ClassMixedWithDSLHelpers 4 | include Beaker::DSL::Helpers::BeakerQaI18n 5 | 6 | def logger 7 | RSpec::Mocks::Double.new('logger').as_null_object 8 | end 9 | 10 | end 11 | 12 | describe ClassMixedWithDSLHelpers do 13 | 14 | describe 'release conditions' do 15 | 16 | it 'has updated the version number from the original template' do 17 | expect( Beaker::DSL::Helpers::BeakerQaI18n::Version::STRING ).to_not be === '0.0.1rc0' 18 | end 19 | 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /release-prep: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # bundle install 4 | docker run -it --rm \ 5 | -v $(pwd):/app \ 6 | ruby:3.1-slim-bullseye \ 7 | /bin/bash -c 'apt-get update -qq && apt-get install -y --no-install-recommends build-essential git make && cd /app && gem install bundler && bundle install --jobs 3; echo "LOCK_FILE_UPDATE_EXIT_CODE=$?"' 8 | 9 | # Update Changelog 10 | docker run -it --rm -e CHANGELOG_GITHUB_TOKEN -v $(pwd):/usr/local/src/your-app \ 11 | githubchangeloggenerator/github-changelog-generator:1.16.2 \ 12 | github_changelog_generator --future-release $(grep STRING lib/beaker-qa-i18n/version.rb |rev |cut -d "'" -f2 |rev) 13 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source ENV['GEM_SOURCE'] || "https://rubygems.org" 2 | 3 | gemspec 4 | 5 | 6 | def location_for(place, fake_version = nil) 7 | if place =~ /^(git:[^#]*)#(.*)/ 8 | [fake_version, { :git => $1, :branch => $2, :require => false }].compact 9 | elsif place =~ /^file:\/\/(.*)/ 10 | ['>= 0', { :path => File.expand_path($1), :require => false }] 11 | else 12 | [place, { :require => false }] 13 | end 14 | end 15 | 16 | 17 | # We don't put beaker in as a test dependency because we 18 | # don't want to create a transitive dependency 19 | group :acceptance_testing do 20 | gem "beaker", *location_for(ENV['BEAKER_VERSION'] || '~> 2.0') 21 | end 22 | 23 | 24 | if File.exists? "#{__FILE__}.local" 25 | eval(File.read("#{__FILE__}.local"), binding) 26 | end 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # beaker-qa-i18n helper 2 | 3 | - [beaker-qa-i18n helper](#beaker-qa-i18n-helper) 4 | - [Examples](#examples) 5 | - [Releasing](#releasing) 6 | 7 | Methods to assist in i18n testing 8 | 9 | ## Examples 10 | 11 | Example: iterates through array of strings of length 10, testing all special characters according to the block 12 | 13 | ```Ruby 14 | test_i18n_strings(10) { |test_string| 15 | # Enter data 16 | create_user("User#{test_string}) 17 | # Validate data 18 | verify_user_name_exists("User#{test_string}") 19 | } 20 | ``` 21 | 22 | ## Releasing 23 | 24 | Open a release prep PR and run the release action: 25 | 26 | 1. Bump the "version" parameter in `lib/beaker-qa-i18n/version.rb` appropriately based merged pull requests since the last release. 27 | 2. Run `./release-prep` to update `Gemfile.lock` and `CHANGELOG.md`. 28 | 3. Commit and push changes to a new branch, then open a pull request against `main` and be sure to add the "maintenance" label. 29 | 4. After the pull request is approved and merged, then navigate to Actions --> Release Action --> run workflow --> Branch: main --> Run workflow. 30 | -------------------------------------------------------------------------------- /.github/workflows/security.yml: -------------------------------------------------------------------------------- 1 | name: Security 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | scan: 10 | name: Mend Scanning 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: checkout repo content 14 | uses: actions/checkout@v4 15 | with: 16 | fetch-depth: 1 17 | - name: setup ruby 18 | uses: ruby/setup-ruby@v1 19 | with: 20 | ruby-version: 2.7 21 | # setup a package lock if one doesn't exist, otherwise do nothing 22 | - name: check lock 23 | run: '[ -f "Gemfile.lock" ] && echo "package lock file exists, skipping" || bundle lock' 24 | # install java 25 | - uses: actions/setup-java@v3 26 | with: 27 | distribution: 'temurin' # See 'Supported distributions' for available options 28 | java-version: '17' 29 | # download mend 30 | - name: download_mend 31 | run: curl -o wss-unified-agent.jar https://unified-agent.s3.amazonaws.com/wss-unified-agent.jar 32 | - name: run mend 33 | run: java -jar wss-unified-agent.jar 34 | env: 35 | WS_APIKEY: ${{ secrets.MEND_API_KEY }} 36 | WS_WSS_URL: https://saas-eu.whitesourcesoftware.com/agent 37 | WS_USERKEY: ${{ secrets.MEND_TOKEN }} 38 | WS_PRODUCTNAME: RE 39 | WS_PROJECTNAME: ${{ github.event.repository.name }} 40 | -------------------------------------------------------------------------------- /beaker-qa-i18n.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $LOAD_PATH.unshift File.expand_path("../lib", __FILE__) 3 | require 'beaker-qa-i18n/version' 4 | 5 | Gem::Specification.new do |s| 6 | s.name = "beaker-qa-i18n" 7 | s.version = Beaker::DSL::Helpers::BeakerQaI18n::Version::STRING 8 | s.authors = ["Puppetlabs"] 9 | s.email = ["qe-team@puppetlabs.com"] 10 | s.homepage = "https://github.com/puppetlabs/beaker-qa-i18n" 11 | s.summary = %q{Beaker DSL Extension Helpers to assist in testing i18n!} 12 | s.description = %q{For use for the Beaker acceptance testing tool} 13 | s.license = 'Apache2' 14 | 15 | s.files = `git ls-files`.split("\n") 16 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 17 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 18 | s.require_paths = ["lib"] 19 | 20 | # Testing dependencies 21 | s.add_development_dependency 'rspec', '~> 3.0' 22 | s.add_development_dependency 'rspec-its' 23 | s.add_development_dependency 'fakefs', '~> 0.6' 24 | s.add_development_dependency 'rake', '~> 13.0' 25 | s.add_development_dependency 'simplecov' 26 | s.add_development_dependency 'pry', '~> 0.10' 27 | 28 | # Documentation dependencies 29 | s.add_development_dependency 'yard' 30 | s.add_development_dependency 'markdown' 31 | s.add_development_dependency 'thin' 32 | 33 | # Run time dependencies 34 | s.add_runtime_dependency 'stringify-hash', '~> 0.0.0' 35 | 36 | end 37 | 38 | -------------------------------------------------------------------------------- /spec/beaker-qa-i18n/i18n_string_generator_spec.rb: -------------------------------------------------------------------------------- 1 | class ClassMixedWithDSLHelpers 2 | include Beaker::DSL::Helpers::BeakerQaI18n::I18nStringGenerator 3 | 4 | def logger 5 | RSpec::Mocks::Double.new('logger').as_null_object 6 | end 7 | end 8 | 9 | describe ClassMixedWithDSLHelpers do 10 | subject { ClassMixedWithDSLHelpers.new } 11 | 12 | describe '.test_i18n_strings' do 13 | 14 | it 'should execute code in block for each string' do 15 | (1..30).to_a.each { |str_length| 16 | subject.test_i18n_strings(str_length) { |int_string| 17 | puts "'#{int_string}'" 18 | expect(int_string.length).to eq(str_length) 19 | } 20 | } 21 | end 22 | end 23 | describe '.random_multi_lang' do 24 | it 'should return the correct number characters for random multi' do 25 | (1..30).to_a.each { |str_length| 26 | int_string = subject.random_multi_lang(str_length) 27 | puts "'#{int_string}'" 28 | expect(int_string.length).to eq(str_length) 29 | } 30 | end 31 | 32 | it 'should return expected string if seed is used' do 33 | int_string = subject.random_multi_lang(30, 90856893022021252569391894093740379386) 34 | puts "'#{int_string}'" 35 | expect(int_string).to eq('mNPEhDBOE\YF三Ce丒PE业E丁s且E_^丟ZeÄ') 36 | end 37 | end 38 | 39 | describe '.random_multi_lang_sentence' do 40 | it 'should return the correct number of characters for random multi sentence' do 41 | (2..30).to_a.each { |str_length| 42 | int_string = subject.random_multi_lang_sentence(str_length) 43 | puts "'#{int_string}'" 44 | expect(int_string.length).to eq(str_length) 45 | } 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [1.0.0](https://github.com/puppetlabs/beaker-qa-i18n/tree/1.0.0) (2023-06-28) 4 | 5 | [Full Changelog](https://github.com/puppetlabs/beaker-qa-i18n/compare/0.0.5...1.0.0) 6 | 7 | **Merged pull requests:** 8 | 9 | - \(RE-15616\) Add standard release workflow [\#21](https://github.com/puppetlabs/beaker-qa-i18n/pull/21) ([yachub](https://github.com/yachub)) 10 | - \(RE-15111\) Add Mend Scanning [\#15](https://github.com/puppetlabs/beaker-qa-i18n/pull/15) ([yachub](https://github.com/yachub)) 11 | - \(RE-14811\) Add RE as codeowners [\#12](https://github.com/puppetlabs/beaker-qa-i18n/pull/12) ([yachub](https://github.com/yachub)) 12 | - Update rake requirement from ~\> 10.1 to ~\> 13.0 [\#6](https://github.com/puppetlabs/beaker-qa-i18n/pull/6) ([dependabot[bot]](https://github.com/apps/dependabot)) 13 | - Add Dependabot to keep thins up to date [\#4](https://github.com/puppetlabs/beaker-qa-i18n/pull/4) ([genebean](https://github.com/genebean)) 14 | 15 | ## [0.0.5](https://github.com/puppetlabs/beaker-qa-i18n/tree/0.0.5) (2016-09-13) 16 | 17 | [Full Changelog](https://github.com/puppetlabs/beaker-qa-i18n/compare/0.0.4...0.0.5) 18 | 19 | **Merged pull requests:** 20 | 21 | - \(maint\) Fix get\_i18n\_string to return string [\#2](https://github.com/puppetlabs/beaker-qa-i18n/pull/2) ([samwoods1](https://github.com/samwoods1)) 22 | 23 | ## [0.0.4](https://github.com/puppetlabs/beaker-qa-i18n/tree/0.0.4) (2016-08-30) 24 | 25 | [Full Changelog](https://github.com/puppetlabs/beaker-qa-i18n/compare/0dae0788ef864004f1df8c4687a3befce0affba4...0.0.4) 26 | 27 | **Merged pull requests:** 28 | 29 | - \(QA-2617\) Prep for generating gem [\#1](https://github.com/puppetlabs/beaker-qa-i18n/pull/1) ([samwoods1](https://github.com/samwoods1)) 30 | 31 | 32 | 33 | \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* 34 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release Gem 2 | 3 | on: workflow_dispatch 4 | 5 | jobs: 6 | release: 7 | runs-on: ubuntu-latest 8 | if: github.repository == 'puppetlabs/beaker-qa-i18n' 9 | steps: 10 | - uses: actions/checkout@v4 11 | 12 | - name: Get Current Version 13 | uses: actions/github-script@v6 14 | id: cv 15 | with: 16 | script: | 17 | const { data: response } = await github.rest.repos.getLatestRelease({ 18 | owner: context.repo.owner, 19 | repo: context.repo.repo, 20 | }) 21 | console.log(`The latest release is ${response.tag_name}`) 22 | return response.tag_name 23 | result-encoding: string 24 | 25 | - name: Get Next Version 26 | id: nv 27 | run: | 28 | version=$(grep STRING lib/beaker-qa-i18n/version.rb |rev |cut -d "'" -f2 |rev) 29 | echo "version=$version" >> $GITHUB_OUTPUT 30 | echo "Found version $version from lib/beaker-qa-i18n/version.rb" 31 | 32 | - name: Generate Changelog 33 | uses: docker://githubchangeloggenerator/github-changelog-generator:1.16.2 34 | with: 35 | args: >- 36 | --future-release ${{ steps.nv.outputs.version }} 37 | env: 38 | CHANGELOG_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 39 | 40 | - name: Validate Changelog 41 | run : | 42 | set -e 43 | if [[ -n $(git status --porcelain) ]]; then 44 | echo "Here is the current git status:" 45 | git status 46 | echo 47 | echo "The following changes were detected:" 48 | git --no-pager diff 49 | echo "Uncommitted PRs found in the changelog. Please submit a release prep PR of changes after running `./update-changelog`" 50 | exit 1 51 | fi 52 | 53 | - name: Generate Release Notes 54 | uses: docker://githubchangeloggenerator/github-changelog-generator:1.16.2 55 | with: 56 | args: >- 57 | --since-tag ${{ steps.cv.outputs.result }} 58 | --future-release ${{ steps.nv.outputs.version }} 59 | --output release-notes.md 60 | env: 61 | CHANGELOG_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 62 | 63 | - name: Tag Release 64 | uses: ncipollo/release-action@v1 65 | with: 66 | tag: ${{ steps.nv.outputs.version }} 67 | token: ${{ secrets.GITHUB_TOKEN }} 68 | bodyfile: release-notes.md 69 | draft: false 70 | prerelease: false 71 | 72 | - name: Install Ruby 3.2 73 | uses: ruby/setup-ruby@v1 74 | with: 75 | ruby-version: '3.2' 76 | 77 | - name: Build gem 78 | run: gem build *.gemspec 79 | 80 | - name: Publish gem 81 | run: | 82 | mkdir -p $HOME/.gem 83 | touch $HOME/.gem/credentials 84 | chmod 0600 $HOME/.gem/credentials 85 | printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials 86 | gem push *.gem 87 | env: 88 | GEM_HOST_API_KEY: '${{ secrets.RUBYGEMS_AUTH_TOKEN }}' 89 | -------------------------------------------------------------------------------- /spec/helpers.rb: -------------------------------------------------------------------------------- 1 | module TestFileHelpers 2 | def create_files file_array 3 | file_array.each do |f| 4 | FileUtils.mkdir_p File.dirname(f) 5 | FileUtils.touch f 6 | end 7 | end 8 | 9 | def fog_file_contents 10 | { :default => { :aws_access_key_id => "IMANACCESSKEY", 11 | :aws_secret_access_key => "supersekritkey", 12 | :aix_hypervisor_server => "aix_hypervisor.labs.net", 13 | :aix_hypervisor_username => "aixer", 14 | :aix_hypervisor_keyfile => "/Users/user/.ssh/id_rsa-acceptance", 15 | :solaris_hypervisor_server => "solaris_hypervisor.labs.net", 16 | :solaris_hypervisor_username => "harness", 17 | :solaris_hypervisor_keyfile => "/Users/user/.ssh/id_rsa-old.private", 18 | :solaris_hypervisor_vmpath => "rpoooool/zs", 19 | :solaris_hypervisor_snappaths => ["rpoooool/USER/z0"], 20 | :vsphere_server => "vsphere.labs.net", 21 | :vsphere_username => "vsphere@labs.com", 22 | :vsphere_password => "supersekritpassword"} } 23 | end 24 | 25 | end 26 | 27 | module HostHelpers 28 | HOST_DEFAULTS = { :platform => 'unix', 29 | :snapshot => 'pe', 30 | :box => 'box_name', 31 | :roles => ['agent'], 32 | :snapshot => 'snap', 33 | :ip => 'default.ip.address', 34 | :box => 'default_box_name', 35 | :box_url => 'http://default.box.url', 36 | } 37 | 38 | HOST_NAME = "vm%d" 39 | HOST_SNAPSHOT = "snapshot%d" 40 | HOST_IP = "ip.address.for.%s" 41 | HOST_BOX = "%s_of_my_box" 42 | HOST_BOX_URL = "http://address.for.my.box.%s" 43 | HOST_TEMPLATE = "%s_has_a_template" 44 | 45 | def logger 46 | double( 'logger' ).as_null_object 47 | end 48 | 49 | def make_opts 50 | opts = StringifyHash.new 51 | opts.merge( { :logger => logger, 52 | :host_config => 'sample.config', 53 | :type => nil, 54 | :pooling_api => 'http://vcloud.delivery.puppetlabs.net/', 55 | :datastore => 'instance0', 56 | :folder => 'Delivery/Quality Assurance/Staging/Dynamic', 57 | :resourcepool => 'delivery/Quality Assurance/Staging/Dynamic', 58 | :gce_project => 'beaker-compute', 59 | :gce_keyfile => '/path/to/keyfile.p12', 60 | :gce_password => 'notasecret', 61 | :gce_email => '12345678910@developer.gserviceaccount.com' } ) 62 | end 63 | 64 | def generate_result (name, opts ) 65 | result = double( 'result' ) 66 | stdout = opts.has_key?(:stdout) ? opts[:stdout] : name 67 | stderr = opts.has_key?(:stderr) ? opts[:stderr] : name 68 | exit_code = opts.has_key?(:exit_code) ? opts[:exit_code] : 0 69 | exit_code = [exit_code].flatten 70 | allow( result ).to receive( :stdout ).and_return( stdout ) 71 | allow( result ).to receive( :stderr ).and_return( stderr ) 72 | allow( result ).to receive( :exit_code ).and_return( *exit_code ) 73 | result 74 | end 75 | 76 | def make_host_opts name, opts 77 | make_opts.merge( { 'HOSTS' => { name => opts } } ).merge( opts ) 78 | end 79 | 80 | def make_host name, host_hash 81 | host_hash = StringifyHash.new.merge(HOST_DEFAULTS.merge(host_hash)) 82 | 83 | host = make_opts.merge(host_hash) 84 | 85 | allow(host).to receive( :name ).and_return( name ) 86 | allow(host).to receive( :to_s ).and_return( name ) 87 | allow(host).to receive( :exec ).and_return( generate_result( name, host_hash ) ) 88 | host 89 | end 90 | 91 | def make_hosts preset_opts = {}, amt = 3 92 | hosts = [] 93 | (1..amt).each do |num| 94 | name = HOST_NAME % num 95 | opts = { :snapshot => HOST_SNAPSHOT % num, 96 | :ip => HOST_IP % name, 97 | :template => HOST_TEMPLATE % name, 98 | :box => HOST_BOX % name, 99 | :box_url => HOST_BOX_URL % name }.merge( preset_opts ) 100 | hosts << make_host(name, opts) 101 | end 102 | hosts 103 | end 104 | 105 | def make_instance instance_data = {} 106 | OpenStruct.new instance_data 107 | end 108 | 109 | end 110 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rspec/core/rake_task' 2 | 3 | namespace :test do 4 | 5 | namespace :spec do 6 | 7 | desc "Run spec tests" 8 | RSpec::Core::RakeTask.new(:run) do |t| 9 | t.rspec_opts = ['--color'] 10 | t.pattern = 'spec/' 11 | end 12 | 13 | desc "Run spec tests with coverage" 14 | RSpec::Core::RakeTask.new(:coverage) do |t| 15 | ENV['BEAKER_TEMPLATE_COVERAGE'] = 'y' 16 | t.rspec_opts = ['--color'] 17 | t.pattern = 'spec/' 18 | end 19 | 20 | end 21 | 22 | namespace :acceptance do 23 | 24 | desc <<-EOS 25 | A quick acceptance test, named because it has no pre-suites to run 26 | EOS 27 | task :quick do 28 | 29 | sh("beaker", 30 | "--hosts", ENV['CONFIG'] || "acceptance/config/nodes/vagrant-ubuntu-1404.yml", 31 | "--tests", "acceptance/tests", 32 | "--log-level", "debug", 33 | "--keyfile", ENV['KEY'] || "#{ENV['HOME']}/.ssh/id_rsa") 34 | end 35 | 36 | end 37 | 38 | end 39 | 40 | # namespace-named default tasks. 41 | # these are the default tasks invoked when only the namespace is referenced. 42 | # they're needed because `task :default` in those blocks doesn't work as expected. 43 | task 'test:spec' => 'test:spec:run' 44 | task 'test:acceptance' => 'test:acceptance:quick' 45 | 46 | # global defaults 47 | task :test => 'test:spec' 48 | task :default => :test 49 | 50 | ########################################################### 51 | # 52 | # Documentation Tasks 53 | # 54 | ########################################################### 55 | DOCS_DAEMON = "yard server --reload --daemon --server thin" 56 | FOREGROUND_SERVER = 'bundle exec yard server --reload --verbose --server thin lib/beaker' 57 | 58 | def running?( cmdline ) 59 | ps = `ps -ef` 60 | found = ps.lines.grep( /#{Regexp.quote( cmdline )}/ ) 61 | if found.length > 1 62 | raise StandardError, "Found multiple YARD Servers. Don't know what to do." 63 | end 64 | 65 | yes = found.empty? ? false : true 66 | return yes, found.first 67 | end 68 | 69 | def pid_from( output ) 70 | output.squeeze(' ').strip.split(' ')[1] 71 | end 72 | 73 | desc 'Start the documentation server in the foreground' 74 | task :docs => 'docs:clear' do 75 | original_dir = Dir.pwd 76 | Dir.chdir( File.expand_path(File.dirname(__FILE__)) ) 77 | sh FOREGROUND_SERVER 78 | Dir.chdir( original_dir ) 79 | end 80 | 81 | namespace :docs do 82 | 83 | desc 'Clear the generated documentation cache' 84 | task :clear do 85 | original_dir = Dir.pwd 86 | Dir.chdir( File.expand_path(File.dirname(__FILE__)) ) 87 | sh 'rm -rf docs' 88 | Dir.chdir( original_dir ) 89 | end 90 | 91 | desc 'Generate static documentation' 92 | task :gen => 'docs:clear' do 93 | original_dir = Dir.pwd 94 | Dir.chdir( File.expand_path(File.dirname(__FILE__)) ) 95 | output = `bundle exec yard doc` 96 | puts output 97 | if output =~ /\[warn\]|\[error\]/ 98 | fail "Errors/Warnings during yard documentation generation" 99 | end 100 | Dir.chdir( original_dir ) 101 | end 102 | 103 | desc 'Run the documentation server in the background, alias `bg`' 104 | task :background => 'docs:clear' do 105 | yes, output = running?( DOCS_DAEMON ) 106 | if yes 107 | puts "Not starting a new YARD Server..." 108 | puts "Found one running with pid #{pid_from( output )}." 109 | else 110 | original_dir = Dir.pwd 111 | Dir.chdir( File.expand_path(File.dirname(__FILE__)) ) 112 | sh "bundle exec #{DOCS_DAEMON}" 113 | Dir.chdir( original_dir ) 114 | end 115 | end 116 | 117 | task(:bg) { Rake::Task['docs:background'].invoke } 118 | 119 | desc 'Check the status of the documentation server' 120 | task :status do 121 | yes, output = running?( DOCS_DAEMON ) 122 | if yes 123 | pid = pid_from( output ) 124 | puts "Found a YARD Server running with pid #{pid}" 125 | else 126 | puts "Could not find a running YARD Server." 127 | end 128 | end 129 | 130 | desc "Stop a running YARD Server" 131 | task :stop do 132 | yes, output = running?( DOCS_DAEMON ) 133 | if yes 134 | pid = pid_from( output ) 135 | puts "Found a YARD Server running with pid #{pid}" 136 | `kill #{pid}` 137 | puts "Stopping..." 138 | yes, output = running?( DOCS_DAEMON ) 139 | if yes 140 | `kill -9 #{pid}` 141 | yes, output = running?( DOCS_DAEMON ) 142 | if yes 143 | puts "Could not Stop Server!" 144 | else 145 | puts "Server stopped." 146 | end 147 | else 148 | puts "Server stopped." 149 | end 150 | else 151 | puts "Could not find a running YARD Server" 152 | end 153 | end 154 | end 155 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | beaker-qa-i18n (1.0.0) 5 | stringify-hash (~> 0.0.0) 6 | 7 | GEM 8 | remote: https://rubygems.org/ 9 | specs: 10 | CFPropertyList (2.3.6) 11 | activesupport (7.0.5.1) 12 | concurrent-ruby (~> 1.0, >= 1.0.2) 13 | i18n (>= 1.6, < 2) 14 | minitest (>= 5.1) 15 | tzinfo (~> 2.0) 16 | addressable (2.4.0) 17 | aws-sdk-v1 (1.67.0) 18 | json (~> 1.4) 19 | nokogiri (~> 1) 20 | beaker (2.52.0) 21 | aws-sdk-v1 (~> 1.57) 22 | beaker-answers (~> 0.0) 23 | beaker-hiera (~> 0.0) 24 | beaker-hostgenerator 25 | beaker-pe (~> 0.0) 26 | docker-api 27 | fission (~> 0.4) 28 | fog (~> 1.25, < 1.35.0) 29 | fog-google (~> 0.0.9) 30 | google-api-client (~> 0.8, < 0.9.5) 31 | hocon (~> 1.0) 32 | in-parallel (~> 0.1) 33 | inifile (~> 2.0) 34 | json (~> 1.8) 35 | mime-types (~> 2.99) 36 | minitest (~> 5.4) 37 | net-scp (~> 1.2) 38 | net-ssh (~> 2.9) 39 | open_uri_redirections (~> 0.2.1) 40 | public_suffix (< 1.5.0) 41 | rbvmomi (~> 1.8, < 1.9.0) 42 | rsync (~> 1.0.9) 43 | stringify-hash (~> 0.0) 44 | unf (~> 0.1) 45 | beaker-answers (0.29.0) 46 | hocon (~> 1.0) 47 | require_all (>= 1.3.2, < 3.1.0) 48 | stringify-hash (~> 0.0.0) 49 | beaker-hiera (0.2.0) 50 | stringify-hash (~> 0.0.0) 51 | beaker-hostgenerator (2.2.0) 52 | deep_merge (~> 1.0) 53 | beaker-pe (0.12.2) 54 | stringify-hash (~> 0.0.0) 55 | builder (3.2.4) 56 | coderay (1.1.3) 57 | concurrent-ruby (1.2.2) 58 | daemons (1.4.1) 59 | deep_merge (1.2.2) 60 | diff-lcs (1.5.0) 61 | docile (1.4.0) 62 | docker-api (2.2.0) 63 | excon (>= 0.47.0) 64 | multi_json 65 | dry-inflector (1.0.0) 66 | eventmachine (1.2.7) 67 | excon (0.100.0) 68 | fakefs (0.20.1) 69 | faraday (1.10.3) 70 | faraday-em_http (~> 1.0) 71 | faraday-em_synchrony (~> 1.0) 72 | faraday-excon (~> 1.1) 73 | faraday-httpclient (~> 1.0) 74 | faraday-multipart (~> 1.0) 75 | faraday-net_http (~> 1.0) 76 | faraday-net_http_persistent (~> 1.0) 77 | faraday-patron (~> 1.0) 78 | faraday-rack (~> 1.0) 79 | faraday-retry (~> 1.0) 80 | ruby2_keywords (>= 0.0.4) 81 | faraday-em_http (1.0.0) 82 | faraday-em_synchrony (1.0.0) 83 | faraday-excon (1.1.0) 84 | faraday-httpclient (1.0.1) 85 | faraday-multipart (1.0.4) 86 | multipart-post (~> 2) 87 | faraday-net_http (1.0.1) 88 | faraday-net_http_persistent (1.2.0) 89 | faraday-patron (1.0.0) 90 | faraday-rack (1.0.0) 91 | faraday-retry (1.0.3) 92 | fission (0.5.0) 93 | CFPropertyList (~> 2.2) 94 | fog (1.34.0) 95 | fog-atmos 96 | fog-aws (>= 0.6.0) 97 | fog-brightbox (~> 0.4) 98 | fog-core (~> 1.32) 99 | fog-dynect (~> 0.0.2) 100 | fog-ecloud (~> 0.1) 101 | fog-google (>= 0.0.2) 102 | fog-json 103 | fog-local 104 | fog-powerdns (>= 0.1.1) 105 | fog-profitbricks 106 | fog-radosgw (>= 0.0.2) 107 | fog-riakcs 108 | fog-sakuracloud (>= 0.0.4) 109 | fog-serverlove 110 | fog-softlayer 111 | fog-storm_on_demand 112 | fog-terremark 113 | fog-vmfusion 114 | fog-voxel 115 | fog-xml (~> 0.1.1) 116 | ipaddress (~> 0.5) 117 | nokogiri (~> 1.5, >= 1.5.11) 118 | fog-atmos (0.1.0) 119 | fog-core 120 | fog-xml 121 | fog-aws (2.0.1) 122 | fog-core (~> 1.38) 123 | fog-json (~> 1.0) 124 | fog-xml (~> 0.1) 125 | ipaddress (~> 0.8) 126 | fog-brightbox (0.16.1) 127 | dry-inflector 128 | fog-core 129 | fog-json 130 | mime-types 131 | fog-core (1.45.0) 132 | builder 133 | excon (~> 0.58) 134 | formatador (~> 0.2) 135 | fog-dynect (0.0.3) 136 | fog-core 137 | fog-json 138 | fog-xml 139 | fog-ecloud (0.3.0) 140 | fog-core 141 | fog-xml 142 | fog-google (0.0.9) 143 | fog-core 144 | fog-json 145 | fog-xml 146 | fog-json (1.2.0) 147 | fog-core 148 | multi_json (~> 1.10) 149 | fog-local (0.8.0) 150 | fog-core (>= 1.27, < 3.0) 151 | fog-powerdns (0.2.0) 152 | fog-core 153 | fog-json 154 | fog-xml 155 | fog-profitbricks (4.1.1) 156 | fog-core (~> 1.42) 157 | fog-json (~> 1.0) 158 | fog-radosgw (0.0.5) 159 | fog-core (>= 1.21.0) 160 | fog-json 161 | fog-xml (>= 0.0.1) 162 | fog-riakcs (0.1.0) 163 | fog-core 164 | fog-json 165 | fog-xml 166 | fog-sakuracloud (1.7.5) 167 | fog-core 168 | fog-json 169 | fog-serverlove (0.1.2) 170 | fog-core 171 | fog-json 172 | fog-softlayer (1.1.4) 173 | fog-core 174 | fog-json 175 | fog-storm_on_demand (0.1.1) 176 | fog-core 177 | fog-json 178 | fog-terremark (0.1.0) 179 | fog-core 180 | fog-xml 181 | fog-vmfusion (0.1.0) 182 | fission 183 | fog-core 184 | fog-voxel (0.1.0) 185 | fog-core 186 | fog-xml 187 | fog-xml (0.1.4) 188 | fog-core 189 | nokogiri (>= 1.5.11, < 2.0.0) 190 | formatador (0.3.0) 191 | google-api-client (0.9.4) 192 | addressable (~> 2.3) 193 | googleauth (~> 0.5) 194 | httpclient (~> 2.7) 195 | hurley (~> 0.1) 196 | memoist (~> 0.11) 197 | mime-types (>= 1.6) 198 | representable (~> 2.3.0) 199 | retriable (~> 2.0) 200 | thor (~> 0.19) 201 | googleauth (0.17.1) 202 | faraday (>= 0.17.3, < 2.0) 203 | jwt (>= 1.4, < 3.0) 204 | memoist (~> 0.16) 205 | multi_json (~> 1.11) 206 | os (>= 0.9, < 2.0) 207 | signet (~> 0.15) 208 | hocon (1.4.0) 209 | httpclient (2.8.3) 210 | hurley (0.2) 211 | i18n (1.14.1) 212 | concurrent-ruby (~> 1.0) 213 | in-parallel (0.1.17) 214 | inifile (2.0.2) 215 | iniparser (1.0.1) 216 | ipaddress (0.8.3) 217 | json (1.8.6) 218 | jwt (2.7.1) 219 | kramdown (2.4.0) 220 | rexml 221 | logutils (0.6.1) 222 | markdown (1.2.0) 223 | kramdown (>= 1.5.0) 224 | props (>= 1.1.2) 225 | textutils (>= 0.10.0) 226 | memoist (0.16.2) 227 | method_source (1.0.0) 228 | mime-types (2.99.3) 229 | minitest (5.18.1) 230 | multi_json (1.15.0) 231 | multipart-post (2.3.0) 232 | net-scp (1.2.1) 233 | net-ssh (>= 2.6.5) 234 | net-ssh (2.9.4) 235 | nokogiri (1.15.2-aarch64-linux) 236 | racc (~> 1.4) 237 | open_uri_redirections (0.2.1) 238 | os (1.1.4) 239 | props (1.2.0) 240 | iniparser (>= 0.1.0) 241 | pry (0.14.2) 242 | coderay (~> 1.1) 243 | method_source (~> 1.0) 244 | public_suffix (1.4.6) 245 | racc (1.7.1) 246 | rack (2.2.7) 247 | rake (13.0.6) 248 | rbvmomi (1.8.5) 249 | builder 250 | nokogiri (>= 1.4.1) 251 | trollop 252 | representable (2.3.0) 253 | uber (~> 0.0.7) 254 | require_all (3.0.0) 255 | retriable (2.1.0) 256 | rexml (3.2.5) 257 | rspec (3.12.0) 258 | rspec-core (~> 3.12.0) 259 | rspec-expectations (~> 3.12.0) 260 | rspec-mocks (~> 3.12.0) 261 | rspec-core (3.12.2) 262 | rspec-support (~> 3.12.0) 263 | rspec-expectations (3.12.3) 264 | diff-lcs (>= 1.2.0, < 2.0) 265 | rspec-support (~> 3.12.0) 266 | rspec-its (1.3.0) 267 | rspec-core (>= 3.0.0) 268 | rspec-expectations (>= 3.0.0) 269 | rspec-mocks (3.12.5) 270 | diff-lcs (>= 1.2.0, < 2.0) 271 | rspec-support (~> 3.12.0) 272 | rspec-support (3.12.1) 273 | rsync (1.0.9) 274 | ruby2_keywords (0.0.5) 275 | rubyzip (2.3.2) 276 | signet (0.15.0) 277 | addressable (~> 2.3) 278 | faraday (>= 0.17.3, < 2.0) 279 | jwt (>= 1.5, < 3.0) 280 | multi_json (~> 1.10) 281 | simplecov (0.22.0) 282 | docile (~> 1.1) 283 | simplecov-html (~> 0.11) 284 | simplecov_json_formatter (~> 0.1) 285 | simplecov-html (0.12.3) 286 | simplecov_json_formatter (0.1.4) 287 | stringify-hash (0.0.2) 288 | textutils (1.4.0) 289 | activesupport 290 | logutils (>= 0.6.1) 291 | props (>= 1.1.2) 292 | rubyzip (>= 1.0.0) 293 | thin (1.8.2) 294 | daemons (~> 1.0, >= 1.0.9) 295 | eventmachine (~> 1.0, >= 1.0.4) 296 | rack (>= 1, < 3) 297 | thor (0.20.3) 298 | trollop (2.9.10) 299 | tzinfo (2.0.6) 300 | concurrent-ruby (~> 1.0) 301 | uber (0.0.15) 302 | unf (0.1.4) 303 | unf_ext 304 | unf_ext (0.0.8.2) 305 | yard (0.9.34) 306 | 307 | PLATFORMS 308 | aarch64-linux 309 | 310 | DEPENDENCIES 311 | beaker (~> 2.0) 312 | beaker-qa-i18n! 313 | fakefs (~> 0.6) 314 | markdown 315 | pry (~> 0.10) 316 | rake (~> 13.0) 317 | rspec (~> 3.0) 318 | rspec-its 319 | simplecov 320 | thin 321 | yard 322 | 323 | BUNDLED WITH 324 | 2.4.14 325 | -------------------------------------------------------------------------------- /lib/beaker-qa-i18n/i18n_string_generator.rb: -------------------------------------------------------------------------------- 1 | module Beaker 2 | module DSL 3 | module Helpers 4 | module BeakerQaI18n 5 | module I18nStringGenerator 6 | CHINESE_CHARACTERS =[*"\u4E00".."\u4E20"] 7 | GERMAN_CHARACTERS =["\u00C4", "\u00E4", "\u00D6", "\u00F6", "\u00DC", "\u00FC"] 8 | ENGLISH_CHARACTERS =[*"\u0041".."\u007A"] 9 | NUMERIC_CHARACTERS =[*"\u0030".."\u0039"] 10 | 11 | 12 | WHITE_SPACE_CHARACTERS=[" ", *"\u2002".."\u200B"] 13 | MAX_LENGTH_CHARACTERS =["\u00DF"] 14 | SYNTAX_CHARACTERS =['&', '+', '/', '\\', '"', "'", '(', ')', '?', '.', '#', '@', '_', '-', '~'] 15 | 16 | # Gets a random number generator with optional seed 17 | # @param [Int] seed - Random seed for re-playing random value. 18 | # @return [Object] - an instance of the Random class 19 | def get_rng(seed = nil) 20 | seed ||= Random.new_seed 21 | logger.debug "random seed used: #{seed}" 22 | Random.new(seed) 23 | end 24 | 25 | # Gets a string consisting of all values in this library for the specified character type 26 | # @param [Symbol] character_type - :chinese, :german, :english, :numeric, :max_length, :white_space, :syntax 27 | # @return [String] - a string containing all of the characters from the character type 28 | def get_i18n_string(character_type) 29 | array = instance_eval("#{character_type.to_s.upcase}_CHARACTERS") 30 | get_strings_of_length_from_char_array(array, nil)[0] 31 | end 32 | 33 | # produces a random multiple language string including Chinese, German and English characters 34 | # @param [Int] length - Length of string desired 35 | # @param [Int] seed - Random seed for re-playing random value. 36 | # @return [String] - The random string 37 | def random_multi_lang(length, seed=nil) 38 | random_characters([CHINESE_CHARACTERS, ENGLISH_CHARACTERS, GERMAN_CHARACTERS], length, seed) 39 | end 40 | 41 | # produces a random multiple language sentence including Chinese, German and English characters 42 | # @param [Int] length - Length of string desired 43 | # @param [Int] seed - Random seed for re-playing random value. 44 | # @return [String] - The random string 45 | def random_multi_lang_sentence(length, seed = nil) 46 | raise('length of sentence must be at least 2') unless length > 1 47 | chars = random_characters([CHINESE_CHARACTERS, ENGLISH_CHARACTERS, GERMAN_CHARACTERS], length, seed) 48 | index = 0 49 | while index + 13 < length 50 | index = index + (1..12).to_a.sample(1)[0] 51 | chars.insert(index, ' ') 52 | end 53 | chars[0..(length-2)] + '.' 54 | end 55 | 56 | # produces a random Chinese language string 57 | # The Chinese, Japanese and Korean (CJK) scripts share a common background, collectively known as CJK characters 58 | # @param [Int] length - Length of string desired 59 | # @param [Int] seed - Random seed for re-playing random value. 60 | # @return [String] - The random string 61 | def random_chinese_characters(length, seed = nil) 62 | random_characters(CHINESE_CHARACTERS, length, seed) 63 | end 64 | 65 | # produces a random English language string 66 | # @param [Int] length - Length of string desired 67 | # @param [Int] seed - Random seed for re-playing random value. 68 | # @return [String] - The random string 69 | def random_english(length, seed = nil) 70 | random_characters(ENGLISH_CHARACTERS, length, seed) 71 | end 72 | 73 | # produces a random English language sentence 74 | # @param [Int] length - Length of string desired 75 | # @param [Int] seed - Random seed for re-playing random value. 76 | # @return [String] - The random string 77 | def random_english_sentence(length, seed = nil) 78 | raise('length of sentence must be at least 2') unless length > 1 79 | chars = random_characters(ENGLISH_CHARACTERS, length, seed) 80 | index = 0 81 | while index + 13 < length 82 | index = index + (1..12).to_a.sample(1) 83 | chars.insert(index, ' ') 84 | end 85 | chars[0..(length-1)] + '.' 86 | end 87 | 88 | # produces a random English alpha-numeric string 89 | # @param [Int] length - Length of string desired 90 | # @param [Int] seed - Random seed for re-playing random value. 91 | # @return [String] - The random string 92 | def random_alpha_numeric(length, seed = nil) 93 | random_characters([ENGLISH_CHARACTERS, NUMERIC_CHARACTERS], length, seed) 94 | end 95 | 96 | # produces a random German string 97 | # @param [Int] length - Length of string desired 98 | # @param [Int] seed - Random seed for re-playing random value. 99 | # @return [String] - The random string 100 | def random_german(length, seed = nil) 101 | random_characters(GERMAN_CHARACTERS, length, seed) 102 | end 103 | 104 | # Generate random strings from various utf8 character ranges. Can repeat characters. 105 | # @param [Array>] utf8_ranges - an array of an array of character ranges 106 | # @param [Int] length - Length of string desired 107 | # @param [Int] seed - Random seed for re-playing random value. 108 | # @return [String] - The random string 109 | def random_characters(utf8_ranges, length, seed = nil) 110 | (length.times.map { utf8_ranges.flatten }).flatten # flatten the arrays to a single array, then duplicate the array length 111 | .sample(length, random: get_rng(seed)).join("") # Sample the resultant array with a seeded random number generator 112 | end 113 | 114 | # Creates an array of strings of a certain length, then iterates through them passing each string 115 | # to a block for testing. You can optionally exclude syntax and white space chararcters if the 116 | # input being tested does not allow them. 117 | # Example: iterates through array of strings of length 10, testing all special characters according to the block 118 | # test_i18n_strings(10) { |test_string| 119 | # # Enter data 120 | # create_user("User#{test_string}) 121 | # # Validate data 122 | # verify_user_name_exists("User#{test_string}") 123 | # } 124 | # 125 | # @param [Int] string_length - The length of the string you want to test with 126 | # @param [Array] exclude - String types to exclude from testing :syntax or :white_space 127 | def test_i18n_strings(string_length, exclude=[], &block) 128 | raise("test_i18n_strings requires a block with arity of 1") unless block_given? && block.arity == 1 129 | 130 | logger.debug('Testing Chinese characters') 131 | get_strings_of_length_from_char_array(CHINESE_CHARACTERS, string_length).each { |string| 132 | yield string 133 | } 134 | logger.debug 'Testing German characters' 135 | get_strings_of_length_from_char_array(GERMAN_CHARACTERS, string_length).each { |string| 136 | yield string 137 | } 138 | logger.debug 'Testing max length characters' 139 | get_strings_of_length_from_char_array(MAX_LENGTH_CHARACTERS, string_length).each { |string| 140 | yield string 141 | } 142 | logger.debug 'Testing syntax characters' unless exclude.include?(:syntax) 143 | get_strings_of_length_from_char_array(SYNTAX_CHARACTERS, string_length).each { |string| 144 | yield string 145 | } unless exclude.include?(:syntax) 146 | logger.debug 'Testing white space characters' unless exclude.include?(:white_space) 147 | get_strings_of_length_from_char_array(WHITE_SPACE_CHARACTERS, string_length).each { |string| 148 | yield string 149 | } unless exclude.include?(:white_space) 150 | end 151 | 152 | 153 | def get_strings_of_length_from_char_array(array, string_length) 154 | string_length ||= array.length 155 | strings = [] 156 | if (array.length == string_length) 157 | strings.push(array.join('').encode('UTF-8')) 158 | elsif (array.length < string_length) 159 | my_string = array.join('').encode!('UTF-8') 160 | while (my_string.length < string_length) 161 | my_string = (my_string + array.join('')).encode('UTF-8') 162 | end 163 | strings.push(my_string[0...string_length].encode('UTF-8')) 164 | else 165 | my_string = array.join('') 166 | while (my_string.length > string_length) 167 | strings.push(my_string.slice!(0...string_length).encode('UTF-8')) 168 | end 169 | # For the final characters, add the first characters from the array to equal the expected string length 170 | strings.push(my_string + array[0...(string_length - my_string.length)].join('').encode('UTF-8')) 171 | end 172 | end 173 | 174 | private :get_strings_of_length_from_char_array 175 | end 176 | end 177 | end 178 | end 179 | end 180 | 181 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | --------------------------------------------------------------------------------