├── .github ├── dependabot.yml └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── example ├── version_f_ex1.f90 └── version_f_ex2.f90 ├── fpm.toml ├── src └── version_f.f90 └── test └── version_f_test.f90 /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 7 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: push 3 | 4 | jobs: 5 | fpm-test: 6 | strategy: 7 | fail-fast: false 8 | matrix: 9 | os: [ubuntu-latest, macos-13] 10 | toolchain: 11 | - { compiler: gcc, version: "13" } 12 | - { compiler: intel, version: "2024.1" } 13 | - { compiler: intel-classic, version: "2021.12" } 14 | exclude: 15 | - os: macos-13 16 | toolchain: { compiler: intel, version: "2024.1" } 17 | - os: macos-13 18 | toolchain: { compiler: intel-classic, version: "2021.12" } 19 | include: 20 | - os: ubuntu-latest 21 | toolchain: { compiler: nvidia-hpc, version: "23.11" } 22 | - os: macos-13 23 | toolchain: { compiler: intel-classic, version: "2021.10" } 24 | - os: windows-latest 25 | toolchain: { compiler: gcc, version: "13" } 26 | runs-on: ${{ matrix.os }} 27 | steps: 28 | - uses: actions/checkout@v4 29 | - uses: fortran-lang/setup-fortran@v1 30 | id: setup-fortran 31 | with: 32 | compiler: ${{ matrix.toolchain.compiler }} 33 | version: ${{ matrix.toolchain.version }} 34 | - name: macos workaround 35 | if: runner.os == 'macos' 36 | run: brew install gcc@10 37 | - uses: fortran-lang/setup-fpm@v5 38 | with: 39 | github-token: ${{ secrets.GITHUB_TOKEN }} 40 | - run: fpm test 41 | 42 | test-msys2: 43 | strategy: 44 | matrix: 45 | include: 46 | [ 47 | { msystem: mingw64, build_variant: "" }, 48 | { msystem: ucrt64, build_variant: "-ucrt" }, 49 | ] 50 | runs-on: windows-latest 51 | defaults: 52 | run: 53 | shell: msys2 {0} 54 | steps: 55 | - uses: actions/checkout@v4 56 | - uses: msys2/setup-msys2@v2 57 | with: 58 | msystem: ${{ matrix.msystem }} 59 | install: >- 60 | mingw-w64${{ matrix.build_variant }}-x86_64-gcc-fortran 61 | mingw-w64${{ matrix.build_variant }}-x86_64-fpm 62 | - run: fpm test 63 | 64 | make-test: 65 | strategy: 66 | fail-fast: false 67 | matrix: 68 | os: [ubuntu-latest, macos-latest] 69 | toolchain: 70 | - { compiler: gcc, version: "13" } 71 | include: 72 | - os: ubuntu-latest 73 | toolchain: { compiler: intel, version: "2024.1" } 74 | - os: ubuntu-latest 75 | toolchain: { compiler: intel-classic, version: "2021.12" } 76 | - os: macos-latest 77 | toolchain: { compiler: intel-classic, version: "2021.10" } 78 | - os: ubuntu-latest 79 | toolchain: { compiler: nvidia-hpc, version: "23.11" } 80 | runs-on: ${{ matrix.os }} 81 | steps: 82 | - uses: actions/checkout@v4 83 | - uses: fortran-lang/setup-fortran@v1 84 | id: setup-fortran 85 | with: 86 | compiler: ${{ matrix.toolchain.compiler }} 87 | version: ${{ matrix.toolchain.version }} 88 | - run: ${{ steps.setup-fortran.outputs.fc }} --version 89 | - run: make test FC=${{ steps.setup-fortran.outputs.fc }} 90 | 91 | format: 92 | runs-on: ubuntu-latest 93 | steps: 94 | - uses: actions/checkout@v4 95 | - uses: actions/setup-python@v5 96 | with: 97 | python-version: "3.12" 98 | - name: Check formatting 99 | run: | 100 | pip install fprettify 101 | 102 | diff=$(fprettify -i 2 -r . -d) 103 | 104 | if [[ $diff ]]; then 105 | red="\033[0;31m" 106 | cyan="\033[0;36m" 107 | reset="\033[0m" 108 | printf -- "$diff\n" 109 | printf "${red}The code is not correctly formatted. Please run: ${reset}\n" 110 | printf "${cyan}fprettify -i 2 -r .${reset}\n" 111 | exit 1 112 | fi 113 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build artifacts 2 | build 3 | 4 | # Locally generated libraries 5 | *.a 6 | *.so 7 | *.dylib 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .POSIX: 2 | .SUFFIXES: 3 | .PHONY: all static shared test clean 4 | 5 | FC := gfortran 6 | FFLAGS := -O2 7 | AR := ar 8 | ARFLAGS := rcs 9 | 10 | NAME := version-f 11 | STATIC := lib$(NAME).a 12 | 13 | ifeq ($(shell uname), Linux) 14 | SHARED := lib$(NAME).so 15 | LDFLAGS := -Wl,-rpath=. 16 | else ifeq ($(shell uname), Darwin) 17 | SHARED := lib$(NAME).dylib 18 | endif 19 | 20 | SRCDIR := src 21 | TESTDIR := test 22 | EXMPLDIR := example 23 | BUILDDIR := build/Makefile 24 | MODDIR := $(BUILDDIR)/mod 25 | OBJDIR := $(BUILDDIR)/obj 26 | EXEDIR := $(BUILDDIR)/exe 27 | 28 | SRCS := $(wildcard $(SRCDIR)/*.f90) 29 | OBJS := $(patsubst $(SRCDIR)/%.f90,$(OBJDIR)/%.o,$(SRCS)) 30 | EXESRCS := $(foreach dir,$(TESTDIR) $(EXMPLDIR),$(wildcard $(dir)/*.f90)) 31 | EXESSTATIC := $(patsubst %.f90,$(EXEDIR)/%_static.out,$(notdir $(EXESRCS))) 32 | EXESSHARED := $(patsubst %.f90,$(EXEDIR)/%_shared.out,$(notdir $(EXESRCS))) 33 | 34 | ifeq ($(FC),gfortran) 35 | MODIN := -I$(MODDIR) 36 | MODOUT := -J$(MODDIR) 37 | else 38 | MODIN := -module $(MODDIR) 39 | MODOUT := $(MODIN) 40 | endif 41 | 42 | all: $(STATIC) $(SHARED) 43 | static: $(STATIC) 44 | shared: $(SHARED) 45 | 46 | $(OBJDIR)/%.o: $(SRCDIR)/%.f90 47 | mkdir -p $(MODDIR) $(OBJDIR) 48 | $(FC) $(FFLAGS) $(MODOUT) -c $< -o $@ 49 | 50 | $(STATIC): $(OBJS) 51 | $(AR) $(ARFLAGS) $@ $< 52 | 53 | $(SHARED): $(SRCS) 54 | mkdir -p $(MODDIR) 55 | $(FC) $(FFLAGS) -fpic -shared $(MODOUT) -o $@ $< 56 | 57 | $(EXEDIR): 58 | mkdir -p $(EXEDIR) 59 | 60 | $(EXEDIR)/%_static.out: $(TESTDIR)/%.f90 $(STATIC) | $(EXEDIR) 61 | $(FC) $(FFLAGS) $(MODIN) -o $@ $^ 62 | 63 | $(EXEDIR)/%_static.out: $(EXMPLDIR)/%.f90 $(STATIC) | $(EXEDIR) 64 | $(FC) $(FFLAGS) $(MODIN) -o $@ $^ 65 | 66 | $(EXEDIR)/%_shared.out: $(TESTDIR)/%.f90 $(SHARED) | $(EXEDIR) 67 | $(FC) $(FFLAGS) $(MODIN) -o $@ $^ $(LDFLAGS) 68 | 69 | $(EXEDIR)/%_shared.out: $(EXMPLDIR)/%.f90 $(SHARED) | $(EXEDIR) 70 | $(FC) $(FFLAGS) $(MODIN) -o $@ $^ $(LDFLAGS) 71 | 72 | test: $(EXESSTATIC) $(EXESSHARED) 73 | @for f in $^; do $$f; done 74 | 75 | clean: 76 | rm -rf $(BUILDDIR) 77 | rm -f $(STATIC) 78 | rm -f $(SHARED) 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # version-f 2 | 3 | [![License](https://img.shields.io/github/license/minhqdao/version-f?label=License&color=teal)](https://opensource.org/licenses/Apache-2.0) 4 | [![Release](https://img.shields.io/github/release/minhqdao/version-f?label=Release)](https://github.com/minhqdao/version-f/releases) 5 | [![CI](https://github.com/minhqdao/version-f/actions/workflows/ci.yml/badge.svg)](https://github.com/minhqdao/version-f/actions/workflows/ci.yml) 6 | 7 | 8 | This package provides a complete Fortran implementation of 9 | [Semantic Versioning 2.0.0](https://semver.org). It aims to be a user-friendly 10 | tool for handling versions in your Fortran projects. 11 | 12 | It follows the `major`.`minor`.`patch` pattern and allows the inclusion of 13 | `prerelease` labels and `build` metadata. Versions can be created or parsed from 14 | strings, compared, incremented and printed as strings. 15 | 16 | ## Installation 17 | 18 | ### fpm 19 | 20 | If you are using [fpm](https://fpm.fortran-lang.org/en/index.html), you can 21 | simply add this package as a dependency to your `fpm.toml` file: 22 | 23 | ```toml 24 | [dependencies] 25 | 26 | [dependencies.version-f] 27 | git = "https://github.com/minhqdao/version-f.git" 28 | tag = "v0.4.0" 29 | ``` 30 | 31 | Then import the `version_f` module into your Fortran code: 32 | 33 | ```fortran 34 | use version_f, only: version_t, error_t 35 | ``` 36 | 37 | Run `fpm build` to download the dependency. 38 | 39 | ### make 40 | 41 | To build the library using the provided `Makefile`, navigate to the project's root directory and execute the following command if you are on a Unix-like system: 42 | 43 | ```bash 44 | make 45 | ``` 46 | 47 | This will generate the static library `libversion-f.a` in the root directory. You can compile it alongside your project or link it using the `-L` and `-l` flags. 48 | 49 | A dynamic library will also be created for use in your projects (suffix `.so` on Linux systems and `.dylib` on macOS). 50 | 51 | If you wish to use a compiler other than `gfortran`, simply specify it by running: 52 | 53 | ```bash 54 | make FC= 55 | ``` 56 | 57 | ## Create versions 58 | 59 | Create versions using one of the following commands: 60 | 61 | ```fortran 62 | type(version_t) :: version 63 | type(error_t), allocatable :: error 64 | 65 | ! The default way using individual arguments 66 | version = version_t(0, 1, 0) 67 | 68 | ! Parse from string 69 | version = version_t('0.1.0') 70 | 71 | ! From arguments with error handling 72 | call version%create(0, 1, 0, error=error) 73 | 74 | ! From string with error handling 75 | call version%parse('0.1.0', error) 76 | 77 | ! From arguments with prerelease labels and build metadata 78 | call version%create(0, 1, 0, 'alpha', '1', error) 79 | 80 | ! From string with prerelease labels and build metadata 81 | call version%parse('0.1.0-alpha+1', error) 82 | ``` 83 | 84 | ## Prerelase labels 85 | 86 | `prerelease` labels can be included and will be appended after the `patch` via a `-` sign. The identifiers must comprise only ASCII alphanumerics and hyphens `[0-9A-Za-z-]` and are separated by dots. Numerical identifiers must not start with a `0` digit. A version containing `prerelease` data has lower precedence than the equivalent version without. `prerelease` information is cleared each time the version is incremented. A `prerelease` can be [incremented](#increment-versions). 87 | 88 | ```fortran 89 | type(version_t) :: v1, v2 90 | 91 | v1 = version_t(0, 5, 3, 'beta.1') 92 | print *, v1%to_string() ! '0.5.3-beta.1' 93 | 94 | v2 = version_t(0, 5, 3) 95 | print *, v2%to_string() ! '0.5.3' 96 | 97 | print *, v1 < v2 ! true 98 | print *, v1 == v2 ! false 99 | 100 | call v1%increment_patch() ! 0.5.4 101 | call v1%increment_prerelease() ! 0.5.4-1 102 | call v1%increment_prerelease() ! 0.5.4-2 103 | ``` 104 | 105 | ## Build metadata 106 | 107 | `build` metadata can be included and will be appended after the `patch` or the `prerelease` via a `+` sign. The identifiers must comprise only ASCII alphanumerics and hyphens `[0-9A-Za-z-]` and are separated by dots. Numerical identifiers must not start with a `0` digit. `build` data is not used for comparison and it is cleared every time a `major`, `minor`, `patch` or `prerelease` version is incremented. A `build` value can be [incremented](#increment-versions). 108 | 109 | ```fortran 110 | type(version_t) :: v1, v2 111 | 112 | v1 = version_t(0, 5, 3, build='1') 113 | print *, v1%to_string() ! '0.5.3+1' 114 | 115 | v2 = version_t(0, 5, 3, build='abc.1-13') 116 | print *, v2%to_string() ! '0.5.3+abc.1-13' 117 | 118 | print *, v1 == v2 ! true 119 | 120 | call v1%increment_patch() ! 0.5.4 121 | call v1%increment_build() ! 0.5.4+1 122 | 123 | v1 = version_t(0, 5, 3, 'alpha.1' '1') 124 | print *, v1%to_string() ! '0.5.3-alpha.1+1' 125 | print *, v1%increment_build() ! '0.5.3-alpha.1+2' 126 | ``` 127 | 128 | ## Compare versions 129 | 130 | Versions can be compared using the standard Fortran operators. Be aware that a version containing `prerelease` labels has lower precedence than the equivalent version without. `build` information isn't used for comparison. However, you can use the [is_exactly()](#is_exactly) function to include it. 131 | 132 | ```fortran 133 | type(version_t) :: v1, v2, v3, v4 134 | 135 | v1 = version_t(0, 1, 0) 136 | v2 = version_t(1, 0, 0) 137 | v3 = version_t(1, 0, 0, 'alpha') 138 | v4 = version_t(1, 0, 0, build='1') 139 | 140 | if (v1 < v2) then ! true 141 | if (v1 <= v2) then ! true 142 | if (v1 > v2) then ! false 143 | if (v1 <= v2) then ! false 144 | if (v1 == v2) then ! false 145 | if (v1 /= v2) then ! true 146 | 147 | ! With prerelease labels 148 | if (v2 == v3) then ! false 149 | if (v2 > v3) then ! true 150 | 151 | ! With build metadata 152 | if (v2 == v4) then ! true 153 | ``` 154 | 155 | ## Version ranges 156 | 157 | Use the `satisfies`/`try_satisfy` procedures to verify whether a version meets a range. A range encompasses one or more comparator sets, with multiple sets separated by || (logical OR). Each comparator set combines one or more comparators using a space-separated arrangement (logical AND). A comparator consists of an operator and a version. The available operators include the following: 158 | 159 | - `=`: Equal to 160 | - `!=`: Not equal to 161 | - `>`: Greater than 162 | - `>=`: Greater than or equal to 163 | - `<`: Less than 164 | - `<=`: Less than or equal to 165 | 166 | ```fortran 167 | program main 168 | use version_f 169 | 170 | type(version_t) :: version 171 | logical :: is_satisfied 172 | type(error_t), allocatable :: error 173 | 174 | version = version_t(0, 1, 0) 175 | 176 | print *, version%satisfies('0.1.0') ! true 177 | print *, version%satisfies('=0.1.0') ! true 178 | print *, version%satisfies('!=0.1.0') ! false 179 | print *, version%satisfies('>0.1.0') ! false 180 | print *, version%satisfies('>=0.1.0') ! true 181 | print *, version%satisfies('<0.1.0') ! false 182 | print *, version%satisfies('<=0.1.0') ! true 183 | print *, version%satisfies('>=0.1.0 <0.2.0') ! true 184 | print *, version%satisfies('>0.1.0 <0.2.0') ! false 185 | print *, version%satisfies('>0.1.0 <0.2.0 || 0.1.0') ! true 186 | print *, version%satisfies('0.0.8 || 0.0.9 || >0.1.0 <0.2.0') ! false 187 | 188 | call version%try_satisfy('<=0.1.0', is_satisfied, error) 189 | if (allocated(error)) call exit(1) 190 | print *, is_satisfied ! true 191 | end 192 | ``` 193 | 194 | ## Strict mode 195 | 196 | In `strict_mode` (optional parameter in `create`, `parse` and `is_version`), all `major`, `minor` and `patch` numbers must be provided. Implicit zeros are forbidden. 197 | 198 | ```fortran 199 | type(version_t) :: version 200 | type(error_t), allocatable :: error 201 | 202 | call version%create(1, error=error, strict_mode=.true.) 203 | print *, allocated(error) ! true 204 | 205 | call version%parse('1.2', error=error, strict_mode=.true.) 206 | print *, allocated(error) ! true 207 | 208 | print *, is_version('0.1.0-alpha.1', strict_mode=.true.) ! true 209 | print *, is_version('0.1.-alpha.1', strict_mode=.true.) ! false 210 | print *, is_version('0.1.-alpha.1') ! true 211 | print *, is_version('0.1-alpha.1', strict_mode=.true.) ! false 212 | print *, is_version('0.1-alpha.1') ! true 213 | ``` 214 | 215 | ## Increment versions 216 | 217 | `Prerelease` and `build` data are cleared every time a major, minor or patch number is incremented. `prerelease` and `build` values are incremented by adding `1` to the their last identifier if it is numeric. If the last identifier isn't a number or no identifiers exist, a new identifier is added with the value of `1`. Existing `build` information is cleared each time a `prerelease` is incremented. 218 | 219 | ```fortran 220 | type(version_t) :: version 221 | 222 | version = version_t(0, 5, 3, 'beta.1', '1') 223 | 224 | call version%increment_build() ! 0.5.3-beta.1+2 225 | call version%increment_prerelease() ! 0.5.3-beta.2 226 | call version%increment_patch() ! 0.5.4 227 | call version%increment_minor() ! 0.6.0 228 | call version%increment_major() ! 1.0.0 229 | ``` 230 | 231 | ## is_version() 232 | 233 | The `is_version()` function can be used to conveniently check if the string is 234 | a valid version. Use `parse` to receive detailed error messages. 235 | 236 | ```fortran 237 | print *, is_version('0.1.0-alpha.1') ! true 238 | print *, is_version('abc') ! false 239 | ``` 240 | 241 | ## is_stable() 242 | 243 | The `is_stable()` function returns `true` if the `major` version is strictly positive and the version does not have a `prerelease` label. 244 | 245 | ```fortran 246 | v1 = version_t(0, 9, 10) 247 | print *, v1%is_stable() ! false 248 | v1 = version_t(1, 0, 0) 249 | print *, v1%is_stable() ! true 250 | v1 = version_t(1, 0, 0, 'alpha') 251 | print *, v1%is_stable() ! false 252 | ``` 253 | 254 | ## is_exactly() 255 | 256 | The `is_exactly()` function has been added for convencience and isn't part of the original Semantic Versioning 2.0.0 specification. It is `true` if both versions are equal _including_ the `build` metadata. 257 | 258 | ```fortran 259 | v1 = version_t(0, 1, 0, 'a', '1') 260 | v2 = version_t(0, 1, 0, 'a', '1') 261 | print *, v1%is_exactly(v2) ! true 262 | 263 | v1 = version_t(0, 1, 0, 'a', '1') 264 | v2 = version_t(0, 1, 0, 'a', '2') 265 | print *, v1%is_exactly(v2) ! false 266 | print *, v1 == v2 ! true 267 | ``` 268 | 269 | ## to_string() 270 | 271 | Versions are converted to strings using the `to_string()` method. 272 | 273 | ```fortran 274 | type(version_t) :: version 275 | 276 | version = version_t(0, 5, 3, 'beta.1', '1-100') 277 | 278 | print *, version%to_string() ! '0.5.3-beta.1+1-100' 279 | ``` 280 | 281 | ## More examples 282 | 283 | ```fortran 284 | type(version_t) :: version 285 | 286 | version = version_t(0) ! 0.0.0 287 | version = version_t(1) ! 1.0.0 288 | version = version_t(3, 2) ! 3.2.0 289 | version = version_t('4.1') ! 4.1.0 290 | version = version_t('.5.') ! 0.5.0 291 | version = version_t('..1') ! 0.0.1 292 | ``` 293 | There are also full examples in the [example](https://github.com/minhqdao/version-f/tree/main/example) folder. Run them with: 294 | 295 | ```bash 296 | fpm run --example 297 | ``` 298 | 299 | ## Tests 300 | 301 | Run tests with: 302 | 303 | ```bash 304 | fpm test 305 | ``` 306 | 307 | Alternatively, you can use the provided `Makefile` if you are on a Unix-like system: 308 | 309 | ```bash 310 | make test 311 | ``` 312 | 313 | ## Formatting 314 | 315 | The CI will fail if the code is not formatted correctly. Please configure your 316 | editor to use [fprettify](https://pypi.org/project/fprettify/) and use an 317 | indentation width of 2 or run `fprettify -i 2 -r .` before committing. 318 | 319 | ## Contribute 320 | 321 | Feel free to [create an issue](https://github.com/minhqdao/version-f/issues) in case you found a bug, have any questions or 322 | want to propose further improvements. Please stick to the existing coding style 323 | when you open a pull request. 324 | 325 | ## License 326 | 327 | You can use, redistribute and/or modify it under the terms of the [Apache License, Version 2.0](https://github.com/minhqdao/version-f/blob/main/LICENSE). -------------------------------------------------------------------------------- /example/version_f_ex1.f90: -------------------------------------------------------------------------------- 1 | !> A program that loads boxes and finds the box with the highest version number. 2 | program ex1 3 | use version_f, only: version_t 4 | 5 | implicit none 6 | 7 | type box 8 | character(len=:), allocatable :: name 9 | character(len=:), allocatable :: version 10 | end type 11 | 12 | type(box), allocatable :: loaded_boxes(:) 13 | type(version_t), allocatable :: versions(:) 14 | type(box) :: latest_box 15 | type(version_t) :: highest_version 16 | integer :: i 17 | 18 | call load_boxes(loaded_boxes) 19 | 20 | ! Create and register versions 21 | allocate (versions(size(loaded_boxes))) 22 | do i = 1, size(loaded_boxes) 23 | versions(i) = version_t(loaded_boxes(i)%version) 24 | end do 25 | 26 | ! Find the highest version number 27 | highest_version = versions(1) 28 | latest_box = loaded_boxes(1) 29 | do i = 1, size(versions) 30 | if (versions(i) > highest_version) then 31 | highest_version = versions(i) 32 | latest_box = loaded_boxes(i) 33 | end if 34 | end do 35 | 36 | print *, "The latest box is '", latest_box%name, "'." 37 | print *, "It has the version '", highest_version%to_string(), "'." 38 | 39 | contains 40 | 41 | subroutine load_boxes(boxes) 42 | type(box), allocatable, intent(out) :: boxes(:) 43 | 44 | boxes = [ & 45 | & box('nice box', '1.0'), & 46 | & box('prototype box', '0.0.1'), & 47 | & box('nicest box', '2.1'), & 48 | & box('nearly nicest box', '2.1.0-alpha+1') & 49 | & ] 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /example/version_f_ex2.f90: -------------------------------------------------------------------------------- 1 | !> This program loads a list of versions and checks whether they satisfy given version ranges. 2 | program ex2 3 | use version_f 4 | 5 | implicit none 6 | 7 | type(version_t), allocatable :: all_versions(:) 8 | type(version_t), allocatable :: smaller_versions(:) 9 | type(version_t), allocatable :: versions_between(:) 10 | type(version_t), allocatable :: greater_versions(:) 11 | integer :: i 12 | 13 | call load_versions(all_versions) 14 | 15 | allocate (smaller_versions(0)) 16 | allocate (versions_between(0)) 17 | allocate (greater_versions(0)) 18 | 19 | do i = 1, size(all_versions) 20 | if (all_versions(i)%satisfies('<1.0.0')) then 21 | smaller_versions = [smaller_versions, all_versions(i)] 22 | else if (all_versions(i)%satisfies('>=1.0.0 <2.0.0')) then 23 | versions_between = [versions_between, all_versions(i)] 24 | else if (all_versions(i)%satisfies('>=2.0.0')) then 25 | greater_versions = [greater_versions, all_versions(i)] 26 | end if 27 | end do 28 | 29 | print *, 'Versions less than 1.0.0:' 30 | do i = 1, size(smaller_versions) 31 | print *, smaller_versions(i)%to_string() 32 | end do 33 | 34 | print *, '' 35 | print *, 'Versions greater or equal 1.0.0 and less than 2.0.0:' 36 | do i = 1, size(versions_between) 37 | print *, versions_between(i)%to_string() 38 | end do 39 | 40 | print *, '' 41 | print *, 'Versions greater or equal 2.0.0:' 42 | do i = 1, size(greater_versions) 43 | print *, greater_versions(i)%to_string() 44 | end do 45 | 46 | contains 47 | 48 | subroutine load_versions(versions) 49 | type(version_t), allocatable, intent(out) :: versions(:) 50 | 51 | versions = [ & 52 | & version_t(0, 2, 5), & 53 | & version_t(1, 2, 4, 'alpha'), & 54 | & version_t(2, 5), & 55 | & version_t(1, 2), & 56 | & version_t(2, build='one'), & 57 | & version_t(1, 2, 4), & 58 | & version_t(0, 2, 5, 'pre'), & 59 | & version_t(2, prerelease='pre'), & 60 | & version_t(1, 2, 4, 'alpha', '1'), & 61 | & version_t(2, 3, 5), & 62 | & version_t(0, 99, 999) & 63 | & ] 64 | end 65 | 66 | end 67 | -------------------------------------------------------------------------------- /fpm.toml: -------------------------------------------------------------------------------- 1 | name = "version-f" 2 | version = "0.4.0" 3 | license = "Apache-2.0" 4 | author = "Minh Dao" 5 | maintainer = "hello@minhdao.de" 6 | copyright = "Copyright 2023, Minh Dao" 7 | 8 | [build] 9 | module-naming = true 10 | -------------------------------------------------------------------------------- /src/version_f.f90: -------------------------------------------------------------------------------- 1 | !> The main and only module of `version-f` containing all the types and 2 | !> procedures that are necessary to create, parse, compare, convert and 3 | !> manipulate version numbers. 4 | module version_f 5 | implicit none 6 | private 7 | 8 | public :: version_t, string_t, error_t, is_version, version_range_t, & 9 | comparator_set_t, comparator_t, operator_index 10 | 11 | type :: string_t 12 | character(:), allocatable :: str 13 | contains 14 | generic :: num => string_t_2i 15 | procedure, private :: string_t_2i 16 | generic :: is_numeric => string_t_is_numeric 17 | procedure, private :: string_t_is_numeric 18 | end type 19 | 20 | interface string_t 21 | module procedure :: create_string_t 22 | end interface 23 | 24 | !> Contains all version information. 25 | type :: version_t 26 | !> The major version number. Incremented when breaking changes are made. 27 | integer :: major 28 | !> The minor version number. It is incremented when new functionality is 29 | !> added in a backwards-compatible manner. 30 | integer :: minor 31 | !> The patch version number. Incremented for backwards-compatible bug fixes. 32 | integer :: patch 33 | !> Pre-release version identifiers that are used for comparisons. 34 | type(string_t), allocatable :: prerelease(:) 35 | !> Build metadata that does not contribute to sorting. 36 | type(string_t), allocatable :: build(:) 37 | 38 | contains 39 | 40 | procedure :: to_string, increment_major, increment_minor, increment_patch, & 41 | & increment_prerelease, increment_build, is_exactly, satisfies, try_satisfy, & 42 | & satisfies_comp_set, satisfies_comp, is_stable 43 | 44 | generic :: create => try_create 45 | procedure, private :: try_create 46 | 47 | generic :: parse => try_parse 48 | procedure, private :: try_parse 49 | 50 | generic :: operator(==) => equals 51 | procedure, private :: equals 52 | 53 | generic :: operator(/=) => not_equals 54 | procedure, private :: not_equals 55 | 56 | generic :: operator(>) => greater_than 57 | procedure, private :: greater_than 58 | 59 | generic :: operator(<) => less_than 60 | procedure, private :: less_than 61 | 62 | generic :: operator(>=) => greater_equals 63 | procedure, private :: greater_equals 64 | 65 | generic :: operator(<=) => less_equals 66 | procedure, private :: less_equals 67 | end type 68 | 69 | interface version_t 70 | module procedure create, parse 71 | end interface 72 | 73 | type :: error_t 74 | character(:), allocatable :: msg 75 | end type 76 | 77 | interface error_t 78 | module procedure :: create_error_t 79 | end interface 80 | 81 | type :: comparator_t 82 | character(:), allocatable :: op 83 | type(version_t) :: version 84 | contains 85 | procedure, private :: parse_comp_and_crop_str 86 | end type 87 | 88 | interface comparator_t 89 | module procedure :: create_comp 90 | end interface 91 | 92 | type :: comparator_set_t 93 | type(comparator_t), allocatable :: comps(:) 94 | contains 95 | generic :: parse => parse_comp_set 96 | procedure, private :: parse_comp_set 97 | generic :: extend_with => extend_comps 98 | procedure, private :: extend_comps 99 | end type 100 | 101 | interface comparator_set_t 102 | module procedure :: create_comp_set 103 | end interface 104 | 105 | type :: version_range_t 106 | type(comparator_set_t), allocatable :: comp_sets(:) 107 | contains 108 | generic :: parse => parse_version_range 109 | procedure, private :: parse_version_range 110 | generic :: extend_with => extend_comp_sets 111 | procedure, private :: extend_comp_sets 112 | end type 113 | 114 | contains 115 | 116 | !> Wrapper function for `try_create`. 117 | !> 118 | !> Can be invoked by calling the default constructor. 119 | !> 120 | !> In strict mode, all major, minor and patch versions must be provided. 121 | function create(major, minor, patch, prerelease, build, strict_mode) result(this) 122 | integer, intent(in) :: major 123 | integer, optional, intent(in) :: minor 124 | integer, optional, intent(in) :: patch 125 | character(*), optional, intent(in) :: prerelease 126 | character(*), optional, intent(in) :: build 127 | logical, optional, intent(in) :: strict_mode 128 | type(version_t) :: this 129 | 130 | type(error_t), allocatable :: error 131 | 132 | call try_create(this, major, minor, patch, prerelease, build, error, strict_mode) 133 | if (allocated(error)) error stop error%msg 134 | end 135 | 136 | !> Create a version from individual major, minor, patch, prerelease and build 137 | !> arguments. 138 | !> 139 | !> Version numbers must be positive integers. 140 | !> 141 | !> Prelease and build versions are entered through a series of dot-separated 142 | !> identifiers. The identifiers must be composed of ASCII letters, digits or 143 | !> hyphens. They must not be empty and must not begin or end with 144 | !> with a dot. Numerical identifiers must not start with a zero. 145 | !> 146 | !> Valid examples: 147 | !> 148 | !> ```fortran 149 | !> type(version_t) :: v 150 | !> type(error_t), allocatable :: err 151 | !> 152 | !> call v%create(0, 1, 0, error=err) ! 0.1.0 153 | !> call v%create(1, error=err) ! 1.0.0 154 | !> call v%create(1, 2, 3, 'alpha.1', 'build.1', err) ! 1.2.3-alpha.1+build.1 155 | !> ``` 156 | !> 157 | !> Invalid examples: 158 | !> 159 | !> ```fortran 160 | !> type(version_t) :: v 161 | !> type(error_t), allocatable :: err 162 | !> 163 | !> call v%create(0, -1, 0, error=err) ! allocated(err) == .true. 164 | !> call v%create(1, build='0.0', error=err) ! allocated(err) == .true. 165 | !> call v%create(1, prerelease='.hi.', error=err) ! allocated(err) == .true. 166 | !> ``` 167 | !> 168 | !> The default way is to create a version using the constructor. 169 | !> 170 | !> Use this procedure if you want to handle errors yourself. 171 | !> 172 | !> In strict mode, all major, minor and patch versions must be provided. 173 | subroutine try_create(this, major, minor, patch, prerelease, build, error, strict_mode) 174 | class(version_t), intent(out) :: this 175 | integer, intent(in) :: major 176 | integer, optional, intent(in) :: minor 177 | integer, optional, intent(in) :: patch 178 | character(*), optional, intent(in) :: prerelease 179 | character(*), optional, intent(in) :: build 180 | type(error_t), allocatable, intent(out) :: error 181 | logical, optional, intent(in) :: strict_mode 182 | 183 | logical :: is_strict_mode 184 | 185 | if (present(strict_mode)) then 186 | is_strict_mode = strict_mode 187 | else 188 | is_strict_mode = .false. 189 | end if 190 | 191 | if (major < 0) then 192 | error = error_t('Version numbers must not be negative.'); return 193 | end if 194 | this%major = major 195 | 196 | if (present(minor)) then 197 | if (minor < 0) then 198 | error = error_t('Version numbers must not be negative.'); return 199 | end if 200 | this%minor = minor 201 | else 202 | if (is_strict_mode) then 203 | error = error_t('Strict mode: Minor version must be provided.'); return 204 | end if 205 | this%minor = 0 206 | end if 207 | 208 | if (present(patch)) then 209 | if (patch < 0) then 210 | error = error_t('Version numbers must not be negative.'); return 211 | end if 212 | this%patch = patch 213 | else 214 | if (is_strict_mode) then 215 | error = error_t('Strict mode: Patch version must be provided.'); return 216 | end if 217 | this%patch = 0 218 | end if 219 | 220 | if (present(prerelease)) then 221 | call build_identifiers(this%prerelease, prerelease, error) 222 | if (allocated(error)) return 223 | end if 224 | 225 | if (present(build)) then 226 | call build_identifiers(this%build, build, error) 227 | if (allocated(error)) return 228 | end if 229 | end 230 | 231 | !> Returns a string representation of the version including prerelease and 232 | !> build data. 233 | pure function to_string(this) result(str) 234 | class(version_t), intent(in) :: this 235 | character(:), allocatable :: str 236 | 237 | integer :: i 238 | 239 | str = trim(int2s(this%major))//'.' & 240 | & //trim(int2s(this%minor))//'.' & 241 | & //trim(int2s(this%patch)) 242 | 243 | if (allocated(this%prerelease)) then 244 | str = str//'-' 245 | do i = 1, size(this%prerelease) 246 | str = str//this%prerelease(i)%str 247 | if (i < size(this%prerelease)) str = str//'.' 248 | end do 249 | end if 250 | 251 | if (allocated(this%build)) then 252 | str = str//'+' 253 | do i = 1, size(this%build) 254 | str = str//this%build(i)%str 255 | if (i < size(this%build)) str = str//'.' 256 | end do 257 | end if 258 | end 259 | 260 | !> Increments the major version number and resets the minor and patch number 261 | !> as well as the prerelease and build data. 262 | elemental subroutine increment_major(this) 263 | class(version_t), intent(inout) :: this 264 | 265 | this%major = this%major + 1 266 | this%minor = 0 267 | this%patch = 0 268 | 269 | if (allocated(this%prerelease)) deallocate (this%prerelease) 270 | if (allocated(this%build)) deallocate (this%build) 271 | end 272 | 273 | !> Increments the minor version number and resets patch, prerelease and build. 274 | elemental subroutine increment_minor(this) 275 | class(version_t), intent(inout) :: this 276 | 277 | this%minor = this%minor + 1 278 | this%patch = 0 279 | 280 | if (allocated(this%prerelease)) deallocate (this%prerelease) 281 | if (allocated(this%build)) deallocate (this%build) 282 | end 283 | 284 | !> Increments the patch version number and resets prerelease and build. 285 | elemental subroutine increment_patch(this) 286 | class(version_t), intent(inout) :: this 287 | 288 | this%patch = this%patch + 1 289 | 290 | if (allocated(this%prerelease)) deallocate (this%prerelease) 291 | if (allocated(this%build)) deallocate (this%build) 292 | end 293 | 294 | !> Increment prerelease and reset build data. 295 | elemental subroutine increment_prerelease(this) 296 | class(version_t), intent(inout) :: this 297 | 298 | call increment_identifier(this%prerelease) 299 | if (allocated(this%build)) deallocate (this%build) 300 | end 301 | 302 | !> Increment build metadata. 303 | elemental subroutine increment_build(this) 304 | class(version_t), intent(inout) :: this 305 | 306 | call increment_identifier(this%build) 307 | end 308 | 309 | !> Increment prerelease or build identifiers. If the last identifier is 310 | !> numeric, increment it by 1. Otherwise add a new identifier with the value 311 | !> 1. 312 | pure subroutine increment_identifier(ids) 313 | type(string_t), allocatable, intent(inout) :: ids(:) 314 | 315 | if (allocated(ids)) then 316 | if (ids(size(ids))%is_numeric()) then 317 | ids = [ids(1:size(ids) - 1), string_t(trim(int2s(ids(size(ids))%num() + 1)))] 318 | else 319 | ids = [ids, string_t('1')] 320 | end if 321 | else 322 | allocate (ids(1)) 323 | ids(1)%str = '1' 324 | end if 325 | end 326 | 327 | !> Parse a string into a version including prerelease and build data. 328 | !> 329 | !> Wrapper function for `try_parse`. 330 | !> 331 | !> Can be invoked by calling the default constructor. 332 | !> 333 | !> In strict mode, all major, minor and patch versions must be provided. Implicit 334 | !> zeros are forbidden in strict mode. 335 | function parse(str, strict_mode) result(version) 336 | character(*), intent(in) :: str 337 | logical, optional, intent(in) :: strict_mode 338 | type(version_t) :: version 339 | 340 | type(error_t), allocatable :: error 341 | 342 | call version%parse(str, error, strict_mode) 343 | if (allocated(error)) error stop error%msg 344 | end 345 | 346 | !> Attempt to parse a string into a version including prerelease and build 347 | !> data. In strict mode, all major, minor and patch versions must be provided. 348 | !> Implicit zeros are forbidden in strict mode. 349 | subroutine try_parse(this, string, error, strict_mode) 350 | class(version_t), intent(out) :: this 351 | character(*), intent(in) :: string 352 | type(error_t), allocatable, intent(out) :: error 353 | logical, optional, intent(in) :: strict_mode 354 | 355 | integer :: i, j 356 | character(:), allocatable :: str 357 | 358 | str = trim(adjustl(string)) 359 | 360 | i = index(str, '-') 361 | j = index(str, '+') 362 | 363 | if (i == 0 .and. j == 0) then 364 | call build_mmp(this, str, error, strict_mode); return 365 | else if (i /= 0 .and. j == 0) then 366 | call build_mmp(this, str(1:i - 1), error, strict_mode) 367 | if (allocated(error)) return 368 | call build_identifiers(this%prerelease, str(i + 1:len_trim(str)), error); return 369 | else if ((i == 0 .and. j /= 0) .or. ((i /= 0 .and. j /= 0) .and. (i > j))) then 370 | call build_mmp(this, str(1:j - 1), error, strict_mode) 371 | if (allocated(error)) return 372 | call build_identifiers(this%build, str(j + 1:len_trim(str)), error); return 373 | else if (i /= 0 .and. j /= 0) then 374 | call build_mmp(this, str(1:i - 1), error, strict_mode) 375 | if (allocated(error)) return 376 | call build_identifiers(this%prerelease, str(i + 1:j - 1), error) 377 | if (allocated(error)) return 378 | call build_identifiers(this%build, str(j + 1:len_trim(str)), error); return 379 | end if 380 | end 381 | 382 | !> Build the `major.minor.patch` part of the version. In strict mode, all 383 | !> major, minor and patch versions must be provided. Implicit zeros are 384 | !> forbidden in strict mode. 385 | subroutine build_mmp(this, str, error, strict_mode) 386 | type(version_t), intent(out) :: this 387 | character(*), intent(in) :: str 388 | type(error_t), allocatable, intent(out) :: error 389 | logical, optional, intent(in) :: strict_mode 390 | 391 | integer :: i, j, l 392 | logical :: is_strict_mode 393 | 394 | if (present(strict_mode)) then 395 | is_strict_mode = strict_mode 396 | else 397 | is_strict_mode = .false. 398 | end if 399 | 400 | this%major = 0 401 | this%minor = 0 402 | this%patch = 0 403 | 404 | i = index(str, '.') 405 | l = len_trim(str) 406 | 407 | if (l == 0) then 408 | error = error_t('Version must not be empty.'); return 409 | end if 410 | 411 | if (i == 0) then 412 | if (is_strict_mode) then 413 | error = error_t('Strict mode: No minor and patch versions provided.'); return 414 | end if 415 | call s2int(str, this%major, error) 416 | if (allocated(error)) return 417 | else 418 | if (is_strict_mode .and. i == 1) then 419 | error = error_t('Strict mode: Major version has to be number.'); return 420 | end if 421 | call s2int(str(1:i - 1), this%major, error) 422 | if (allocated(error)) return 423 | j = index(str(i + 1:l), '.') 424 | if (j == 0) then 425 | if (is_strict_mode) then 426 | error = error_t('Strict mode: No patch version provided.'); return 427 | end if 428 | call s2int(str(i + 1:l), this%minor, error) 429 | if (allocated(error)) return 430 | else 431 | if (is_strict_mode .and. j == 1) then 432 | error = error_t('Strict mode: Minor version has to be number.'); return 433 | end if 434 | call s2int(str(i + 1:i + j - 1), this%minor, error) 435 | if (allocated(error)) return 436 | if (is_strict_mode .and. len(str) == i + j) then 437 | error = error_t('Strict mode: Patch version has to be number.'); return 438 | end if 439 | call s2int(str(i + j + 1:l), this%patch, error) 440 | if (allocated(error)) return 441 | end if 442 | end if 443 | end 444 | 445 | !> Convert a string to an integer. 446 | pure subroutine s2int(str, num, error) 447 | character(*), intent(in) :: str 448 | integer, intent(out) :: num 449 | type(error_t), allocatable, intent(out) :: error 450 | 451 | integer :: i 452 | character :: c 453 | 454 | num = 0 455 | do i = 1, len(str) 456 | c = str(i:i) 457 | if (c >= '0' .and. c <= '9') then 458 | num = num*10 + index('0123456789', c) - 1 459 | else 460 | error = error_t("Contains non-digit: '"//str//"'."); return 461 | end if 462 | end do 463 | end 464 | 465 | !> Wrapper function for `s2int`. 466 | elemental integer function s2i(str) 467 | character(*), intent(in) :: str 468 | 469 | type(error_t), allocatable :: e 470 | 471 | call s2int(str, s2i, e) 472 | if (allocated(e)) error stop e%msg 473 | end 474 | 475 | !> Convert a `string_t` to an integer. 476 | elemental integer function string_t_2i(this) 477 | class(string_t), intent(in) :: this 478 | 479 | type(error_t), allocatable :: e 480 | 481 | call s2int(this%str, string_t_2i, e) 482 | if (allocated(e)) error stop e%msg 483 | end 484 | 485 | !> Convert an integer to a string. 486 | pure function int2s(num) result(str) 487 | integer, intent(in) :: num 488 | character(:), allocatable :: str 489 | 490 | integer :: digits, tmp 491 | 492 | tmp = num 493 | digits = 0 494 | 495 | do 496 | digits = digits + 1 497 | tmp = tmp/10 498 | if (tmp == 0) exit 499 | end do 500 | 501 | allocate (character(digits) :: str) 502 | write (str, '(I0)') num 503 | end 504 | 505 | !> Check for valid prerelease or build data and build identfiers from 506 | !> the string. 507 | pure subroutine build_identifiers(ids, str, error) 508 | type(string_t), allocatable, intent(out) :: ids(:) 509 | character(*), intent(in) :: str 510 | type(error_t), allocatable, intent(out) :: error 511 | 512 | character(*), parameter :: valid_chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWYYZ-.' 513 | character(:), allocatable :: string 514 | integer :: i 515 | 516 | if (len_trim(str) == 0) then 517 | error = error_t('Identifier must not be empty.'); return 518 | end if 519 | 520 | do i = 1, len(str) 521 | if (index(valid_chars, str(i:i)) == 0) then 522 | error = error_t("Invalid character in '"//str//"'."); return 523 | end if 524 | end do 525 | 526 | ! Last character must not be a dot. 527 | if (str(len(str):len(str)) == '.') then 528 | error = error_t('Identifier must not end with a dot.'); return 529 | end if 530 | 531 | string = str 532 | allocate (ids(0)) 533 | 534 | do 535 | i = index(string, '.') 536 | 537 | ! No dots (left), record last identifier and return. 538 | if (i == 0) then 539 | call validate_identifier(string, error) 540 | if (allocated(error)) return 541 | ids = [ids, string_t(string)]; return 542 | end if 543 | 544 | ! Validate and record identifier, then shorten string. 545 | call validate_identifier(string(1:i - 1), error) 546 | if (allocated(error)) return 547 | ids = [ids, string_t(string(1:i - 1))] 548 | string = string(i + 1:len(string)) 549 | end do 550 | end 551 | 552 | !> Validate an identifier. 553 | pure subroutine validate_identifier(str, error) 554 | character(*), intent(in) :: str 555 | type(error_t), allocatable, intent(out) :: error 556 | 557 | ! Empty identifiers are not allowed. 558 | if (len_trim(str) == 0) then 559 | error = error_t('Identifier must not be empty.'); return 560 | end if 561 | 562 | ! Identifiers must not start with `.`. 563 | if (str(1:1) == '.') then 564 | error = error_t("Identifiers must not start with '.'"); return 565 | end if 566 | 567 | ! Numerical identifiers must not start with 0. 568 | if (is_numerical(str) .and. str(1:1) == '0') then 569 | error = error_t("Numerical identifiers must not start with '0'."); return 570 | end if 571 | end 572 | 573 | !> Check if the string is purely numerical. 574 | elemental function is_numerical(str) 575 | character(*), intent(in) :: str 576 | logical :: is_numerical 577 | 578 | is_numerical = verify(str, '0123456789') == 0 579 | end 580 | 581 | !> Check if string_t is purely numeric. 582 | elemental function string_t_is_numeric(this) 583 | class(string_t), intent(in) :: this 584 | logical :: string_t_is_numeric 585 | 586 | string_t_is_numeric = verify(this%str, '0123456789') == 0 587 | end 588 | 589 | !> Check two versions for equality. 590 | elemental logical function equals(lhs, rhs) 591 | class(version_t), intent(in) :: lhs 592 | class(version_t), intent(in) :: rhs 593 | 594 | integer :: i 595 | 596 | equals = lhs%major == rhs%major & 597 | & .and. lhs%minor == rhs%minor & 598 | & .and. lhs%patch == rhs%patch 599 | 600 | if (.not. equals) return 601 | 602 | if (allocated(lhs%prerelease) .and. allocated(rhs%prerelease)) then 603 | if (size(lhs%prerelease) /= size(rhs%prerelease)) then 604 | equals = .false.; return 605 | end if 606 | do i = 1, size(lhs%prerelease) 607 | if (lhs%prerelease(i)%str /= rhs%prerelease(i)%str) then 608 | equals = .false.; return 609 | end if 610 | end do 611 | else if (allocated(lhs%prerelease) .or. allocated(rhs%prerelease)) then 612 | equals = .false. 613 | end if 614 | end 615 | 616 | !> Check two versions for inequality. 617 | elemental logical function not_equals(lhs, rhs) 618 | class(version_t), intent(in) :: lhs 619 | class(version_t), intent(in) :: rhs 620 | 621 | not_equals = .not. lhs == rhs 622 | end 623 | 624 | !> Check if the first version is greater than the second. 625 | elemental logical function greater_than(lhs, rhs) 626 | class(version_t), intent(in) :: lhs 627 | class(version_t), intent(in) :: rhs 628 | 629 | greater_than = lhs%major > rhs%major & 630 | & .or. (lhs%major == rhs%major & 631 | & .and. lhs%minor > rhs%minor) & 632 | & .or. (lhs%major == rhs%major & 633 | & .and. lhs%minor == rhs%minor & 634 | & .and. lhs%patch > rhs%patch) 635 | 636 | if (greater_than) return 637 | 638 | if (lhs%major == rhs%major .and. lhs%minor == rhs%minor .and. lhs%patch == rhs%patch) then 639 | if (allocated(lhs%prerelease) .and. .not. allocated(rhs%prerelease)) then 640 | greater_than = .false. 641 | else if (.not. allocated(lhs%prerelease) .and. allocated(rhs%prerelease)) then 642 | greater_than = .true. 643 | else if (allocated(lhs%prerelease) .and. allocated(rhs%prerelease)) then 644 | greater_than = is_greater(lhs%prerelease, rhs%prerelease) 645 | end if 646 | end if 647 | end 648 | 649 | !> Check if the first version is smaller than the second. 650 | elemental logical function less_than(lhs, rhs) 651 | class(version_t), intent(in) :: lhs 652 | class(version_t), intent(in) :: rhs 653 | 654 | less_than = .not. lhs > rhs .and. .not. lhs == rhs 655 | end 656 | 657 | !> Check if the first version is greater than or equal to the second. 658 | elemental logical function greater_equals(lhs, rhs) 659 | class(version_t), intent(in) :: lhs 660 | class(version_t), intent(in) :: rhs 661 | 662 | greater_equals = lhs > rhs .or. lhs == rhs 663 | end 664 | 665 | !> Check if the first version is smaller than or equal to the second. 666 | elemental logical function less_equals(lhs, rhs) 667 | class(version_t), intent(in) :: lhs 668 | class(version_t), intent(in) :: rhs 669 | 670 | less_equals = .not. lhs > rhs 671 | end 672 | 673 | !> Check if the first prerelease (`lhs`) is greater than the second (`rhs`). 674 | pure logical function is_greater(lhs, rhs) 675 | type(string_t), intent(in) :: lhs(:) 676 | type(string_t), intent(in) :: rhs(:) 677 | 678 | integer :: i, j 679 | 680 | do i = 1, min(size(lhs), size(rhs)) 681 | if (lhs(i)%str == rhs(i)%str) cycle 682 | if (lhs(i)%is_numeric() .and. rhs(i)%is_numeric()) then 683 | is_greater = s2i(lhs(i)%str) > s2i(rhs(i)%str); return 684 | else if (lhs(i)%is_numeric()) then 685 | is_greater = .false.; return 686 | else if (rhs(i)%is_numeric()) then 687 | is_greater = .true.; return 688 | end if 689 | 690 | do j = 1, min(len(lhs(i)%str), len(rhs(i)%str)) 691 | if (lhs(i)%str(j:j) == rhs(i)%str(j:j)) cycle 692 | is_greater = lhs(i)%str(j:j) > rhs(i)%str(j:j); return 693 | end do 694 | 695 | if (len(lhs(i)%str) /= len(rhs(i)%str)) then 696 | is_greater = len(lhs(i)%str) > len(rhs(i)%str); return 697 | end if 698 | end do 699 | 700 | is_greater = size(lhs) > size(rhs) 701 | end 702 | 703 | !> True if both versions are exactly the same including the build metadata. 704 | !> This procedure has been added for conveniece. It is not part of the 705 | !> Semantic Versioning 2.0.0 specification. 706 | elemental logical function is_exactly(self, other) 707 | class(version_t), intent(in) :: self 708 | type(version_t), intent(in) :: other 709 | 710 | integer :: i 711 | 712 | is_exactly = self == other; 713 | if (.not. is_exactly) return 714 | 715 | if (allocated(self%build) .and. allocated(other%build)) then 716 | if (size(self%build) /= size(other%build)) then 717 | is_exactly = .false.; return 718 | end if 719 | 720 | do i = 1, size(self%build) 721 | if (self%build(i)%str /= other%build(i)%str) then 722 | is_exactly = .false.; return 723 | end if 724 | end do 725 | else if (allocated(self%build) .or. allocated(other%build)) then 726 | is_exactly = .false.; return 727 | end if 728 | end 729 | 730 | !> True if the string can be parsed as a valid `version_t`. Use `parse` if you 731 | !> wish to receive detailed error messages. In strict mode, all major, minor 732 | !> and patch versions must be provided. Implicit zeros are forbidden in strict 733 | !> mode. 734 | logical function is_version(str, strict_mode) 735 | 736 | !> Input string. 737 | character(*), intent(in) :: str 738 | 739 | !> If true, all major, minor and patch versions must be provided. Implicit 740 | !> zeros are forbidden in strict mode. 741 | logical, optional, intent(in) :: strict_mode 742 | 743 | type(version_t) :: version 744 | type(error_t), allocatable :: error 745 | 746 | call version%parse(str, error, strict_mode) 747 | is_version = .not. allocated(error) 748 | end 749 | 750 | !> Helper function to generate a new `string_t` instance. 751 | elemental function create_string_t(inp_str) result(string) 752 | 753 | !> Input string. 754 | character(*), intent(in) :: inp_str 755 | 756 | !> The string instance. 757 | type(string_t) :: string 758 | 759 | string%str = inp_str 760 | end 761 | 762 | !> Helper function to generate a new `error_t` instance. 763 | elemental function create_error_t(msg) result(err) 764 | 765 | !> Error message. 766 | character(*), intent(in) :: msg 767 | 768 | !> The error instance. 769 | type(error_t) :: err 770 | 771 | err%msg = msg 772 | end 773 | 774 | !> Determine whether the version meets the comparison expressed in `str`. 775 | !> 776 | !> Valid operators are `>`, `>=`, `<`, `<=`, `=` and `!=`. 777 | !> 778 | !> Example: 779 | !> 780 | !> program main 781 | !> use version_f 782 | !> implicit none 783 | !> 784 | !> type(version_t) :: version 785 | !> character(*), parameter :: requirement = '>=1.2.3' 786 | !> logical :: is_satisfied 787 | !> type(error_t), allocatable :: error 788 | !> 789 | !> version = version_t(1, 2, 3) 790 | !> call version%try_satisfy(requirement, is_satisfied, error) 791 | !> if (allocated(error)) return 792 | !> 793 | !> if (is_satisfied) then 794 | !> print *, "Version '", version%to_string(), "' meets the requirement '", requirement, "'." 795 | !> else 796 | !> print *, "Version '", version%to_string(), "' does not meet the requirement '", requirement, "'." 797 | !> end if 798 | !> end 799 | subroutine try_satisfy(this, string, is_satisfied, error) 800 | 801 | !> Version to be evaluated. 802 | class(version_t), intent(in) :: this 803 | 804 | !> Input string to be evaluated. 805 | character(*), intent(in) :: string 806 | 807 | !> Whether the version meets the comparison expressed in `str`. 808 | logical, intent(out) :: is_satisfied 809 | 810 | !> Error handling. 811 | type(error_t), allocatable, intent(out) :: error 812 | 813 | character(:), allocatable :: str 814 | type(version_range_t) :: version_range 815 | integer :: i 816 | 817 | str = trim(adjustl(string)) 818 | 819 | if (len(str) == 0) then 820 | error = error_t('Do not compare empty expressions.'); return 821 | end if 822 | 823 | call version_range%parse(str, error) 824 | if (allocated(error)) return 825 | 826 | do i = 1, size(version_range%comp_sets) 827 | call this%satisfies_comp_set(version_range%comp_sets(i), is_satisfied, error) 828 | if (is_satisfied .or. allocated(error)) return 829 | end do 830 | end 831 | 832 | !> Convenience function to determine whether the version meets the comparison. 833 | !> 834 | !> Wrapper function for `try_satisfy`, which returns `.false.` if the 835 | !> comparison fails. 836 | logical function satisfies(this, str) 837 | 838 | !> Instance of `version_t` to be evaluated. 839 | class(version_t), intent(in) :: this 840 | 841 | !> Input string to be evaluated. 842 | character(*), intent(in) :: str 843 | 844 | type(error_t), allocatable :: error 845 | 846 | call this%try_satisfy(str, satisfies, error) 847 | if (allocated(error)) satisfies = .false. 848 | end 849 | 850 | !> Create sets of comparators that are separated by `||`. An example of a 851 | !> version range is `4.2.3 || 5.0.0 - 7.2.3`. 852 | subroutine parse_version_range(this, string, error) 853 | 854 | !> Sets of comparators to be determined. They are separated by `||` if there 855 | !> are multiple sets. 856 | class(version_range_t), intent(out) :: this 857 | 858 | !> Input string to be evaluated. 859 | character(*), intent(in) :: string 860 | 861 | !> Error handling. 862 | type(error_t), allocatable, intent(out) :: error 863 | 864 | integer :: i_sep 865 | character(:), allocatable :: str 866 | type(comparator_set_t) :: comp_set 867 | 868 | str = string 869 | allocate (this%comp_sets(0)) 870 | 871 | i_sep = index(str, '||') 872 | 873 | do while (i_sep /= 0) 874 | call comp_set%parse_comp_set(str(1:i_sep - 1), error) 875 | if (allocated(error)) return 876 | 877 | call this%extend_with(comp_set) 878 | str = str(i_sep + 2:) 879 | i_sep = index(str, '||') 880 | end do 881 | 882 | call comp_set%parse_comp_set(str, error) 883 | if (allocated(error)) return 884 | 885 | call this%extend_with(comp_set) 886 | end 887 | 888 | !> Extend array of comparator sets within version range with another comparator. 889 | subroutine extend_comp_sets(range, comp_set) 890 | class(version_range_t), intent(inout) :: range 891 | type(comparator_set_t), intent(in) :: comp_set 892 | 893 | type(comparator_set_t), allocatable :: tmp(:) 894 | 895 | allocate (tmp(size(range%comp_sets) + 1)) 896 | tmp(1:size(range%comp_sets)) = range%comp_sets 897 | tmp(size(tmp)) = comp_set 898 | call move_alloc(tmp, range%comp_sets) 899 | end 900 | 901 | !> Parse a set of comparators that are separated by ` ` from a string. An 902 | !> example of a set of two comparators is `>=1.2.3 <2.0.0`. 903 | subroutine parse_comp_set(this, string, error) 904 | 905 | !> Set of comparators to be determined. They are separated by ` ` if there 906 | !> are multiple comparators. 907 | class(comparator_set_t), intent(out) :: this 908 | 909 | !> Input string to be evaluated. 910 | character(*), intent(in) :: string 911 | 912 | !> Error handling. 913 | type(error_t), allocatable, intent(out) :: error 914 | 915 | character(:), allocatable :: str 916 | type(comparator_t) :: comp 917 | 918 | str = trim(adjustl(string)) 919 | 920 | if (len(str) == 0) then 921 | error = error_t('Comparator set cannot be empty.'); return 922 | end if 923 | 924 | allocate (this%comps(0)) 925 | 926 | do 927 | if (len(str) == 0) then 928 | call comp%parse_comp_and_crop_str('', str, error) 929 | else if (str(1:1) == '>') then 930 | if (len(str) == 1) then 931 | call comp%parse_comp_and_crop_str('>', str, error) 932 | else if (str(2:2) == '=') then 933 | call comp%parse_comp_and_crop_str('>=', str, error) 934 | else 935 | call comp%parse_comp_and_crop_str('>', str, error) 936 | end if 937 | else if (str(1:1) == '<') then 938 | if (len(str) == 1) then 939 | call comp%parse_comp_and_crop_str('<', str, error) 940 | else if (str(2:2) == '=') then 941 | call comp%parse_comp_and_crop_str('<=', str, error) 942 | else 943 | call comp%parse_comp_and_crop_str('<', str, error) 944 | end if 945 | else if (str(1:1) == '=') then 946 | call comp%parse_comp_and_crop_str('=', str, error) 947 | else if (len(str) == 1) then 948 | call comp%parse_comp_and_crop_str('', str, error) 949 | else if (str(1:2) == '!=') then 950 | call comp%parse_comp_and_crop_str('!=', str, error) 951 | else 952 | call comp%parse_comp_and_crop_str('', str, error) 953 | end if 954 | 955 | if (allocated(error)) return 956 | call this%extend_with(comp) 957 | if (str == '') return 958 | str = trim(adjustl(str)) 959 | end do 960 | end 961 | 962 | !> Extend array of comparators within comparator set with another comparator. 963 | subroutine extend_comps(set, comp) 964 | class(comparator_set_t), intent(inout) :: set 965 | type(comparator_t), intent(in) :: comp 966 | 967 | type(comparator_t), allocatable :: tmp(:) 968 | 969 | allocate (tmp(size(set%comps) + 1)) 970 | tmp(1:size(set%comps)) = set%comps 971 | tmp(size(tmp)) = comp 972 | call move_alloc(tmp, set%comps) 973 | end 974 | 975 | !> Create a comparator from a string. A comparator consists of an operator and 976 | !> a version. An example of a comparator is `>=1.2.3`. 977 | subroutine parse_comp_and_crop_str(comp, op, str, error) 978 | 979 | !> Comparator to be determined. 980 | class(comparator_t), intent(out) :: comp 981 | 982 | !> The operator of the comparator. 983 | character(*), intent(in) :: op 984 | 985 | !> Input string to be evaluated. 986 | character(*), intent(inout) :: str 987 | 988 | !> Error handling. 989 | type(error_t), allocatable, intent(out) :: error 990 | 991 | integer :: i 992 | 993 | comp%op = op 994 | str = trim(adjustl(str(len(op) + 1:))) 995 | 996 | i = operator_index(str) 997 | if (i == 0) then 998 | call comp%version%parse(str, error) 999 | str = '' 1000 | else 1001 | call comp%version%parse(str(:i - 1), error) 1002 | str = str(i:) 1003 | end if 1004 | if (allocated(error)) return 1005 | end 1006 | 1007 | !> Index of the first operator (`>`, `<`, `!`, `=` or ` `) within a string. 1008 | elemental integer function operator_index(str) 1009 | 1010 | !> Input string to be evaluated. 1011 | character(*), intent(in) :: str 1012 | 1013 | integer :: i 1014 | character :: char 1015 | 1016 | do i = 1, len(str) 1017 | char = str(i:i) 1018 | if (char == '>' .or. char == '<' .or. char == '!' .or. char == '=' .or. char == ' ') then 1019 | operator_index = i; return 1020 | end if 1021 | end do 1022 | 1023 | operator_index = 0 1024 | end 1025 | 1026 | !> Attempt to evaluate a comparator set. A comparator set consists of multiple 1027 | !> comparators that are separated by ` `. An example of a comparator set is 1028 | !> `>=1.2.3 <2.0.0`. A comparator set is satisfied if all of its comparators 1029 | !> are satisfied. 1030 | pure subroutine satisfies_comp_set(version, comp_set, is_satisfied, error) 1031 | 1032 | !> Instance of `version_t` to be evaluated. 1033 | class(version_t), intent(in) :: version 1034 | 1035 | !> Set of comparators to be evaluated. 1036 | type(comparator_set_t), intent(in) :: comp_set 1037 | 1038 | !> Whether the comparator set is satisfied. 1039 | logical, intent(out) :: is_satisfied 1040 | 1041 | !> Error handling. 1042 | type(error_t), allocatable, intent(out) :: error 1043 | 1044 | integer :: i 1045 | 1046 | if (size(comp_set%comps) == 0) then 1047 | error = error_t('Comparator set cannot be empty.'); return 1048 | end if 1049 | 1050 | do i = 1, size(comp_set%comps) 1051 | call version%satisfies_comp(comp_set%comps(i), is_satisfied, error) 1052 | if (.not. is_satisfied .or. allocated(error)) return 1053 | end do 1054 | end 1055 | 1056 | !> Attempt to evaluate a comparator which consists of a comparison operator 1057 | !> and a version string. 1058 | pure subroutine satisfies_comp(this, comparator, is_satisfied, error) 1059 | 1060 | !> Instance of `version_t` to be evaluated. 1061 | class(version_t), intent(in) :: this 1062 | 1063 | !> Comparator to be evaluated. 1064 | type(comparator_t), intent(in) :: comparator 1065 | 1066 | !> Whether the version meets the comparison expressed in `comparator`. 1067 | logical, intent(out) :: is_satisfied 1068 | 1069 | !> Error handling. 1070 | type(error_t), allocatable, intent(out) :: error 1071 | 1072 | if (comparator%op == '>') then 1073 | is_satisfied = this > comparator%version 1074 | else if (comparator%op == '>=') then 1075 | is_satisfied = this >= comparator%version 1076 | else if (comparator%op == '<') then 1077 | is_satisfied = this < comparator%version 1078 | else if (comparator%op == '<=') then 1079 | is_satisfied = this <= comparator%version 1080 | else if (comparator%op == '!=') then 1081 | is_satisfied = this /= comparator%version 1082 | else if (comparator%op == '=' .or. comparator%op == '') then 1083 | is_satisfied = this == comparator%version 1084 | else 1085 | error = error_t("Invalid operator: '"//comparator%op//"'.") 1086 | end if 1087 | end 1088 | 1089 | !> Create instance of `comparator_t` using an operator (`op`) and a version. 1090 | elemental function create_comp(op, version) result(comparator) 1091 | 1092 | !> The operator of the comparator. 1093 | character(*), intent(in) :: op 1094 | 1095 | !> The version of the comparator. 1096 | type(version_t), intent(in) :: version 1097 | 1098 | !> Instance of `comparator_t` created from `op` and `version`. 1099 | type(comparator_t) :: comparator 1100 | 1101 | comparator%op = op 1102 | comparator%version = version 1103 | end 1104 | 1105 | !> Create instance of `comparator_set_t` using an array of comparators. 1106 | pure function create_comp_set(comps) result(comp_set) 1107 | 1108 | !> Array of comparators to create the set from. 1109 | type(comparator_t), intent(in) :: comps(:) 1110 | 1111 | !> Instance of `comparator_set_t` created from `comps`. 1112 | type(comparator_set_t) :: comp_set 1113 | 1114 | allocate (comp_set%comps(size(comps))) 1115 | comp_set%comps = comps 1116 | end 1117 | 1118 | !> Returns true if the version is stable. A version is stable if its major 1119 | !> version is greater than zero and the version is not a prerelease. 1120 | elemental logical function is_stable(version) 1121 | 1122 | !> Instance of `version_t` to be evaluated. 1123 | class(version_t), intent(in) :: version 1124 | 1125 | is_stable = version%major > 0 .and. .not. allocated(version%prerelease) 1126 | end 1127 | end 1128 | -------------------------------------------------------------------------------- /test/version_f_test.f90: -------------------------------------------------------------------------------- 1 | program test 2 | use version_f 3 | 4 | implicit none 5 | 6 | type(version_t) :: v1, v2 7 | logical :: is_satisfied 8 | type(comparator_t), allocatable :: comps(:) 9 | type(comparator_set_t) :: comp_set 10 | type(version_range_t) :: range 11 | type(error_t), allocatable :: e 12 | 13 | !################################### Create ###################################! 14 | 15 | v1 = version_t(0, 0, 0) 16 | if (v1%to_string() /= '0.0.0') then 17 | call fail("Parsing failed for '"//v1%to_string()//"'") 18 | end if 19 | 20 | v1 = version_t(1, 2, 3) 21 | if (v1%to_string() /= '1.2.3') then 22 | call fail("Parsing failed for '"//v1%to_string()//"'") 23 | end if 24 | 25 | v1 = version_t(9999999, 0, 21) 26 | if (v1%to_string() /= '9999999.0.21') then 27 | call fail("Parsing failed for '"//v1%to_string()//"'") 28 | end if 29 | 30 | v1 = version_t(0) 31 | if (v1%to_string() /= '0.0.0') then 32 | call fail("Parsing failed for '"//v1%to_string()//"'") 33 | end if 34 | 35 | v1 = version_t(10) 36 | if (v1%to_string() /= '10.0.0') then 37 | call fail("Parsing failed for '"//v1%to_string()//"'") 38 | end if 39 | 40 | v1 = version_t(2, 25) 41 | if (v1%to_string() /= '2.25.0') then 42 | call fail("Parsing failed for '"//v1%to_string()//"'") 43 | end if 44 | 45 | v1 = version_t(00002, 25, 0000090) 46 | if (v1%to_string() /= '2.25.90') then 47 | call fail("Parsing failed for '"//v1%to_string()//"'") 48 | end if 49 | 50 | call v1%create(-1, error=e) 51 | if (.not. allocated(e)) call fail('A negative number should fail.') 52 | 53 | call v1%create(1, -3, error=e) 54 | if (.not. allocated(e)) call fail('A negative number should fail.') 55 | 56 | call v1%create(1, 5, -3, error=e) 57 | if (.not. allocated(e)) call fail('A negative number should fail.') 58 | 59 | call v1%create(1, 5, 3, '', error=e) 60 | if (.not. allocated(e)) call fail('An empty string should fail.') 61 | 62 | call v1%create(1, 5, 3, '1234/', error=e) 63 | if (.not. allocated(e)) call fail('Invalid character.') 64 | 65 | call v1%create(1, 5, 3, 'abc', '', e) 66 | if (.not. allocated(e)) call fail('An empty string should fail.') 67 | 68 | call v1%create(1, 5, 3, 'abc', 'abc&def', e) 69 | if (.not. allocated(e)) call fail('Invalid character.') 70 | 71 | v1 = version_t(1, 5, 3, 'abc', '789') 72 | if (v1%to_string() /= '1.5.3-abc+789') then 73 | call fail("Parsing failed for '"//v1%to_string()//"'") 74 | end if 75 | 76 | v1 = version_t(1, prerelease='abc.def---', build='---789.abc') 77 | if (v1%to_string() /= '1.0.0-abc.def---+---789.abc') then 78 | call fail("Parsing failed for '"//v1%to_string()//"'") 79 | end if 80 | 81 | call v1%create(1, prerelease='abc....ded', build='---789.abc', error=e) 82 | if (.not. allocated(e)) call fail('Invalid prerelease missed.') 83 | 84 | call v1%create(1, prerelease='abc.ded', build='--..-789.abc', error=e) 85 | if (.not. allocated(e)) call fail('Invalid build missed.') 86 | 87 | call v1%create(1, prerelease='0abc.ded', build='---789.ab missedc', error=e) 88 | if (.not. allocated(e)) call fail('Invalid prerelease missed.') 89 | 90 | call v1%create(1, prerelease='abc.ded', build='05567.abc', error=e) 91 | if (.not. allocated(e)) call fail('Invalid build missed.') 92 | 93 | v1 = version_t(0, 1, 0, prerelease='abc.def---', build='0---789.abc') 94 | if (v1%to_string() /= '0.1.0-abc.def---+0---789.abc') then 95 | call fail("Parsing failed for '"//v1%to_string()//"'") 96 | end if 97 | 98 | call v1%create(1, prerelease='abc.ded', build='.', error=e) 99 | if (.not. allocated(e)) call fail('Invalid build missed.') 100 | 101 | call v1%create(1, prerelease='d.', build='9', error=e) 102 | if (.not. allocated(e)) call fail('Invalid prerelease missed.') 103 | 104 | v1 = version_t(1, 3, prerelease='a.b.c.d.e', build='---789.abc') 105 | if (v1%to_string() /= '1.3.0-a.b.c.d.e+---789.abc') then 106 | call fail("Parsing failed for '"//v1%to_string()//"'") 107 | end if 108 | 109 | v1 = version_t(1, 3, prerelease='a000', build='---789.abc') 110 | if (v1%to_string() /= '1.3.0-a000+---789.abc') then 111 | call fail("Parsing failed for '"//v1%to_string()//"'") 112 | end if 113 | 114 | v1 = version_t(1, 3, prerelease='0abc', build='000-') 115 | if (v1%to_string() /= '1.3.0-0abc+000-') then 116 | call fail("Parsing failed for '"//v1%to_string()//"'") 117 | end if 118 | 119 | call v1%create(1, prerelease='d', build='9.0', error=e) 120 | if (.not. allocated(e)) call fail('Invalid build missed.') 121 | 122 | !################################# Increment ##################################! 123 | 124 | v1 = version_t(2, 25, 0, 'ab0c', '123') 125 | call v1%increment_major() 126 | if (v1%to_string() /= '3.0.0') then 127 | call fail("Parsing failed for '"//v1%to_string()//"'") 128 | end if 129 | 130 | v1 = version_t(2, 25, 46, 'abc', '12tg3') 131 | call v1%increment_minor() 132 | if (v1%to_string() /= '2.26.0') then 133 | call fail("Parsing failed for '"//v1%to_string()//"'") 134 | end if 135 | 136 | v1 = version_t(2, 25, 46, 'abc.789', '---123') 137 | call v1%increment_patch() 138 | if (v1%to_string() /= '2.25.47') then 139 | call fail("Parsing failed for '"//v1%to_string()//"'") 140 | end if 141 | 142 | v1 = version_t(1, 2, 3) 143 | call v1%increment_prerelease() 144 | if (v1%to_string() /= '1.2.3-1') then 145 | call fail("Parsing failed for '"//v1%to_string()//"'") 146 | end if 147 | 148 | v1 = version_t(1, 2, 3, build='123') 149 | call v1%increment_prerelease() 150 | if (v1%to_string() /= '1.2.3-1') then 151 | call fail("Parsing failed for '"//v1%to_string()//"'") 152 | end if 153 | 154 | v1 = version_t(1, 2, 3, '80') 155 | call v1%increment_prerelease() 156 | if (v1%to_string() /= '1.2.3-81') then 157 | call fail("Parsing failed for '"//v1%to_string()//"'") 158 | end if 159 | 160 | v1 = version_t(1, 2, 3, '80', '123') 161 | call v1%increment_prerelease() 162 | if (v1%to_string() /= '1.2.3-81') then 163 | call fail("Parsing failed for '"//v1%to_string()//"'") 164 | end if 165 | 166 | v1 = version_t(1, 2, 3, 'abc.789') 167 | call v1%increment_prerelease() 168 | if (v1%to_string() /= '1.2.3-abc.790') then 169 | call fail("Parsing failed for '"//v1%to_string()//"'") 170 | end if 171 | 172 | v1 = version_t(1, 2, 3, '123.789') 173 | call v1%increment_prerelease() 174 | if (v1%to_string() /= '1.2.3-123.790') then 175 | call fail("Parsing failed for '"//v1%to_string()//"'") 176 | end if 177 | 178 | v1 = version_t(1, 2, 3, 'abc') 179 | call v1%increment_prerelease() 180 | if (v1%to_string() /= '1.2.3-abc.1') then 181 | call fail("Parsing failed for '"//v1%to_string()//"'") 182 | end if 183 | 184 | v1 = version_t(1, 2, 3, 'a23c') 185 | call v1%increment_prerelease() 186 | if (v1%to_string() /= '1.2.3-a23c.1') then 187 | call fail("Parsing failed for '"//v1%to_string()//"'") 188 | end if 189 | 190 | v1 = version_t(1, 2, 3, build='1') 191 | call v1%increment_build() 192 | if (v1%to_string() /= '1.2.3+2') then 193 | call fail("Parsing failed for '"//v1%to_string()//"'") 194 | end if 195 | 196 | v1 = version_t(1, 2, 3) 197 | call v1%increment_build() 198 | if (v1%to_string() /= '1.2.3+1') then 199 | call fail("Parsing failed for '"//v1%to_string()//"'") 200 | end if 201 | 202 | v1 = version_t(1, 2, 3, 'abc') 203 | call v1%increment_build() 204 | if (v1%to_string() /= '1.2.3-abc+1') then 205 | call fail("Parsing failed for '"//v1%to_string()//"'") 206 | end if 207 | 208 | v1 = version_t(1, 2, 3, 'abc', '123') 209 | call v1%increment_build() 210 | if (v1%to_string() /= '1.2.3-abc+124') then 211 | call fail("Parsing failed for '"//v1%to_string()//"'") 212 | end if 213 | 214 | v1 = version_t(1, 2, 3, '123', '123') 215 | call v1%increment_build() 216 | if (v1%to_string() /= '1.2.3-123+124') then 217 | call fail("Parsing failed for '"//v1%to_string()//"'") 218 | end if 219 | 220 | v1 = version_t(1, 2, 3, '123', 'abc') 221 | call v1%increment_build() 222 | if (v1%to_string() /= '1.2.3-123+abc.1') then 223 | call fail("Parsing failed for '"//v1%to_string()//"'") 224 | end if 225 | 226 | v1 = version_t(1, 2, 3, '123', '78H') 227 | call v1%increment_build() 228 | if (v1%to_string() /= '1.2.3-123+78H.1') then 229 | call fail("Parsing failed for '"//v1%to_string()//"'") 230 | end if 231 | 232 | v1 = version_t(1, 2, 3, '123', '78-') 233 | call v1%increment_build() 234 | if (v1%to_string() /= '1.2.3-123+78-.1') then 235 | call fail("Parsing failed for '"//v1%to_string()//"'") 236 | end if 237 | 238 | !################################### Parse ####################################! 239 | 240 | call v1%parse('0', e) 241 | if (allocated(e)) then 242 | call fail(e%msg) 243 | else if (v1%to_string() /= '0.0.0') then 244 | call fail("Parsing failed for '"//v1%to_string()//"'") 245 | end if 246 | 247 | call v1%parse('.', e) 248 | if (allocated(e)) then 249 | call fail(e%msg) 250 | else if (v1%to_string() /= '0.0.0') then 251 | call fail("Parsing failed for '"//v1%to_string()//"'") 252 | end if 253 | 254 | call v1%parse('0.1', e) 255 | if (allocated(e)) then 256 | call fail(e%msg) 257 | else if (v1%to_string() /= '0.1.0') then 258 | call fail("Parsing failed for '"//v1%to_string()//"'") 259 | end if 260 | 261 | call v1%parse('..988', e) 262 | if (allocated(e)) then 263 | call fail(e%msg) 264 | else if (v1%to_string() /= '0.0.988') then 265 | call fail("Parsing failed for '"//v1%to_string()//"'") 266 | end if 267 | 268 | call v1%parse('1..988', e) 269 | if (allocated(e)) then 270 | call fail(e%msg) 271 | else if (v1%to_string() /= '1.0.988') then 272 | call fail("Parsing failed for '"//v1%to_string()//"'") 273 | end if 274 | 275 | call v1%parse('.1.', e) 276 | if (allocated(e)) then 277 | call fail(e%msg) 278 | else if (v1%to_string() /= '0.1.0') then 279 | call fail("Parsing failed for '"//v1%to_string()//"'") 280 | end if 281 | 282 | call v1%parse('..', e) 283 | if (allocated(e)) then 284 | call fail(e%msg) 285 | else if (v1%to_string() /= '0.0.0') then 286 | call fail("Parsing failed for '"//v1%to_string()//"'") 287 | end if 288 | 289 | call v1%parse('', e) 290 | if (.not. allocated(e)) call fail('An empty string should fail.') 291 | 292 | call v1%parse('-1', e) 293 | if (.not. allocated(e)) call fail('A negative number should fail.') 294 | 295 | call v1%parse('.-1.', e) 296 | if (.not. allocated(e)) call fail('A negative number should fail.') 297 | 298 | call v1%parse('a', e) 299 | if (.not. allocated(e)) call fail('Invalid character should fail.') 300 | 301 | call v1%parse('..a', e) 302 | if (.not. allocated(e)) call fail('Invalid character should fail.') 303 | 304 | call v1%parse('0.1.0.2', e) 305 | if (.not. allocated(e)) call fail('Too many dots.') 306 | 307 | call v1%parse('1-1', e) 308 | if (allocated(e)) then 309 | call fail(e%msg) 310 | else if (v1%to_string() /= '1.0.0-1') then 311 | call fail("Parsing failed for '"//v1%to_string()//"'") 312 | end if 313 | 314 | call v1%parse('8.1-1', e) 315 | if (allocated(e)) then 316 | call fail(e%msg) 317 | else if (v1%to_string() /= '8.1.0-1') then 318 | call fail("Parsing failed for '"//v1%to_string()//"'") 319 | end if 320 | 321 | call v1%parse('1-1.1', e) 322 | if (allocated(e)) then 323 | call fail(e%msg) 324 | else if (v1%to_string() /= '1.0.0-1.1') then 325 | call fail("Parsing failed for '"//v1%to_string()//"'") 326 | end if 327 | 328 | call v1%parse('8.1-1.9-9--.2', e) 329 | if (allocated(e)) then 330 | call fail(e%msg) 331 | else if (v1%to_string() /= '8.1.0-1.9-9--.2') then 332 | call fail("Parsing failed for '"//v1%to_string()//"'") 333 | end if 334 | 335 | call v1%parse('1+1', e) 336 | if (allocated(e)) then 337 | call fail(e%msg) 338 | else if (v1%to_string() /= '1.0.0+1') then 339 | call fail("Parsing failed for '"//v1%to_string()//"'") 340 | end if 341 | 342 | call v1%parse('1+1.1-0P.2', e) 343 | if (allocated(e)) then 344 | call fail(e%msg) 345 | else if (v1%to_string() /= '1.0.0+1.1-0P.2') then 346 | call fail("Parsing failed for '"//v1%to_string()//"'") 347 | end if 348 | 349 | call v1%parse('1+f-1', e) 350 | if (allocated(e)) then 351 | call fail(e%msg) 352 | else if (v1%to_string() /= '1.0.0+f-1') then 353 | call fail("Parsing failed for '"//v1%to_string()//"'") 354 | end if 355 | 356 | call v1%parse('1-23+1-1', e) 357 | if (allocated(e)) then 358 | call fail(e%msg) 359 | else if (v1%to_string() /= '1.0.0-23+1-1') then 360 | call fail("Parsing failed for '"//v1%to_string()//"'") 361 | end if 362 | 363 | call v1%parse('1.0.1-43.fs23+1-1', e) 364 | if (allocated(e)) then 365 | call fail(e%msg) 366 | else if (v1%to_string() /= '1.0.1-43.fs23+1-1') then 367 | call fail("Parsing failed for '"//v1%to_string()//"'") 368 | end if 369 | 370 | call v1%parse('1-0', e) 371 | if (.not. allocated(e)) call fail('Leading zero identifier not allowed.') 372 | 373 | call v1%parse('1-hff.08', e) 374 | if (.not. allocated(e)) call fail('Leading zero identifier not allowed.') 375 | 376 | call v1%parse('1-hff.87+08', e) 377 | if (.not. allocated(e)) call fail('Leading zero identifier not allowed.') 378 | 379 | call v1%parse('1-hff.87+fejf.08', e) 380 | if (.not. allocated(e)) call fail('Leading zero identifier not allowed.') 381 | 382 | call v1%parse('1-..', e) 383 | if (.not. allocated(e)) call fail('No consecutive dots.') 384 | 385 | call v1%parse('1-irhife..oihie', e) 386 | if (.not. allocated(e)) call fail('No consecutive dots.') 387 | 388 | call v1%parse('1-irh+ife..oihie.', e) 389 | if (.not. allocated(e)) call fail('Trailing dot.') 390 | 391 | call v1%parse('1-irh+.ife..oihie', e) 392 | if (.not. allocated(e)) call fail('Leading dot.') 393 | 394 | !################################## Compare ###################################! 395 | 396 | v1 = version_t(1, 2, 3) 397 | v2 = version_t(1, 2, 3) 398 | if (.not. v1 == v2) call fail('Equality failed.') 399 | if (v1 /= v2) call fail('Inequality failed.') 400 | if (v1 < v2) call fail('Less than failed.') 401 | if (v1 > v2) call fail('Greater than failed.') 402 | if (.not. v1 <= v2) call fail('Less than or equal failed.') 403 | if (.not. v1 >= v2) call fail('Greater than or equal failed.') 404 | 405 | v1 = version_t(1, 2, 3) 406 | v2 = version_t('1.2.3') 407 | if (.not. v1 == v2) call fail('Equality failed.') 408 | if (v1 /= v2) call fail('Inequality failed.') 409 | if (v1 < v2) call fail('Less than failed.') 410 | if (v1 > v2) call fail('Greater than failed.') 411 | if (.not. v1 <= v2) call fail('Less than or equal failed.') 412 | if (.not. v1 >= v2) call fail('Greater than or equal failed.') 413 | 414 | v1 = version_t(90, 3, 0) 415 | v2 = version_t(90, 3) 416 | if (.not. v1 == v2) call fail('Equality failed.') 417 | if (v1 /= v2) call fail('Inequality failed.') 418 | if (v1 < v2) call fail('Less than failed.') 419 | if (v1 > v2) call fail('Greater than failed.') 420 | if (.not. v1 <= v2) call fail('Less than or equal failed.') 421 | if (.not. v1 >= v2) call fail('Greater than or equal failed.') 422 | 423 | v1 = version_t(1, 5, 3) 424 | v2 = version_t(90) 425 | if (v1 == v2) call fail('Equality failed.') 426 | if (.not. v1 /= v2) call fail('Inequality failed.') 427 | if (.not. v1 < v2) call fail('Less than failed.') 428 | if (v1 > v2) call fail('Greater than failed.') 429 | if (.not. v1 <= v2) call fail('Less than or equal failed.') 430 | if (v1 >= v2) call fail('Greater than or equal failed.') 431 | 432 | v1 = version_t(1, 5, 1) 433 | v2 = version_t(1, 0, 1) 434 | if (v1 == v2) call fail('Equality failed.') 435 | if (.not. v1 /= v2) call fail('Inequality failed.') 436 | if (v1 < v2) call fail('Less than failed.') 437 | if (.not. v1 > v2) call fail('Greater than failed.') 438 | if (v1 <= v2) call fail('Less than or equal failed.') 439 | if (.not. v1 >= v2) call fail('Greater than or equal failed.') 440 | 441 | v1 = version_t('..999') 442 | v2 = version_t('.1.') 443 | if (v1 == v2) call fail('Equality failed.') 444 | if (.not. v1 /= v2) call fail('Inequality failed.') 445 | if (.not. v1 < v2) call fail('Less than failed.') 446 | if (v1 > v2) call fail('Greater than failed.') 447 | if (.not. v1 <= v2) call fail('Less than or equal failed.') 448 | if (v1 >= v2) call fail('Greater than or equal failed.') 449 | 450 | v1 = version_t(1) 451 | v2 = version_t(0, 0, 1) 452 | if (v1 == v2) call fail('Equality failed.') 453 | if (.not. v1 /= v2) call fail('Inequality failed.') 454 | if (v1 < v2) call fail('Less than failed.') 455 | if (.not. v1 > v2) call fail('Greater than failed.') 456 | if (v1 <= v2) call fail('Less than or equal failed.') 457 | if (.not. v1 >= v2) call fail('Greater than or equal failed.') 458 | 459 | call v1%create(1, build='abc', error=e) 460 | call v2%create(1, build='abc', error=e) 461 | if (.not. v1 == v2) call fail('Equality failed.') 462 | if (v1 /= v2) call fail('InequalityInequality failed.') 463 | if (v1 < v2) call fail('Less than failed.') 464 | if (v1 > v2) call fail('Greater than failed.') 465 | if (.not. v1 <= v2) call fail('Less than or equal failed.') 466 | if (.not. v1 >= v2) call fail('Greater than or equal failed.') 467 | 468 | call v1%create(1, build='abc', error=e) 469 | call v2%create(1, build='123', error=e) 470 | if (.not. v1 == v2) call fail('Equality failed.') 471 | if (v1 /= v2) call fail('Inequality failed.') 472 | if (v1 < v2) call fail('Less than failed.') 473 | if (v1 > v2) call fail('Greater than failed.') 474 | if (.not. v1 <= v2) call fail('Less than or equal failed.') 475 | if (.not. v1 >= v2) call fail('Greater than or equal failed.') 476 | 477 | v1 = version_t(1, prerelease='abc') 478 | v2 = version_t(0, 1, prerelease='abc') 479 | if (v1 == v2) call fail('Equality failed.') 480 | if (.not. v1 /= v2) call fail('Inequality failed.') 481 | if (v1 < v2) call fail('Less than failed.') 482 | if (.not. v1 > v2) call fail('Greater than failed.') 483 | if (v1 <= v2) call fail('Less than or equal failed.') 484 | if (.not. v1 >= v2) call fail('Greater than or equal failed.') 485 | 486 | v1 = version_t(0, 3, 0, 'abc0123---') 487 | v2 = version_t(0, 3, 0, 'abc0123---') 488 | if (.not. v1 == v2) call fail('Equality failed.') 489 | if (v1 /= v2) call fail('Inequality failed.') 490 | if (v1 < v2) call fail('Less than failed.') 491 | if (v1 > v2) call fail('Greater than failed.') 492 | if (.not. v1 <= v2) call fail('Less than or equal failed.') 493 | if (.not. v1 >= v2) call fail('Greater than or equal failed.') 494 | 495 | v1 = version_t(0, 3, 0, 'abc.123.---') 496 | v2 = version_t(0, 3, 0, 'abc.123.---') 497 | if (.not. v1 == v2) call fail('Equality failed.') 498 | if (v1 /= v2) call fail('Inequality failed.') 499 | if (v1 < v2) call fail('Less than failed.') 500 | if (v1 > v2) call fail('Greater than failed.') 501 | if (.not. v1 <= v2) call fail('Less than or equal failed.') 502 | if (.not. v1 >= v2) call fail('Greater than or equal failed.') 503 | 504 | v1 = version_t(0, 3, 0, 'abc.123.---') 505 | v2 = version_t(0, 3, 0, 'abc.123.--') 506 | if (v1 == v2) call fail('Equality failed.') 507 | if (.not. v1 /= v2) call fail('Inequality failed.') 508 | if (v1 < v2) call fail('Less than failed.') 509 | if (.not. v1 > v2) call fail('Greater than failed.') 510 | if (v1 <= v2) call fail('Less than or equal failed.') 511 | if (.not. v1 >= v2) call fail('Greater than or equal failed.') 512 | 513 | v1 = version_t(0, 3, 0, 'abc.123.--') 514 | v2 = version_t(0, 3, 0, 'abc.123.---') 515 | if (v1 == v2) call fail('Equality failed.') 516 | if (.not. v1 /= v2) call fail('Inequality failed.') 517 | if (.not. v1 < v2) call fail('Less than failed.') 518 | if (v1 > v2) call fail('Greater than failed.') 519 | if (.not. v1 <= v2) call fail('Less than or equal failed.') 520 | if (v1 >= v2) call fail('Greater than or equal failed.') 521 | 522 | v1 = version_t(0, 3, 0, 'abc.123') 523 | v2 = version_t(0, 3, 0, 'abc') 524 | if (v1 == v2) call fail('Equality failed.') 525 | if (.not. v1 /= v2) call fail('Inequality failed.') 526 | if (v1 < v2) call fail('Less than failed.') 527 | if (.not. v1 > v2) call fail('Greater than failed.') 528 | if (v1 <= v2) call fail('Less than or equal failed.') 529 | if (.not. v1 >= v2) call fail('Greater than or equal failed.') 530 | 531 | v1 = version_t(0, 3, 0, 'abc') 532 | v2 = version_t(0, 3, 0, 'abc.123') 533 | if (v1 == v2) call fail('Equality failed.') 534 | if (.not. v1 /= v2) call fail('Inequality failed.') 535 | if (.not. v1 < v2) call fail('Less than failed.') 536 | if (v1 > v2) call fail('Greater than failed.') 537 | if (.not. v1 <= v2) call fail('Less than or equal failed.') 538 | if (v1 >= v2) call fail('Greater than or equal failed.') 539 | 540 | v1 = version_t(0, 4, 0, '2') 541 | v2 = version_t(0, 4, 0, '3') 542 | if (v1 == v2) call fail('Equality failed.') 543 | if (.not. v1 /= v2) call fail('Inequality failed.') 544 | if (.not. v1 < v2) call fail('Less than failed.') 545 | if (v1 > v2) call fail('Greater than failed.') 546 | if (.not. v1 <= v2) call fail('Less than or equal failed.') 547 | if (v1 >= v2) call fail('Greater than or equal failed.') 548 | 549 | v1 = version_t(0, 4, 0, '3') 550 | v2 = version_t(0, 4, 0, '2') 551 | if (v1 == v2) call fail('Equality failed.') 552 | if (.not. v1 /= v2) call fail('Inequality failed.') 553 | if (v1 < v2) call fail('Less than failed.') 554 | if (.not. v1 > v2) call fail('Greater than failed.') 555 | if (v1 <= v2) call fail('Less than or equal failed.') 556 | if (.not. v1 >= v2) call fail('Greater than or equal failed.') 557 | 558 | v1 = version_t(0, 4, 0, '911') 559 | v2 = version_t(0, 4, 0, '199') 560 | if (v1 == v2) call fail('Equality failed.') 561 | if (.not. v1 /= v2) call fail('Inequality failed.') 562 | if (v1 < v2) call fail('Less than failed.') 563 | if (.not. v1 > v2) call fail('Greater than failed.') 564 | if (v1 <= v2) call fail('Less than or equal failed.') 565 | if (.not. v1 >= v2) call fail('Greater than or equal failed.') 566 | 567 | v1 = version_t(0, 4, 0, '911') 568 | v2 = version_t(0, 4, 0, '1991') 569 | if (v1 == v2) call fail('Equality failed.') 570 | if (.not. v1 /= v2) call fail('Inequality failed.') 571 | if (.not. v1 < v2) call fail('Less than failed.') 572 | if (v1 > v2) call fail('Greater than failed.') 573 | if (.not. v1 <= v2) call fail('Less than or equal failed.') 574 | if (v1 >= v2) call fail('Greater than or equal failed.') 575 | 576 | v1 = version_t(0, 3, 0, '123') 577 | v2 = version_t(0, 3, 0, '---') 578 | if (v1 == v2) call fail('Equality failed.') 579 | if (.not. v1 /= v2) call fail('Inequality failed.') 580 | if (.not. v1 < v2) call fail('Less than failed.') 581 | if (v1 > v2) call fail('Greater than failed.') 582 | if (.not. v1 <= v2) call fail('Less than or equal failed.') 583 | if (v1 >= v2) call fail('Greater than or equal failed.') 584 | 585 | v1 = version_t(0, 3, 0, '---') 586 | v2 = version_t(0, 3, 0, '123') 587 | if (v1 == v2) call fail('Equality failed.') 588 | if (.not. v1 /= v2) call fail('Inequality failed.') 589 | if (v1 < v2) call fail('Less than failed.') 590 | if (.not. v1 > v2) call fail('Greater than failed.') 591 | if (v1 <= v2) call fail('Less than or equal failed.') 592 | if (.not. v1 >= v2) call fail('Greater than or equal failed.') 593 | 594 | v1 = version_t(0, 3, 0, 'abc') 595 | v2 = version_t(0, 3, 0, '---') 596 | if (v1 == v2) call fail('Equality failed.') 597 | if (.not. v1 /= v2) call fail('Inequality failed.') 598 | if (v1 < v2) call fail('Less than failed.') 599 | if (.not. v1 > v2) call fail('Greater than failed.') 600 | if (v1 <= v2) call fail('Less than or equal failed.') 601 | if (.not. v1 >= v2) call fail('Greater than or equal failed.') 602 | 603 | v1 = version_t(0, 3, 0, '---') 604 | v2 = version_t(0, 3, 0, 'abc') 605 | if (v1 == v2) call fail('Equality failed.') 606 | if (.not. v1 /= v2) call fail('Inequality failed.') 607 | if (.not. v1 < v2) call fail('Less than failed.') 608 | if (v1 > v2) call fail('Greater than failed.') 609 | if (.not. v1 <= v2) call fail('Less than or equal failed.') 610 | if (v1 >= v2) call fail('Greater than or equal failed.') 611 | 612 | v1 = version_t(0, 3, 0, 'abc') 613 | v2 = version_t(0, 3, 0, '1') 614 | if (v1 == v2) call fail('Equality failed.') 615 | if (.not. v1 /= v2) call fail('Inequality failed.') 616 | if (v1 < v2) call fail('Less than failed.') 617 | if (.not. v1 > v2) call fail('Greater than failed.') 618 | if (v1 <= v2) call fail('Less than or equal failed.') 619 | if (.not. v1 >= v2) call fail('Greater than or equal failed.') 620 | 621 | v1 = version_t(0, 3, 0, '1') 622 | v2 = version_t(0, 3, 0, 'abc') 623 | if (v1 == v2) call fail('Equality failed.') 624 | if (.not. v1 /= v2) call fail('Inequality failed.') 625 | if (.not. v1 < v2) call fail('Less than failed.') 626 | if (v1 > v2) call fail('Greater than failed.') 627 | if (.not. v1 <= v2) call fail('Less than or equal failed.') 628 | if (v1 >= v2) call fail('Greater than or equal failed.') 629 | 630 | v1 = version_t(0, 3, 0, '123.789') 631 | v2 = version_t(0, 3, 0, '789.123') 632 | if (v1 == v2) call fail('Equality failed.') 633 | if (.not. v1 /= v2) call fail('Inequality failed.') 634 | if (.not. v1 < v2) call fail('Less than failed.') 635 | if (v1 > v2) call fail('Greater than failed.') 636 | if (.not. v1 <= v2) call fail('Less than or equal failed.') 637 | if (v1 >= v2) call fail('Greater than or equal failed.') 638 | 639 | v1 = version_t(0, 3, 0, 'beta') 640 | v2 = version_t(0, 3, 0, 'alpha') 641 | if (v1 == v2) call fail('Equality failed.') 642 | if (.not. v1 /= v2) call fail('Inequality failed.') 643 | if (v1 < v2) call fail('Less than failed.') 644 | if (.not. v1 > v2) call fail('Greater than failed.') 645 | if (v1 <= v2) call fail('Less than or equal failed.') 646 | if (.not. v1 >= v2) call fail('Greater than or equal failed.') 647 | 648 | v1 = version_t(0, 3, 0, 'beta') 649 | v2 = version_t(0, 3, 0, 'alpha.1') 650 | if (v1 == v2) call fail('Equality failed.') 651 | if (.not. v1 /= v2) call fail('Inequality failed.') 652 | if (v1 < v2) call fail('Less than failed.') 653 | if (.not. v1 > v2) call fail('Greater than failed.') 654 | if (v1 <= v2) call fail('Less than or equal failed.') 655 | if (.not. v1 >= v2) call fail('Greater than or equal failed.') 656 | 657 | v1 = version_t(0, 3, 0, 'beta') 658 | v2 = version_t(0, 3, 0, 'beta.1') 659 | if (v1 == v2) call fail('Equality failed.') 660 | if (.not. v1 /= v2) call fail('Inequality failed.') 661 | if (.not. v1 < v2) call fail('Less than failed.') 662 | if (v1 > v2) call fail('Greater than failed.') 663 | if (.not. v1 <= v2) call fail('Less than or equal failed.') 664 | if (v1 >= v2) call fail('Greater than or equal failed.') 665 | 666 | v1 = version_t(0, 3, 0, 'alpha.999') 667 | v2 = version_t(0, 3, 0, 'beta.100') 668 | if (v1 == v2) call fail('Equality failed.') 669 | if (.not. v1 /= v2) call fail('Inequality failed.') 670 | if (.not. v1 < v2) call fail('Less than failed.') 671 | if (v1 > v2) call fail('Greater than failed.') 672 | if (.not. v1 <= v2) call fail('Less than or equal failed.') 673 | if (v1 >= v2) call fail('Greater than or equal failed.') 674 | 675 | v1 = version_t(0, 3, 0, 'alpha.999') 676 | v2 = version_t(0, 3, 0, 'alphaa.1') 677 | if (v1 == v2) call fail('Equality failed.') 678 | if (.not. v1 /= v2) call fail('Inequality failed.') 679 | if (.not. v1 < v2) call fail('Less than failed.') 680 | if (v1 > v2) call fail('Greater than failed.') 681 | if (.not. v1 <= v2) call fail('Less than or equal failed.') 682 | if (v1 >= v2) call fail('Greater than or equal failed.') 683 | 684 | v1 = version_t(0, 3, 0, 'alphaa.1') 685 | v2 = version_t(0, 3, 0, 'alpha.999') 686 | if (v1 == v2) call fail('Equality failed.') 687 | if (.not. v1 /= v2) call fail('Inequality failed.') 688 | if (v1 < v2) call fail('Less than failed.') 689 | if (.not. v1 > v2) call fail('Greater than failed.') 690 | if (v1 <= v2) call fail('Less than or equal failed.') 691 | if (.not. v1 >= v2) call fail('Greater than or equal failed.') 692 | 693 | v1 = version_t(0, 3, 0, 'alpha.9') 694 | v2 = version_t(0, 3, 0, 'alpha.10') 695 | if (v1 == v2) call fail('Equality failed.') 696 | if (.not. v1 /= v2) call fail('Inequality failed.') 697 | if (.not. v1 < v2) call fail('Less than failed.') 698 | if (v1 > v2) call fail('Greater than failed.') 699 | if (.not. v1 <= v2) call fail('Less than or equal failed.') 700 | if (v1 >= v2) call fail('Greater than or equal failed.') 701 | 702 | v1 = version_t(0, 3, 0, 'alpha10') 703 | v2 = version_t(0, 3, 0, 'alpha9') 704 | if (v1 == v2) call fail('Equality failed.') 705 | if (.not. v1 /= v2) call fail('Inequality failed.') 706 | if (.not. v1 < v2) call fail('Less than failed.') 707 | if (v1 > v2) call fail('Greater than failed.') 708 | if (.not. v1 <= v2) call fail('Less than or equal failed.') 709 | if (v1 >= v2) call fail('Greater than or equal failed.') 710 | 711 | v1 = version_t(0, 3, 0, 'alpha9') 712 | v2 = version_t(0, 3, 0, 'alpha10') 713 | if (v1 == v2) call fail('Equality failed.') 714 | if (.not. v1 /= v2) call fail('Inequality failed.') 715 | if (v1 < v2) call fail('Less than failed.') 716 | if (.not. v1 > v2) call fail('Greater than failed.') 717 | if (v1 <= v2) call fail('Less than or equal failed.') 718 | if (.not. v1 >= v2) call fail('Greater than or equal failed.') 719 | 720 | v1 = version_t(0, 3, 0, 'abc-b') 721 | v2 = version_t(0, 3, 0, 'abc-a') 722 | if (v1 == v2) call fail('Equality failed.') 723 | if (.not. v1 /= v2) call fail('Inequality failed.') 724 | if (v1 < v2) call fail('Less than failed.') 725 | if (.not. v1 > v2) call fail('Greater than failed.') 726 | if (v1 <= v2) call fail('Less than or equal failed.') 727 | if (.not. v1 >= v2) call fail('Greater than or equal failed.') 728 | 729 | v1 = version_t(90, 3, 0, '---') 730 | v2 = version_t(90, 3, prerelease='--') 731 | if (v1 == v2) call fail('Equality failed.') 732 | if (.not. v1 /= v2) call fail('Inequality failed.') 733 | if (v1 < v2) call fail('Less than failed.') 734 | if (.not. v1 > v2) call fail('Greater than failed.') 735 | if (v1 <= v2) call fail('Less than or equal failed.') 736 | if (.not. v1 >= v2) call fail('Greater than or equal failed.') 737 | 738 | !############################### is_version #################################! 739 | 740 | if (is_version('')) call fail("'' isn't a version.") 741 | if (is_version(' ')) call fail("' ' isn't a version.") 742 | if (is_version('a')) call fail("'a' isn't version.") 743 | if (is_version('1a')) call fail("'1a' isn't a version.") 744 | if (is_version('1.0.0a')) call fail("'1.0.0a' isn't a version.") 745 | if (is_version('1.0.0.a')) call fail("'1.0.0.a' isn't a version.") 746 | if (is_version('1.0.0.a-a')) call fail("'1.0.0.a-a' isn't a version.") 747 | if (is_version('1.0.0-(')) call fail("'1.0.0-(' isn't a version.") 748 | if (is_version('1.0.0-')) call fail("'1.0.0-' isn't a version.") 749 | if (is_version('1.0.0-+')) call fail("'1.0.0-+' isn't a version.") 750 | if (is_version('1.0.0-0')) call fail("'1.0.0-' isn't a version.") 751 | if (is_version('1.0.0-ab..cd')) call fail("'1.0.0-ab..cd' isn't a version.") 752 | if (is_version('...')) call fail("'...' isn't a version.") 753 | if (is_version('-')) call fail("'-' isn't a version.") 754 | if (is_version('+')) call fail("'+' isn't a version.") 755 | if (.not. is_version('0.0.0')) call fail("'0.0.0' is a version.") 756 | if (.not. is_version('0.0.99999')) call fail("'0.0.99999' a version.") 757 | if (.not. is_version('7')) call fail("'7' is a version.") 758 | if (.not. is_version('7.49')) call fail("'7.49' is a version.") 759 | if (.not. is_version('7.49-a')) call fail("'7.49-a' is a version.") 760 | if (.not. is_version('7.49-a')) call fail("'7.49-a' is a version.") 761 | if (.not. is_version('7.49-a+12.a')) call fail("'7.49-a+12.a' is a version.") 762 | 763 | !############################### is_exactly #################################! 764 | 765 | v1 = version_t(0, 1, 0) 766 | if (.not. v1%is_exactly(v1)) call fail('0.1.0 is exactly 0.1.0.') 767 | v1 = version_t(0, 1, 0, 'a', '123') 768 | if (.not. v1%is_exactly(v1)) call fail('0.1.0-a+123 is exactly 0.1.0-a+123.') 769 | v1 = version_t(0, 1, 0) 770 | v2 = version_t(0, 1, 0) 771 | if (.not. v1%is_exactly(v2)) call fail('0.1.0 is exactly 0.1.0.') 772 | if (.not. v2%is_exactly(v1)) call fail('0.1.0 is exactly 0.1.0.') 773 | v1 = version_t(0, 0, 1) 774 | v2 = version_t(0, 1, 0) 775 | if (v1%is_exactly(v2)) call fail('0.0.1 is not exactly 0.1.0.') 776 | if (v2%is_exactly(v1)) call fail('0.0.1 is not exactly 0.1.0.') 777 | v1 = version_t(0, 1, 0, '123') 778 | v2 = version_t(0, 1, 0, '123') 779 | if (.not. v1%is_exactly(v2)) call fail('0.1.0-123 is exactly 0.1.0-123.') 780 | if (.not. v2%is_exactly(v1)) call fail('0.1.0-123 is exactly 0.1.0-123.') 781 | v1 = version_t(0, 1, 0, '123') 782 | v2 = version_t(0, 1, 0, 'abc') 783 | if (v1%is_exactly(v2)) call fail('0.1.0-123 is not exactly 0.1.0-abc.') 784 | if (v2%is_exactly(v1)) call fail('0.1.0-123 is not exactly 0.1.0-abc.') 785 | v1 = version_t(0, 1, 0, 'a', '1') 786 | v2 = version_t(0, 1, 0, 'a', '1') 787 | if (.not. v1%is_exactly(v2)) call fail('0.1.0-a+1 is exactly 0.1.0-a+1.') 788 | if (.not. v2%is_exactly(v1)) call fail('0.1.0-a+1 is exactly 0.1.0-a+1.') 789 | v1 = version_t(0, 1, 0, 'a', '1') 790 | v2 = version_t(0, 1, 0, 'a', '2') 791 | if (v1%is_exactly(v2)) call fail('0.1.0-a+1 is not exactly 0.1.0-a+2.') 792 | if (v2%is_exactly(v1)) call fail('0.1.0-a+1 is not exactly 0.1.0-a+2.') 793 | v1 = version_t(0, 1, 0, 'a') 794 | v2 = version_t(0, 1, 0, 'a', '2') 795 | if (v1%is_exactly(v2)) call fail('0.1.0-a is not exactly 0.1.0-a+2.') 796 | if (v2%is_exactly(v1)) call fail('0.1.0-a is not exactly 0.1.0-a+2.') 797 | v1 = version_t(0, 1, 0, 'a', '1') 798 | v2 = version_t(0, 1, 0, 'a') 799 | if (v1%is_exactly(v2)) call fail('0.1.0-a+1 is not exactly 0.1.0-a.') 800 | if (v2%is_exactly(v1)) call fail('0.1.0-a+1 is not exactly 0.1.0-a.') 801 | v1 = version_t(0, 1, 0, 'a', '1.1') 802 | v2 = version_t(0, 1, 0, 'a', '1') 803 | if (v1%is_exactly(v2)) call fail('0.1.0-a+1.1 is not exactly 0.1.0-1.') 804 | if (v2%is_exactly(v1)) call fail('0.1.0-a+1.1 is not exactly 0.1.0-1.') 805 | v1 = version_t(0, 1, 0, 'a', '1') 806 | v2 = version_t(0, 1, 0, 'a', '1.1') 807 | if (v1%is_exactly(v2)) call fail('0.1.0-a+1 is not exactly 0.1.0-1.1.') 808 | if (v2%is_exactly(v1)) call fail('0.1.0-a+1 is not exactly 0.1.0-1.1.') 809 | v1 = version_t(0, 1, 0, 'a', '1.123') 810 | v2 = version_t(0, 1, 0, 'a', '1.1') 811 | if (v1%is_exactly(v2)) call fail('0.1.0-a+1.123 is not exactly 0.1.0-1.1.') 812 | if (v2%is_exactly(v1)) call fail('0.1.0-a+1.123 is not exactly 0.1.0-1.1.') 813 | v1 = version_t(0, 1, 0, 'a', '1.1') 814 | v2 = version_t(0, 1, 0, 'a', '1.123') 815 | if (v1%is_exactly(v2)) call fail('0.1.0-a+1.1 is not exactly 0.1.0-1.123.') 816 | if (v2%is_exactly(v1)) call fail('0.1.0-a+1.1 is not exactly 0.1.0-1.123.') 817 | 818 | !############################### strict_mode ################################! 819 | 820 | v1 = version_t(1, 0, 0, strict_mode=.true.) 821 | if (v1%to_string() /= '1.0.0') then 822 | call fail("Strict mode: Parsing failed for '"//v1%to_string()//"'") 823 | end if 824 | 825 | v1 = version_t(0, 0, 0, strict_mode=.true.) 826 | if (v1%to_string() /= '0.0.0') then 827 | call fail("Strict mode: Parsing failed for '"//v1%to_string()//"'") 828 | end if 829 | 830 | v1 = version_t(0, 1, 0, 'abc', 'def', strict_mode=.true.) 831 | if (v1%to_string() /= '0.1.0-abc+def') then 832 | call fail("Strict mode: Parsing failed for '"//v1%to_string()//"'") 833 | end if 834 | 835 | v1 = version_t(3, strict_mode=.false.) 836 | if (v1%to_string() /= '3.0.0') then 837 | call fail("Parsing failed for '"//v1%to_string()//"'") 838 | end if 839 | 840 | v1 = version_t(3, patch=2, build='cde', strict_mode=.false.) 841 | if (v1%to_string() /= '3.0.2+cde') then 842 | call fail("Parsing failed for '"//v1%to_string()//"'") 843 | end if 844 | 845 | call v1%create(1, error=e, strict_mode=.true.) 846 | if (.not. allocated(e)) call fail('Strict mode: No minor and patch.') 847 | 848 | call v1%create(1, 2, error=e, strict_mode=.true.) 849 | if (.not. allocated(e)) call fail('Strict mode: Patch not provided.') 850 | 851 | call v1%create(1, patch=2, error=e, strict_mode=.true.) 852 | if (.not. allocated(e)) call fail('Strict mode: Minor not provided.') 853 | 854 | call v1%create(1, 2, 3, error=e, strict_mode=.true.) 855 | if (allocated(e)) call fail('Strict mode: Everything is provided.') 856 | 857 | call v1%create(1, 2, 3, '1', '2', error=e, strict_mode=.true.) 858 | if (allocated(e)) call fail('Strict mode: Everything is provided.') 859 | 860 | call v1%create(1, 2, prerelease='1', build='2', error=e, strict_mode=.true.) 861 | if (.not. allocated(e)) call fail('Strict mode: No patch.') 862 | 863 | call v1%create(1, patch=2, prerelease='1', build='2', error=e, strict_mode=.true.) 864 | if (.not. allocated(e)) call fail('Strict mode: No minor.') 865 | 866 | call v1%create(1, 2, prerelease='1', build='2', error=e, strict_mode=.false.) 867 | if (allocated(e)) call fail('No strict mode: Missing patch.') 868 | 869 | call v1%create(1, error=e, strict_mode=.false.) 870 | if (allocated(e)) call fail('No strict mode: Missing minor and patch.') 871 | 872 | call v1%parse('1.2.3', error=e, strict_mode=.true.) 873 | if (allocated(e)) call fail('Strict mode: All is provided.') 874 | 875 | call v1%parse('1.2', error=e, strict_mode=.true.) 876 | if (.not. allocated(e)) call fail('Strict mode: No patch.') 877 | 878 | call v1%parse('1', error=e, strict_mode=.true.) 879 | if (.not. allocated(e)) call fail('Strict mode: No minor, no patch.') 880 | 881 | call v1%parse('1-1+1', error=e, strict_mode=.true.) 882 | if (.not. allocated(e)) call fail('Strict mode: No minor, no patch.') 883 | 884 | call v1%parse('0', e, strict_mode=.true.) 885 | if (.not. allocated(e)) call fail('Strict mode: No minor, no patch.') 886 | 887 | call v1%parse('.', e, strict_mode=.true.) 888 | if (.not. allocated(e)) call fail('Strict mode: No patch.') 889 | 890 | call v1%parse('0.1', e, strict_mode=.true.) 891 | if (.not. allocated(e)) call fail('Strict mode: No patch.') 892 | 893 | call v1%parse('.2.988', e, strict_mode=.true.) 894 | if (.not. allocated(e)) call fail('Strict mode: No implicit major.') 895 | 896 | call v1%parse('1..988', e, strict_mode=.true.) 897 | if (.not. allocated(e)) call fail('Strict mode: No implicit minor.') 898 | 899 | call v1%parse('100.1.', e, strict_mode=.true.) 900 | if (.not. allocated(e)) call fail('Strict mode: No implicit patch.') 901 | 902 | call v1%parse('..', e, strict_mode=.true.) 903 | if (.not. allocated(e)) call fail('Strict mode: No implicit major, minor and patch.') 904 | 905 | call v1%parse('.', e, strict_mode=.false.) 906 | if (allocated(e)) call fail('No strict mode: Has implicit major, minor and patch.') 907 | 908 | call v1%parse('1.2', e, strict_mode=.false.) 909 | if (allocated(e)) call fail('No strict mode: Has implicit major, minor and patch.') 910 | 911 | if (is_version('1', strict_mode=.true.)) call fail("Strict mode: Missing minor and patch.") 912 | if (is_version('1+123', strict_mode=.true.)) call fail("Strict mode: Missing minor and patch.") 913 | if (is_version('1.0+123', strict_mode=.true.)) call fail("Strict mode: Missing patch.") 914 | if (is_version('1.0', strict_mode=.true.)) call fail("Strict mode: Missing patch.") 915 | if (.not. is_version('1.0.0+123', strict_mode=.true.)) call fail("Strict mode: Is valid version.") 916 | if (.not. is_version('1.0.0+123', strict_mode=.false.)) call fail("No strict mode: Is valid version.") 917 | if (.not. is_version('11.0', strict_mode=.false.)) call fail("No strict mode: Is valid version.") 918 | 919 | !################################operator_index################################! 920 | 921 | if (operator_index('') /= 0) call fail('op-index-1 failed.') 922 | if (operator_index(' ') /= 1) call fail('op-index-2 failed.') 923 | if (operator_index(' ') /= 1) call fail('op-index-3 failed.') 924 | if (operator_index('<') /= 1) call fail('op-index-4 failed.') 925 | if (operator_index('>') /= 1) call fail('op-index-5 failed.') 926 | if (operator_index('!') /= 1) call fail('op-index-6 failed.') 927 | if (operator_index('=') /= 1) call fail('op-index-7 failed.') 928 | if (operator_index('asdfj76r58>') /= 11) call fail('op-index-8 failed.') 929 | if (operator_index('asdfj76r58 ') /= 11) call fail('op-index-8 failed.') 930 | if (operator_index('asdfj76r58=z73z242 ') /= 11) call fail('op-index-8 failed.') 931 | if (operator_index('asdfj76r58 z73z242 ') /= 11) call fail('op-index-9 failed.') 932 | if (operator_index('2.3.4 > 9.2.4') /= 6) call fail('op-index-10 failed.') 933 | if (operator_index('2.3.4 > 9.2.4 <38 >>= 8') /= 6) call fail('op-index-11 failed.') 934 | 935 | !##################################try_satisfy#################################! 936 | 937 | v1 = version_t(0, 1, 0) 938 | 939 | call v1%try_satisfy('', is_satisfied, e) 940 | if (.not. allocated(e)) call fail('satisfy-1 should fail.') 941 | 942 | call v1%try_satisfy(' ', is_satisfied, e) 943 | if (.not. allocated(e)) call fail('satisfy-2 should fail.') 944 | 945 | call v1%try_satisfy('0.1.0', is_satisfied, e) 946 | if (.not. is_satisfied) call fail('satisfy-3 should satisfy.') 947 | if (allocated(e)) call fail('satisfy-3 should not fail.') 948 | 949 | v1 = version_t(0, 1, 0, 'abc') 950 | call v1%try_satisfy('0.1.0', is_satisfied, e) 951 | if (is_satisfied) call fail('satisfy-4 should not satisfy.') 952 | if (allocated(e)) call fail('satisfy-4 should not fail.') 953 | 954 | v1 = version_t(0, 1, 0) 955 | call v1%try_satisfy('0.1.0-abc', is_satisfied, e) 956 | if (is_satisfied) call fail('satisfy-5 should not satisfy.') 957 | if (allocated(e)) call fail('satisfy-5 should not fail.') 958 | 959 | v1 = version_t(0, 1, 0, 'abc') 960 | call v1%try_satisfy('0.1.0-cde', is_satisfied, e) 961 | if (is_satisfied) call fail('satisfy-6 should not satisfy.') 962 | if (allocated(e)) call fail('satisfy-6 should not fail.') 963 | 964 | v1 = version_t(0, 1, 0, 'abc', 'cde') 965 | call v1%try_satisfy('0.1.0-abc', is_satisfied, e) 966 | if (.not. is_satisfied) call fail('satisfy-7 should satisfy.') 967 | if (allocated(e)) call fail('satisfy-7 should not fail.') 968 | 969 | v1 = version_t(0, 1, 0, 'abc', 'cde') 970 | call v1%try_satisfy('0.1.0-abc+xyz', is_satisfied, e) 971 | if (.not. is_satisfied) call fail('satisfy-8 should satisfy.') 972 | if (allocated(e)) call fail('satisfy-8 should not fail.') 973 | 974 | v1 = version_t(0, 1, 0) 975 | call v1%try_satisfy(' 0.1.0 ', is_satisfied, e) 976 | if (.not. is_satisfied) call fail('satisfy-9 should satisfy.') 977 | if (allocated(e)) call fail('satisfy-9 should not fail.') 978 | 979 | call v1%try_satisfy(' 0.1.0+abc ', is_satisfied, e) 980 | if (.not. is_satisfied) call fail('satisfy-10 should satisfy.') 981 | if (allocated(e)) call fail('satisfy-10 should not fail.') 982 | 983 | call v1%try_satisfy('0.2.0', is_satisfied, e) 984 | if (is_satisfied) call fail('satisfy-11 should not satisfy.') 985 | if (allocated(e)) call fail('satisfy-11 should not fail.') 986 | 987 | call v1%try_satisfy('0.2.0', is_satisfied, e) 988 | if (is_satisfied) call fail('satisfy-12 should not satisfy.') 989 | if (allocated(e)) call fail('satisfy-12 should not fail.') 990 | 991 | call v1%try_satisfy('=0.1.0', is_satisfied, e) 992 | if (.not. is_satisfied) call fail('satisfy-13 should satisfy.') 993 | if (allocated(e)) call fail('satisfy-13 should not fail.') 994 | 995 | call v1%try_satisfy('= 0.1.0', is_satisfied, e) 996 | if (.not. is_satisfied) call fail('satisfy-14 should satisfy.') 997 | if (allocated(e)) call fail('satisfy-14 should not fail.') 998 | 999 | call v1%try_satisfy('= 0.2.0', is_satisfied, e) 1000 | if (is_satisfied) call fail('satisfy-15 should not satisfy.') 1001 | if (allocated(e)) call fail('satisfy-15 should not fail.') 1002 | 1003 | call v1%try_satisfy('!=0.2.0', is_satisfied, e) 1004 | if (.not. is_satisfied) call fail('satisfy-16 should satisfy.') 1005 | if (allocated(e)) call fail('satisfy-16 should not fail.') 1006 | 1007 | call v1%try_satisfy('!=0.1.0', is_satisfied, e) 1008 | if (is_satisfied) call fail('satisfy-17 should not satisfy.') 1009 | if (allocated(e)) call fail('satisfy-17 should not fail.') 1010 | 1011 | call v1%try_satisfy('!= 0.1.0', is_satisfied, e) 1012 | if (is_satisfied) call fail('satisfy-18 should not satisfy.') 1013 | if (allocated(e)) call fail('satisfy-18 should not fail.') 1014 | 1015 | call v1%try_satisfy('0.1.0abcd', is_satisfied, e) 1016 | if (.not. allocated(e)) call fail('satisfy-19 should fail.') 1017 | 1018 | call v1%try_satisfy('=0.1.0abcd', is_satisfied, e) 1019 | if (.not. allocated(e)) call fail('satisfy-20 should fail.') 1020 | 1021 | call v1%try_satisfy('>0.1.0', is_satisfied, e) 1022 | if (is_satisfied) call fail('satisfy-21 should not satisfy.') 1023 | if (allocated(e)) call fail('satisfy-21 should not fail.') 1024 | 1025 | call v1%try_satisfy('>0.1.0-1', is_satisfied, e) 1026 | if (.not. is_satisfied) call fail('satisfy-22 should satisfy.') 1027 | if (allocated(e)) call fail('satisfy-22 should not fail.') 1028 | 1029 | call v1%try_satisfy('> 0.0.9', is_satisfied, e) 1030 | if (.not. is_satisfied) call fail('satisfy-23 should satisfy.') 1031 | if (allocated(e)) call fail('satisfy-23 should not fail.') 1032 | 1033 | call v1%try_satisfy('>=0.1.0', is_satisfied, e) 1034 | if (.not. is_satisfied) call fail('satisfy-24 should satisfy.') 1035 | if (allocated(e)) call fail('satisfy-24 should not fail.') 1036 | 1037 | call v1%try_satisfy('>= 0.1.0-678', is_satisfied, e) 1038 | if (.not. is_satisfied) call fail('satisfy-25 should satisfy.') 1039 | if (allocated(e)) call fail('satisfy-25 should not fail.') 1040 | 1041 | call v1%try_satisfy('>=0.1.0+123', is_satisfied, e) 1042 | if (.not. is_satisfied) call fail('satisfy-26 should satisfy.') 1043 | if (allocated(e)) call fail('satisfy-26 should not fail.') 1044 | 1045 | call v1%try_satisfy('<0.1.0', is_satisfied, e) 1046 | if (is_satisfied) call fail('satisfy-27 should not satisfy.') 1047 | if (allocated(e)) call fail('satisfy-27 should not fail.') 1048 | 1049 | call v1%try_satisfy('< 0.1.0-1', is_satisfied, e) 1050 | if (is_satisfied) call fail('satisfy-28 should not satisfy.') 1051 | if (allocated(e)) call fail('satisfy-28 should not fail.') 1052 | 1053 | call v1%try_satisfy('< 0.0.9', is_satisfied, e) 1054 | if (is_satisfied) call fail('satisfy-29 should not satisfy.') 1055 | if (allocated(e)) call fail('satisfy-29 should not fail.') 1056 | 1057 | call v1%try_satisfy('<=0.1.0', is_satisfied, e) 1058 | if (.not. is_satisfied) call fail('satisfy-30 should satisfy.') 1059 | if (allocated(e)) call fail('satisfy-30 should not fail.') 1060 | 1061 | call v1%try_satisfy('<= 0.1.0-678', is_satisfied, e) 1062 | if (is_satisfied) call fail('satisfy-31 should not satisfy.') 1063 | if (allocated(e)) call fail('satisfy-31 should not fail.') 1064 | 1065 | call v1%try_satisfy('<=0.1.0+123', is_satisfied, e) 1066 | if (.not. is_satisfied) call fail('satisfy-32 should satisfy.') 1067 | if (allocated(e)) call fail('satisfy-32 should not fail.') 1068 | 1069 | call v1%try_satisfy(' abc ', is_satisfied, e) 1070 | if (.not. allocated(e)) call fail('satisfy-33 should fail.') 1071 | 1072 | call v1%try_satisfy('0.0.1 1.0.0', is_satisfied, e) 1073 | if (is_satisfied) call fail('satisfy-34 should not satisfy.') 1074 | if (allocated(e)) call fail('satisfy-34 should not fail.') 1075 | 1076 | call v1%try_satisfy('0.0.1 0.1.0', is_satisfied, e) 1077 | if (is_satisfied) call fail('satisfy-35 should not satisfy.') 1078 | if (allocated(e)) call fail('satisfy-35 should not fail.') 1079 | 1080 | call v1%try_satisfy(' > 1.0.1 < 2.1.0 ', is_satisfied, e) 1081 | if (is_satisfied) call fail('satisfy-36 should not satisfy.') 1082 | if (allocated(e)) call fail('satisfy-36 should not fail.') 1083 | 1084 | call v1%try_satisfy('>0.0.1 <=0.1.0', is_satisfied, e) 1085 | if (.not. is_satisfied) call fail('satisfy-37 should satisfy.') 1086 | if (allocated(e)) call fail('satisfy-37 should not fail.') 1087 | 1088 | call v1%try_satisfy('>0.0.1 <0.1.0', is_satisfied, e) 1089 | if (is_satisfied) call fail('satisfy-38 should not satisfy.') 1090 | if (allocated(e)) call fail('satisfy-38 should not fail.') 1091 | 1092 | call v1%try_satisfy('<0.1.0 || 0.0.1', is_satisfied, e) 1093 | if (is_satisfied) call fail('satisfy-39 should not satisfy.') 1094 | if (allocated(e)) call fail('satisfy-39 should not fail.') 1095 | 1096 | call v1%try_satisfy('<0.1.0 || 0.1.0', is_satisfied, e) 1097 | if (.not. is_satisfied) call fail('satisfy-40 should satisfy.') 1098 | if (allocated(e)) call fail('satisfy-40 should not fail.') 1099 | 1100 | call v1%try_satisfy('<0.1.0 || >0.1.0 || != 0.1.0', is_satisfied, e) 1101 | if (is_satisfied) call fail('satisfy-41 should not satisfy.') 1102 | if (allocated(e)) call fail('satisfy-41 should not fail.') 1103 | 1104 | call v1%try_satisfy('<0.1.0 || >0.1.0 || =0.1.0', is_satisfied, e) 1105 | if (.not. is_satisfied) call fail('satisfy-42 should satisfy.') 1106 | if (allocated(e)) call fail('satisfy-42 should not fail.') 1107 | 1108 | call v1%try_satisfy('<0.1.0 0.1.0 >2.0.0 || !=0.1.0 <0.2.1', is_satisfied, e) 1109 | if (is_satisfied) call fail('satisfy-43 should not satisfy.') 1110 | if (allocated(e)) call fail('satisfy-43 should not fail.') 1111 | 1112 | call v1%try_satisfy('>=0.1.0 <2.0.0 || >0.2.0', is_satisfied, e) 1113 | if (.not. is_satisfied) call fail('satisfy-44 should satisfy.') 1114 | if (allocated(e)) call fail('satisfy-44 should not fail.') 1115 | 1116 | call v1%try_satisfy('2.1.0 2.0.0 0.1.0 || >=0.2.0', is_satisfied, e) 1117 | if (is_satisfied) call fail('satisfy-45 should not satisfy.') 1118 | if (allocated(e)) call fail('satisfy-45 should not fail.') 1119 | 1120 | call v1%try_satisfy('>0.1.0 <2.0.0 0.1.0 || <=0.2.0 0.1.0', is_satisfied, e) 1121 | if (.not. is_satisfied) call fail('satisfy-46 should satisfy.') 1122 | if (allocated(e)) call fail('satisfy-46 should not fail.') 1123 | 1124 | call v1%try_satisfy('>0.1.0 <2.0.0 0.1.0 || <=0.2.0 0.0.9', is_satisfied, e) 1125 | if (is_satisfied) call fail('satisfy-47 should not satisfy.') 1126 | if (allocated(e)) call fail('satisfy-47 should not fail.') 1127 | 1128 | call v1%try_satisfy(' bx || <=0.2.0 0.0.9', is_satisfied, e) 1129 | if (.not. allocated(e)) call fail('satisfy-48 should fail.') 1130 | 1131 | call v1%try_satisfy(' 0.1.0 || ahc', is_satisfied, e) 1132 | if (.not. allocated(e)) call fail('satisfy-49 should fail.') 1133 | 1134 | call v1%try_satisfy(' || 0.1.0', is_satisfied, e) 1135 | if (.not. allocated(e)) call fail('satisfy-50 should fail.') 1136 | 1137 | call v1%try_satisfy('0.1.0 || ', is_satisfied, e) 1138 | if (.not. allocated(e)) call fail('satisfy-51 should fail.') 1139 | 1140 | call v1%try_satisfy('>=0.1.0<2.0.0 0.1.0||<=0.2.0 0.0.9', is_satisfied, e) 1141 | if (.not. is_satisfied) call fail('satisfy-52 should satisfy.') 1142 | if (allocated(e)) call fail('satisfy-52 should not fail.') 1143 | 1144 | call v1%try_satisfy('>0.1.0-abc', is_satisfied, e) 1145 | if (.not. is_satisfied) call fail('satisfy-53 should satisfy.') 1146 | if (allocated(e)) call fail('satisfy-53 should not fail.') 1147 | 1148 | call v1%try_satisfy('>0.0.1-abc', is_satisfied, e) 1149 | if (.not. is_satisfied) call fail('satisfy-54 should satisfy.') 1150 | if (allocated(e)) call fail('satisfy-54 should not fail.') 1151 | 1152 | call v1%try_satisfy('>0.1.0+abc', is_satisfied, e) 1153 | if (is_satisfied) call fail('satisfy-55 should not satisfy.') 1154 | if (allocated(e)) call fail('satisfy-55 should not fail.') 1155 | 1156 | call v1%try_satisfy('>=0.1.0+abc', is_satisfied, e) 1157 | if (.not. is_satisfied) call fail('satisfy-56 should satisfy.') 1158 | if (allocated(e)) call fail('satisfy-56 should not fail.') 1159 | 1160 | call v1%try_satisfy('0.1.0-abc', is_satisfied, e) 1161 | if (is_satisfied) call fail('satisfy-57 should not satisfy.') 1162 | if (allocated(e)) call fail('satisfy-57 should not fail.') 1163 | 1164 | call v1%try_satisfy('0.1.0+abc', is_satisfied, e) 1165 | if (.not. is_satisfied) call fail('satisfy-58 should satisfy.') 1166 | if (allocated(e)) call fail('satisfy-58 should not fail.') 1167 | 1168 | !###################################satisfies##################################! 1169 | 1170 | v1 = version_t(0, 1, 0) 1171 | if (v1%satisfies(' ')) call fail('satisfies-1 should fail.') 1172 | if (v1%satisfies('abc')) call fail('satisfies-2 should fail.') 1173 | if (.not. v1%satisfies('0.1.0')) call fail('satisfies-3 should not fail.') 1174 | if (.not. v1%satisfies('>=0.1.0')) call fail('satisfies-4 should not fail.') 1175 | if (v1%satisfies('>0.1.0')) call fail('satisfies-5 should fail.') 1176 | if (v1%satisfies('>=0.1.0-....')) call fail('satisfies-6 should fail.') 1177 | if (.not. v1%satisfies('>0.0.99 <1.0.0')) call fail('satisfies-7 should not fail.') 1178 | if (.not. v1%satisfies('>0.0.99 <0.1.0 || 0.1.0')) call fail('satisfies-8 should not fail.') 1179 | if (v1%satisfies('>0.0.99 <0.1.0 || >0.1.0')) call fail('satisfies-9 should fail.') 1180 | if (.not. v1%satisfies('<0.0.1 || >0.1.0-123')) call fail('satisfies-10 should not fail.') 1181 | 1182 | !################################satisfies_comp################################! 1183 | 1184 | v1 = version_t(0, 1, 0) 1185 | call v1%satisfies_comp(comparator_t('abc', version_t(1)), is_satisfied, e) 1186 | if (.not. allocated(e)) call fail('try_satisfy-comp-1 should fail.') 1187 | 1188 | call v1%satisfies_comp(comparator_t('=', version_t(1)), is_satisfied, e) 1189 | if (allocated(e)) call fail('try_satisfy-comp-2 should not fail.') 1190 | if (is_satisfied) call fail('try_satisfy-comp-2 should not be true.') 1191 | call v1%satisfies_comp(comparator_t('', version_t(1)), is_satisfied, e) 1192 | if (allocated(e)) call fail('try_satisfy-comp-3 should not fail.') 1193 | if (is_satisfied) call fail('try_satisfy-comp-3 should not be true.') 1194 | call v1%satisfies_comp(comparator_t('!=', version_t(1)), is_satisfied, e) 1195 | if (allocated(e)) call fail('try_satisfy-comp-4 should not fail.') 1196 | if (.not. is_satisfied) call fail('try_satisfy-comp-4 should be true.') 1197 | call v1%satisfies_comp(comparator_t('>', version_t(1)), is_satisfied, e) 1198 | if (allocated(e)) call fail('try_satisfy-comp-5 should not fail.') 1199 | if (is_satisfied) call fail('try_satisfy-comp-5 should not be true.') 1200 | call v1%satisfies_comp(comparator_t('>=', version_t(1)), is_satisfied, e) 1201 | if (allocated(e)) call fail('try_satisfy-comp-6 should not fail.') 1202 | if (is_satisfied) call fail('try_satisfy-comp-6 should not be true.') 1203 | call v1%satisfies_comp(comparator_t('<', version_t(1)), is_satisfied, e) 1204 | if (allocated(e)) call fail('try_satisfy-comp-7 should not fail.') 1205 | if (.not. is_satisfied) call fail('try_satisfy-comp-7 should be true.') 1206 | call v1%satisfies_comp(comparator_t('<=', version_t(1)), is_satisfied, e) 1207 | if (allocated(e)) call fail('try_satisfy-comp-8 should not fail.') 1208 | if (.not. is_satisfied) call fail('try_satisfy-comp-8 should be true.') 1209 | 1210 | call v1%satisfies_comp(comparator_t('=', version_t(0, 0, 9)), is_satisfied, e) 1211 | if (allocated(e)) call fail('try_satisfy-comp-9 should not fail.') 1212 | if (is_satisfied) call fail('try_satisfy-comp-9 should not be true.') 1213 | call v1%satisfies_comp(comparator_t('', version_t(0, 0, 9)), is_satisfied, e) 1214 | if (allocated(e)) call fail('try_satisfy-comp-10 should not fail.') 1215 | if (is_satisfied) call fail('try_satisfy-comp-10 should not be true.') 1216 | call v1%satisfies_comp(comparator_t('!=', version_t(0, 0, 9)), is_satisfied, e) 1217 | if (allocated(e)) call fail('try_satisfy-comp-11 should not fail.') 1218 | if (.not. is_satisfied) call fail('try_satisfy-comp-11 should be true.') 1219 | call v1%satisfies_comp(comparator_t('>', version_t(0, 0, 9)), is_satisfied, e) 1220 | if (allocated(e)) call fail('try_satisfy-comp-12 should not fail.') 1221 | if (.not. is_satisfied) call fail('try_satisfy-comp-12 should be true.') 1222 | call v1%satisfies_comp(comparator_t('>=', version_t(0, 0, 9)), is_satisfied, e) 1223 | if (allocated(e)) call fail('try_satisfy-comp-13 should not fail.') 1224 | if (.not. is_satisfied) call fail('try_satisfy-comp-13 should be true.') 1225 | call v1%satisfies_comp(comparator_t('<', version_t(0, 0, 9)), is_satisfied, e) 1226 | if (allocated(e)) call fail('try_satisfy-comp-14 should not fail.') 1227 | if (is_satisfied) call fail('try_satisfy-comp-14 should not be true.') 1228 | call v1%satisfies_comp(comparator_t('<=', version_t(0, 0, 9)), is_satisfied, e) 1229 | if (allocated(e)) call fail('try_satisfy-comp-15 should not fail.') 1230 | if (is_satisfied) call fail('try_satisfy-comp-15 should not be true.') 1231 | 1232 | call v1%satisfies_comp(comparator_t('=', version_t(0, 1, 0)), is_satisfied, e) 1233 | if (allocated(e)) call fail('try_satisfy-comp-16 should not fail.') 1234 | if (.not. is_satisfied) call fail('try_satisfy-comp-16 should be true.') 1235 | call v1%satisfies_comp(comparator_t('', version_t(0, 1, 0)), is_satisfied, e) 1236 | if (allocated(e)) call fail('try_satisfy-comp-17 should not fail.') 1237 | if (.not. is_satisfied) call fail('try_satisfy-comp-17 should be true.') 1238 | call v1%satisfies_comp(comparator_t('!=', version_t(0, 1, 0)), is_satisfied, e) 1239 | if (allocated(e)) call fail('try_satisfy-comp-18 should not fail.') 1240 | if (is_satisfied) call fail('try_satisfy-comp-18 should not be true.') 1241 | call v1%satisfies_comp(comparator_t('>', version_t(0, 1, 0)), is_satisfied, e) 1242 | if (allocated(e)) call fail('try_satisfy-comp-19 should not fail.') 1243 | if (is_satisfied) call fail('try_satisfy-comp-19 should not be true.') 1244 | call v1%satisfies_comp(comparator_t('>=', version_t(0, 1, 0)), is_satisfied, e) 1245 | if (allocated(e)) call fail('try_satisfy-comp-20 should not fail.') 1246 | if (.not. is_satisfied) call fail('try_satisfy-comp-20 should be true.') 1247 | call v1%satisfies_comp(comparator_t('<', version_t(0, 1, 0)), is_satisfied, e) 1248 | if (allocated(e)) call fail('try_satisfy-comp-21 should not fail.') 1249 | if (is_satisfied) call fail('try_satisfy-comp-21 should not be true.') 1250 | call v1%satisfies_comp(comparator_t('<=', version_t(0, 1, 0)), is_satisfied, e) 1251 | if (allocated(e)) call fail('try_satisfy-comp-22 should not fail.') 1252 | if (.not. is_satisfied) call fail('try_satisfy-comp-22 should be true.') 1253 | 1254 | call v1%satisfies_comp(comparator_t('=', version_t(0, 1, 0, 'abc')), is_satisfied, e) 1255 | if (allocated(e)) call fail('try_satisfy-comp-23 should not fail.') 1256 | if (is_satisfied) call fail('try_satisfy-comp-23 should not be true.') 1257 | call v1%satisfies_comp(comparator_t('', version_t(0, 1, 0, 'abc')), is_satisfied, e) 1258 | if (allocated(e)) call fail('try_satisfy-comp-24 should not fail.') 1259 | if (is_satisfied) call fail('try_satisfy-comp-24 should not be true.') 1260 | call v1%satisfies_comp(comparator_t('!=', version_t(0, 1, 0, 'abc')), is_satisfied, e) 1261 | if (allocated(e)) call fail('try_satisfy-comp-25 should not fail.') 1262 | if (.not. is_satisfied) call fail('try_satisfy-comp-25 should be true.') 1263 | call v1%satisfies_comp(comparator_t('>', version_t(0, 1, 0, 'abc')), is_satisfied, e) 1264 | if (allocated(e)) call fail('try_satisfy-comp-26 should not fail.') 1265 | if (.not. is_satisfied) call fail('try_satisfy-comp-26 should be true.') 1266 | call v1%satisfies_comp(comparator_t('>=', version_t(0, 1, 0, 'abc')), is_satisfied, e) 1267 | if (allocated(e)) call fail('try_satisfy-comp-27 should not fail.') 1268 | if (.not. is_satisfied) call fail('try_satisfy-comp-27 should be true.') 1269 | call v1%satisfies_comp(comparator_t('<', version_t(0, 1, 0, 'abc')), is_satisfied, e) 1270 | if (allocated(e)) call fail('try_satisfy-comp-28 should not fail.') 1271 | if (is_satisfied) call fail('try_satisfy-comp-28 should not be true.') 1272 | call v1%satisfies_comp(comparator_t('<=', version_t(0, 1, 0, 'abc')), is_satisfied, e) 1273 | if (allocated(e)) call fail('try_satisfy-comp-29 should not fail.') 1274 | if (is_satisfied) call fail('try_satisfy-comp-29 should not be true.') 1275 | 1276 | call v1%satisfies_comp(comparator_t('=', version_t(0, 1, 0, build='1')), is_satisfied, e) 1277 | if (allocated(e)) call fail('try_satisfy-comp-30 should not fail.') 1278 | if (.not. is_satisfied) call fail('try_satisfy-comp-30 should be true.') 1279 | call v1%satisfies_comp(comparator_t('', version_t(0, 1, 0, build='1')), is_satisfied, e) 1280 | if (allocated(e)) call fail('try_satisfy-comp-31 should not fail.') 1281 | if (.not. is_satisfied) call fail('try_satisfy-comp-31 should be true.') 1282 | call v1%satisfies_comp(comparator_t('!=', version_t(0, 1, 0, build='1')), is_satisfied, e) 1283 | if (allocated(e)) call fail('try_satisfy-comp-32 should not fail.') 1284 | if (is_satisfied) call fail('try_satisfy-comp-32 should not be true.') 1285 | call v1%satisfies_comp(comparator_t('>', version_t(0, 1, 0, build='1')), is_satisfied, e) 1286 | if (allocated(e)) call fail('try_satisfy-comp-33 should not fail.') 1287 | if (is_satisfied) call fail('try_satisfy-comp-33 should not be true.') 1288 | call v1%satisfies_comp(comparator_t('>=', version_t(0, 1, 0, build='1')), is_satisfied, e) 1289 | if (allocated(e)) call fail('try_satisfy-comp-34 should not fail.') 1290 | if (.not. is_satisfied) call fail('try_satisfy-comp-34 should be true.') 1291 | call v1%satisfies_comp(comparator_t('<', version_t(0, 1, 0, build='1')), is_satisfied, e) 1292 | if (allocated(e)) call fail('try_satisfy-comp-29 should not fail.') 1293 | if (is_satisfied) call fail('try_satisfy-comp-29 should not be true.') 1294 | call v1%satisfies_comp(comparator_t('<=', version_t(0, 1, 0, build='1')), is_satisfied, e) 1295 | if (allocated(e)) call fail('try_satisfy-comp-30 should not fail.') 1296 | if (.not. is_satisfied) call fail('try_satisfy-comp-30 should be true.') 1297 | 1298 | !##############################satisfies_comp_set##############################! 1299 | 1300 | v1 = version_t(0, 1, 0) 1301 | 1302 | if (allocated(comps)) deallocate (comps) 1303 | allocate (comps(0)) 1304 | call v1%satisfies_comp_set(comparator_set_t(comps), is_satisfied, e) 1305 | if (.not. allocated(e)) call fail('try_satisfy-comp-set-1 should fail.') 1306 | 1307 | comps = [comparator_t('=', version_t(0, 1, 0))] 1308 | call v1%satisfies_comp_set(comparator_set_t(comps), is_satisfied, e) 1309 | if (.not. is_satisfied) call fail('try_satisfy-comp-set-2 should satisfy.') 1310 | if (allocated(e)) call fail('try_satisfy-comp-set-2 should not fail.') 1311 | comps = [comparator_t('', version_t(0, 1, 0))] 1312 | call v1%satisfies_comp_set(comparator_set_t(comps), is_satisfied, e) 1313 | if (.not. is_satisfied) call fail('try_satisfy-comp-set-2 should satisfy.') 1314 | if (allocated(e)) call fail('try_satisfy-comp-set-2 should not fail.') 1315 | comps = [comparator_t('!=', version_t(0, 1, 0))] 1316 | call v1%satisfies_comp_set(comparator_set_t(comps), is_satisfied, e) 1317 | if (is_satisfied) call fail('try_satisfy-comp-set-3 should not satisfy.') 1318 | if (allocated(e)) call fail('try_satisfy-comp-set-3 should not fail.') 1319 | comps = [comparator_t('>', version_t(0, 1, 0))] 1320 | call v1%satisfies_comp_set(comparator_set_t(comps), is_satisfied, e) 1321 | if (is_satisfied) call fail('try_satisfy-comp-set-4 should not satisfy.') 1322 | if (allocated(e)) call fail('try_satisfy-comp-set-4 should not fail.') 1323 | comps = [comparator_t('>=', version_t(0, 1, 0))] 1324 | call v1%satisfies_comp_set(comparator_set_t(comps), is_satisfied, e) 1325 | if (.not. is_satisfied) call fail('try_satisfy-comp-set-5 should satisfy.') 1326 | if (allocated(e)) call fail('try_satisfy-comp-set-5 should not fail.') 1327 | comps = [comparator_t('<', version_t(0, 1, 0))] 1328 | call v1%satisfies_comp_set(comparator_set_t(comps), is_satisfied, e) 1329 | if (is_satisfied) call fail('try_satisfy-comp-set-6 should not satisfy.') 1330 | if (allocated(e)) call fail('try_satisfy-comp-set-6 should not fail.') 1331 | comps = [comparator_t('<=', version_t(0, 1, 0))] 1332 | call v1%satisfies_comp_set(comparator_set_t(comps), is_satisfied, e) 1333 | if (.not. is_satisfied) call fail('try_satisfy-comp-set-7 should satisfy.') 1334 | if (allocated(e)) call fail('try_satisfy-comp-set-7 should not fail.') 1335 | 1336 | comps = [comparator_t('=', version_t(1))] 1337 | call v1%satisfies_comp_set(comparator_set_t(comps), is_satisfied, e) 1338 | if (is_satisfied) call fail('try_satisfy-comp-set-8 should not satisfy.') 1339 | if (allocated(e)) call fail('try_satisfy-comp-set-8 should not fail.') 1340 | comps = [comparator_t('', version_t(1))] 1341 | call v1%satisfies_comp_set(comparator_set_t(comps), is_satisfied, e) 1342 | if (is_satisfied) call fail('try_satisfy-comp-set-8 should not satisfy.') 1343 | if (allocated(e)) call fail('try_satisfy-comp-set-8 should not fail.') 1344 | comps = [comparator_t('!=', version_t(1))] 1345 | call v1%satisfies_comp_set(comparator_set_t(comps), is_satisfied, e) 1346 | if (.not. is_satisfied) call fail('try_satisfy-comp-set-9 should satisfy.') 1347 | if (allocated(e)) call fail('try_satisfy-comp-set-9 should not fail.') 1348 | comps = [comparator_t('>', version_t(1))] 1349 | call v1%satisfies_comp_set(comparator_set_t(comps), is_satisfied, e) 1350 | if (is_satisfied) call fail('try_satisfy-comp-set-10 should not satisfy.') 1351 | if (allocated(e)) call fail('try_satisfy-comp-set-10 should not fail.') 1352 | comps = [comparator_t('>=', version_t(1))] 1353 | call v1%satisfies_comp_set(comparator_set_t(comps), is_satisfied, e) 1354 | if (is_satisfied) call fail('try_satisfy-comp-set-11 should not satisfy.') 1355 | if (allocated(e)) call fail('try_satisfy-comp-set-11 should not fail.') 1356 | comps = [comparator_t('<', version_t(1))] 1357 | call v1%satisfies_comp_set(comparator_set_t(comps), is_satisfied, e) 1358 | if (.not. is_satisfied) call fail('try_satisfy-comp-set-12 should satisfy.') 1359 | if (allocated(e)) call fail('try_satisfy-comp-set-12 should not fail.') 1360 | comps = [comparator_t('<=', version_t(1))] 1361 | call v1%satisfies_comp_set(comparator_set_t(comps), is_satisfied, e) 1362 | if (.not. is_satisfied) call fail('try_satisfy-comp-set-13 should satisfy.') 1363 | if (allocated(e)) call fail('try_satisfy-comp-set-13 should not fail.') 1364 | 1365 | comps = [comparator_t('!=', version_t(1)), comparator_t('>', version_t(0, 9))] 1366 | call v1%satisfies_comp_set(comparator_set_t(comps), is_satisfied, e) 1367 | if (is_satisfied) call fail('try_satisfy-comp-set-14 should not satisfy.') 1368 | if (allocated(e)) call fail('try_satisfy-comp-set-14 should not fail.') 1369 | 1370 | comps = [comparator_t('=', version_t(1)), comparator_t('<', version_t(0, 9))] 1371 | call v1%satisfies_comp_set(comparator_set_t(comps), is_satisfied, e) 1372 | if (is_satisfied) call fail('try_satisfy-comp-set-15 should not satisfy.') 1373 | if (allocated(e)) call fail('try_satisfy-comp-set-15 should not fail.') 1374 | 1375 | comps = [comparator_t('', version_t(1)), comparator_t('>', version_t(0, 9))] 1376 | call v1%satisfies_comp_set(comparator_set_t(comps), is_satisfied, e) 1377 | if (is_satisfied) call fail('try_satisfy-comp-set-16 should not satisfy.') 1378 | if (allocated(e)) call fail('try_satisfy-comp-set-16 should not fail.') 1379 | 1380 | comps = [comparator_t('!=', version_t(1)), comparator_t('<', version_t(0, 9))] 1381 | call v1%satisfies_comp_set(comparator_set_t(comps), is_satisfied, e) 1382 | if (.not. is_satisfied) call fail('try_satisfy-comp-set-17 should satisfy.') 1383 | if (allocated(e)) call fail('try_satisfy-comp-set-17 should not fail.') 1384 | 1385 | comps = [comparator_t('', version_t(1)), comparator_t('', version_t(2))] 1386 | call v1%satisfies_comp_set(comparator_set_t(comps), is_satisfied, e) 1387 | if (is_satisfied) call fail('try_satisfy-comp-set-18 should not satisfy.') 1388 | if (allocated(e)) call fail('try_satisfy-comp-set-18 should not fail.') 1389 | 1390 | comps = [comparator_t('', version_t(0, 1)), comparator_t('', version_t(1))] 1391 | call v1%satisfies_comp_set(comparator_set_t(comps), is_satisfied, e) 1392 | if (is_satisfied) call fail('try_satisfy-comp-set-19 should not satisfy.') 1393 | if (allocated(e)) call fail('try_satisfy-comp-set-19 should not fail.') 1394 | 1395 | comps = [comparator_t('', version_t(1)), comparator_t('', version_t(0, 1))] 1396 | call v1%satisfies_comp_set(comparator_set_t(comps), is_satisfied, e) 1397 | if (is_satisfied) call fail('try_satisfy-comp-set-20 should not satisfy.') 1398 | if (allocated(e)) call fail('try_satisfy-comp-set-20 should not fail.') 1399 | 1400 | comps = [comparator_t('!=', version_t(1)), comparator_t('', version_t(0, 1))] 1401 | call v1%satisfies_comp_set(comparator_set_t(comps), is_satisfied, e) 1402 | if (.not. is_satisfied) call fail('try_satisfy-comp-set-21 should satisfy.') 1403 | if (allocated(e)) call fail('try_satisfy-comp-set-21 should not fail.') 1404 | 1405 | comps = [comparator_t('<', version_t(0, 1)), comparator_t('', version_t(0, 1)), comparator_t('>', version_t(2))] 1406 | call v1%satisfies_comp_set(comparator_set_t(comps), is_satisfied, e) 1407 | if (is_satisfied) call fail('try_satisfy-comp-set-22 should not satisfy.') 1408 | if (allocated(e)) call fail('try_satisfy-comp-set-22 should not fail.') 1409 | 1410 | !################################parse_comp_set################################! 1411 | 1412 | call comp_set%parse('abc', e) 1413 | if (.not. allocated(e)) call fail('parse-comp-set-1 should fail.') 1414 | 1415 | call comp_set%parse('>0.0.1', e) 1416 | if (comp_set%comps(1)%op /= '>') call fail("parse-comp-set-2: Wrong operator parsed.") 1417 | if (comp_set%comps(1)%version /= version_t(0, 0, 1)) call fail('parse-comp-set-2: Version does not match.') 1418 | if (allocated(e)) call fail('parse-comp-set-2 should not fail.') 1419 | 1420 | call comp_set%parse('> 0.0.1', e) 1421 | if (comp_set%comps(1)%op /= '>') call fail("parse-comp-set-3: Wrong operator parsed.") 1422 | if (comp_set%comps(1)%version /= version_t(0, 0, 1)) call fail('parse-comp-set-3: Version does not match.') 1423 | if (allocated(e)) call fail('parse-comp-set-3 should not fail.') 1424 | 1425 | call comp_set%parse('1', e) 1426 | if (comp_set%comps(1)%op /= '') call fail("parse-comp-set-4: Wrong operator parsed.") 1427 | if (comp_set%comps(1)%version /= version_t(1)) call fail('parse-comp-set-4: Version does not match.') 1428 | if (allocated(e)) call fail('parse-comp-set-4 should not fail.') 1429 | 1430 | call comp_set%parse('1.0.0 1.0.1', e) 1431 | if (comp_set%comps(1)%op /= '') call fail("parse-comp-set-5: Wrong operator parsed.") 1432 | if (comp_set%comps(1)%version /= version_t(1)) call fail('parse-comp-set-5: Version does not match.') 1433 | if (comp_set%comps(2)%op /= '') call fail("parse-comp-set-5: Wrong operator parsed.") 1434 | if (comp_set%comps(2)%version /= version_t(1, 0, 1)) call fail('parse-comp-set-5: Version does not match.') 1435 | if (allocated(e)) call fail('parse-comp-set-5 should not fail.') 1436 | 1437 | call comp_set%parse('=1.0.0 !=1.0.1', e) 1438 | if (comp_set%comps(1)%op /= '=') call fail("parse-comp-set-6: Wrong operator parsed.") 1439 | if (comp_set%comps(1)%version /= version_t(1)) call fail('parse-comp-set-6: Version does not match.') 1440 | if (comp_set%comps(2)%op /= '!=') call fail("parse-comp-set-6: Wrong operator parsed.") 1441 | if (comp_set%comps(2)%version /= version_t(1, 0, 1)) call fail('parse-comp-set-6: Version does not match.') 1442 | if (allocated(e)) call fail('parse-comp-set-6 should not fail.') 1443 | 1444 | call comp_set%parse('< 1.0.0 > 1.0.1 =2', e) 1445 | if (size(comp_set%comps) /= 3) call fail("parse-comp-set-7: Wrong number of comparators.") 1446 | if (comp_set%comps(1)%op /= '<') call fail("parse-comp-set-7: Wrong operator parsed.") 1447 | if (comp_set%comps(1)%version /= version_t(1)) call fail('parse-comp-set-7: Version does not match.') 1448 | if (comp_set%comps(2)%op /= '>') call fail("parse-comp-set-7: Wrong operator parsed.") 1449 | if (comp_set%comps(2)%version /= version_t(1, 0, 1)) call fail('parse-comp-set-7: Version does not match.') 1450 | if (comp_set%comps(3)%op /= '=') call fail("parse-comp-set-7: Wrong operator parsed.") 1451 | if (comp_set%comps(3)%version /= version_t(2)) call fail('parse-comp-set-7: Version does not match.') 1452 | if (allocated(e)) call fail('parse-comp-set-7 should not fail.') 1453 | 1454 | call comp_set%parse(' > 1.0.1 < 2.1.0 ', e) 1455 | if (size(comp_set%comps) /= 2) call fail("parse-comp-set-8: Wrong number of comparators.") 1456 | if (comp_set%comps(1)%op /= '>') call fail("parse-comp-set-8: Wrong operator parsed.") 1457 | if (comp_set%comps(1)%version /= version_t(1, 0, 1)) call fail('parse-comp-set-8: Version does not match.') 1458 | if (comp_set%comps(2)%op /= '<') call fail("parse-comp-set-8: Wrong operator parsed.") 1459 | if (comp_set%comps(2)%version /= version_t(2, 1, 0)) call fail('parse-comp-set-8: Version does not match.') 1460 | if (allocated(e)) call fail('parse-comp-set-8 should not fail.') 1461 | 1462 | call comp_set%parse(' >= 1.0.1 <= 2.1.0 ', e) 1463 | if (size(comp_set%comps) /= 2) call fail("parse-comp-set-9: Wrong number of comparators.") 1464 | if (comp_set%comps(1)%op /= '>=') call fail("parse-comp-set-9: Wrong operator parsed.") 1465 | if (comp_set%comps(1)%version /= version_t(1, 0, 1)) call fail('parse-comp-set-9: Version does not match.') 1466 | if (comp_set%comps(2)%op /= '<=') call fail("parse-comp-set-9: Wrong operator parsed.") 1467 | if (comp_set%comps(2)%version /= version_t(2, 1, 0)) call fail('parse-comp-set-9: Version does not match.') 1468 | if (allocated(e)) call fail('parse-comp-set-9 should not fail.') 1469 | 1470 | !##############################parse_version_range#############################! 1471 | 1472 | call range%parse('', e) 1473 | if (.not. allocated(e)) call fail('parse-version-range-1 should fail.') 1474 | 1475 | call range%parse('abc', e) 1476 | if (.not. allocated(e)) call fail('parse-version-range-2 should fail.') 1477 | 1478 | call range%parse('abc||abc', e) 1479 | if (.not. allocated(e)) call fail('parse-version-range-3 should fail.') 1480 | 1481 | call range%parse('abc||', e) 1482 | if (.not. allocated(e)) call fail('parse-version-range-4 should fail.') 1483 | 1484 | call range%parse('||abc', e) 1485 | if (.not. allocated(e)) call fail('parse-version-range-5 should fail.') 1486 | 1487 | call range%parse('||', e) 1488 | if (.not. allocated(e)) call fail('parse-version-range-6 should fail.') 1489 | 1490 | call range%parse('0.2.0', e) 1491 | if (size(range%comp_sets) /= 1) call fail('parse-version-range-7 has wrong number of sets.') 1492 | if (range%comp_sets(1)%comps(1)%op /= '') call fail('parse-version-range-7: Wrong operator.') 1493 | if (range%comp_sets(1)%comps(1)%version /= version_t(0, 2)) call fail('parse-version-range-7: Wrong version.') 1494 | if (allocated(e)) call fail('parse-version-range-7 should not fail.') 1495 | 1496 | call range%parse('0.2.0 || 0.3.0', e) 1497 | if (size(range%comp_sets) /= 2) call fail('parse-version-range-8 has wrong number of sets.') 1498 | if (range%comp_sets(1)%comps(1)%op /= '') call fail('parse-version-range-8: Wrong operator.') 1499 | if (range%comp_sets(1)%comps(1)%version /= version_t(0, 2)) call fail('parse-version-range-8: Wrong version.') 1500 | if (range%comp_sets(2)%comps(1)%op /= '') call fail('parse-version-range-8: Wrong operator.') 1501 | if (range%comp_sets(2)%comps(1)%version /= version_t(0, 3)) call fail('parse-version-range-8: Wrong version.') 1502 | if (allocated(e)) call fail('parse-version-range-8 should not fail.') 1503 | 1504 | call range%parse('0.2.0 || <0.3.0 || >= 0.4.0', e) 1505 | if (size(range%comp_sets) /= 3) call fail('parse-version-range-9 has wrong number of sets.') 1506 | if (range%comp_sets(1)%comps(1)%op /= '') call fail('parse-version-range-9 has parsed the wrong operator.') 1507 | if (range%comp_sets(1)%comps(1)%version /= version_t(0, 2)) call fail('parse-version-range-9 has parsed the wrong version.') 1508 | if (range%comp_sets(2)%comps(1)%op /= '<') call fail('parse-version-range-9 has parsed the wrong operator.') 1509 | if (range%comp_sets(2)%comps(1)%version /= version_t(0, 3)) call fail('parse-version-range-9 has parsed the wrong version.') 1510 | if (range%comp_sets(3)%comps(1)%op /= '>=') call fail('parse-version-range-9 has parsed the wrong operator.') 1511 | if (range%comp_sets(3)%comps(1)%version /= version_t(0, 4)) call fail('parse-version-range-9 has parsed the wrong version.') 1512 | if (allocated(e)) call fail('parse-version-range-9 should not fail.') 1513 | 1514 | call range%parse('0.2.0 <0.3.0 || >= 0.4.0 !=0.4.1 =0.5.0 || 0.6.0', e) 1515 | if (size(range%comp_sets) /= 3) call fail('parse-version-range-10 has wrong number of sets.') 1516 | if (range%comp_sets(1)%comps(1)%op /= '') call fail('parse-version-range-10: Wrong operator.') 1517 | if (range%comp_sets(1)%comps(1)%version /= version_t(0, 2)) call fail('parse-version-range-10: Wrong version.') 1518 | if (range%comp_sets(1)%comps(2)%op /= '<') call fail('parse-version-range-10: Wrong operator.') 1519 | if (range%comp_sets(1)%comps(2)%version /= version_t(0, 3)) call fail('parse-version-range-10: Wrong version.') 1520 | if (range%comp_sets(2)%comps(1)%op /= '>=') call fail('parse-version-range-10: Wrong operator.') 1521 | if (range%comp_sets(2)%comps(1)%version /= version_t(0, 4)) call fail('parse-version-range-10: Wrong version.') 1522 | if (range%comp_sets(2)%comps(2)%op /= '!=') call fail('parse-version-range-10: Wrong operator.') 1523 | if (range%comp_sets(2)%comps(2)%version /= version_t(0, 4, 1)) call fail('parse-version-range-10: Wrong version.') 1524 | if (range%comp_sets(2)%comps(3)%op /= '=') call fail('parse-version-range-10: Wrong operator.') 1525 | if (range%comp_sets(2)%comps(3)%version /= version_t(0, 5)) call fail('parse-version-range-10: Wrong version.') 1526 | if (range%comp_sets(3)%comps(1)%op /= '') call fail('parse-version-range-10: Wrong operator.') 1527 | if (range%comp_sets(3)%comps(1)%version /= version_t(0, 6)) call fail('parse-version-range-10: Wrong version.') 1528 | if (allocated(e)) call fail('parse-version-range-10 should not fail.') 1529 | 1530 | call range%parse('0.2.0 0.2.1 0.3.0', e) 1531 | if (size(range%comp_sets) /= 1) call fail('parse-version-range-11 has wrong number of sets.') 1532 | if (range%comp_sets(1)%comps(1)%op /= '') call fail('parse-version-range-11: Wrong operator.') 1533 | if (range%comp_sets(1)%comps(1)%version /= version_t(0, 2)) call fail('parse-version-range-11: Wrong version.') 1534 | if (range%comp_sets(1)%comps(2)%op /= '') call fail('parse-version-range-11: Wrong operator.') 1535 | if (range%comp_sets(1)%comps(2)%version /= version_t(0, 2, 1)) call fail('parse-version-range-11: Wrong version.') 1536 | if (range%comp_sets(1)%comps(3)%op /= '') call fail('parse-version-range-11: Wrong operator.') 1537 | if (range%comp_sets(1)%comps(3)%version /= version_t(0, 3)) call fail('parse-version-range-11: Wrong version.') 1538 | if (allocated(e)) call fail('parse-version-range-11 should not fail.') 1539 | 1540 | !###################################is_stable##################################! 1541 | 1542 | v1 = version_t(0, 9, 99) 1543 | if (v1%is_stable()) call fail('is_stable-1 should not be stable') 1544 | v1 = version_t(1, 0, 0) 1545 | if (.not. v1%is_stable()) call fail('is_stable-2 should be stable') 1546 | v1 = version_t(1, 0, 0, 'alpha') 1547 | if (v1%is_stable()) call fail('is_stable-3 should not be stable') 1548 | v1 = version_t(0, 0, 1, 'alpha') 1549 | if (v1%is_stable()) call fail('is_stable-4 should not be stable') 1550 | v1 = version_t(0, 0, 1, build='alpha') 1551 | if (v1%is_stable()) call fail('is_stable-5 should not be stable') 1552 | v1 = version_t(1, 0, 1, build='alpha') 1553 | if (.not. v1%is_stable()) call fail('is_stable-6 should be stable') 1554 | 1555 | !#################################final_message################################! 1556 | 1557 | print *, achar(10)//achar(27)//'[92m All tests passed.'//achar(27) 1558 | 1559 | contains 1560 | 1561 | subroutine fail(msg) 1562 | character(*), intent(in) :: msg 1563 | print *, achar(27)//'[31m'//'Test failed: '//msg//achar(27)//'[0m' 1564 | stop 1 1565 | end 1566 | end 1567 | --------------------------------------------------------------------------------