├── .gitignore
├── .vscode
└── launch.json
├── LICENSE
├── README.md
├── SECURITY.md
├── build.sh
├── cmd
├── check.go
├── clean.go
├── create.go
├── list.go
├── root.go
└── version.go
├── generate-windowsdata.sh
├── go.mod
├── go.sum
├── main.go
├── pkg
├── aggregator
│ ├── aggregator.go
│ ├── aggregator_test.go
│ ├── download.go
│ ├── file.go
│ └── types.go
├── browser
│ ├── browser_darwin.go
│ ├── browser_linux.go
│ └── browser_windows.go
├── deps
│ ├── checker.go
│ ├── checker_test.go
│ └── constants.go
├── extractor
│ ├── tar.go
│ ├── tar_test.go
│ └── testdata
│ │ ├── README.md
│ │ ├── golden.tar.gz
│ │ └── ok.tar.gz
└── ui
│ ├── cli.go
│ ├── cli_test.go
│ └── testdata
│ ├── cpp.json
│ ├── python.json
│ └── zoo
│ ├── cpp.tar.gz
│ └── python.tar.gz
├── test.sh
├── windowsdata.syso
└── winres.rc
/.gitignore:
--------------------------------------------------------------------------------
1 | /bin/
2 | /build/
3 | /config.*
4 | !config.toml.dist
5 | /docker-compose.override.yml
6 | /var/
7 | /vendor/
8 | cover.out
9 |
10 | # IDE integration
11 | /.vscode/*
12 | !/.vscode/launch.json
13 | !/.vscode/tasks.json
14 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Launch",
9 | "type": "go",
10 | "request": "launch",
11 | "mode": "auto",
12 | "program": "${workspaceFolder}",
13 | "env": {},
14 | "args": [""]
15 | }
16 | ]
17 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | This license file applies to the oneapi-cli application.
2 |
3 | If you wish to obtain access to the source for the above-licensed component,
4 | please visit https://github.com/intel/vscode-sample-browser.
5 |
6 | SPDX-License-Identifier: BSD-3-Clause
7 | https://opensource.org/licenses/BSD-3-Clause
8 |
9 | _____________________________________________________________________________
10 |
11 | BSD 3-Clause "New" or "Revised" License
12 |
13 | Copyright (C) 2019, Intel Corporation. All rights reserved.
14 |
15 |
16 | Redistribution and use in source and binary forms, with or without modification,
17 | are permitted provided that the following conditions are met:
18 |
19 | - Redistributions of source code must retain the above copyright notice,
20 | this list of conditions and the following disclaimer.
21 |
22 | - Redistributions in binary form must reproduce the above copyright notice,
23 | this list of conditions and the following disclaimer in the documentation
24 | and/or other materials provided with the distribution.
25 |
26 | - Neither the name of the copyright holder nor the names of its contributors may
27 | be used to endorse or promote products derived from this software without
28 | specific prior written permission.
29 |
30 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
31 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
32 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
33 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
34 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
35 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
36 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
37 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
39 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # oneapi-cli tool
2 | [](https://goreportcard.com/report/github.com/intel/oneapi-cli)
3 |
4 | `oneapi-cli` is a tool to help you get started with Intel® oneAPI
5 |
6 | ## Where to find Intel® oneAPI.
7 |
8 | This tool does not provide any of the tools that may be required to compile/run the samples `oneapi-cli` can extract for you.
9 |
10 | Please visit https://software.intel.com/en-us/oneapi for details.
11 |
12 | ## Development Install
13 |
14 | Fetch using
15 | ```bash
16 | go get github.com/intel/oneapi-cli
17 | ```
18 | Alternatively see the tags/releases for a binary build for your OS.
19 |
20 | ## Building
21 | Go 1.20 should be used to build the CLI/TUI app.
22 |
23 | ```bash
24 | git clone https://github.com/intel/oneapi-cli.git
25 | cd oneapi-cli
26 | go build
27 | ./oneapi-cli
28 | ```
29 |
30 | There is also a `build.sh` which will embed version information within the build.
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 | Intel is committed to rapidly addressing security vulnerabilities affecting our customers and providing clear guidance on the solution, impact, severity and mitigation.
3 |
4 | ## Reporting a Vulnerability
5 | Please report any security vulnerabilities in this project [utilizing the guidelines here](https://www.intel.com/content/www/us/en/security-center/vulnerability-handling-guidelines.html).
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | export CGO_ENABLED=0
3 |
4 | version=$(git describe --long --dirty --abbrev=10 --tags)
5 | lf="-X github.com/intel/oneapi-cli/cmd.version=${version}"
6 |
7 | GOOS=linux GOARCH=amd64 go build -trimpath -mod=readonly -gcflags="all=-spectre=all -N -l" -asmflags="all=-spectre=all" -ldflags="all=-s -w $lf" -o linux/bin/oneapi-cli
8 | GOOS=windows GOARCH=amd64 go build -trimpath -mod=readonly -gcflags="all=-spectre=all -N -l" -asmflags="all=-spectre=all" -ldflags="all=-s -w $lf" -o win/bin/oneapi-cli.exe
9 | GOOS=darwin GOARCH=amd64 go build -trimpath -mod=readonly -gcflags="all=-spectre=all -N -l" -asmflags="all=-spectre=all" -ldflags="all=-s -w $lf" -o osx/bin/oneapi-cli
--------------------------------------------------------------------------------
/cmd/check.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Intel Corporation
2 | // SPDX-License-Identifier: BSD-3-Clause
3 |
4 | package cmd
5 |
6 | import (
7 | "fmt"
8 | "os"
9 |
10 | "github.com/intel/oneapi-cli/pkg/deps"
11 | "github.com/spf13/cobra"
12 | )
13 |
14 | var depsParam []string
15 | var root string
16 |
17 | // checkCmd represents the check command
18 | var checkCmd = &cobra.Command{
19 | Use: "check",
20 | Short: "check dependencies",
21 | Hidden: true,
22 | Long: `check dependencies, returns an error/retrieve-it- message if dependencies are absent`,
23 | Run: func(cmd *cobra.Command, args []string) {
24 |
25 | if root == "" {
26 | //Find the oneAPI root
27 | var err error
28 | root, err = deps.GetOneAPIRoot()
29 | if err != nil {
30 | fmt.Println(err) //Failed to find the Env, may be unset.
31 | os.Exit(-1)
32 | }
33 |
34 | }
35 |
36 | //Check the deps at the found root.
37 | msg, errCode := deps.CheckDeps(depsParam, root)
38 | if errCode != 0 {
39 | fmt.Println(msg)
40 | os.Exit(errCode)
41 | }
42 |
43 | },
44 | }
45 |
46 | func init() {
47 | rootCmd.AddCommand(checkCmd)
48 | checkCmd.Flags().StringSliceVarP(&depsParam, "deps", "", nil, "comma seperated dependency array")
49 | checkCmd.Flags().StringVar(&root, "oneapi-root", "", "(optional) path to oneAPI root, default attempts to use environment varible ONEAPI_ROOT")
50 | }
51 |
--------------------------------------------------------------------------------
/cmd/clean.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Intel Corporation
2 | // SPDX-License-Identifier: BSD-3-Clause
3 |
4 | package cmd
5 |
6 | import (
7 | "fmt"
8 | "os"
9 | "path/filepath"
10 |
11 | "github.com/intel/oneapi-cli/pkg/aggregator"
12 | "github.com/spf13/cobra"
13 | )
14 |
15 | // cleanCmd represents the clean command
16 | var cleanCmd = &cobra.Command{
17 | Use: "clean",
18 | Short: "Clean Sample Cache",
19 | Long: `Removes local Sample Cache`,
20 | Run: func(cmd *cobra.Command, args []string) {
21 | if err := os.RemoveAll(filepath.Join(baseFilePath, aggregator.AggregatorLocalAPILevel)); err != nil {
22 | fmt.Println("Failed to clean sample cache.")
23 | fmt.Printf("%s \n", err)
24 | os.Exit(1)
25 | }
26 | },
27 | }
28 |
29 | func init() {
30 | rootCmd.AddCommand(cleanCmd)
31 | }
32 |
--------------------------------------------------------------------------------
/cmd/create.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Intel Corporation
2 | // SPDX-License-Identifier: BSD-3-Clause
3 |
4 | package cmd
5 |
6 | import (
7 | "fmt"
8 | "os"
9 |
10 | "github.com/intel/oneapi-cli/pkg/aggregator"
11 | "github.com/intel/oneapi-cli/pkg/extractor"
12 | "github.com/spf13/cobra"
13 | )
14 |
15 | var sampleLang string
16 |
17 | // listCmd represents the list command
18 | var createCmd = &cobra.Command{
19 | Use: "create",
20 | Short: "Create Sample",
21 | Hidden: true,
22 | Long: `Creates the sample based on the passed in path
23 |
24 | i.e. oneapi-cli create -s cpp my/long/path/from/index/json /tmp/mynewproject`,
25 | Run: func(cmd *cobra.Command, args []string) {
26 |
27 | //Arg 0 being sample
28 | //arg 1 being where to create the sample. Complete path
29 |
30 | if len(args) != 2 || args[0] == "" || args[1] == "" {
31 | fmt.Println("Please pass both a sample and where you want it extracted to")
32 | os.Exit(1)
33 | }
34 |
35 | tarPath, err := aggregator.GetTarBall(baseFilePath, baseURL, sampleLang, args[0])
36 | if err != nil {
37 | fmt.Println(err)
38 | os.Exit(2)
39 | }
40 | err = extractor.ExtractTarGz(tarPath, args[1])
41 | if err != nil {
42 | fmt.Println(err)
43 | os.Exit(3)
44 | }
45 |
46 | },
47 | }
48 |
49 | func init() {
50 | rootCmd.AddCommand(createCmd)
51 | createCmd.Flags().StringVarP(&sampleLang, "sampleLangauge", "s", "cpp", "specific language of the samples you want to create")
52 | }
53 |
--------------------------------------------------------------------------------
/cmd/list.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Intel Corporation
2 | // SPDX-License-Identifier: BSD-3-Clause
3 |
4 | package cmd
5 |
6 | import (
7 | "encoding/json"
8 | "fmt"
9 | "os"
10 |
11 | "github.com/spf13/cobra"
12 | )
13 |
14 | func prettyPrint(i interface{}) string {
15 | s, err := json.MarshalIndent(i, "", "\t")
16 | if err != nil {
17 | fmt.Println("Failed to pretty print Json")
18 | fmt.Printf("%s \n", err)
19 | os.Exit(1)
20 | }
21 | return string(s)
22 | }
23 |
24 | var language string
25 | var outputJSON bool
26 |
27 | // listCmd represents the list command
28 | var listCmd = &cobra.Command{
29 | Use: "list",
30 | Short: "List Samples",
31 | Long: `Lists the available samples. Checks online if newer sample index
32 | is available`,
33 | Run: func(cmd *cobra.Command, args []string) {
34 |
35 | if language == "" {
36 | for _, l := range getAggregator().GetLanguages() {
37 | fmt.Printf("%s\n", l)
38 | }
39 | os.Exit(1)
40 | }
41 |
42 | //Check language is part of the support ones from the aggregator module
43 | var found bool
44 | for _, l := range getAggregator().GetLanguages() {
45 | if l == language {
46 | found = true
47 | break
48 | }
49 | }
50 | if !found {
51 | fmt.Printf("Invalid language provided, available languages: %v\n", getAggregator().GetLanguages())
52 | os.Exit(1)
53 | }
54 |
55 | if getAggregator().Samples[language] == nil {
56 | if outputJSON {
57 | fmt.Printf("[]")
58 | }
59 | return
60 | }
61 |
62 | if outputJSON {
63 | fmt.Printf("%s\n", prettyPrint(getAggregator().Samples[language]))
64 | return
65 | }
66 |
67 | for _, s := range getAggregator().Samples[language] {
68 | fmt.Printf("%s:\n\t%s\n", s.Fields.Name, s.Fields.Description)
69 | }
70 | },
71 | }
72 |
73 | func init() {
74 | rootCmd.AddCommand(listCmd)
75 | listCmd.Flags().StringVarP(&language, "output", "o", "", "specific language samples you want to list")
76 | listCmd.Flags().BoolVarP(&outputJSON, "json", "j", false, "output as JSON")
77 | }
78 |
--------------------------------------------------------------------------------
/cmd/root.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Intel Corporation
2 | // SPDX-License-Identifier: BSD-3-Clause
3 |
4 | package cmd
5 |
6 | import (
7 | "bufio"
8 | "fmt"
9 | "log"
10 | "os"
11 | "path/filepath"
12 |
13 | "github.com/intel/oneapi-cli/pkg/aggregator"
14 | "github.com/intel/oneapi-cli/pkg/ui"
15 | "github.com/spf13/cobra"
16 | )
17 |
18 | const SamplesEndpointDefault = "https://iotdk.intel.com/samples-iss"
19 |
20 | const SampleLatestKey = "latest"
21 |
22 | const LocalStorageDefault = ".oneapi-cli"
23 |
24 | var baseURL string
25 | var baseFilePath string
26 | var cAggregator *aggregator.Aggregator
27 | var defaultLanguages = []string{"cpp", "python", "fortran"}
28 | var enabledLanguages []string
29 | var userHome string
30 | var ignoreOS bool
31 | var bulk bool
32 |
33 | // rootCmd represents the base command when called without any subcommands
34 | var rootCmd = &cobra.Command{
35 | Use: "oneapi-cli",
36 | Short: "oneapi-cli a tool to fetch samples",
37 | Long: `oneapi-cli is tool for fetching samples. It intends to be used either
38 | interactively or called from another tool`,
39 |
40 | Run: func(cmd *cobra.Command, args []string) {
41 | fmt.Printf("Connecting to online Sample Aggregator, this may take some time based on network conditions\n")
42 | app, err := ui.NewCLI(getAggregator(), userHome)
43 | if err != nil {
44 | log.Fatal(err)
45 | }
46 | app.Show()
47 |
48 | },
49 | }
50 |
51 | func getAggregator() *aggregator.Aggregator {
52 | if cAggregator == nil {
53 | var err error
54 | cAggregator, err = aggregator.NewAggregator(baseURL, baseFilePath, enabledLanguages, ignoreOS, bulk)
55 | if err != nil && err != aggregator.ErrCacheLock {
56 | //Most errors we are going to find are network related :/
57 | fmt.Printf("Failed to fetch sample index, this *may* be your network/proxy environment.\nYou might try setting http_proxy in your environment, for example:\n")
58 | fmt.Printf("\tLinux/Mac: export http_proxy=http://your.proxy:8080\n")
59 | fmt.Printf("\tWindows: set http_proxy=http://your.proxy:8080\n")
60 | fmt.Printf("%v\n", err)
61 | os.Exit(1)
62 | }
63 | if err == aggregator.ErrCacheLock {
64 | fmt.Printf("Local Sample cache is corrupt! Please clean the cache and retry!\n")
65 | fmt.Printf("\toneapi-cli clean\n")
66 | fmt.Printf("\toneapi-cli\n")
67 | os.Exit(1)
68 | }
69 | }
70 | return cAggregator
71 |
72 | }
73 |
74 | // Execute adds all child commands to the root command and sets flags appropriately.
75 | // This is called by main.main(). It only needs to happen once to the rootCmd.
76 | func Execute() {
77 | if err := rootCmd.Execute(); err != nil {
78 | fmt.Println(err)
79 | os.Exit(1)
80 | }
81 | }
82 |
83 | func init() {
84 |
85 | var err error
86 | userHome, err = os.UserHomeDir()
87 | if err != nil {
88 | fmt.Printf("Unable to locate Home Directory - %v\n", err)
89 | os.Exit(1)
90 | }
91 | defaultBaseFilePath := filepath.Join(userHome, LocalStorageDefault)
92 |
93 | rootCmd.PersistentFlags().StringVarP(&baseURL, "url", "u", getVersionInfo(), "URL of remote sample aggregator")
94 | rootCmd.PersistentFlags().StringVarP(&baseFilePath, "directory", "d", defaultBaseFilePath, "location to store local oneapi samples cache")
95 | rootCmd.PersistentFlags().StringSliceVarP(&enabledLanguages, "languages", "l", defaultLanguages, "enabled languages")
96 | rootCmd.PersistentFlags().BoolVar(&ignoreOS, "ignore-os", false, "ignore Host-OS based filtering when showing/outputting samples")
97 | rootCmd.PersistentFlags().BoolVar(&bulk, "full-sync", false, "download all samples at startup")
98 |
99 | }
100 |
101 | // looks at the command bin path and looks for "samples-version-tag.txt" which
102 |
103 | // points to which sample version to look at. If it cant find it it
104 | // returns the Latestkey const
105 | func getVersionInfo() string {
106 | bin, err := os.Executable()
107 | if err != nil {
108 | return fmt.Sprintf("%s/%s/", SamplesEndpointDefault, SampleLatestKey)
109 | }
110 | bin, err = filepath.EvalSymlinks(bin)
111 | if err != nil {
112 | return fmt.Sprintf("%s/%s/", SamplesEndpointDefault, SampleLatestKey)
113 | }
114 | versionPath := filepath.Join(filepath.Dir(filepath.Dir(bin)), "etc", "samples-version-tag.txt")
115 | file, err := os.Open(versionPath)
116 | if err != nil {
117 | return fmt.Sprintf("%s/%s/", SamplesEndpointDefault, SampleLatestKey)
118 | }
119 | defer file.Close()
120 |
121 | scanner := bufio.NewScanner(file)
122 | scanner.Scan()
123 | if err := scanner.Err(); err != nil {
124 | return fmt.Sprintf("%s/%s/", SamplesEndpointDefault, SampleLatestKey)
125 | }
126 | return fmt.Sprintf("%s/%s/", SamplesEndpointDefault, scanner.Text())
127 | }
128 |
--------------------------------------------------------------------------------
/cmd/version.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Intel Corporation
2 | // SPDX-License-Identifier: BSD-3-Clause
3 |
4 | package cmd
5 |
6 | import (
7 | "fmt"
8 |
9 | "github.com/spf13/cobra"
10 | )
11 |
12 | // Version set during build via `go build -ldflags -X "-X main.Version=version"`
13 | var version string
14 |
15 | // versionCmd represents the version command
16 | var versionCmd = &cobra.Command{
17 | Use: "version",
18 | Short: "Show the CLI version information",
19 | Long: `Show the CLI version information`,
20 | Run: func(cmd *cobra.Command, args []string) {
21 | if version == "" {
22 | version = "devel"
23 | }
24 | fmt.Printf("%s\n", version)
25 | },
26 | }
27 |
28 | func init() {
29 | rootCmd.AddCommand(versionCmd)
30 | }
31 |
--------------------------------------------------------------------------------
/generate-windowsdata.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # How windres is named on your system may vary!!
4 | x86_64-w64-mingw32-windres -i winres.rc -O coff -o windowsdata.syso
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/intel/oneapi-cli
2 |
3 | go 1.24.1
4 |
5 | require (
6 | github.com/gdamore/tcell v1.4.0
7 | github.com/mattn/go-ieproxy v0.0.1
8 | github.com/spf13/cobra v1.6.1
9 | gitlab.com/tslocum/cview v1.4.4
10 | // golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43
11 | golang.org/x/sys v0.31.0
12 | )
13 |
14 | require (
15 | github.com/gdamore/encoding v1.0.0 // indirect
16 | github.com/inconshreveable/mousetrap v1.0.1 // indirect
17 | github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
18 | github.com/mattn/go-runewidth v0.0.8 // indirect
19 | github.com/rivo/uniseg v0.4.2 // indirect
20 | github.com/spf13/pflag v1.0.5 // indirect
21 | golang.org/x/net v0.38.0 // indirect
22 | golang.org/x/text v0.23.0 // indirect
23 | )
24 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
2 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
3 | github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
4 | github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
5 | github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM=
6 | github.com/gdamore/tcell v1.4.0 h1:vUnHwJRvcPQa3tzi+0QI4U9JINXYJlOz9yiaiPQ2wMU=
7 | github.com/gdamore/tcell v1.4.0/go.mod h1:vxEiSDZdW3L+Uhjii9c3375IlDmR05bzxY404ZVSMo0=
8 | github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
9 | github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
10 | github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
11 | github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
12 | github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
13 | github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
14 | github.com/mattn/go-ieproxy v0.0.1 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqfI=
15 | github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E=
16 | github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
17 | github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
18 | github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0=
19 | github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
20 | github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
21 | github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8=
22 | github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
23 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
24 | github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
25 | github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
26 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
27 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
28 | gitlab.com/tslocum/cview v1.4.4 h1:sh1MUSN5zFd7vK+lHEq1jAxRD82TJb6uFW+EnECsEyc=
29 | gitlab.com/tslocum/cview v1.4.4/go.mod h1:+bEf1cg6IoWvL16YHJAKwGGpQf5s/nxXAA7YJr+WOHE=
30 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
31 | golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
32 | golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
33 | golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
34 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
35 | golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
36 | golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
37 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
38 | golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
39 | golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
40 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
41 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
42 | golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
43 | golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
44 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
45 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
46 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
47 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Intel Corporation
2 | // SPDX-License-Identifier: BSD-3-Clause
3 |
4 | package main
5 |
6 | import (
7 | cmd "github.com/intel/oneapi-cli/cmd"
8 | )
9 |
10 | func main() {
11 | cmd.Execute()
12 | }
13 |
--------------------------------------------------------------------------------
/pkg/aggregator/aggregator.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Intel Corporation
2 | // SPDX-License-Identifier: BSD-3-Clause
3 |
4 | package aggregator
5 |
6 | import (
7 | "bytes"
8 | "encoding/json"
9 | "errors"
10 | "fmt"
11 | "io/ioutil"
12 | "log"
13 | "net/url"
14 | "os"
15 | "path/filepath"
16 | "runtime"
17 | "strings"
18 | "sync"
19 | )
20 |
21 | type sampleWorkItem struct {
22 | language string
23 | s Sample
24 | retry int
25 | }
26 |
27 | //Aggregator struct representing the sample store. Not really thread safe
28 | type Aggregator struct {
29 | baseURL *url.URL
30 | localPath string
31 | languages []string
32 | jobs chan sampleWorkItem
33 | results chan error
34 | wg sync.WaitGroup
35 | sampleCount sync.WaitGroup
36 | Samples Samples
37 | Online bool
38 | ignoreOS bool
39 | Bulk bool
40 | }
41 |
42 | const defaultRetry = 3
43 |
44 | //Samples a map containing an array of avaible samples for that language
45 | // i.e. Samples['cpp"]
46 | type Samples map[string][]Sample
47 |
48 | //AggregatorLocalAPILevel the current level of the local file cache. use BaseDir plus this
49 | const AggregatorLocalAPILevel = "v1"
50 |
51 | const cacheLockName = "lock"
52 |
53 | //ErrCacheLock Is thrown when aggregator's local cache is locked.
54 | var ErrCacheLock = errors.New("aggregator cache is locked")
55 |
56 | //HTTPTimeout timeout in seconds for HTTP operations
57 | const HTTPTimeout = 10
58 |
59 | //NewAggregator Gives you a Aggregator.
60 | func NewAggregator(URL string, FilePath string, languages []string, ignoreOS bool, bulk bool) (*Aggregator, error) {
61 | var a Aggregator
62 | if URL == "" {
63 | return nil, fmt.Errorf("no sample url passed")
64 | }
65 | u, err := checkURL(URL)
66 | if err != nil {
67 | return nil, err
68 | }
69 | a.baseURL = u
70 |
71 | if FilePath == "" {
72 | return nil, fmt.Errorf("no base directory passed")
73 | }
74 |
75 | a.ignoreOS = ignoreOS
76 | a.Bulk = bulk
77 |
78 | //Add Current file APP level
79 | a.localPath = filepath.Join(FilePath, AggregatorLocalAPILevel)
80 |
81 | a.languages = languages
82 | //Create Directory for local path
83 | if err := os.MkdirAll(a.localPath, 0750); err != nil {
84 | return nil, err //Package Tests do not cover this
85 | }
86 |
87 | if a.isLocked() {
88 | return nil, ErrCacheLock
89 | }
90 |
91 | if languages == nil || len(languages) < 1 {
92 | return nil, fmt.Errorf("No Languages are being selected")
93 | }
94 |
95 | a.Samples = make(map[string][]Sample)
96 |
97 | if err := a.Update(); err != nil {
98 | return nil, err
99 | }
100 |
101 | return &a, nil
102 | }
103 |
104 | func (a *Aggregator) isLocked() bool {
105 | return FileExists(filepath.Join(a.localPath, cacheLockName))
106 | }
107 |
108 | func (a *Aggregator) lock() {
109 | _, err := os.Create(filepath.Join(a.localPath, cacheLockName))
110 | if err != nil {
111 | log.Fatalf("failed to create cache lock file - %v", err)
112 | }
113 | }
114 |
115 | func sampleWorker(a *Aggregator, jobs <-chan sampleWorkItem, results chan<- error, wg *sync.WaitGroup) {
116 | defer wg.Done()
117 | for j := range jobs {
118 | j.retry--
119 | err := a.workSample(j) // Try sync the sample
120 | if err == nil {
121 | a.sampleCount.Done()
122 | continue // Sync was good, move on
123 | }
124 | if j.retry > 0 {
125 | a.jobs <- j //Resumbit if retry is
126 | continue
127 | }
128 | a.sampleCount.Done()
129 | results <- err // Ran out of retries, submit failure to results
130 | }
131 | }
132 |
133 | func (a *Aggregator) workSample(w sampleWorkItem) error {
134 | _, err := GetTarBall(a.localPath, a.baseURL.String(), w.language, w.s.Path)
135 | if err != nil {
136 | return err
137 | }
138 | return nil
139 | }
140 |
141 | func (a *Aggregator) setupWorkers(n int) {
142 | a.jobs = make(chan sampleWorkItem, 50)
143 | a.results = make(chan error, 100)
144 |
145 | for i := 0; i <= n; i++ {
146 | a.wg.Add(1)
147 | go sampleWorker(a, a.jobs, a.results, &a.wg)
148 | }
149 | }
150 |
151 | //syncLanguages interates over configured lanauges, and if a newer version is available online
152 |
153 | func (a *Aggregator) syncLanguagesIndex() error {
154 | var workingLanguages []string
155 | for _, language := range a.languages {
156 | localPath := filepath.Join(a.localPath, language+".json")
157 | update := false
158 | a.Online = true
159 |
160 | remoteHash, remote, indexErr := sha512URL(a.baseURL.String() + "/" + language + ".json")
161 | if indexErr != nil {
162 | log.Printf("failed to connect to sample aggregator for %s samples, attempting to use local cache\n", language)
163 | a.Online = false
164 | }
165 | if FileExists(localPath) {
166 | localHash, err := localHash(localPath)
167 | if err != nil {
168 | return err
169 | }
170 | if !bytes.Equal(remoteHash, localHash) {
171 | update = true
172 |
173 | }
174 | } else {
175 | if !a.Online {
176 | log.Printf("operating offline and local cache for %s samples does not exist\n\t%s\n", language, indexErr)
177 | continue
178 | }
179 | update = true
180 | }
181 | if update && a.Online {
182 | err := ioutil.WriteFile(localPath, remote, 0644)
183 | if err != nil {
184 | return err
185 | }
186 |
187 | }
188 | //Ensure Directory for local path of language exists
189 | if err := os.MkdirAll(filepath.Join(a.localPath, language), 0750); err != nil {
190 | return err
191 | }
192 | workingLanguages = append(workingLanguages, language)
193 | }
194 |
195 | if len(workingLanguages) < 1 {
196 | return fmt.Errorf("no working sample languages configured %v", a.languages)
197 | }
198 | //Overwrite with known working languages.
199 | a.languages = workingLanguages
200 |
201 | return nil
202 | }
203 |
204 | //Update updates the local cache
205 | func (a *Aggregator) Update() error {
206 | err := a.syncLanguagesIndex()
207 | if err != nil {
208 | return err
209 | }
210 |
211 | var outerrors error
212 |
213 | var errWorker sync.WaitGroup
214 | if a.Bulk {
215 | //Setup threading pools for downloading all samples
216 | a.setupWorkers(5) //Start workerpool with 5
217 |
218 | //Start Error collection go routine. Will just return a generic error to
219 | //this function, will print real errors to fmt. for now
220 | errWorker.Add(1)
221 | go func() {
222 | for e := range a.results {
223 | if e != nil {
224 | fmt.Println(e)
225 | outerrors = fmt.Errorf("error occured on worker")
226 | }
227 | }
228 | if outerrors != nil {
229 | a.lock() //Poison the cache
230 | }
231 | errWorker.Done()
232 | }()
233 | }
234 |
235 | for _, language := range a.languages {
236 | localPath := filepath.Join(a.localPath, language+".json")
237 | if !FileExists(localPath) {
238 | return fmt.Errorf("unable to find configured language json (%s)", language)
239 | }
240 | languageIndex, err := ioutil.ReadFile(localPath)
241 | if err != nil {
242 | return err
243 | }
244 | var collected []Sample
245 | jsonErr := json.Unmarshal(languageIndex, &collected)
246 | if jsonErr != nil {
247 | return (jsonErr)
248 | }
249 |
250 | if !a.ignoreOS {
251 | collected = filterOnOS(collected)
252 | }
253 |
254 | if a.Bulk {
255 | for _, sample := range collected {
256 | a.sampleCount.Add(1)
257 | a.jobs <- sampleWorkItem{language, sample, defaultRetry}
258 | }
259 | }
260 | a.Samples[language] = collected
261 |
262 | }
263 |
264 | if a.Bulk {
265 | a.sampleCount.Wait()
266 | close(a.jobs)
267 | a.wg.Wait() //wait for job channel to be completed.
268 | close(a.results) //tell error channel workers are done.
269 | errWorker.Wait() //wait for the erros to be fully processed.
270 | }
271 |
272 | return outerrors
273 |
274 | }
275 |
276 | func filterOnOS(c []Sample) (filtered []Sample) {
277 | for _, s := range c {
278 | if len(s.Fields.OS) > 0 {
279 | keep := false
280 | for _, os := range s.Fields.OS {
281 | if strings.EqualFold(os, runtime.GOOS) {
282 | keep = true
283 | }
284 | }
285 | if keep {
286 | filtered = append(filtered, s)
287 | }
288 | } else {
289 | filtered = append(filtered, s)
290 | }
291 | }
292 | return filtered
293 | }
294 |
295 | //GetLocalPath returns the path local path
296 | func (a *Aggregator) GetLocalPath() string {
297 | return a.localPath
298 | }
299 |
300 | //GetURL gets the base URL used for fetching
301 | func (a *Aggregator) GetURL() string {
302 | return a.baseURL.String()
303 | }
304 |
305 | //GetLanguages gets the base URL used for fetching
306 | func (a *Aggregator) GetLanguages() []string {
307 | return a.languages
308 | }
309 |
310 | //GetTarBall Path of the tarball
311 | func GetTarBall(base string, baseURL string, language string, path string) (tar string, err error) {
312 | tarPath := filepath.Join(base, language, path, language+".tar.gz")
313 |
314 | if FileExists(tarPath) {
315 | return tarPath, nil
316 | }
317 | //Download tarball
318 |
319 | url := baseURL + "/" + path + "/" + language + ".tar.gz"
320 | if err := downloadFileDirect(tarPath, url); err != nil {
321 | return "", fmt.Errorf("failed to download sample '%s' - %v", path, err)
322 | }
323 |
324 | return tarPath, nil
325 | }
326 |
--------------------------------------------------------------------------------
/pkg/aggregator/aggregator_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Intel Corporation
2 | // SPDX-License-Identifier: BSD-3-Clause
3 | package aggregator
4 |
5 | import (
6 | "fmt"
7 | "io/ioutil"
8 | "net/http"
9 | "net/http/httptest"
10 | "os"
11 | "path/filepath"
12 | "reflect"
13 | "testing"
14 | )
15 |
16 | const testJSONdate = "[{\"path\":\"testrepo/simple-test-test\",\"sha\":\"2c755297a2073d7f317440e8429d274b284a9051\",\"example\":{\"name\":\"Simple Test Test\",\"category\":\"Unit Test\",\"categories\":[\"TestCat\"],\"description\":\"I am a simple test\",\"author\":\"Intel Corporation\",\"date\":\"1970-01-01\",\"tag\":\"test\",\"sample_readme_uri\":\"https://test.com\"}}]"
17 |
18 | type testNewAggregatorData struct {
19 | dir string
20 | ts *httptest.Server
21 | testLanguages []string
22 | }
23 |
24 | func setupAggregatorTest(t *testing.T) *testNewAggregatorData {
25 | t.Helper()
26 | var td testNewAggregatorData
27 |
28 | //Get Cache to use
29 | dir, err := ioutil.TempDir("", "example")
30 | if err != nil {
31 | t.Error(err)
32 | }
33 | td.dir = dir
34 |
35 | // Get a test http server
36 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
37 | fmt.Fprintln(w, testJSONdate)
38 | }))
39 | td.ts = ts
40 |
41 | td.testLanguages = []string{"cpp"}
42 |
43 | return &td
44 | }
45 |
46 | func (tdata *testNewAggregatorData) removeLock(t *testing.T) {
47 | t.Helper()
48 | os.Remove(filepath.Join(tdata.dir, AggregatorLocalAPILevel, cacheLockName))
49 | }
50 |
51 | func (tdata *testNewAggregatorData) cleanup() {
52 | os.RemoveAll(tdata.dir)
53 | tdata.ts.Close()
54 | }
55 |
56 | func TestNewAggregator(t *testing.T) {
57 | td := setupAggregatorTest(t)
58 | defer td.cleanup()
59 |
60 | _, err := NewAggregator("", "", []string{}, true, true)
61 | if err == nil {
62 | t.Errorf("this NewAggregator setup should have failed! With empty URL")
63 | }
64 | td.removeLock(t)
65 |
66 | _, err = NewAggregator("1asd://sd", "", []string{}, true, true)
67 | if err == nil {
68 | t.Errorf("this NewAggregator setup should have failed! With malformed URL ")
69 | }
70 | td.removeLock(t)
71 |
72 | _, err = NewAggregator("http://abcIShouldNotExist.intel.com/", "", []string{}, true, true)
73 | if err == nil {
74 | t.Errorf("this NewAggregator setup should have failed! With empty directory passed")
75 | }
76 | td.removeLock(t)
77 |
78 | // I wanted to test failing to create the local cache directory but I could think of a
79 | // way todo it: a) crossplatform b) running as admin could be valid usecase
80 |
81 | _, err = NewAggregator("http://abcIShouldNotExist.intel.com/", td.dir, []string{}, true, true)
82 | if err == nil {
83 | t.Errorf("lenth or nil lanauges array should have failed")
84 | }
85 | td.removeLock(t)
86 |
87 | _, err = NewAggregator("http://abcIShouldNotExist.intel.com/", td.dir, td.testLanguages, true, true)
88 | if err == nil {
89 | t.Errorf("should not be able to find cpp.json here, err should be network or http related")
90 | }
91 | td.removeLock(t)
92 |
93 | //404 Test (non 200)
94 | badTS := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
95 | w.WriteHeader(http.StatusNotFound)
96 | }))
97 |
98 | defer badTS.Close()
99 |
100 | _, err = NewAggregator(badTS.URL, td.dir, td.testLanguages, true, true)
101 | if err == nil {
102 | t.Errorf("should have failed with 404 HTTP code")
103 | }
104 | td.removeLock(t)
105 |
106 | //Server does not return JSON test
107 | badJSONTS := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
108 | fmt.Fprintln(w, "This is not JSON ¯\\_(ツ)_/¯ ")
109 | }))
110 | defer badJSONTS.Close()
111 |
112 | _, err = NewAggregator(badJSONTS.URL, td.dir, td.testLanguages, true, true)
113 | if err == nil {
114 | t.Errorf("should have failed garbage JSON")
115 | }
116 | td.removeLock(t)
117 |
118 | _, err = NewAggregator(td.ts.URL, td.dir, td.testLanguages, true, true)
119 | if err != nil {
120 | t.Error(err)
121 | }
122 | td.removeLock(t)
123 |
124 | }
125 |
126 | func TestGetLocalPath(t *testing.T) {
127 | td := setupAggregatorTest(t)
128 | defer td.cleanup()
129 |
130 | a, err := NewAggregator(td.ts.URL, td.dir, td.testLanguages, true, true)
131 | if err != nil {
132 | t.Error(err)
133 | }
134 |
135 | expected := filepath.Join(td.dir, AggregatorLocalAPILevel)
136 | returned := a.GetLocalPath()
137 |
138 | if returned != expected {
139 | t.Errorf("directory passed %s was not returned %s as returned by the aggregator", expected, returned)
140 | }
141 | }
142 |
143 | func TestGetURL(t *testing.T) {
144 | td := setupAggregatorTest(t)
145 | defer td.cleanup()
146 |
147 | a, err := NewAggregator(td.ts.URL, td.dir, td.testLanguages, true, true)
148 | if err != nil {
149 | t.Error(err)
150 | }
151 |
152 | returned := a.GetURL()
153 |
154 | if a.GetURL() != td.ts.URL {
155 | t.Errorf("URL passed %s was not returned %s as returned by the aggregator", td.ts.URL, returned)
156 | }
157 | }
158 |
159 | func TestGetLanguages(t *testing.T) {
160 | td := setupAggregatorTest(t)
161 | defer td.cleanup()
162 |
163 | a, err := NewAggregator(td.ts.URL, td.dir, td.testLanguages, true, true)
164 | if err != nil {
165 | t.Error(err)
166 | }
167 |
168 | returned := a.GetLanguages()
169 | if !reflect.DeepEqual(returned, td.testLanguages) { //Maybe should not test order of []string
170 | t.Errorf("URL passed %s was not returned %s as returned by the aggregator", td.testLanguages, returned)
171 | }
172 | }
173 |
174 | func TestBadTLS(t *testing.T) {
175 | td := setupAggregatorTest(t)
176 | defer td.cleanup()
177 | badTLS := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
178 | fmt.Fprintln(w, "I am a baaad guy")
179 | }))
180 | defer badTLS.Close()
181 |
182 | _, err := NewAggregator(badTLS.URL, td.dir, td.testLanguages, true, true)
183 | if err == nil {
184 | t.Errorf("should have failed due to invalid certificate")
185 | }
186 | }
187 |
188 | func TestOSFiltering(t *testing.T) {
189 | td := setupAggregatorTest(t)
190 | defer td.cleanup()
191 | td.ts.Close()
192 |
193 | //Make a more specifc test server
194 | const a = "[{\"path\":\"hpc-toolkit-samples-0af2a44aa341bf20ea53d5b908c93d467f65aacf/Nbody\",\"sha\":\"0af2a44aa341bf20ea53d5b908c93d467f65aacf\",\"example\":{\"name\":\"nbody\",\"categories\":[\"Intel\u00AE oneAPI HPC Toolkit/Segment Samples\"],\"description\":\"An N-body simulation is a simulation of a dynamical system of particles, usually under the influence of physical forces, such as gravity. This nbody sample code is implemented using C++ and SYCL language for CPU and GPU.\"}},{\"path\":\"hpc-toolkit-samples-0af2a44aa341bf20ea53d5b908c93d467f65aacf/Particle_Diffusion\",\"sha\":\"0af2a44aa341bf20ea53d5b908c93d467f65aacf\",\"example\":{\"name\":\"Particle-Diffusion\",\"categories\":[\"Intel\u00AE oneAPI HPC Toolkit/Segment Samples\"],\"description\":\"This code sample shows a simple (non-optimized) implementation of a Monte Carlo simulation of the diffusion of water molecules in tissue.\"}},{\"path\":\"hpc-toolkit-samples-0af2a44aa341bf20ea53d5b908c93d467f65aacf/iso3dfd_dpcpp\",\"sha\":\"0af2a44aa341bf20ea53d5b908c93d467f65aacf\",\"example\":{\"name\":\"ISO3DFD\",\"categories\":[\"Intel\u00AE oneAPI HPC Toolkit/Segment Samples\"],\"description\":\"A finite difference stencil kernel for solving 3D acoustic isotropic wave equation\",\"toolchain\":[\"dpcpp\"],\"os\":[\"noknownOS\"],\"sample_readme_uri\":\"https://software.intel.com/en-us/articles/code-samples-for-intel-oneapibeta-toolkits\"}},{\"path\":\"hpc-toolkit-samples-0af2a44aa341bf20ea53d5b908c93d467f65aacf/mandelbrot\",\"sha\":\"0af2a44aa341bf20ea53d5b908c93d467f65aacf\",\"example\":{\"name\":\"Mandelbrot\",\"categories\":[\"Intel\u00AE oneAPI HPC Toolkit/Segment Samples\"],\"description\":\"mandelbrot sample.\",\"os\":[\"noknownOS\"]}}]"
195 |
196 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
197 | fmt.Fprintln(w, a)
198 | }))
199 | td.ts = ts
200 | defer td.ts.Close()
201 |
202 | filtered, err := NewAggregator(td.ts.URL, td.dir, td.testLanguages, false, true)
203 | if err != nil {
204 | t.Errorf("failed to setup aggregator with good configs")
205 | }
206 | td.removeLock(t)
207 |
208 | if len(filtered.Samples[td.testLanguages[0]]) != 2 {
209 | t.Errorf("aggregator should have only seen two samples %v", len(filtered.Samples[td.testLanguages[0]]))
210 | }
211 |
212 | unFiltered, err := NewAggregator(td.ts.URL, td.dir, td.testLanguages, true, true)
213 | if err != nil {
214 | t.Errorf("failed to setup aggregator with good configs")
215 | }
216 | if len(unFiltered.Samples[td.testLanguages[0]]) != 4 {
217 | t.Errorf("aggregator should have only seen two samples")
218 | }
219 |
220 | }
221 |
--------------------------------------------------------------------------------
/pkg/aggregator/download.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Intel Corporation
2 | // SPDX-License-Identifier: BSD-3-Clause
3 |
4 | package aggregator
5 |
6 | import (
7 | "crypto/sha512"
8 | "fmt"
9 | "io"
10 | "io/ioutil"
11 | "net/http"
12 | "net/url"
13 | "os"
14 | "path/filepath"
15 | "time"
16 |
17 | "github.com/mattn/go-ieproxy"
18 | )
19 |
20 | func init() {
21 | http.DefaultTransport.(*http.Transport).Proxy = ieproxy.GetProxyFunc()
22 | }
23 |
24 | //downloadFileDirect Fetchs URL into local file
25 | func downloadFileDirect(path string, url string) error {
26 |
27 | // Get the data
28 | c := &http.Client{
29 | Timeout: HTTPTimeout * time.Second,
30 | }
31 | // Get the data
32 | resp, err := c.Get(url)
33 | if err != nil {
34 | return err
35 | }
36 | defer resp.Body.Close()
37 | if resp.StatusCode != http.StatusOK {
38 | return fmt.Errorf("HTTP-%v on %s", resp.StatusCode, url)
39 | }
40 |
41 | //Ensure Directory for local path of language exists
42 |
43 | if err := os.MkdirAll(filepath.Dir(path), 0750); err != nil {
44 | return err
45 | }
46 |
47 | // Create the file
48 | out, err := os.Create(path)
49 | if err != nil {
50 | return err
51 | }
52 | defer out.Close()
53 |
54 | // Write the body to file
55 | _, err = io.Copy(out, resp.Body)
56 | return err
57 | }
58 |
59 | func sha512URL(url string) ([]byte, []byte, error) {
60 |
61 | c := &http.Client{
62 | Timeout: HTTPTimeout * time.Second,
63 | }
64 | // Get the data
65 | resp, err := c.Get(url)
66 | if err != nil {
67 | return nil, nil, err
68 | }
69 | defer resp.Body.Close()
70 | if resp.StatusCode != http.StatusOK {
71 | return nil, nil, fmt.Errorf("HTTP-%v on %s", resp.StatusCode, url)
72 | }
73 | // Write the body to file
74 | hasher := sha512.New()
75 |
76 | body, err := ioutil.ReadAll(resp.Body)
77 | if err != nil {
78 | return nil, nil, err
79 | }
80 |
81 | _, err = hasher.Write(body)
82 | if err != nil {
83 | return nil, nil, nil
84 | }
85 |
86 | return hasher.Sum(nil), body, nil
87 | }
88 |
89 | func checkURL(URL string) (*url.URL, error) {
90 | return url.ParseRequestURI(URL)
91 | }
92 |
--------------------------------------------------------------------------------
/pkg/aggregator/file.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Intel Corporation
2 | // SPDX-License-Identifier: BSD-3-Clause
3 |
4 | package aggregator
5 |
6 | import (
7 | "crypto/sha512"
8 | "io/ioutil"
9 | "os"
10 | )
11 |
12 | //FileExists helper function for checking a file exists
13 | func FileExists(path string) bool {
14 | if _, err := os.Stat(path); os.IsNotExist(err) {
15 | return false
16 | }
17 | return true
18 | }
19 |
20 | //localhash returns hash, error
21 | func localHash(path string) ([]byte, error) {
22 | hasher := sha512.New()
23 | local, err := ioutil.ReadFile(path)
24 | if err != nil {
25 | return nil, err
26 | }
27 |
28 | _, err = hasher.Write(local)
29 | if err != nil {
30 | return nil, err
31 | }
32 | return hasher.Sum(nil), nil
33 | }
34 |
--------------------------------------------------------------------------------
/pkg/aggregator/types.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Intel Corporation
2 | // SPDX-License-Identifier: BSD-3-Clause
3 |
4 | package aggregator
5 |
6 | // Sample Type
7 | type Sample struct {
8 | Path string `json:"path"`
9 | SHA string `json:"sha"`
10 | Fields Fields `json:"example"`
11 | }
12 |
13 | // Fields type (nested struct in sample type)
14 | type Fields struct {
15 | Name string `json:"name"`
16 | Description string `json:"description"`
17 | Categories []string `json:"categories"`
18 | Author string `json:"author"`
19 | Date string `json:"date"`
20 | Tag string `json:"tag"`
21 | Dependencies []string `json:"dependencies"`
22 | OS []string `json:"os"`
23 | ReadmeURI string `json:"sample_readme_uri"`
24 | TargetDevice []string `json:"targetDevice"`
25 | Builder []string `json:"builder"`
26 | Toolchain []string `json:"toolchain"`
27 |
28 | //Not Parsing these out atm
29 | ProjectOptions []interface{} `json:"projectOptions"`
30 | MakeVariables map[string]interface{}
31 | IndexerVariables map[string]interface{}
32 | }
33 |
--------------------------------------------------------------------------------
/pkg/browser/browser_darwin.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Intel Corporation
2 | // SPDX-License-Identifier: BSD-3-Clause
3 |
4 | package browser
5 |
6 | import (
7 | exec "golang.org/x/sys/execabs"
8 | )
9 |
10 | //OpenBrowser opens the url passed
11 | func OpenBrowser(url string) error {
12 | cmd := exec.Command("open", url)
13 | return cmd.Run()
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/browser/browser_linux.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Intel Corporation
2 | // SPDX-License-Identifier: BSD-3-Clause
3 |
4 | package browser
5 |
6 | import (
7 | exec "golang.org/x/sys/execabs"
8 | )
9 |
10 | //OpenBrowser opens the url passed
11 | func OpenBrowser(url string) error {
12 | cmd := exec.Command("xdg-open", url)
13 | //Maybe we need to disown the process, but we should be better
14 | //than just calling run.
15 | return cmd.Start()
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/pkg/browser/browser_windows.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Intel Corporation
2 | // SPDX-License-Identifier: BSD-3-Clause
3 |
4 | package browser
5 |
6 | import (
7 | exec "golang.org/x/sys/execabs"
8 | "os"
9 | "path/filepath"
10 | "strings"
11 | "syscall"
12 | )
13 |
14 | //OpenBrowser opens the url passed
15 | func OpenBrowser(url string) error {
16 | r := strings.NewReplacer("&", "^&")
17 | rundll32 := filepath.Join(os.Getenv("SystemRoot"), "System32", "rundll32.exe")
18 |
19 | cmd := exec.Command(rundll32, "url.dll,FileProtocolHandler", r.Replace(url))
20 | cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
21 | return cmd.Run()
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/pkg/deps/checker.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Intel Corporation
2 | // SPDX-License-Identifier: BSD-3-Clause
3 | package deps
4 |
5 | import (
6 | "encoding/json"
7 | "fmt"
8 | "io/ioutil"
9 | "os"
10 | "os/exec"
11 | "path/filepath"
12 | "regexp"
13 | "runtime"
14 | "strings"
15 | )
16 |
17 | const (
18 | rootEnvKey = "ONEAPI_ROOT"
19 |
20 | baseURL = "https://www.intel.com/content/www/us/en/developer/tools/oneapi/overview"
21 |
22 | formatStr = `The following tools are needed to build this sample but are not locally installed: (%s)
23 | You may continue and view the sample without the prerequisites. To install the missing prerequisites, visit:
24 | %s%s
25 | `
26 |
27 | pkgReg = "pkg\\|([^|]*)\\|?(.*)"
28 | compilerReg = "compiler\\|(.*)"
29 | )
30 |
31 | // CheckDeps as
32 | func CheckDeps(dependencies []string, root string) (msg string, errCode int) {
33 | //dependencies are both "normal" component dependencies ( ["mkl", "vtune"])
34 | //and "special" dependencies ( ["pkg|mraa", "compiler|icc"])
35 | componentDeps, specialDeps := separatethSheepsGoats(dependencies)
36 |
37 | componentMsg, componentErrCode := checkComponentDeps(componentDeps, root)
38 | specialMsg, specialErrCode := checkSpecialDeps(specialDeps, root)
39 |
40 | //now gather results and return
41 | msg, errCode = simplifyMsgErrCode(specialMsg, specialErrCode, componentMsg, componentErrCode)
42 | return msg, errCode
43 | }
44 | func simplifyMsgErrCode(msg1 string, errCode1 int, msg2 string, errCode2 int) (msg string, errCode int) {
45 | //takes a two pairs of messages and error codes and returns their concatenation (or whatever is appropriate)
46 | msg = ""
47 | errCode = 0
48 | divider := ""
49 |
50 | if errCode1 != 0 {
51 | msg = msg1
52 | errCode = errCode1
53 | divider = "\n"
54 | }
55 | if errCode2 != 0 {
56 | msg = msg + divider + msg2
57 | errCode = errCode2
58 | }
59 | return msg, errCode
60 | }
61 |
62 | func checkComponentDeps(dependencies []string, root string) (msg string, errCode int) {
63 |
64 | var missing []string
65 | for _, k := range dependencies {
66 | if _, err := os.Stat(filepath.Join(root, k)); os.IsNotExist(err) {
67 | //does NOT EXIST
68 | missing = append(missing, k)
69 | }
70 | }
71 | // Something was missing, get a message
72 | if len(missing) > 0 {
73 | msg := GenerateMessage(missing)
74 | return msg, -1
75 | }
76 | return "", 0
77 | }
78 |
79 | func checkPackageDeps(packageDeps []string, root string) (msg string, errCode int) {
80 | // deps = pckg||url
81 | // 1. check for pkg-config
82 | // 1.F if not: message returned says "this sample requires which we unable to verify. Be sure it is installed. ."
83 | // 1.T if so: call `pkg-config --exists `
84 | // 1.T.T if exists - OK, no message
85 | // 1.T.F if not: "this sample requires which is not installed. You can obtain it here: "
86 |
87 | //errCode -1 no package , -2 no pkg-config
88 |
89 | msg = ""
90 | errCode = 0
91 | divider := ""
92 |
93 | //0. setup regex that will parse dependency
94 | regEx := regexp.MustCompile(pkgReg)
95 |
96 | //1.
97 | _, pkgErr := exec.LookPath("pkg-config")
98 |
99 | for _, dep := range packageDeps {
100 | pkg, url := parseDep(regEx, dep)
101 | err := pkgErr
102 | if err == nil {
103 | cmd := exec.Command("pkg-config", "--exists", pkg)
104 | err = cmd.Run()
105 | if err == nil {
106 | //we are good to go.
107 | } else {
108 | msg = msg + divider + fmt.Sprintf("this sample requires %s which is not installed. To obtain: %s", pkg, url)
109 | divider = "\n"
110 | errCode = -1
111 | }
112 | } else {
113 | msg = msg + divider + fmt.Sprintf("this sample requires %s which we are unable to verify. Please make sure it is installed. %s", pkg, url)
114 | divider = "\n"
115 | errCode = -2
116 | }
117 | }
118 |
119 | return msg, errCode
120 | }
121 |
122 | func parseDep(re *regexp.Regexp, dep string) (pkg string, url string) {
123 | //given a regex and a string, parses it out to package and url.
124 | // this parser can be used for both pkg| and compiler| , use constants pkgReg or compilerReg as first arg
125 | // example: parseDep(pgkReg, "pkg|mraa|www.intel.com") => "mraa", "www.intel.com"
126 | // parseDep(compilerReg, "compiler|icc") => "icc", ""
127 | match := re.FindStringSubmatch(dep)
128 | pkg = ""
129 |
130 | if len(match) > 1 {
131 | pkg = match[1]
132 | url = fmt.Sprintf("Search for 'install %s' for help.", pkg)
133 | }
134 | if len(match) > 2 {
135 | url = match[2]
136 | }
137 | return pkg, url
138 | }
139 |
140 | func checkCompilerDeps(compilerDeps []string, root string) (msg string, errCode int) {
141 |
142 | msg = ""
143 | errCode = 0
144 | var missing []string
145 |
146 | winCompilers := map[string]string{
147 | "icc": "bin/intel64/icl.exe",
148 | "fortran": "bin/intel64/ifort.exe",
149 | "dpcpp": "bin/dpcpp.exe",
150 | "icpc": "bin/intel64/icpc.exe",
151 | "icx": "bin/icx.exe",
152 | "icpcx": "bin/icpcx.exe",
153 | "ifx": "bin/ifx.exe",
154 | }
155 | linCompilers := map[string]string{
156 | "icc": "bin/intel64/icc",
157 | "fortran": "bin/intel64/ifort",
158 | "dpcpp": "bin/dpcpp",
159 | "icpc": "bin/intel64/icpc",
160 | "icx": "bin/icx",
161 | "icpcx": "bin/icpcx",
162 | "ifx": "bin/ifx",
163 | }
164 | macCompilers := map[string]string{
165 | "icpc": "bin/intel64/icpc",
166 | "icc": "bin/intel64/icc",
167 | "ifort": "bin/intel64/ifort",
168 | "icx": "bin/icx",
169 | "icpcx": "bin/icpcx",
170 | "ifx": "bin/ifx",
171 | }
172 |
173 | compilerRoot := GetCompilerRoot(root)
174 |
175 | //0. setup regex that will parse dependency
176 | regEx := regexp.MustCompile(compilerReg)
177 |
178 | for _, dep := range compilerDeps {
179 | compiler, _ := parseDep(regEx, dep)
180 | var pathTail string
181 |
182 | switch runtime.GOOS {
183 | case "linux":
184 | pathTail = linCompilers[compiler]
185 | case "windows":
186 | pathTail = winCompilers[compiler]
187 | case "darwin":
188 | pathTail = macCompilers[compiler]
189 | default:
190 | msg = "Cannot check Compiler, unsupported OS"
191 | errCode = 01
192 | return msg, errCode
193 | }
194 |
195 | fullPath := filepath.Join(compilerRoot, pathTail)
196 | if pathTail == "" || !fileExists(fullPath) {
197 | missing = append(missing, compiler)
198 | }
199 | }
200 | if len(missing) > 0 {
201 | msg = GenerateMessage(missing)
202 | errCode = 01
203 | }
204 | return msg, errCode
205 | }
206 |
207 | func fileExists(path string) bool {
208 | _, err := os.Stat(path)
209 | return !os.IsNotExist(err)
210 | }
211 |
212 | func checkSpecialDeps(specialDependencies []string, root string) (msg string, errCode int) {
213 | packageDeps, remainingDeps := separatethSheepsGoatsRhematosC(specialDependencies, func(dep string) bool { return strings.HasPrefix(dep, "pkg|") })
214 | compilerDeps, remainingDeps := separatethSheepsGoatsRhematosC(remainingDeps, func(dep string) bool { return strings.HasPrefix(dep, "compiler|") })
215 |
216 | packageMsg, packageErrCode := checkPackageDeps(packageDeps, root)
217 | compilerMsg, compilerErrCode := checkCompilerDeps(compilerDeps, root)
218 | msg, errCode = simplifyMsgErrCode(packageMsg, packageErrCode, compilerMsg, compilerErrCode)
219 | return msg, errCode
220 | }
221 |
222 | // GetOneAPIRoot gets the root the OneAPI installation
223 | // based on the ONEAPI_ROOT
224 | func GetOneAPIRoot() (path string, err error) {
225 | root, ok := os.LookupEnv(rootEnvKey)
226 |
227 | if !ok {
228 | return "", fmt.Errorf("%s not defined. Be sure to run oneapi environment script ( source setvars.sh )", rootEnvKey)
229 | }
230 |
231 | tmp := filepath.Dir(root)
232 | tmp = strings.ToLower(filepath.Base(tmp))
233 | if tmp == "oneapi" {
234 | return filepath.Dir(root), nil
235 | }
236 |
237 | return root, nil
238 | }
239 |
240 | // CMPLR_ROOT was, for awhile, always defined in the environment. But no longer.
241 | func GetCompilerRoot(root string) (compilerRoot string) {
242 | compilerRoot = filepath.Join(root, "compiler", "latest")
243 | return compilerRoot
244 | }
245 |
246 | type suiteComponent struct {
247 | SuiteId string `json:"suiteId"`
248 | ComponentId string `json:"componentId"`
249 | Primary bool `json:"primary"`
250 | }
251 |
252 | type compDir struct {
253 | Dir string `json:"dir"`
254 | ComponentId string `json:"componentId"`
255 | }
256 |
257 | type suite struct {
258 | SuiteId string `json:"id"`
259 | Label string `json:"label"`
260 | UrlSlug string `json:"urlSlug"`
261 | BaseToolkit string `json:"baseToolkit"`
262 | }
263 |
264 | func GenerateMessage(missing []string) string {
265 |
266 | //1.0 read in compmapping.json
267 | //1.1 - translate missing to id list
268 | //2.0 read in suite-components.json
269 | //2.1 expand id-list to toolkits
270 | //3.0 count/find most frequent toolkit (if any)
271 | //4.0 read suites.json
272 | //4.1 get slug from toolkit
273 |
274 | //5. return message with url.
275 |
276 | //baseURL := "https://www.intel.com/content/www/us/en/developer/tools/oneapi/overview" // this is captured in format String
277 | var slug string
278 | fallbackMsg := fmt.Sprintf(formatStr, strings.Join(missing, " "), baseURL, slug)
279 |
280 | //1 read in compmapping.json
281 | var mapping []compDir
282 | //err := readSomeJSON(filepath.Join("json", "compmapping.json"), &mapping)
283 | err := parseSomeJSON(compmappingJSON, &mapping)
284 | if err != nil {
285 | return fallbackMsg
286 | }
287 | //1.1 translate missing to id list
288 | idList := mapStringArr(missing, func(miss string) string {
289 | for _, v := range mapping {
290 | if v.Dir == miss {
291 | return v.ComponentId
292 | }
293 | }
294 | return ""
295 | })
296 |
297 | //2.0 read in suite-components.json
298 | var sweetComps []suiteComponent
299 | //err = readSomeJSON(filepath.Join("json", "suite-components.json"), &sweetComps)
300 | err = parseSomeJSON(sweetComponentsJSON, &sweetComps)
301 | if err != nil {
302 | return fallbackMsg
303 | }
304 | //2.1 expand id-list to toolkits
305 |
306 | var matchedSuite []string
307 |
308 | for _, suiteComp := range sweetComps {
309 | if contains(idList, suiteComp.ComponentId) {
310 | matchedSuite = append(matchedSuite, suiteComp.SuiteId)
311 | }
312 | }
313 |
314 | if len(matchedSuite) > 1 {
315 | //4.0 read suites.json
316 | var suites []suite
317 | //err = readSomeJSON(filepath.Join("json", "suites.json"), &suites)
318 | err = parseSomeJSON(suitesJSON, &suites)
319 | if err != nil {
320 | return fallbackMsg
321 | }
322 |
323 | var composed string
324 |
325 | for _, suite := range matchedSuite {
326 | slug := findSlug(suites, suite)
327 |
328 | if len(composed) == 0 {
329 | composed = fmt.Sprintf(formatStr, strings.Join(missing, " "), baseURL, slug)
330 | } else {
331 | composed = fmt.Sprintf("%sor %s%s", composed, baseURL, slug)
332 | }
333 | }
334 | return composed
335 | }
336 |
337 | return fallbackMsg
338 | }
339 |
340 | func readSomeJSON(path string, something interface{}) error {
341 | jsonFile, err := os.Open(path)
342 | if err != nil {
343 | return err
344 | }
345 | byteValue, err := ioutil.ReadAll(jsonFile)
346 | if err != nil {
347 | return err
348 | }
349 | err = json.Unmarshal(byteValue, something)
350 | if err != nil {
351 | return err
352 | }
353 | return nil
354 | }
355 |
356 | // keeping the same interface as the file reading function for now.
357 | func parseSomeJSON(str string, something interface{}) error {
358 | err := json.Unmarshal([]byte(str), something)
359 | if err != nil {
360 | return err
361 | }
362 | return nil
363 | }
364 |
365 | func mapStringArr(arr []string, f func(a string) string) []string {
366 | var result []string
367 | for _, v := range arr {
368 | result = append(result, f(v))
369 | }
370 | return result
371 | }
372 |
373 | func contains(arr []string, needle string) bool {
374 | for _, v := range arr {
375 | if needle == v {
376 | return true
377 | }
378 | }
379 | return false
380 | }
381 |
382 | func findSlug(suites []suite, maxSuite string) string {
383 | for _, aSuite := range suites {
384 | if aSuite.SuiteId == maxSuite {
385 | return aSuite.UrlSlug
386 | }
387 | }
388 | //not worth dealing with the error
389 | return ""
390 | }
391 |
392 | func separatethSheepsGoats(dependencies []string) ([]string, []string) {
393 | // the dependencies consist of "normal" component dependencies ( ["mkl", "vtune"]) i.e. "sheep"
394 | // and "special" dependencies ( ["pkg|mraa", "compiler|icc"]) i.e. "goats"
395 | // as foretold, they are separateth into two groups.
396 | return separatethSheepsGoatsRhematosC(dependencies, func(dep string) bool { return !strings.Contains(dep, "|") })
397 | }
398 |
399 | func separatethSheepsGoatsRhematosC(dependencies []string, predicate func(a string) bool) ([]string, []string) {
400 | // And before him shall be gathered all nations, and he shall separate them one from another as a shepherd separateth the sheep from the goats.
401 | // this function takes a divine predicate and uses that to split a string array in twain
402 | var sheep []string
403 | var goats []string
404 | for _, dep := range dependencies {
405 | if predicate(dep) {
406 | sheep = append(sheep, dep)
407 | } else {
408 | goats = append(goats, dep)
409 | }
410 | }
411 | return sheep, goats
412 | }
413 |
--------------------------------------------------------------------------------
/pkg/deps/checker_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Intel Corporation
2 | // SPDX-License-Identifier: BSD-3-Clause
3 | package deps
4 |
5 | import (
6 | "fmt"
7 | "io/ioutil"
8 | "os"
9 | "path/filepath"
10 | "reflect"
11 | "regexp"
12 | "strings"
13 | "testing"
14 | )
15 |
16 | func TestGetOneAPIRoot(t *testing.T) {
17 |
18 | os.Unsetenv(rootEnvKey) //Clear Env, just incase
19 | _, err := GetOneAPIRoot()
20 | if err == nil {
21 | t.Errorf("should have failed to find the env key, we unset it for test")
22 | }
23 |
24 | const testValue = "/opt/inteltestval/"
25 | os.Setenv(rootEnvKey, testValue)
26 | val, err := GetOneAPIRoot()
27 | if err != nil {
28 | t.Error(err)
29 | }
30 | if val != testValue {
31 | t.Errorf("unexpected env value receieved")
32 | }
33 | os.Unsetenv(rootEnvKey) //Clear Env, just incase
34 | }
35 |
36 | func setupTestRoot(t *testing.T, testDeps []string) (root string) {
37 | t.Helper()
38 | root, err := ioutil.TempDir("", "depscheck")
39 | if err != nil {
40 | t.Error(err)
41 |
42 | }
43 | for _, k := range testDeps {
44 | err := os.MkdirAll(filepath.Join(root, k), os.ModePerm)
45 | if err != nil {
46 | t.Error(err)
47 | }
48 | }
49 | return root
50 | }
51 |
52 | func TestCheckDeps(t *testing.T) {
53 |
54 | testingGold := []string{"cheese", "milk"}
55 |
56 | root := setupTestRoot(t, testingGold)
57 |
58 | msg, errCode := CheckDeps(testingGold, root)
59 | if errCode > 0 {
60 | t.Errorf("Golden test failed found missing %s", msg)
61 | }
62 |
63 | }
64 |
65 | func TestGenerateMessage(t *testing.T) {
66 | missing := []string{"foo"}
67 | msg := GenerateMessage(missing)
68 | fmt.Println(msg)
69 |
70 | if !strings.Contains(msg, "(foo)") {
71 | t.Errorf("foo wasn't reported")
72 | }
73 | }
74 |
75 | func TestCheckCompilerDeps(t *testing.T) {
76 | testingGold := []string{"cheese", "milk"}
77 |
78 | root := setupTestRoot(t, testingGold)
79 | deps := []string{"compiler|gomer"}
80 | _, errCode := checkCompilerDeps(deps, root)
81 | if errCode == 0 {
82 | t.Errorf("gomer compiler should never have been found")
83 | }
84 | }
85 |
86 | /*
87 | func TestReadSomeJSONAndTheJSON(t *testing.T) {
88 |
89 | var mapping []compDir
90 | err := readSomeJSON(filepath.Join("..", "..", "json", "compmapping.json"), &mapping)
91 | if err != nil {
92 | t.Errorf("failure loading compmapping.json - %s", err)
93 | }
94 |
95 | var sweetComps []suiteComponent
96 | err = readSomeJSON(filepath.Join("..", "..", "json", "suite-components.json"), &sweetComps)
97 | if err != nil {
98 | t.Errorf("failure loading suite-components.json - %s", err)
99 | }
100 |
101 | var suites []suite
102 | err = readSomeJSON(filepath.Join("..", "..", "json", "suites.json"), &suites)
103 | if err != nil {
104 | t.Errorf("failure loading suites.json - %s", err)
105 | }
106 |
107 | //not worrying about invalid-components-hosts or components.json right now.
108 | }
109 | */
110 |
111 | func TestParseSomeJSONAndTheJSON(t *testing.T) {
112 | var mapping []compDir
113 | err := parseSomeJSON(compmappingJSON, &mapping)
114 | if err != nil {
115 | t.Errorf("failure parsing Compmappingjson - %s", err)
116 | }
117 | if len(mapping) == 0 {
118 | t.Errorf("compmappingJSON empty after parsing")
119 | }
120 |
121 | var sweetComps []suiteComponent
122 | err = parseSomeJSON(sweetComponentsJSON, &sweetComps)
123 | if err != nil {
124 | t.Errorf("failure parsing sweetComponentsJSON - %s", err)
125 | }
126 | if len(sweetComps) == 0 {
127 | t.Errorf("sweetComponentsJSON empty after parsing")
128 | }
129 |
130 | var suites []suite
131 | err = parseSomeJSON(suitesJSON, &suites)
132 | if err != nil {
133 | t.Errorf("failure parsing suitesJSON - %s", err)
134 | }
135 | if len(suites) == 0 {
136 | t.Errorf("suitesJSON empty after parsing")
137 | }
138 | }
139 |
140 | func TestContains(t *testing.T) {
141 | haystack := []string{"marvel", "crunch", "america", "picard"}
142 | needle := "picard"
143 | youshouldlivesolong := "ryker"
144 | wat := ""
145 |
146 | if !contains(haystack, needle) {
147 | t.Errorf("%s not found", needle)
148 | }
149 |
150 | if contains(haystack, youshouldlivesolong) {
151 | t.Errorf("captain %s?? Never", youshouldlivesolong)
152 | }
153 |
154 | if contains(haystack, wat) {
155 | t.Errorf("Alfred North Whitehead hates you")
156 | }
157 | }
158 |
159 | func TestMapStringArr(t *testing.T) {
160 | haystack := []string{"marvel", "crunch", "america", "picard"}
161 | fuzz := mapStringArr(haystack, func(a string) string {
162 | return a[0:1]
163 | })
164 | expectation := []string{"m", "c", "a", "p"}
165 | if !reflect.DeepEqual(fuzz, expectation) {
166 | t.Errorf("string mapping not that hard, maybe consider career change?")
167 | }
168 |
169 | emptyArr := []string{}
170 | ress := mapStringArr(emptyArr, func(a string) string {
171 | return a
172 | })
173 | if len(ress) > 0 {
174 | t.Errorf("string mapping not that hard, maybe consider career change?")
175 | }
176 | //amazing enuogh, DeepEqual returns the wrong value. These are both empty, it says not equal.
177 | // if !reflect.DeepEqual(emptyArr, ress) {
178 | // t.Errorf("string mapping not that hard, maybe consider career change? %s %s", emptyArr, ress)
179 | // }
180 |
181 | }
182 |
183 | func findDirectory(mapping []compDir, componentId string) string {
184 | for _, compDir := range mapping {
185 | if compDir.ComponentId == componentId {
186 | return compDir.Dir
187 | }
188 | }
189 | return ""
190 | }
191 |
192 | func TestFindDir(t *testing.T) {
193 | var mapping []compDir
194 | compMapErr := parseSomeJSON(compmappingJSON, &mapping)
195 |
196 | dir := findDirectory(mapping, "intel_advisor")
197 | if dir == "" {
198 | t.Errorf("findDirectory failed, unable to locate advisor in compmappingJSON")
199 | }
200 |
201 | dir = findDirectory(mapping, "xanadu")
202 | if dir != "" {
203 | t.Errorf("In Xanadu did Kubla Khan a stately pleasure dome decree")
204 | }
205 |
206 | if compMapErr != nil {
207 | t.Errorf("failure parsing the JSON constants compmappingJSON: %s", compMapErr)
208 | }
209 | }
210 |
211 | func TestIntegrityOfJSONConstants(t *testing.T) {
212 | // we have a test above that makes sure the constants (suitesJSON, sweetComponentJSON, etc)
213 | // are valid JSON strings.
214 | // this test makes sure that every entry in sweetComponentsJSON has a matching component id in componentsMapping
215 | // extra entries in componentMapping is ok, there may be vestigial components there. Doesn't matter.
216 | // and that every entry in sweetComponentJSON has a suite in suitesJSON, and vice versa.
217 |
218 | var mapping []compDir
219 | compMapErr := parseSomeJSON(compmappingJSON, &mapping)
220 |
221 | var sweetComps []suiteComponent
222 | sweetCompErr := parseSomeJSON(sweetComponentsJSON, &sweetComps)
223 |
224 | var suites []suite
225 | suitesErr := parseSomeJSON(suitesJSON, &suites)
226 |
227 | if compMapErr != nil || sweetCompErr != nil || suitesErr != nil {
228 | t.Errorf("failure parsing the JSON constants compmappingJSON: %s - sweetComponentsJSON: %s - suitesJSON: %s", compMapErr, sweetCompErr, suitesErr)
229 | }
230 |
231 | //run through sweetComps looking for id that is not in compDir map
232 | var dir string
233 | for _, suiteComp := range sweetComps {
234 | dir = findDirectory(mapping, suiteComp.ComponentId)
235 | if dir == "" {
236 | t.Errorf("unable to locate directory in compmapping JSON for component declared in sweetComponentsJSON: %s", suiteComp.ComponentId)
237 | }
238 | }
239 |
240 | //run through sweetComps looking for suite that is not in suitesJSON
241 | var slug string
242 | for _, sweetComp := range sweetComps {
243 | slug = findSlug(suites, sweetComp.SuiteId)
244 | if slug == "" {
245 | t.Errorf("unable to locate suite in suitesJSON for entry declared in sweetComponentsJSON: %s", sweetComp.SuiteId)
246 | }
247 | }
248 |
249 | //run through suites looking for suite that is not in sweetComponentsJSON
250 | for _, suiteEntry := range suites {
251 | match := false
252 | for _, sweetComp := range sweetComps {
253 | if sweetComp.SuiteId == suiteEntry.SuiteId {
254 | match = true
255 | break
256 | }
257 | }
258 | if !match {
259 | t.Errorf("unable to locate suite in sweetComponentsJSON for entry declared in sutiesJSON: %s", suiteEntry.SuiteId)
260 | }
261 | }
262 |
263 | }
264 |
265 | func TestSeparatethSheepsGoats(t *testing.T) {
266 | //violates Deuteronomy 6:16 . Invoke at your peril.
267 | sheep := []string{"Dolly", "Montauciel", "Methuselina", "Lance-Corporal-Derby-XXX"}
268 | goats := []string{"Goat|Billy", "Goat|Pan", "Goat|Nanny", "Goat|Rudy-Giuliani"}
269 |
270 | components, _ := separatethSheepsGoats(sheep)
271 | if !reflect.DeepEqual(components, sheep) {
272 | t.Errorf("repent!")
273 | }
274 |
275 | _, special := separatethSheepsGoats(goats)
276 | if !reflect.DeepEqual(special, goats) {
277 | t.Errorf("repent!")
278 | }
279 |
280 | herd := append(sheep, goats...)
281 |
282 | components, special = separatethSheepsGoats(herd)
283 | if !reflect.DeepEqual(components, sheep) {
284 | t.Errorf("repent!")
285 | }
286 | if !reflect.DeepEqual(special, goats) {
287 | t.Errorf("repent!")
288 | }
289 | }
290 |
291 | func TestSimplifyMsgErrCode(t *testing.T) {
292 | msg, errCode := simplifyMsgErrCode("yams", 0, "hams", 0)
293 | if errCode != 0 {
294 | t.Errorf("errCode should be 0")
295 | }
296 |
297 | msg, errCode = simplifyMsgErrCode("yams", 1, "hams", 0)
298 | if msg != "yams" {
299 | t.Errorf("should have yams")
300 | }
301 |
302 | msg, errCode = simplifyMsgErrCode("yams", 0, "hams", 1)
303 | if msg != "hams" {
304 | t.Errorf("should have hams")
305 | }
306 |
307 | msg, errCode = simplifyMsgErrCode("yams", 1, "hams", 1)
308 | if msg != "yams\nhams" {
309 | t.Errorf("should have yams and hams")
310 | }
311 | }
312 |
313 | func TestParseDep(t *testing.T) {
314 | regEx := regexp.MustCompile(pkgReg)
315 | pkg, url := parseDep(regEx, "pkg|mraa|http://www.intel.com")
316 | if pkg != "mraa" {
317 | t.Errorf("pkg should have parsed to mraa, %s", pkg)
318 | }
319 | if url != "http://www.intel.com" {
320 | t.Errorf("url should have parsed to http://www.intel.com, %s", url)
321 | }
322 |
323 | //url optional
324 | pkg, url = parseDep(regEx, "pkg|npm")
325 | if pkg != "npm" {
326 | t.Errorf("pkg should have parsed to npm, %s", pkg)
327 | }
328 |
329 | regEx = regexp.MustCompile(compilerReg)
330 | compiler, _ := parseDep(regEx, "compiler|icc")
331 | if compiler != "icc" {
332 | t.Errorf("compiler should have parsed to icc, %s", compiler)
333 | }
334 | }
335 |
336 | func TestFileExists(t *testing.T) {
337 | no := fileExists("")
338 | if no {
339 | t.Errorf("why does '' exist?")
340 | }
341 | yes := fileExists(".")
342 | if !yes {
343 | t.Errorf("why doesn't '.' exist?")
344 | }
345 |
346 | }
347 |
--------------------------------------------------------------------------------
/pkg/deps/constants.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Intel Corporation
2 | // SPDX-License-Identifier: BSD-3-Clause
3 | package deps
4 |
5 | const compmappingJSON = `
6 | [
7 | {"componentId":"intel_advisor", "dir":"advisor"},
8 | {"componentId":"collective_communications_library", "dir":"ccl"},
9 | {"componentId":"intel_cluster_checker", "dir":"clck"},
10 | {"componentId":"intel_data_analytics_library", "dir":"daal"},
11 | {"componentId":"1a_debugger", "dir":"debugger"},
12 | {"componentId":"dev-utilities", "dir":"dev-utilities"},
13 | {"componentId":"deep_neural_network", "dir":"oneDNN"},
14 | {"componentId":"dpcpp_compatibility_tool", "dir":"dpcpp-ct"},
15 | {"componentId":"eclipse-iot-plugins", "dir":"eclipse-iot-plugins"},
16 | {"componentId":"intel_embree", "dir":"embree"},
17 | {"componentId":"gstreamer_plugins", "dir":"gvaplugins"},
18 | {"componentId":"intel_inspector", "dir":"inspector"},
19 | {"componentId":"eclipse_based_ide", "dir":"intel-eclipse-ide"},
20 | {"componentId":"python", "dir":"intelpython"},
21 | {"componentId":"intel_integrated_performance_primitives", "dir":"ipp"},
22 | {"componentId":"intel_trace_analyzer_and_collector", "dir":"itac"},
23 | {"componentId":"intel_math_kernel_library", "dir":"mkl"},
24 | {"componentId":"intel_mpi_library", "dir":"mpi"},
25 | {"componentId":"open_image_denoise", "dir":"oidn"},
26 | {"componentId":"openvino", "dir":"openvino"},
27 | {"componentId":"open_volume_kernel_library", "dir":"openvkl"},
28 | {"componentId":"intel_ospray", "dir":"ospray"},
29 | {"componentId":"pstl", "dir":"pstl"},
30 | {"componentId":"intel_pytorch", "dir":"pytorch"},
31 | {"componentId":"intel_socwatch", "dir":"socwatch"},
32 | {"componentId":"intel_system_debugger", "dir":"system_debugger"},
33 | {"componentId":"intel_threading_building_blocks", "dir":"tbb"},
34 | {"componentId":"intel_tensorflow", "dir":"tensorflow"},
35 | {"componentId":"vpl", "dir":"vpl"},
36 | {"componentId":"intel_vtune_amplifier", "dir":"vtune"},
37 |
38 |
39 | {"componentId":"intel_cpp_compiler", "dir":"icc"},
40 | {"componentId":"openmp_fortran", "dir":"fortran"},
41 | {"componentId":"dppcpp_compiler", "dir":"dpcpp"},
42 | {"componentId":"dpcpp_library", "dir":"?"},
43 |
44 |
45 | {"componentId":"intel_iot_connect_upm_mraa_cloud_connectors", "dir":"?"},
46 | {"componentId":"linux_kernel_build_tools", "dir":"?"},
47 |
48 |
49 | {"componentId":"gnu_project_debugger_gdb", "dir":"?"},
50 |
51 | {"componentId":"linux_iot_application_development_using_containerized_toolchains", "dir":"?"}
52 |
53 |
54 | ]
55 | `
56 |
57 | //if copy/pasting JSON from the WebConfigurator team, I recommend linting it first.
58 | // https://jsonlint.com/
59 | // they have lots of small problems (missing quotes, stray commas, missing commas) that
60 | // Node.js forgives.
61 | const sweetComponentsJSON = `
62 | [
63 |
64 | { "suiteId": "HPCKit", "componentId": "intel_cpp_compiler", "primary": true },
65 | { "suiteId": "HPCKit", "componentId": "openmp_fortran", "primary": true },
66 | { "suiteId": "HPCKit", "componentId": "intel_mpi_library", "primary": true },
67 | { "suiteId": "HPCKit", "componentId": "intel_inspector", "primary": true },
68 | { "suiteId": "HPCKit", "componentId": "intel_trace_analyzer_and_collector", "primary": true },
69 | { "suiteId": "HPCKit", "componentId": "intel_cluster_checker", "primary": true },
70 |
71 | { "suiteId": "IOTKit", "componentId": "intel_cpp_compiler", "primary": true },
72 | { "suiteId": "IOTKit", "componentId": "intel_inspector", "primary": true },
73 | {
74 | "suiteId": "IOTKit",
75 | "componentId": "linux_iot_application_development_using_containerized_toolchains",
76 | "primary": true
77 | },
78 | { "suiteId": "IOTKit", "componentId": "eclipse_based_ide", "primary": true },
79 | { "suiteId": "IOTKit", "componentId": "linux_kernel_build_tools", "primary": true },
80 | { "suiteId": "IOTKit", "componentId": "intel_system_debugger", "primary": true },
81 |
82 | { "suiteId": "BringupKit", "componentId": "intel_socwatch", "primary": true },
83 | { "suiteId": "BringupKit", "componentId": "intel_system_debugger", "primary": true },
84 |
85 |
86 | { "suiteId": "DLDevKit", "componentId": "collective_communications_library", "primary": true },
87 | { "suiteId": "DLDevKit", "componentId": "deep_neural_network", "primary": true },
88 |
89 | { "suiteId": "AIKit", "componentId": "intel_tensorflow", "primary": true },
90 | { "suiteId": "AIKit", "componentId": "intel_pytorch", "primary": true },
91 | { "suiteId": "AIKit", "componentId": "python", "primary": true },
92 |
93 | { "suiteId": "oneAPIKit", "componentId": "dppcpp_compiler", "primary": true },
94 | { "suiteId": "oneAPIKit", "componentId": "dpcpp_compatibility_tool", "primary": true },
95 | { "suiteId": "oneAPIKit", "componentId": "dpcpp_library", "primary": true },
96 | { "suiteId": "oneAPIKit", "componentId": "1a_debugger", "primary": true },
97 | { "suiteId": "oneAPIKit", "componentId": "intel_math_kernel_library", "primary": true },
98 | { "suiteId": "oneAPIKit", "componentId": "intel_threading_building_blocks", "primary": true },
99 | { "suiteId": "oneAPIKit", "componentId": "intel_integrated_performance_primitives", "primary": true },
100 | { "suiteId": "oneAPIKit", "componentId": "intel_data_analytics_library", "primary": true },
101 | { "suiteId": "oneAPIKit", "componentId": "python", "primary": true },
102 | { "suiteId": "oneAPIKit", "componentId": "intel_advisor", "primary": true },
103 | { "suiteId": "oneAPIKit", "componentId": "deep_neural_network", "primary": true },
104 | { "suiteId": "oneAPIKit", "componentId": "collective_communications_library", "primary": true },
105 | { "suiteId": "oneAPIKit", "componentId": "vpl", "primary": true },
106 |
107 | { "suiteId": "RenderKit", "componentId": "intel_embree", "primary": true },
108 | { "suiteId": "RenderKit", "componentId": "intel_ospray", "primary": true },
109 | { "suiteId": "RenderKit", "componentId": "open_image_denoise", "primary": true },
110 | { "suiteId": "RenderKit", "componentId": "open_volume_kernel_library", "primary": true },
111 |
112 | { "suiteId": "VTuneProfiler", "componentId": "intel_vtune_amplifier", "primary": true }
113 | ]
114 | `
115 |
116 | const suitesJSON = `
117 | [
118 | { "id": "HPCKit", "label": "Intel® oneAPI HPC Toolkit", "urlSlug": "hpc-kit", "baseToolkit": "dependency" },
119 | { "id": "IOTKit", "label": "Intel® oneAPI IoT Toolkit", "urlSlug": "iot-kit", "baseToolkit": "dependency" },
120 | { "id": "BringupKit", "label": "Intel® System Bring-Up Toolkit", "urlSlug": "bringup-kit", "baseToolkit": "recommended" },
121 | { "id": "DLDevKit", "label": "Intel® oneAPI DL Framework Developer Toolkit", "urlSlug": "dldev-kit", "baseToolkit": "dependency" },
122 | { "id": "AIKit", "label": "Intel® oneAPI AI Analytics Toolkit", "urlSlug": "ai-kit", "baseToolkit": "recommended" },
123 | { "id": "oneAPIKit", "label": "Intel® oneAPI Base Toolkit", "urlSlug": "oneapi-kit" },
124 | { "id": "RenderKit", "label": "Intel® oneAPI Rendering Toolkit", "urlSlug": "render-kit", "baseToolkit": "recommended" },
125 | { "id": "VTuneProfiler", "label": "Intel® VTune™ Profiler", "urlSlug": "vtune-profiler", "baseToolkit": "recommended" }
126 | ]
127 | `
128 |
--------------------------------------------------------------------------------
/pkg/extractor/tar.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Intel Corporation
2 | // SPDX-License-Identifier: BSD-3-Clause
3 | package extractor
4 |
5 | import (
6 | "archive/tar"
7 | "compress/gzip"
8 | "io"
9 | "os"
10 | "path/filepath"
11 | )
12 |
13 | // ExtractTarGz extracts a tar.gz to the destination
14 | func ExtractTarGz(sourcetb string, out string) error {
15 |
16 | //Ensure Output exists
17 | if err := os.MkdirAll(out, 0750); err != nil {
18 | return err
19 | }
20 |
21 | tbz, err := os.Open(sourcetb)
22 | if err != nil {
23 | return err
24 | }
25 |
26 | gzr, err := gzip.NewReader(tbz)
27 | if err != nil {
28 | return err
29 | }
30 | defer gzr.Close()
31 |
32 | tr := tar.NewReader(gzr)
33 |
34 | for {
35 | hdr, err := tr.Next()
36 |
37 | switch {
38 |
39 | case err == io.EOF:
40 | return nil // return when no more files, good path
41 |
42 | case err != nil:
43 | return err
44 | }
45 |
46 | // the target location where the dir/file should be created
47 | target := filepath.Join(out, hdr.Name)
48 |
49 | // check the file type, are we a directory for example
50 | switch hdr.Typeflag {
51 |
52 | case tar.TypeDir:
53 | if err := os.MkdirAll(target, os.FileMode(hdr.Mode)); err != nil {
54 | return err
55 | }
56 |
57 | // we have a file, create it with the stored attr from the header
58 | case tar.TypeReg:
59 | //Sometimes the file can come before its directory listing, or it never has one :S
60 | if !fileExists(filepath.Dir(target)) {
61 | if err := os.MkdirAll(filepath.Dir(target), 0750); err != nil {
62 | return err
63 | }
64 | }
65 |
66 | f, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR, os.FileMode(hdr.Mode))
67 | if err != nil {
68 | return err
69 | }
70 |
71 | // Store into destination
72 | if _, err := io.Copy(f, tr); err != nil {
73 | return err
74 | }
75 |
76 | err = f.Close()
77 | if err != nil {
78 | return err
79 | }
80 | }
81 | }
82 | }
83 |
84 | func fileExists(path string) bool {
85 | if _, err := os.Stat(path); os.IsNotExist(err) {
86 | return false
87 | }
88 | return true
89 | }
90 |
--------------------------------------------------------------------------------
/pkg/extractor/tar_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Intel Corporation
2 | // SPDX-License-Identifier: BSD-3-Clause
3 | package extractor
4 |
5 | import (
6 | "crypto/sha512"
7 | "encoding/hex"
8 | "io/ioutil"
9 | "os"
10 | "path/filepath"
11 | "testing"
12 | )
13 |
14 | //testdata/golden.tar.gz contents
15 | const testalpha = "testalpha.txt"
16 | const testbeta = "testbeta.txt"
17 | const testdir = "dirtest"
18 | const testalphaSum = "10b8eefa145e6f3ff612197247765c0fa15788874b2f483879c8528383489d97f4ac18daed45334341d55342c94602976b401c88e879a9d8fd950c69040e29e2"
19 | const testbetaSum = "e73842277cc6739947522cc2cac9dc150524d2d6683fe54b79da6a098a2cffc8a9498583c2f17d7897db36fa1980d6d0e729f4989780e0be38ef3684210c5d99"
20 |
21 | func setupGoldTemp(t *testing.T) string {
22 | t.Helper()
23 | dir, err := ioutil.TempDir("", "extar")
24 | if err != nil {
25 | t.Error(err)
26 | }
27 | return dir
28 | }
29 |
30 | func cleanupTemp(t *testing.T, path string) {
31 | t.Helper()
32 | os.RemoveAll(path)
33 | }
34 |
35 | //localhash returns hash, error
36 | func localHash(t *testing.T, path string) string {
37 | t.Helper()
38 | hasher := sha512.New()
39 | local, err := ioutil.ReadFile(path)
40 | if err != nil {
41 | t.Error(err)
42 | }
43 |
44 | _, err = hasher.Write(local)
45 | if err != nil {
46 | t.Error(err)
47 | }
48 |
49 | return hex.EncodeToString(hasher.Sum(nil))
50 | }
51 |
52 | func TestExtractTarGz(t *testing.T) {
53 |
54 | golden := filepath.Join("testdata", "golden.tar.gz")
55 |
56 | tempPath := setupGoldTemp(t)
57 | defer cleanupTemp(t, tempPath)
58 | output := filepath.Join(tempPath, "gold")
59 |
60 | err := ExtractTarGz(golden, output)
61 | if err != nil {
62 | t.Error(err) // Failed to run against golden tar
63 | }
64 |
65 | //test the output of alpha was good
66 | extractedAlphaSum := localHash(t, filepath.Join(output, testalpha))
67 | if extractedAlphaSum != testalphaSum {
68 | t.Errorf("outputted alpha file does not match expected output sha512 "+
69 | "golden: %s and we just read: %s", testalphaSum, extractedAlphaSum)
70 | }
71 |
72 | betaCombinedPath := filepath.Join(output, testdir, testbeta)
73 | //test the output of beta was good, this also tests the directory was made!
74 | extractedBetaSum := localHash(t, betaCombinedPath)
75 | if extractedBetaSum != testbetaSum {
76 | t.Errorf("outputted beta file does not match expected output sha512 "+
77 | "golden: %s and we just read: %s", testbetaSum, extractedBetaSum)
78 | }
79 |
80 | }
81 |
82 | func TestOKTarGz(t *testing.T) {
83 |
84 | oktar := filepath.Join("testdata", "ok.tar.gz")
85 |
86 | tempPath := setupGoldTemp(t)
87 | defer cleanupTemp(t, tempPath)
88 | output := filepath.Join(tempPath, "ok")
89 |
90 | err := ExtractTarGz(oktar, output)
91 | if err != nil {
92 | t.Error(err) // Failed to run against ok-ish tar
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/pkg/extractor/testdata/README.md:
--------------------------------------------------------------------------------
1 | golden.tar.gz - A Clean simple TarGZ
2 | ok.tar.gz - A tarball with a non ordered odd header order.
3 |
--------------------------------------------------------------------------------
/pkg/extractor/testdata/golden.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/intel/oneapi-cli/a4af422d6ae50511a98cc3900da792a2b5afdb5f/pkg/extractor/testdata/golden.tar.gz
--------------------------------------------------------------------------------
/pkg/extractor/testdata/ok.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/intel/oneapi-cli/a4af422d6ae50511a98cc3900da792a2b5afdb5f/pkg/extractor/testdata/ok.tar.gz
--------------------------------------------------------------------------------
/pkg/ui/cli.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Intel Corporation
2 | // SPDX-License-Identifier: BSD-3-Clause
3 |
4 | package ui
5 |
6 | import (
7 | "fmt"
8 | "io/ioutil"
9 | "log"
10 | "os"
11 | "path/filepath"
12 | "sort"
13 | "strings"
14 |
15 | "github.com/gdamore/tcell"
16 | "github.com/intel/oneapi-cli/pkg/aggregator"
17 | "github.com/intel/oneapi-cli/pkg/browser"
18 | "github.com/intel/oneapi-cli/pkg/deps"
19 | "github.com/intel/oneapi-cli/pkg/extractor"
20 | "gitlab.com/tslocum/cview"
21 | )
22 |
23 | const depsMissingEnvFmt = `The sample you have chosen requires the following dependencies: %s
24 |
25 | Unfortunately, we are unable to determine if they are present.
26 | Did you use setvars to configure your environment? Are you using a container build environment?`
27 |
28 | // CLI data
29 | type CLI struct {
30 | sidebar *cview.TextView
31 | app *cview.Application
32 | aggregator *aggregator.Aggregator
33 | userHome string
34 | oneAPIRoot string
35 | home *cview.List
36 | langSelect *cview.List
37 | }
38 |
39 | const idzURL = "https://www.intel.com/content/www/us/en/developer/tools/oneapi/overview"
40 |
41 | func optionViewDocsInBrowser(url string) {
42 | //If it cant open it for some reason, it will silently fail
43 | browser.OpenBrowser(url)
44 | }
45 |
46 | // NewCLI create a new *CLI element for showing the CLI
47 | func NewCLI(a *aggregator.Aggregator, uH string) (cli *CLI, err error) {
48 | if a == nil {
49 | return nil, fmt.Errorf("Aggregator passed not valid")
50 | }
51 | if uH == "" {
52 | return nil, fmt.Errorf("User Home not passed")
53 | }
54 |
55 | oneRootPath, err := deps.GetOneAPIRoot()
56 | if err != nil {
57 | log.Printf("Could not find oneAPI environment, will not check for missing dependencies")
58 | }
59 | app := cview.NewApplication()
60 |
61 | if app == nil {
62 | return nil, fmt.Errorf("Failed to create backend application")
63 | }
64 | return &CLI{app: cview.NewApplication(), aggregator: a, userHome: uH, oneAPIRoot: oneRootPath}, nil
65 | }
66 |
67 | // Show displays the UI
68 | func (cli *CLI) Show() {
69 |
70 | list := cview.NewList().
71 | AddItem("Create a project", "", '1', func() {
72 | cli.selectLang()
73 |
74 | }).ShowSecondaryText(false).
75 | AddItem("View oneAPI docs in browser", "", '2', func() {
76 | cli.gotoLinkModel()
77 | }).
78 | AddItem("Quit", "Press to exit", 'q', func() {
79 | cli.app.Stop()
80 | })
81 |
82 | list.SetBorder(true)
83 |
84 | cli.home = list
85 |
86 | //Main Run action!
87 | if err := cli.app.SetRoot(cli.home, true).Run(); err != nil {
88 | log.Fatal(err)
89 | }
90 | }
91 |
92 | func (cli *CLI) gotoLinkModel() {
93 |
94 | optionViewDocsInBrowser(idzURL)
95 | modal := cview.NewModal().
96 | SetText("View oneAPI docs:\n" + idzURL + "\n").
97 | AddButtons([]string{"Back"}).
98 | SetDoneFunc(func(buttonIndex int, buttonLabel string) {
99 | cli.app.SetRoot(cli.home, true)
100 | })
101 |
102 | cli.app.SetRoot(modal, true)
103 | }
104 |
105 | func (cli *CLI) goBackPrj() {
106 | if cli.langSelect != nil {
107 | cli.app.SetRoot(cli.langSelect, true)
108 | return
109 | }
110 | cli.app.SetRoot(cli.home, true)
111 | }
112 |
113 | func (cli *CLI) selectLang() {
114 | if len(cli.aggregator.GetLanguages()) == 1 {
115 | cli.selectProject(cli.aggregator.GetLanguages()[0])
116 | return
117 | }
118 |
119 | list := cview.NewList().ShowSecondaryText(false)
120 |
121 | var start rune
122 | start = '1'
123 |
124 | for _, k := range cli.aggregator.GetLanguages() {
125 | if len(cli.aggregator.Samples[k]) == 0 {
126 | continue
127 | }
128 | list.AddItem(k, "", start, func() {
129 | lang, _ := list.GetItemText(list.GetCurrentItem())
130 | cli.selectProject(lang)
131 | })
132 | start++
133 | }
134 | list.AddItem("Back", "", 'b', func() {
135 | cli.app.SetRoot(cli.home, true)
136 | })
137 | list.AddItem("Quit", "Press to exit", 'q', func() {
138 | cli.app.Stop()
139 | })
140 | list.SetBorder(true).SetTitle("Select sample language")
141 |
142 | cli.langSelect = list
143 |
144 | cli.app.SetRoot(cli.langSelect, true)
145 | }
146 |
147 | func (cli *CLI) selectProject(language string) {
148 | cli.sidebar = cview.NewTextView().SetWordWrap(true).
149 | SetChangedFunc(func() {
150 | cli.app.Draw()
151 | })
152 | cli.sidebar.SetDynamicColors(true)
153 |
154 | cli.sidebar.Box.SetBorder(true).SetTitle("Description")
155 |
156 | inst := cview.NewTextView()
157 | inst.SetBorder(true)
158 | inst.SetText("Press Backspace to return to previous screen!")
159 |
160 | flex := cview.NewFlex().
161 | AddItem(cli.tree(language), 0, 1, true).
162 | AddItem(cview.NewFlex().SetDirection(cview.FlexRow).
163 | AddItem(cli.sidebar, 0, 9, false).
164 | AddItem(inst, 3, 0, false), 0, 1, false)
165 |
166 | cli.app.SetRoot(flex, true)
167 | }
168 |
169 | func newSampleNode(s aggregator.Sample) *cview.TreeNode {
170 | node := cview.NewTreeNode(s.Fields.Name).SetSelectable(true)
171 | node.SetReference(s)
172 | return node
173 | }
174 |
175 | // This take a sample and a category to add it to. TODO revist this
176 | func categoriesSeach(parent *cview.TreeNode, cats []string, s aggregator.Sample) {
177 | if len(cats) == 0 {
178 | parent.AddChild(newSampleNode(s))
179 | return
180 | }
181 | for _, csearch := range parent.GetChildren() {
182 | if csearch.GetText() == cats[0] {
183 | if len(cats) == 1 {
184 | csearch.AddChild(newSampleNode(s))
185 | return
186 | //continue
187 | }
188 | categoriesSeach(csearch, cats[1:], s) //recurse removing current from array-pop
189 | return
190 | }
191 | }
192 | if len(cats) > 0 {
193 | csearch := cview.NewTreeNode(cats[0]).SetColor(tcell.ColorOrange)
194 | parent.AddChild(csearch)
195 | categoriesSeach(csearch, cats[1:], s)
196 | return
197 | }
198 | }
199 |
200 | type byNodeText []*cview.TreeNode
201 |
202 | func (s byNodeText) Len() int {
203 | return len(s)
204 | }
205 | func (s byNodeText) Swap(i, j int) {
206 | s[i], s[j] = s[j], s[i]
207 | }
208 | func (s byNodeText) Less(i, j int) bool {
209 | return strings.ToLower(s[i].GetText()) < strings.ToLower(s[j].GetText())
210 | }
211 |
212 | func (cli *CLI) tree(language string) cview.Primitive {
213 |
214 | root := cview.NewTreeNode("Samples").
215 | SetColor(tcell.ColorOrange)
216 | tree := cview.NewTreeView().
217 | SetRoot(root).
218 | SetCurrentNode(root)
219 |
220 | var missingCat []*cview.TreeNode
221 |
222 | for _, s := range cli.aggregator.Samples[language] {
223 | if len(s.Fields.Categories) == 0 {
224 | missingCat = append(missingCat, newSampleNode(s))
225 | continue //skip samples without any categories
226 | }
227 |
228 | //root.AddChild(node)
229 | for _, c := range s.Fields.Categories {
230 | cats := strings.Split(c, "/")
231 | categoriesSeach(root, cats, s)
232 | }
233 |
234 | }
235 |
236 | if len(missingCat) > 0 {
237 | other := cview.NewTreeNode("Other").SetColor(tcell.ColorOrange)
238 | root.AddChild(other)
239 | for _, missingNode := range missingCat {
240 | other.AddChild(missingNode)
241 | }
242 | }
243 |
244 | root.Walk(func(node *cview.TreeNode, parent *cview.TreeNode) bool {
245 | if len(node.GetChildren()) > 1 {
246 | sort.Sort(byNodeText(node.GetChildren()))
247 | }
248 | return true
249 | })
250 |
251 | tree.SetChangedFunc(func(node *cview.TreeNode) {
252 | reference := node.GetReference()
253 | a, ok := reference.(aggregator.Sample)
254 | if !ok {
255 |
256 | cli.sidebar.Clear()
257 | return
258 | }
259 | var sideTextExtra string
260 | if len(a.Fields.Dependencies) > 0 {
261 | if cli.oneAPIRoot == "" {
262 | sideTextExtra = fmt.Sprintf(depsMissingEnvFmt, a.Fields.Dependencies)
263 | } else {
264 | sideTextExtra, _ = deps.CheckDeps(a.Fields.Dependencies, cli.oneAPIRoot)
265 | }
266 | }
267 | sideTextExtra = cview.Escape(sideTextExtra)
268 |
269 | newText := fmt.Sprintf("%s\n\n[red]%s", a.Fields.Description, sideTextExtra)
270 | cli.sidebar.SetText(newText)
271 |
272 | }).SetSelectedFunc(func(node *cview.TreeNode) {
273 | reference := node.GetReference()
274 | a, ok := reference.(aggregator.Sample)
275 | if !ok {
276 | return
277 | }
278 | cli.askPath(a, language, "")
279 | })
280 | tree.Box.SetBorder(true).SetTitle("Samples")
281 | tree.SetTopLevel(1)
282 |
283 | tree.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
284 |
285 | if event.Key() == tcell.KeyBackspace2 ||
286 | event.Key() == tcell.KeyBackspace {
287 | cli.goBackPrj()
288 | }
289 |
290 | return event
291 |
292 | })
293 |
294 | return tree
295 | }
296 |
297 | func isPathEmpty(path string) bool {
298 | if !aggregator.FileExists(path) {
299 | return true
300 | }
301 | files, err := ioutil.ReadDir(path)
302 | if err != nil {
303 | return true
304 | }
305 | return (len(files) == 0)
306 | }
307 |
308 | func (cli *CLI) askPath(sample aggregator.Sample, language string, path string) {
309 | if path == "" {
310 | pwd, err := os.Getwd()
311 | if err != nil {
312 | log.Println(err)
313 | }
314 | path = filepath.Join(pwd, filepath.Base(sample.Path))
315 | }
316 |
317 | form := cview.NewForm().
318 | AddInputField("Destination", path, 55, nil, func(t string) {
319 | path = t
320 | }).
321 | AddButton("Create", func() {
322 | path, err := cli.calcPath(path)
323 | if err != nil {
324 | return
325 | }
326 | if !isPathEmpty(path) {
327 | cli.confirmOverwrite(sample, language, path)
328 | return
329 | }
330 | outPath, err := cli.createProject(sample, language, path)
331 |
332 | if err != nil {
333 | cli.app.Stop()
334 | log.Fatal(err)
335 | }
336 | cli.successModal(outPath)
337 |
338 | }).AddButton("Back", func() {
339 | cli.selectProject(language)
340 | })
341 |
342 | form.SetBorder(true).SetTitle("Create Project").SetTitleAlign(cview.AlignLeft)
343 | form.SetWrapAround(true)
344 |
345 | cli.app.SetRoot(form, true)
346 | }
347 |
348 | func (cli *CLI) confirmOverwrite(sample aggregator.Sample, language string, path string) {
349 |
350 | text := fmt.Sprintf("Path %s is not empty, Creating the sample may overwrite some files.", path)
351 | buttons := []string{"Back", "Confirm"}
352 |
353 | modal := cview.NewModal().
354 | SetText(text).
355 | AddButtons(buttons).
356 | SetDoneFunc(func(buttonIndex int, buttonLabel string) {
357 |
358 | if buttonLabel != "Back" {
359 | outPath, err := cli.createProject(sample, language, path)
360 |
361 | if err != nil {
362 | cli.app.Stop()
363 | log.Fatal(err)
364 | }
365 | cli.successModal(outPath)
366 | return
367 | }
368 | cli.askPath(sample, language, path)
369 | })
370 | cli.app.SetRoot(modal, true)
371 |
372 | }
373 |
374 | func (cli *CLI) successModal(path string) {
375 |
376 | text := fmt.Sprintf("Sucessfully created project in %s", path)
377 | buttons := []string{"Quit"}
378 | printReadmeText := "View Readme and Quit"
379 |
380 | //check for readme,
381 | readme, err := ioutil.ReadFile(filepath.Join(path, "README.md"))
382 | if err == nil {
383 | buttons = append(buttons, printReadmeText)
384 | } else {
385 | readme, err = ioutil.ReadFile(filepath.Join(path, "readme.md"))
386 | if err == nil {
387 | buttons = append(buttons, printReadmeText)
388 | }
389 | }
390 |
391 | modal := cview.NewModal().
392 | SetText(text).
393 | AddButtons(buttons).
394 | SetDoneFunc(func(buttonIndex int, buttonLabel string) {
395 | cli.app.Stop()
396 | if buttonLabel != "Quit" {
397 |
398 | //The folloiwing is to reset the terminal
399 | s, e := tcell.NewScreen()
400 | if e != nil {
401 | fmt.Fprintf(os.Stderr, "%v\n", e)
402 | os.Exit(1)
403 | }
404 | if e = s.Init(); e != nil {
405 | fmt.Fprintf(os.Stderr, "%v\n", e)
406 | os.Exit(1)
407 | }
408 | s.Clear()
409 | s.Fini()
410 | fmt.Printf("\n")
411 | fmt.Printf("%s\n", string(readme))
412 | }
413 | })
414 | cli.app.SetRoot(modal, true)
415 | }
416 |
417 | func (cli *CLI) calcPath(projectPath string) (string, error) {
418 | //Expand env vars the user might have passed through.
419 | projectPath = os.ExpandEnv(projectPath)
420 | //Check if tilda ~ is being used, then use the home dir
421 | if len(projectPath) > 0 && projectPath[0] == '~' {
422 | //userHome comes from the frontend cli but check the HOME is not empty just incase
423 | projectPath = filepath.Join(cli.userHome, projectPath[1:]) //Prepend home path and trim tilda
424 | }
425 |
426 | projectPath, err := filepath.Abs(projectPath)
427 | if err != nil {
428 | return "", err
429 | }
430 | return projectPath, nil
431 | }
432 |
433 | func (cli *CLI) createProject(selectedSample aggregator.Sample, lang string, projectPath string) (output string, err error) {
434 | //Maybe here we might check if the tarball does not exists and the trigger the aggregator to atempt an update
435 |
436 | tarPath, err := aggregator.GetTarBall(cli.aggregator.GetLocalPath(), cli.aggregator.GetURL(), lang, selectedSample.Path)
437 | if err != nil {
438 | return "", err
439 | }
440 |
441 | err = extractor.ExtractTarGz(tarPath, projectPath)
442 | if err != nil {
443 | return "", err
444 | }
445 | return projectPath, nil
446 | }
447 |
--------------------------------------------------------------------------------
/pkg/ui/cli_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Intel Corporation
2 | // SPDX-License-Identifier: BSD-3-Clause
3 | package ui
4 |
5 | import (
6 | "io/ioutil"
7 | "net/http"
8 | "net/http/httptest"
9 | "os"
10 | "path/filepath"
11 | "sync"
12 | "testing"
13 | "time"
14 |
15 | "github.com/gdamore/tcell"
16 | "github.com/intel/oneapi-cli/pkg/aggregator"
17 | )
18 |
19 | func mkTestScreen(t *testing.T, charset string) tcell.SimulationScreen {
20 | t.Helper()
21 | s := tcell.NewSimulationScreen(charset)
22 | if s == nil {
23 | t.Fatalf("Failed to get simulation screen")
24 | }
25 | if e := s.Init(); e != nil {
26 | t.Fatalf("Failed to initialize screen: %v", e)
27 | }
28 | return s
29 | }
30 |
31 | type testNewAggregatorData struct {
32 | cache string
33 | home string
34 | b string
35 | ts *httptest.Server
36 | testLanguages []string
37 | aggregator *aggregator.Aggregator
38 | }
39 |
40 | func setupAggregatorTest(t *testing.T) *testNewAggregatorData {
41 | t.Helper()
42 | var td testNewAggregatorData
43 |
44 | //Get Cache to use
45 | dir, err := ioutil.TempDir("", "example")
46 | if err != nil {
47 | t.Error(err)
48 | }
49 | td.b = dir
50 | td.cache = filepath.Join(dir, "cache")
51 | td.home = filepath.Join(dir, "home")
52 |
53 | // Get a test http server
54 | fs := http.FileServer(http.Dir("testdata"))
55 | ts := httptest.NewServer(fs)
56 | td.ts = ts
57 |
58 | td.testLanguages = []string{"cpp", "python"}
59 |
60 | td.aggregator, err = aggregator.NewAggregator(td.ts.URL, td.cache, td.testLanguages, true, true)
61 | if err != nil {
62 | t.Error(err)
63 | }
64 |
65 | return &td
66 | }
67 |
68 | func (tdata *testNewAggregatorData) cleanup() {
69 | os.RemoveAll(tdata.b)
70 | tdata.ts.Close()
71 | }
72 |
73 | func TestA(t *testing.T) {
74 | td := setupAggregatorTest(t)
75 | defer td.cleanup()
76 |
77 | cli, err := NewCLI(td.aggregator, td.home)
78 | if err != nil {
79 | t.Error(err)
80 | }
81 |
82 | s := mkTestScreen(t, "")
83 | cli.app = cli.app.SetScreen(s)
84 |
85 | var wg sync.WaitGroup
86 | wg.Add(1)
87 | go func() {
88 | defer wg.Done()
89 | cli.Show()
90 | }()
91 |
92 | s.InjectKey(tcell.KeyRune, 'q', tcell.ModNone)
93 |
94 | wg.Wait()
95 | }
96 |
97 | func TestB(t *testing.T) {
98 | td := setupAggregatorTest(t)
99 | defer td.cleanup()
100 | cli, err := NewCLI(td.aggregator, td.home)
101 | if err != nil {
102 | t.Error(err)
103 | }
104 | s := mkTestScreen(t, "")
105 |
106 | cli.app = cli.app.SetScreen(s)
107 |
108 | var wg sync.WaitGroup
109 | wg.Add(1)
110 | go func() {
111 | defer wg.Done()
112 | cli.Show()
113 | }()
114 |
115 | s.InjectKey(tcell.KeyRune, 'q', tcell.ModNone)
116 |
117 | wg.Wait()
118 | }
119 |
120 | func TestFullFlow(t *testing.T) {
121 | td := setupAggregatorTest(t)
122 | defer td.cleanup()
123 | cli, err := NewCLI(td.aggregator, td.home)
124 | if err != nil {
125 | t.Error(err)
126 | }
127 | s := mkTestScreen(t, "")
128 |
129 | cli.app = cli.app.SetScreen(s)
130 |
131 | var wg sync.WaitGroup
132 | wg.Add(1)
133 | go func() {
134 | defer wg.Done()
135 | cli.Show() //Let the CLI run in another routine
136 | }()
137 |
138 | s.InjectKey(tcell.KeyRune, '1', tcell.ModNone) //Main Screen
139 | s.InjectKey(tcell.KeyRune, '1', tcell.ModNone) //Langauge Select
140 | s.InjectKey(tcell.KeyDown, 'd', tcell.ModNone) //Get the zebra
141 | s.InjectKey(tcell.KeyEnter, 'd', tcell.ModNone) //Enter the sample
142 |
143 | ws, err := ioutil.TempDir("", "ws")
144 | if err != nil {
145 | t.Error(err)
146 | }
147 | defer os.RemoveAll(ws)
148 |
149 | //Remove default input with CTRL+U
150 | s.InjectKey(tcell.KeyCtrlU, 'd', tcell.ModNone)
151 |
152 | for i := 0; i < len(ws); i++ {
153 | s.InjectKey(tcell.KeyRune, rune(ws[i]), tcell.ModNone)
154 | time.Sleep(10 * time.Millisecond) //Need to give time to the key presses :/
155 | }
156 |
157 | s.InjectKey(tcell.KeyTAB, 'd', tcell.ModNone) //Tab
158 | s.InjectKey(tcell.KeyEnter, 'd', tcell.ModNone) //Create
159 | s.InjectKey(tcell.KeyEnter, 'd', tcell.ModNone) //Dismiss success dialog
160 |
161 | wg.Wait()
162 |
163 | //Test for known file in "sample"
164 |
165 | knownZebra := filepath.Join(ws, "this-is-a-zebra.md")
166 |
167 | if !fileExists(t, knownZebra) {
168 | t.Errorf("sample creation flow failed! could not find %s", knownZebra)
169 | }
170 |
171 | }
172 |
173 | func fileExists(t *testing.T, path string) bool {
174 | t.Helper()
175 | if _, err := os.Stat(path); os.IsNotExist(err) {
176 | return false
177 | }
178 | return true
179 | }
180 |
--------------------------------------------------------------------------------
/pkg/ui/testdata/cpp.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "path":"zoo",
4 | "sha":"1",
5 | "example":{
6 | "name":"zoo-zebra",
7 | "categories":[
8 | "TestCats"
9 | ],
10 | "description":"It is I, Zebra",
11 | "sample_readme_uri":"https://software.intel"
12 | }
13 | }
14 | ]
--------------------------------------------------------------------------------
/pkg/ui/testdata/python.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "path":"zoo",
4 | "sha":"1",
5 | "example":{
6 | "name":"zoo-python",
7 | "categories":[
8 | "TestCats"
9 | ],
10 | "description":"It is I, Zebra",
11 | "sample_readme_uri":"https://software.intel"
12 | }
13 | }
14 | ]
--------------------------------------------------------------------------------
/pkg/ui/testdata/zoo/cpp.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/intel/oneapi-cli/a4af422d6ae50511a98cc3900da792a2b5afdb5f/pkg/ui/testdata/zoo/cpp.tar.gz
--------------------------------------------------------------------------------
/pkg/ui/testdata/zoo/python.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/intel/oneapi-cli/a4af422d6ae50511a98cc3900da792a2b5afdb5f/pkg/ui/testdata/zoo/python.tar.gz
--------------------------------------------------------------------------------
/test.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | go test -coverpkg=./... -coverprofile=cover.out ./...
3 | go tool cover -func=cover.out
--------------------------------------------------------------------------------
/windowsdata.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/intel/oneapi-cli/a4af422d6ae50511a98cc3900da792a2b5afdb5f/windowsdata.syso
--------------------------------------------------------------------------------
/winres.rc:
--------------------------------------------------------------------------------
1 | #define RT_MANIFEST 24
2 |
3 | 1 VERSIONINFO
4 | FILEFLAGSMASK 0X3FL
5 | FILEFLAGS 0L
6 | FILEOS 0X40004L
7 | FILETYPE 0X1
8 | FILESUBTYPE 0
9 | BEGIN
10 | BLOCK "StringFileInfo"
11 | BEGIN
12 | BLOCK "040904B0"
13 | BEGIN
14 | VALUE "LegalCopyright", "(C) Intel Corporation. Available under BSD-3-Clause"
15 | VALUE "OriginalFilename", "oneapi-cli.exe"
16 | END
17 | END
18 | BLOCK "VarFileInfo"
19 | BEGIN
20 | VALUE "Translation", 0x0409, 0x04B0
21 | END
22 | END
23 |
--------------------------------------------------------------------------------